Merge "Initial IME integration."

This commit is contained in:
TreeHugger Robot
2017-01-25 19:44:29 +00:00
committed by Android (Google) Code Review
17 changed files with 326 additions and 208 deletions

View File

@@ -46210,6 +46210,13 @@ package android.view.autofill {
field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
}
public final class AutoFillManager {
method public void updateAutoFillInput(android.view.View, int);
method public void updateAutoFillInput(android.view.View, int, android.graphics.Rect, int);
field public static final int FLAG_UPDATE_UI_HIDE = 2; // 0x2
field public static final int FLAG_UPDATE_UI_SHOW = 1; // 0x1
}
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forList();
@@ -46269,7 +46276,7 @@ package android.view.autofill {
public static abstract class VirtualViewDelegate.Callback {
ctor public VirtualViewDelegate.Callback();
method public void onFocusChanged(int, boolean);
method public void onAutoFillInputUpdated(int, android.graphics.Rect, int);
method public void onNodeRemoved(int...);
method public void onValueChanged(int);
}

View File

@@ -49595,6 +49595,13 @@ package android.view.autofill {
field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
}
public final class AutoFillManager {
method public void updateAutoFillInput(android.view.View, int);
method public void updateAutoFillInput(android.view.View, int, android.graphics.Rect, int);
field public static final int FLAG_UPDATE_UI_HIDE = 2; // 0x2
field public static final int FLAG_UPDATE_UI_SHOW = 1; // 0x1
}
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forList();
@@ -49654,7 +49661,7 @@ package android.view.autofill {
public static abstract class VirtualViewDelegate.Callback {
ctor public VirtualViewDelegate.Callback();
method public void onFocusChanged(int, boolean);
method public void onAutoFillInputUpdated(int, android.graphics.Rect, int);
method public void onNodeRemoved(int...);
method public void onValueChanged(int);
}

View File

@@ -46520,6 +46520,13 @@ package android.view.autofill {
field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillId> CREATOR;
}
public final class AutoFillManager {
method public void updateAutoFillInput(android.view.View, int);
method public void updateAutoFillInput(android.view.View, int, android.graphics.Rect, int);
field public static final int FLAG_UPDATE_UI_HIDE = 2; // 0x2
field public static final int FLAG_UPDATE_UI_SHOW = 1; // 0x1
}
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forList();
@@ -46579,7 +46586,7 @@ package android.view.autofill {
public static abstract class VirtualViewDelegate.Callback {
ctor public VirtualViewDelegate.Callback();
method public void onFocusChanged(int, boolean);
method public void onAutoFillInputUpdated(int, android.graphics.Rect, int);
method public void onNodeRemoved(int...);
method public void onValueChanged(int);
}

View File

@@ -112,6 +112,7 @@ import android.os.health.SystemHealthManager;
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
import android.service.autofill.IAutoFillManagerService;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
import android.telecom.TelecomManager;
@@ -126,6 +127,7 @@ import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
import android.view.autofill.AutoFillManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -804,6 +806,14 @@ final class SystemServiceRegistry {
IBinder b = ServiceManager.getServiceOrThrow(Context.FONT_SERVICE);
return new FontManager(IFontManager.Stub.asInterface(b));
}});
registerService(Context.AUTO_FILL_MANAGER_SERVICE, AutoFillManager.class,
new CachedServiceFetcher<AutoFillManager>() {
@Override
public AutoFillManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.AUTO_FILL_MANAGER_SERVICE);
IAutoFillManagerService service = IAutoFillManagerService.Stub.asInterface(b);
return new AutoFillManager(ctx, service);
}});
}
/**

View File

@@ -15,8 +15,6 @@
*/
package android.service.autofill;
import static android.service.voice.VoiceInteractionSession.KEY_FLAGS;
import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
@@ -30,13 +28,11 @@ import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.autofill.AutoFillId;
import android.view.autofill.FillResponse;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
// TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the
@@ -49,8 +45,8 @@ import com.android.internal.os.SomeArgs;
*/
public abstract class AutoFillService extends Service {
static final String TAG = "AutoFillService";
static final boolean DEBUG = true; // TODO: set to false once stable
private static final String TAG = "AutoFillService";
static final boolean DEBUG = true; // TODO(b/33197203): set to false once stable
/**
* The {@link Intent} that must be declared as handled by the service.

View File

@@ -21,7 +21,6 @@ import static android.service.autofill.AutoFillService.DEBUG;
import android.annotation.Nullable;
import android.app.Activity;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.autofill.FillResponse;

View File

@@ -16,7 +16,9 @@
package android.service.autofill;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.autofill.AutoFillId;
/**
* Mediator between apps being auto-filled and auto-fill service implementations.
@@ -24,5 +26,9 @@ import android.os.Bundle;
* {@hide}
*/
oneway interface IAutoFillManagerService {
void showAutoFillInput(in AutoFillId id, in Rect boundaries);
// TODO(b/33197203): remove it and refactor onShellCommand
void requestAutoFill(IBinder activityToken, int userId, in Bundle extras, int flags);
}

View File

@@ -21,7 +21,6 @@ import static android.service.autofill.AutoFillService.DEBUG;
import android.app.Activity;
import android.app.assist.AssistStructure.ViewNode;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.autofill.AutoFillId;

View File

@@ -1765,6 +1765,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
int mAccessibilityViewId = NO_ID;
/**
* The stable ID of this view for auto-fill purposes.
*/
private int mAutoFillId = NO_ID;
private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
SendViewStateChangedAccessibilityEvent mSendViewStateChangedAccessibilityEvent;
@@ -6932,8 +6938,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (forAutoFill) {
// The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
// reuse the accessibility id to save space.
structure.setAutoFillId(getAccessibilityViewId());
mAutoFillId = getAccessibilityViewId();
structure.setAutoFillId(mAutoFillId);
structure.setAutoFillType(getAutoFillType());
}
@@ -7063,7 +7069,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
* Describes the auto-fill type that should be used on callas to
* Describes the auto-fill type that should be used on calls to
* {@link #autoFill(AutoFillValue)} and
* {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}.
*
@@ -7559,6 +7565,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return mAccessibilityViewId;
}
/**
* Gets the unique identifier of this view for auto-fill purposes.
*
* <p>It's only set after {@link #onProvideAutoFillStructure(ViewStructure, int)} is called.
*
* @return The view autofill id or {@link #NO_ID} if
* {@link #onProvideAutoFillStructure(ViewStructure, int)} was not called yet.
*
* @hide
*/
public int getAutoFillViewId() {
return mAutoFillId;
}
/**
* Gets the unique identifier of the window in which this View reseides.
*

View File

@@ -42,6 +42,13 @@ public final class AutoFillId implements Parcelable {
mVirtualId = virtualChildId;
}
/** @hide */
public AutoFillId(int parentId, int virtualChildId) {
mVirtual = true;
mViewId = parentId;
mVirtualId = virtualChildId;
}
/** @hide */
public int getViewId() {
return mViewId;

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view.autofill;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteException;
import android.service.autofill.IAutoFillManagerService;
import android.util.Log;
import android.view.View;
/**
* App entry point to the AutoFill Framework.
*/
// TODO(b/33197203): improve this javadoc
public final class AutoFillManager {
private static final String TAG = "AutoFillManager";
private static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
/**
* Flag used to show the auto-fill UI affordance for a view.
*/
public static final int FLAG_UPDATE_UI_SHOW = 1 << 0;
/**
* Flag used to hide the auto-fill UI affordance for a view.
*/
public static final int FLAG_UPDATE_UI_HIDE = 1 << 1;
private final IAutoFillManagerService mService;
/**
* @hide
*/
public AutoFillManager(@SuppressWarnings("unused") Context context,
IAutoFillManagerService service) {
mService = service;
}
/**
* Updates the auto-fill bar for a given {@link View}.
*
* <b>Typically called twice, with different flags ({@link #FLAG_UPDATE_UI_SHOW} and
* {@link #FLAG_UPDATE_UI_HIDE} respectively), as the user "entered" and "exited" a view.
*
* @param view view to be updated.
* @param flags either {@link #FLAG_UPDATE_UI_SHOW} or
* {@link #FLAG_UPDATE_UI_HIDE}.
*/
public void updateAutoFillInput(View view, int flags) {
if (DEBUG) {
Log.v(TAG, "updateAutoFillInput(" + view.getAutoFillViewId() + "): flags=" + flags);
}
updateAutoFillInput(view, false, View.NO_ID, null, flags);
}
/**
* Updates the auto-fill bar for a virtual child of a given {@link View}.
*
* <b>Typically called twice, with different flags ({@link #FLAG_UPDATE_UI_SHOW} and
* {@link #FLAG_UPDATE_UI_HIDE} respectively), as the user "entered" and "exited" a view.
*
* @param parent parent view.
* @param childId id identifying the virtual child inside the parent view.
* @param boundaries boundaries of the child (inside the parent; could be {@code null} when
* flag is {@link #FLAG_UPDATE_UI_HIDE}.
* @param flags either {@link #FLAG_UPDATE_UI_SHOW} or
* {@link #FLAG_UPDATE_UI_HIDE}.
*/
public void updateAutoFillInput(View parent, int childId, @Nullable Rect boundaries,
int flags) {
if (DEBUG) {
Log.v(TAG, "updateAutoFillInput(" + parent.getAutoFillViewId() + ", " + childId
+ "): boundaries=" + boundaries + ", flags=" + flags);
}
updateAutoFillInput(parent, true, childId, boundaries, flags);
}
private void updateAutoFillInput(View view, boolean virtual, int childId, Rect boundaries,
int flags) {
if ((flags & FLAG_UPDATE_UI_SHOW) != 0) {
final int viewId = view.getAutoFillViewId();
final AutoFillId id = virtual
? new AutoFillId(viewId, childId)
: new AutoFillId(viewId);
showAutoFillInput(id, boundaries);
return;
}
// TODO(b/33197203): handle FLAG_UPDATE_UI_HIDE
}
private void showAutoFillInput(AutoFillId id, Rect boundaries) {
final int autoFillViewId = id.getViewId();
/*
* TODO(b/33197203): currently SHOW_AUTO_FILL_BAR is only set once per activity (i.e, when
* the view does not have an auto-fill id), but it should be called again for views that
* were not part of the initial auto-fill dataset returned by the service. For example:
*
* 1.Activity has 4 fields, `first_name`, `last_name`, and `address`.
* 2.User taps `first_name`.
* 3.Service returns a dataset with ids for `first_name` and `last_name`.
* 4.When user taps `first_name` (again) or `last_name`, flag should not have
* SHOW_AUTO_FILL_BAR set, but when user taps `address`, it should (since that field was
* not part of the initial dataset).
*
* Similarly, once the activity is auto-filled, the flag logic should be reset (so if the
* user taps the view again, a new auto-fill request is made)
*/
if (autoFillViewId != View.NO_ID) {
return;
}
try {
mService.showAutoFillInput(id, boundaries);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -15,6 +15,8 @@
*/
package android.view.autofill;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewStructure;
@@ -71,20 +73,18 @@ public abstract class VirtualViewDelegate {
public abstract static class Callback {
/**
* Sent when the focus inside the hierarchy changed.
* Sent when the auto-fill bar for a child must be updated.
*
* <p>Typically callled twice - for the nodes that lost and gained focus.
*
* <p>This method should only be called when the change was not caused by the AutoFill
* Framework itselft (i.e, through {@link VirtualViewDelegate#autoFill(int, AutoFillValue)},
* but by external causes (for example, when the user changed the value through the view's
* UI).
*
* @param virtualId id of the node whose focus changed.
* @param hasFocus {@code true} when focus was gained, {@code false} when it was lost.
* See {@link AutoFillManager#updateAutoFillInput(View, int, android.graphics.Rect, int)}
* for more details.
*/
public void onFocusChanged(int virtualId, boolean hasFocus) {
if (DEBUG) Log.d(TAG, "onFocusChanged() for " + virtualId + ": " + hasFocus);
// TODO(b/33197203): do we really need it, or should the parent view just call
// AutoFillManager.updateAutoFillInput() directly?
public void onAutoFillInputUpdated(int virtualId, @Nullable Rect boundaries, int flags) {
if (DEBUG) {
Log.v(TAG, "onAutoFillInputUpdated(): virtualId=" + virtualId + ", boundaries="
+ boundaries + ", flags=" + flags);
}
}
/**

View File

@@ -169,8 +169,6 @@ public class EditText extends TextView {
Log.w(VIEW_LOG_TAG, "EditText.autoFill(): no text on AutoFillValue");
return;
}
// TODO(b/33197203): once auto-fill is triggered by the IME, we'll need a new setText()
// or setAutoFillText() method on TextView to avoid re-triggering it.
setText(text);
}

View File

@@ -136,6 +136,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutoFillManager;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -264,6 +265,7 @@ import java.util.Locale;
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
static final String LOG_TAG = "TextView";
static final boolean DEBUG_EXTRACT = false;
static final boolean DEBUG_AUTOFILL = false;
// Enum for the "typeface" XML parameter.
// TODO: How can we get this from the XML instead of hardcoding it here?
@@ -9024,6 +9026,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Spannable sp = (Spannable) mText;
MetaKeyKeyListener.resetMetaState(sp);
}
} else {
final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
if (afm != null) {
if (DEBUG_AUTOFILL) {
Log.v(LOG_TAG, "onFocusChanged(): id=" + getAutoFillViewId() + ", focused= "
+ focused);
}
afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_HIDE);
}
}
startStopMarquee(focused);
@@ -10602,6 +10613,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @hide
*/
protected void viewClicked(InputMethodManager imm) {
final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
if (afm != null) {
if (DEBUG_AUTOFILL) Log.v(LOG_TAG, "viewClicked(): id=" + getAutoFillViewId());
// TODO(b/33197203): integrate with onFocus and/or move to view?
afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_SHOW);
}
if (imm != null) {
imm.viewClicked(this);
}

View File

@@ -19,10 +19,6 @@ package com.android.server.autofill;
import static android.Manifest.permission.MANAGE_AUTO_FILL;
import static android.content.Context.AUTO_FILL_MANAGER_SERVICE;
import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
import static com.android.server.autofill.AutoFillUI.MSG_SHOW_ALL_NOTIFICATIONS;
import static com.android.server.autofill.AutoFillUI.SHOW_ALL_NOTIFICATIONS_DELAY_MS;
import android.Manifest;
import android.app.AppGlobals;
@@ -32,11 +28,13 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -51,9 +49,12 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.autofill.AutoFillId;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.server.FgThread;
import com.android.server.SystemService;
@@ -70,13 +71,12 @@ import java.io.PrintWriter;
public final class AutoFillManagerService extends SystemService {
private static final String TAG = "AutoFillManagerService";
static final boolean DEBUG = true; // TODO: change to false once stable
static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
private static final long SERVICE_BINDING_LIFETIME_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
private static final int ARG_NOT_USED = 0;
protected static final int MSG_UNBIND = 1;
protected static final int MSG_SHOW_AUTO_FILL = 2;
private final AutoFillManagerServiceStub mServiceStub;
private final AutoFillUI mUi;
@@ -85,23 +85,28 @@ public final class AutoFillManagerService extends SystemService {
private final Object mLock = new Object();
private final Handler mHandler = new Handler() {
private final HandlerCaller.Callback mHandlerCallback = new HandlerCaller.Callback() {
@Override
public void handleMessage(Message msg) {
public void executeMessage(Message msg) {
switch (msg.what) {
case MSG_UNBIND:
case MSG_UNBIND: {
removeStaleServiceForUser(msg.arg1);
return;
case MSG_SHOW_ALL_NOTIFICATIONS:
mUi.showAllNotifications();
} case MSG_SHOW_AUTO_FILL: {
final SomeArgs args = (SomeArgs) msg.obj;
showAutoFillInput(msg.arg1, (AutoFillId) args.arg1, (Rect) args.arg2);
return;
default:
} default: {
Slog.w(TAG, "Invalid message: " + msg);
}
}
}
};
private HandlerCaller mHandlerCaller;
/**
* Cache of {@link AutoFillManagerServiceImpl} per user id.
* <p>
@@ -122,6 +127,8 @@ public final class AutoFillManagerService extends SystemService {
public AutoFillManagerService(Context context) {
super(context);
mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
mContext = context;
mUi = new AutoFillUI(context, this, mLock);
mResolver = context.getContentResolver();
@@ -139,14 +146,6 @@ public final class AutoFillManagerService extends SystemService {
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
new SettingsObserver(BackgroundThread.getHandler());
}
if (phase == PHASE_BOOT_COMPLETED) {
// TODO: if sent right away, the notification is not displayed. Since the notification
// mechanism is a temporary approach anyways, just delay it..
if (DEBUG)
Slog.d(TAG, "Showing notifications in " + SHOW_ALL_NOTIFICATIONS_DELAY_MS + "ms");
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ALL_NOTIFICATIONS),
SHOW_ALL_NOTIFICATIONS_DELAY_MS);
}
}
private AutoFillManagerServiceImpl newServiceForUser(int userId) {
@@ -200,7 +199,7 @@ public final class AutoFillManagerService extends SystemService {
}
// Keep service connection alive for a while, in case user needs to interact with it
// (for example, to save the data that was inputted in)
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UNBIND, userId, ARG_NOT_USED),
mHandlerCaller.sendMessageDelayed(mHandlerCaller.obtainMessageI(MSG_UNBIND, userId),
SERVICE_BINDING_LIFETIME_MS);
return service;
}
@@ -245,18 +244,41 @@ public final class AutoFillManagerService extends SystemService {
}
private void requestAutoFillLocked(IBinder activityToken, int userId, Bundle extras,
int flags) {
final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
service.requestAutoFill(activityToken, extras, flags);
}
}
private void showAutoFillInput(int userId, AutoFillId id, Rect rect) {
if (DEBUG) Slog.d(TAG, "handler.showAutoFillInput(): id=" + id + ", rect=" + rect);
synchronized (mLock) {
requestAutoFillLocked(null, userId, null, AUTO_FILL_FLAG_TYPE_FILL);
}
}
final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
@Override
public void showAutoFillInput(AutoFillId id, Rect boundaries) {
if (DEBUG) Slog.d(TAG, "showAutoFillInput(): id=" + id + ", boundaries=" + boundaries);
// TODO(b/33197203): fail if it's not called by same uid as the top activity
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW_AUTO_FILL,
UserHandle.getCallingUserId(), id, boundaries));
}
@Override
public void requestAutoFill(IBinder activityToken, int userId, Bundle extras, int flags) {
if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", userId=" + userId);
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
service.requestAutoFill(activityToken, extras, flags);
}
requestAutoFillLocked(activityToken, userId, extras, flags);
}
}
@@ -307,7 +329,6 @@ public final class AutoFillManagerService extends SystemService {
if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
synchronized (mLock) {
removeCachedServiceForUserLocked(userId);
mUi.updateNotification(userId);
}
}
}

View File

@@ -163,7 +163,6 @@ final class AutoFillManagerServiceImpl {
}
final AssistStructure structure = resultData
.getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
final Bundle data = resultData.getBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS);
final int flags = resultData.getInt(VoiceInteractionSession.KEY_FLAGS, 0);
final ServerCallback serverCallback;

View File

@@ -16,40 +16,29 @@
package com.android.server.autofill;
import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
import static com.android.server.autofill.AutoFillManagerService.DEBUG;
import android.app.Activity;
import android.app.AppGlobals;
import android.app.Notification;
import android.app.Notification.Action;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.AutoFillService;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.view.autofill.Dataset;
import android.view.autofill.AutoFillId;
import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.server.UiThread;
import java.util.Arrays;
@@ -69,9 +58,10 @@ final class AutoFillUI {
AutoFillUI(Context context, AutoFillManagerService service, Object lock) {
mContext = context;
mResolver = context.getContentResolver();
mService = service;
mLock = lock;
setNotificationListener();
}
/**
@@ -121,56 +111,60 @@ final class AutoFillUI {
private static final String EXTRA_FILL_RESPONSE = "fill_response";
private static final String EXTRA_DATASET = "dataset";
private static final String TYPE_EMULATE = "emulate";
private static final String TYPE_OPTIONS = "options";
private static final String TYPE_DELETE_CALLBACK = "delete_callback";
private static final String TYPE_PICK_DATASET = "pick_dataset";
private static final String TYPE_SAVE = "save";
static final int MSG_SHOW_ALL_NOTIFICATIONS = 42;
static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000;
@GuardedBy("mLock")
private BroadcastReceiver mNotificationReceiver;
private final ContentResolver mResolver;
@GuardedBy("mLock")
private final AutoFillManagerService mService;
private final Object mLock;
// Hack used to generate unique pending intents
static int sResultCode = 0;
private void setNotificationListener() {
synchronized (mLock) {
if (mNotificationReceiver == null) {
mNotificationReceiver = new NotificationReceiver();
mContext.registerReceiver(mNotificationReceiver,
new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
}
}
}
final class NotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
if (service == null) {
Slog.w(TAG, "no auto-fill service for user " + userId);
return;
}
final int callbackId = intent.getIntExtra(EXTRA_CALLBACK_ID, -1);
final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
if (type == null) {
Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
return;
}
final FillResponse fillData = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
final Bundle datasetArgs = dataset == null ? null : dataset.getExtras();
final Bundle fillDataArgs = fillData == null ? null : fillData.getExtras();
// Bundle sent on AutoFillService methods - only set if service provided a bundle
final Bundle extras = (datasetArgs == null && fillDataArgs == null)
? null : new Bundle();
if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
+ ", callbackId=" + callbackId);
synchronized (mLock) {
final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
if (service == null) {
Slog.w(TAG, "no auto-fill service for user " + userId);
return;
}
final int callbackId = intent.getIntExtra(EXTRA_CALLBACK_ID, -1);
final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
if (type == null) {
Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
return;
}
final FillResponse fillData = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
final Bundle datasetArgs = dataset == null ? null : dataset.getExtras();
final Bundle fillDataArgs = fillData == null ? null : fillData.getExtras();
// Bundle sent on AutoFillService methods - only set if service provided a bundle
final Bundle extras = (datasetArgs == null && fillDataArgs == null)
? null : new Bundle();
if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
+ ", callbackId=" + callbackId);
switch (type) {
case TYPE_EMULATE:
service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_FILL);
break;
case TYPE_SAVE:
if (datasetArgs != null) {
if (DEBUG) Log.d(TAG, "filldata args on save notificataion: " +
@@ -210,54 +204,6 @@ final class AutoFillUI {
}
}
private ComponentName getProviderForUser(int userId) {
ComponentName serviceComponent = null;
ServiceInfo serviceInfo = null;
final String componentName = Settings.Secure.getStringForUser(
mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId);
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
serviceInfo =
AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId);
} catch (RuntimeException | RemoteException e) {
Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e);
return null;
}
}
if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
+ serviceComponent + ", info: " + serviceInfo);
if (serviceInfo == null) {
Slog.w(TAG, "no service info for " + serviceComponent);
return null;
}
return serviceComponent;
}
void showAllNotifications() {
final UserManager userManager =
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
final List<UserInfo> allUsers = userManager.getUsers(true);
for (UserInfo user : allUsers) {
final ComponentName serviceComponent = getProviderForUser(user.id);
if (serviceComponent != null) {
showMainNotification(serviceComponent, user.id);
}
}
}
void updateNotification(int userId) {
final ComponentName serviceComponent = getProviderForUser(userId);
if (serviceComponent == null) {
cancelMainNotification(userId);
} else {
showMainNotification(serviceComponent, userId);
}
}
private static Intent newNotificationIntent(int userId, String type) {
final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
intent.putExtra(EXTRA_USER_ID, userId);
@@ -295,65 +241,6 @@ final class AutoFillUI {
return builder.append(']').toString();
}
/**
* Shows a permanent notification that triggers the auto-fill workflow for the given user.
*
* <p>It emulates calling the auto-fill service when the IME is shown.
*/
private void showMainNotification(ComponentName serviceComponent, int userId) {
if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent);
synchronized (mLock) {
if (mNotificationReceiver == null) {
mNotificationReceiver = new NotificationReceiver();
mContext.registerReceiver(mNotificationReceiver,
new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
}
}
final Intent fillIntent = newNotificationIntent(userId, TYPE_EMULATE);
final PendingIntent fillPendingIntent = PendingIntent.getBroadcast(mContext,
-1, fillIntent, PendingIntent.FLAG_UPDATE_CURRENT);
final String packageName = serviceComponent.getPackageName();
String providerName = null;
final PackageManager pm = mContext.getPackageManager();
try {
final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId);
if (info != null) {
providerName = pm.getApplicationLabel(info).toString();
}
} catch (Exception e) {
providerName = packageName;
}
final String title = "AutoFill IME Emulation";
final String subTitle = "Tap notification to start auto-fill workflow (by '" + providerName
+ "' on top activity on user " + userId + ".\n"
+ "Once provider replies, a new notification will show your options.";
final Notification notification = new Notification.Builder(mContext)
.setCategory(Notification.CATEGORY_SYSTEM)
.setOngoing(true)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setLocalOnly(true)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(title)
.setStyle(new Notification.BigTextStyle().bigText(subTitle))
.setContentIntent(fillPendingIntent)
.build();
NotificationManager.from(mContext).notify(TYPE_EMULATE, userId, notification);
}
/**
* Cancels the permament notification created by
* {@link #showMainNotification(ComponentName, int)}.
*/
private void cancelMainNotification(int userId) {
if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId);
NotificationManager.from(mContext).cancel(TYPE_EMULATE, userId);
}
/**
* Shows a notification with the results of an auto-fill request, using notications actions
* to emulate the auto-fill bar buttons displaying the dataset names.