summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt10
-rw-r--r--api/system-current.txt10
-rw-r--r--api/test-current.txt10
-rw-r--r--core/java/android/view/autofill/AutoFillManager.java142
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl4
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl6
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerService.java13
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java56
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java16
9 files changed, 248 insertions, 19 deletions
diff --git a/api/current.txt b/api/current.txt
index 1a5ea853b365..6a62fdea1853 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -47452,17 +47452,27 @@ package android.view.autofill {
}
public final class AutoFillManager {
+ method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void reset();
method public void startAutoFillRequest(android.view.View);
method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
method public void stopAutoFillRequest(android.view.View);
method public void stopAutoFillRequestOnVirtualView(android.view.View, int);
+ method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void valueChanged(android.view.View);
method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
+ public static abstract class AutoFillManager.AutofillCallback {
+ ctor public AutoFillManager.AutofillCallback();
+ method public void onAutofillEvent(android.view.View, int);
+ method public void onAutofillEventVirtual(android.view.View, int, int);
+ field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
+ field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+ }
+
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forDate();
diff --git a/api/system-current.txt b/api/system-current.txt
index b5c20e52e913..2c0713dfa017 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -50915,17 +50915,27 @@ package android.view.autofill {
}
public final class AutoFillManager {
+ method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void reset();
method public void startAutoFillRequest(android.view.View);
method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
method public void stopAutoFillRequest(android.view.View);
method public void stopAutoFillRequestOnVirtualView(android.view.View, int);
+ method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void valueChanged(android.view.View);
method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
+ public static abstract class AutoFillManager.AutofillCallback {
+ ctor public AutoFillManager.AutofillCallback();
+ method public void onAutofillEvent(android.view.View, int);
+ method public void onAutofillEventVirtual(android.view.View, int, int);
+ field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
+ field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+ }
+
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forDate();
diff --git a/api/test-current.txt b/api/test-current.txt
index 57cc998ffb5d..fc96532ba383 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -47821,17 +47821,27 @@ package android.view.autofill {
}
public final class AutoFillManager {
+ method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void reset();
method public void startAutoFillRequest(android.view.View);
method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
method public void stopAutoFillRequest(android.view.View);
method public void stopAutoFillRequestOnVirtualView(android.view.View, int);
+ method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void valueChanged(android.view.View);
method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
+ public static abstract class AutoFillManager.AutofillCallback {
+ ctor public AutoFillManager.AutofillCallback();
+ method public void onAutofillEvent(android.view.View, int);
+ method public void onAutofillEventVirtual(android.view.View, int, int);
+ field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
+ field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+ }
+
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forDate();
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index e8325e89f39a..8beaf4e423cb 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -19,7 +19,9 @@ package android.view.autofill;
import static android.view.autofill.Helper.DEBUG;
import static android.view.autofill.Helper.VERBOSE;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -30,7 +32,10 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
+import android.view.WindowManagerGlobal;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.List;
@@ -76,6 +81,8 @@ public final class AutoFillManager {
private final IAutoFillManager mService;
private IAutoFillManagerClient mServiceClient;
+ private AutofillCallback mCallback;
+
private Context mContext;
private boolean mHasSession;
@@ -276,11 +283,11 @@ public final class AutoFillManager {
}
}
- private AutoFillId getAutoFillId(View view) {
+ private static AutoFillId getAutoFillId(View view) {
return new AutoFillId(view.getAccessibilityViewId());
}
- private AutoFillId getAutoFillId(View parent, int childId) {
+ private static AutoFillId getAutoFillId(View parent, int childId) {
return new AutoFillId(parent.getAccessibilityViewId(), childId);
}
@@ -289,10 +296,12 @@ public final class AutoFillManager {
if (DEBUG) {
Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
}
+
try {
mService.startSession(mContext.getActivityToken(), windowToken,
- mServiceClient.asBinder(), id, bounds, value, mContext.getUserId());
- AutoFillClient client = getClient();
+ mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
+ mCallback != null);
+ final AutoFillClient client = getClient();
if (client != null) {
client.resetableStateAvailable();
}
@@ -344,6 +353,119 @@ public final class AutoFillManager {
}
}
+ /**
+ * Registers a {@link AutofillCallback} to receive autofill events.
+ *
+ * @param callback callback to receive events.
+ */
+ public void registerCallback(@Nullable AutofillCallback callback) {
+ if (callback == null) return;
+
+ final boolean hadCallback = mCallback != null;
+ mCallback = callback;
+
+ if (mHasSession && !hadCallback) {
+ try {
+ mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a {@link AutofillCallback} to receive autofill events.
+ *
+ * @param callback callback to stop receiving events.
+ */
+ public void unregisterCallback(@Nullable AutofillCallback callback) {
+ if (callback == null || mCallback == null || callback != mCallback) return;
+
+ mCallback = null;
+
+ if (mHasSession) {
+ try {
+ mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), false);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void onAutofillEvent(IBinder windowToken, AutoFillId id, int event) {
+ if (mCallback == null) return;
+ if (id == null) {
+ Log.w(TAG, "onAutofillEvent(): no id for event " + event);
+ return;
+ }
+
+ final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
+ if (root == null) {
+ Log.w(TAG, "onAutofillEvent() for " + id + ": root view gone");
+ return;
+ }
+ final View view = root.findViewByAccessibilityIdTraversal(id.getViewId());
+ if (view == null) {
+ Log.w(TAG, "onAutofillEvent() for " + id + ": view gone");
+ return;
+ }
+ if (id.isVirtual()) {
+ mCallback.onAutofillEventVirtual(view, id.getVirtualChildId(), event);
+ } else {
+ mCallback.onAutofillEvent(view, event);
+ }
+ }
+
+ /**
+ * Callback for auto-fill related events.
+ *
+ * <p>Typically used for applications that display their own "auto-complete" views, so they can
+ * enable / disable such views when the auto-fill UI affordance is shown / hidden.
+ */
+ public abstract static class AutofillCallback {
+
+ /** @hide */
+ @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AutofillEventType {}
+
+ /**
+ * The auto-fill input UI affordance associated with the view was shown.
+ *
+ * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
+ * should be hidden upon receiving this event.
+ */
+ public static final int EVENT_INPUT_SHOWN = 1;
+
+ /**
+ * The auto-fill input UI affordance associated with the view was hidden.
+ *
+ * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
+ * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
+ */
+ public static final int EVENT_INPUT_HIDDEN = 2;
+
+ /**
+ * Called after a change in the autofill state associated with a view.
+ *
+ * @param view view associated with the change.
+ *
+ * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
+ */
+ public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {}
+
+ /**
+ * Called after a change in the autofill state associated with a virtual view.
+ *
+ * @param view parent view associated with the change.
+ * @param childId id identifying the virtual child inside the parent view.
+ *
+ * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
+ */
+ public void onAutofillEventVirtual(@NonNull View view, int childId,
+ @AutofillEventType int event) {}
+ }
+
private static final class AutoFillManagerClient extends IAutoFillManagerClient.Stub {
private final WeakReference<AutoFillManager> mAutoFillManager;
@@ -385,5 +507,17 @@ public final class AutoFillManager {
});
}
}
+
+ @Override
+ public void onAutofillEvent(IBinder windowToken, AutoFillId id, int event) {
+ final AutoFillManager autoFillManager = mAutoFillManager.get();
+ if (autoFillManager != null) {
+ autoFillManager.mContext.getMainThreadHandler().post(() -> {
+ if (autoFillManager.getClient() != null) {
+ autoFillManager.onAutofillEvent(windowToken, id, event);
+ }
+ });
+ }
+ }
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index d054e979ddb7..b36c0f15d58a 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -31,10 +31,12 @@ import android.view.autofill.IAutoFillManagerClient;
interface IAutoFillManager {
boolean addClient(in IAutoFillManagerClient client, int userId);
oneway void startSession(in IBinder activityToken, IBinder windowToken, in IBinder appCallback,
- in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId);
+ in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId,
+ boolean hasCallback);
oneway void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds,
in AutoFillValue value, int flags, int userId);
oneway void finishSession(in IBinder activityToken, int userId);
oneway void setAuthenticationResult(in Bundle data,
in IBinder activityToken, int userId);
+ oneway void setHasCallback(in IBinder activityToken, int userId, boolean hasIt);
}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 45f363d32301..9eef7d0a6e69 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -20,6 +20,7 @@ import java.util.List;
import android.content.Intent;
import android.content.IntentSender;
+import android.os.IBinder;
import android.view.autofill.AutoFillId;
import android.view.autofill.AutoFillValue;
@@ -43,4 +44,9 @@ oneway interface IAutoFillManagerClient {
* Authenticates a fill response or a data set.
*/
void authenticate(in IntentSender intent, in Intent fillInIntent);
+
+ /**
+ * Notifies the client when the auto-fill UI changed.
+ */
+ void onAutofillEvent(in IBinder windowToken, in AutoFillId id, int event);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 40491e91ea82..e943c4c388f5 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -305,14 +305,23 @@ public final class AutoFillManagerService extends SystemService {
}
@Override
+ public void setHasCallback(IBinder activityToken, int userId, boolean hasIt) {
+ synchronized (mLock) {
+ final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ service.setHasCallback(activityToken, hasIt);
+ }
+ }
+
+ @Override
public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
- AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId) {
+ AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId,
+ boolean hasCallback) {
// TODO(b/33197203): make sure it's called by resumed / focused activity
synchronized (mLock) {
final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, windowToken, appCallback,
- autoFillId, bounds, value);
+ autoFillId, bounds, value, hasCallback);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 5e852f1738f1..c4d06488f7b5 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -274,15 +274,25 @@ final class AutoFillManagerServiceImpl {
}
}
+ void setHasCallback(IBinder activityToken, boolean hasIt) {
+ if (!hasService()) {
+ return;
+ }
+ final Session session = mSessions.get(activityToken);
+ if (session != null) {
+ session.setHasCallback(hasIt);
+ }
+ }
+
void startSessionLocked(IBinder activityToken, IBinder windowToken, IBinder appCallbackToken,
- AutoFillId autoFillId, Rect bounds, AutoFillValue value) {
+ AutoFillId autoFillId, Rect bounds, AutoFillValue value, boolean hasCallback) {
if (!hasService()) {
return;
}
final String historyItem = "s=" + mInfo.getServiceInfo().packageName
+ " u=" + mUserId + " a=" + activityToken
- + " i=" + autoFillId + " b=" + bounds;
+ + " i=" + autoFillId + " b=" + bounds + " hc=" + hasCallback;
mRequestsHistory.log(historyItem);
// TODO(b/33197203): Handle partitioning
@@ -293,7 +303,7 @@ final class AutoFillManagerServiceImpl {
}
final Session newSession = createSessionByTokenLocked(activityToken,
- windowToken, appCallbackToken);
+ windowToken, appCallbackToken, hasCallback);
newSession.updateLocked(autoFillId, bounds, value, FLAG_START_SESSION);
}
@@ -312,9 +322,9 @@ final class AutoFillManagerServiceImpl {
}
private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken,
- IBinder appCallbackToken) {
+ IBinder appCallbackToken, boolean hasCallback) {
final Session newSession = new Session(mContext, activityToken,
- windowToken, appCallbackToken);
+ windowToken, appCallbackToken, hasCallback);
mSessions.put(activityToken, newSession);
/*
@@ -599,12 +609,18 @@ final class AutoFillManagerServiceImpl {
@GuardedBy("mLock")
private AssistStructure mStructure;
+ /**
+ * Whether the client has an {@link android.view.autofill.AutoFillManager.AutofillCallback}.
+ */
+ private boolean mHasCallback;
+
private Session(Context context, IBinder activityToken, IBinder windowToken,
- IBinder client) {
+ IBinder client, boolean hasCallback) {
mRemoteFillService = new RemoteFillService(context,
mInfo.getServiceInfo().getComponentName(), mUserId, this);
mActivityToken = activityToken;
mWindowToken = windowToken;
+ mHasCallback = hasCallback;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
try {
@@ -712,6 +728,14 @@ final class AutoFillManagerServiceImpl {
.sendToTarget();
}
+ // AutoFillUiCallback
+ @Override
+ public void onEvent(AutoFillId id, int event) {
+ mHandlerCaller.getHandler().post(() -> {
+ notifyChangeToClient(id, event);
+ });
+ }
+
public void setAuthenticationResultLocked(Bundle data) {
if (mCurrentResponse == null || data == null) {
removeSelf();
@@ -731,6 +755,10 @@ final class AutoFillManagerServiceImpl {
}
}
+ public void setHasCallback(boolean hasIt) {
+ mHasCallback = hasIt;
+ }
+
/**
* Show the save UI, when session can be saved.
*/
@@ -814,6 +842,7 @@ final class AutoFillManagerServiceImpl {
node.updateAutoFillValue(value);
}
+ // Sanitize structure before it's sent to service.
mStructure.sanitizeForParceling(false);
if (VERBOSE) {
@@ -871,7 +900,7 @@ final class AutoFillManagerServiceImpl {
if ((flags & FLAG_FOCUS_GAINED) != 0) {
// Remove the UI if the ViewState has changed.
if (mCurrentViewState != viewState) {
- mUi.hideFillUi();
+ mUi.hideFillUi(mCurrentViewState != null ? mCurrentViewState.mId : null);
mCurrentViewState = viewState;
}
@@ -888,7 +917,7 @@ final class AutoFillManagerServiceImpl {
if ((flags & FLAG_FOCUS_LOST) != 0) {
if (mCurrentViewState == viewState) {
- mUi.hideFillUi();
+ mUi.hideFillUi(viewState.mId);
mCurrentViewState = null;
}
return;
@@ -912,6 +941,15 @@ final class AutoFillManagerServiceImpl {
getUiForShowing().showFillUi(filledId, response, bounds, filterText);
}
+ private void notifyChangeToClient(AutoFillId id, int event) {
+ if (!mHasCallback) return;
+ try {
+ mClient.onAutofillEvent(mWindowToken, id, event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client on change: id=" + id + ", event=" + event, e);
+ }
+ }
+
private void processResponseLocked(FillResponse response) {
if (DEBUG) {
Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication()
@@ -993,7 +1031,7 @@ final class AutoFillManagerServiceImpl {
pw.println("null");
}
}
-
+ pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
mRemoteFillService.dump(prefix, pw);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index f00d6c58d206..c86a8828d0b1 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -15,6 +15,9 @@
*/
package com.android.server.autofill.ui;
+import static android.view.autofill.AutoFillManager.AutofillCallback.EVENT_INPUT_HIDDEN;
+import static android.view.autofill.AutoFillManager.AutofillCallback.EVENT_INPUT_SHOWN;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -26,8 +29,8 @@ import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveInfo;
import android.text.TextUtils;
-import android.util.Slog;
import android.text.format.DateUtils;
+import android.util.Slog;
import android.view.autofill.AutoFillId;
import android.widget.Toast;
@@ -62,6 +65,7 @@ public final class AutoFillUI {
void authenticate(@NonNull IntentSender intent);
void fill(@NonNull Dataset dataset);
void save();
+ void onEvent(AutoFillId id, int event);
}
public AutoFillUI(@NonNull Context context) {
@@ -97,8 +101,13 @@ public final class AutoFillUI {
/**
* Hides the fill UI.
*/
- public void hideFillUi() {
- mHandler.post(this::hideFillUiUiThread);
+ public void hideFillUi(AutoFillId id) {
+ mHandler.post(() -> {
+ hideFillUiUiThread();
+ if (mCallback != null) {
+ mCallback.onEvent(id, EVENT_INPUT_HIDDEN);
+ }
+ });
}
/**
@@ -175,6 +184,7 @@ public final class AutoFillUI {
// TODO(b/33197203): add MetricsLogger call
}
});
+ mCallback.onEvent(focusedId, EVENT_INPUT_SHOWN);
});
}