summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java79
-rw-r--r--core/java/android/content/Context.java24
-rw-r--r--core/java/android/service/autofill/AutofillService.java3
-rw-r--r--core/java/android/view/View.java42
-rw-r--r--core/java/android/view/Window.java7
-rw-r--r--core/java/android/view/WindowCallbackWrapper.java5
-rw-r--r--core/java/android/view/autofill/AutofillManager.java481
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl15
-rw-r--r--core/java/com/android/internal/policy/DecorView.java1
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java58
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java183
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java124
12 files changed, 709 insertions, 313 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index af3bf2aee049..42192356ed10 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -724,8 +724,9 @@ public class Activity extends ContextThemeWrapper
public static final int FINISH_TASK_WITH_ACTIVITY = 2;
static final String FRAGMENTS_TAG = "android:fragments";
- static final String AUTOFILL_RESET_NEEDED_TAG = "android:autofillResetNeeded";
+ private static final String LAST_ACCESSIBILITY_ID = "android:lastAccessibilityId";
+ private static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded";
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
@@ -773,6 +774,9 @@ public class Activity extends ContextThemeWrapper
private SearchManager mSearchManager;
private MenuInflater mMenuInflater;
+ /** The autofill manager. Always access via {@link #getAutofillManager()}. */
+ @Nullable private AutofillManager mAutofillManager;
+
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
@@ -853,6 +857,9 @@ public class Activity extends ContextThemeWrapper
private boolean mAutoFillResetNeeded;
+ /** The last accessibility id that was returned from {@link #getNextAccessibilityId()} */
+ private int mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID;
+
private AutofillPopupWindow mAutofillPopupWindow;
private static native String getDlWarning();
@@ -930,6 +937,19 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * (Create and) return the autofill manager
+ *
+ * @return The autofill manager
+ */
+ @NonNull private AutofillManager getAutofillManager() {
+ if (mAutofillManager == null) {
+ mAutofillManager = getSystemService(AutofillManager.class);
+ }
+
+ return mAutofillManager;
+ }
+
+ /**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
* activity's UI, using {@link #findViewById} to programmatically interact
@@ -970,6 +990,13 @@ public class Activity extends ContextThemeWrapper
}
}
if (savedInstanceState != null) {
+ mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
+ mLastAccessibilityId = savedInstanceState.getInt(LAST_ACCESSIBILITY_ID, View.NO_ID);
+
+ if (mAutoFillResetNeeded) {
+ getAutofillManager().onCreate(savedInstanceState);
+ }
+
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
@@ -1058,12 +1085,6 @@ public class Activity extends ContextThemeWrapper
* @see #onSaveInstanceState
*/
protected void onRestoreInstanceState(Bundle savedInstanceState) {
- mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED_TAG, false);
-
- if (mAutoFillResetNeeded) {
- getSystemService(AutofillManager.class).onRestoreInstanceState(savedInstanceState);
- }
-
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
@@ -1312,6 +1333,27 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Gets the next accessibility ID.
+ *
+ * <p>All IDs will be bigger than {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs returned
+ * will be unique.
+ *
+ * @return A ID that is unique in the activity
+ *
+ * {@hide}
+ */
+ @Override
+ public int getNextAccessibilityId() {
+ if (mLastAccessibilityId == Integer.MAX_VALUE - 1) {
+ mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID;
+ }
+
+ mLastAccessibilityId++;
+
+ return mLastAccessibilityId;
+ }
+
+ /**
* Check whether this activity is running as part of a voice interaction with the user.
* If true, it should perform its interaction with the user through the
* {@link VoiceInteractor} returned by {@link #getVoiceInteractor}.
@@ -1505,13 +1547,15 @@ public class Activity extends ContextThemeWrapper
*/
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
+
+ outState.putInt(LAST_ACCESSIBILITY_ID, mLastAccessibilityId);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mAutoFillResetNeeded) {
- outState.putBoolean(AUTOFILL_RESET_NEEDED_TAG, mAutoFillResetNeeded);
- getSystemService(AutofillManager.class).onSaveInstanceState(outState);
+ outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
+ getAutofillManager().onSaveInstanceState(outState);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
@@ -1802,7 +1846,7 @@ public class Activity extends ContextThemeWrapper
mTranslucentCallback = null;
mCalled = true;
if (isFinishing() && mAutoFillResetNeeded) {
- getSystemService(AutofillManager.class).commit();
+ getAutofillManager().commit();
}
}
@@ -3057,6 +3101,19 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Called before {@link #onAttachedToWindow}.
+ *
+ * @hide
+ */
+ @CallSuper
+ public void onBeforeAttachedToWindow() {
+ if (mAutoFillResetNeeded) {
+ getAutofillManager().onAttachedToWindow(
+ getWindow().getDecorView().getRootView().getWindowToken());
+ }
+ }
+
+ /**
* Called when the main window associated with the activity has been
* attached to the window manager.
* See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
@@ -7125,7 +7182,7 @@ public class Activity extends ContextThemeWrapper
}
} else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {
Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null;
- getSystemService(AutofillManager.class).onAuthenticationResult(resultData);
+ getAutofillManager().onAuthenticationResult(resultData);
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 368c7b8106b3..88bade1691a6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -61,6 +61,7 @@ import android.provider.MediaStore;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayAdjustments;
+import android.view.View;
import android.view.ViewDebug;
import android.view.WindowManager;
import android.view.textclassifier.TextClassificationManager;
@@ -436,6 +437,29 @@ public abstract class Context {
*/
public abstract Context getApplicationContext();
+ /** Non-activity related accessibility ids are unique in the app */
+ private static int sLastAccessibilityId = View.NO_ID;
+
+ /**
+ * Gets the next accessibility ID.
+ *
+ * <p>All IDs will be smaller or the same as {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs
+ * returned will be unique.
+ *
+ * @return A ID that is unique in the process
+ *
+ * {@hide}
+ */
+ public int getNextAccessibilityId() {
+ if (sLastAccessibilityId == View.LAST_APP_ACCESSIBILITY_ID - 1) {
+ sLastAccessibilityId = View.NO_ID;
+ }
+
+ sLastAccessibilityId++;
+
+ return sLastAccessibilityId;
+ }
+
/**
* Add a new {@link ComponentCallbacks} to the base application of the
* Context, which will be called at the same times as the ComponentCallbacks
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index aae22c1bb750..c82b9ebb6b39 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -82,8 +82,7 @@ public abstract class AutofillService extends Service {
// Internal extras
/** @hide */
- public static final String EXTRA_ACTIVITY_TOKEN =
- "android.service.autofill.extra.ACTIVITY_TOKEN";
+ public static final String EXTRA_SESSION_ID = "android.service.autofill.extra.SESSION_ID";
// Handler messages.
private static final int MSG_CONNECT = 1;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b4100123a5b8..a09db9c3972d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -797,6 +797,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public static final int NO_ID = -1;
/**
+ * Last ID that is given to Views that are no part of activities.
+ *
+ * {@hide}
+ */
+ public static final int LAST_APP_ACCESSIBILITY_ID = Integer.MAX_VALUE / 2;
+
+ /**
* Signals that compatibility booleans have been initialized according to
* target SDK versions.
*/
@@ -1967,11 +1974,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private SparseArray<Object> mKeyedTags;
/**
- * The next available accessibility id.
- */
- private static int sNextAccessibilityViewId;
-
- /**
* The animation currently associated with this view.
* @hide
*/
@@ -2013,10 +2015,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@ViewDebug.ExportedProperty(resolveId = true)
int mID = NO_ID;
- /**
- * The stable ID of this view for accessibility purposes.
+ /** The ID of this view for accessibility and autofill purposes.
+ * <ul>
+ * <li>== {@link #NO_ID}: ID has not been assigned yet
+ * <li>&le; {@link #LAST_APP_ACCESSIBILITY_ID}: View is not part of a activity. The ID is
+ * unique in the process. This might change
+ * over activity lifecycle events.
+ * <li>&gt; {@link #LAST_APP_ACCESSIBILITY_ID}: View is part of a activity. The ID is
+ * unique in the activity. This stays the same
+ * over activity lifecycle events.
*/
- int mAccessibilityViewId = NO_ID;
+ private int mAccessibilityViewId = NO_ID;
private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
@@ -8144,7 +8153,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
public int getAccessibilityViewId() {
if (mAccessibilityViewId == NO_ID) {
- mAccessibilityViewId = sNextAccessibilityViewId++;
+ mAccessibilityViewId = mContext.getNextAccessibilityId();
}
return mAccessibilityViewId;
}
@@ -17159,7 +17168,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@CallSuper
protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
- if (mStartActivityRequestWho != null || isAutofilled()) {
+ if (mStartActivityRequestWho != null || isAutofilled()
+ || mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
if (mStartActivityRequestWho != null) {
@@ -17170,8 +17180,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
}
+ if (mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) {
+ state.mSavedData |= BaseSavedState.ACCESSIBILITY_ID;
+ }
+
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
state.mIsAutofilled = isAutofilled();
+ state.mAccessibilityViewId = mAccessibilityViewId;
return state;
}
return BaseSavedState.EMPTY_STATE;
@@ -17249,6 +17264,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
setAutofilled(baseState.mIsAutofilled);
}
+ if ((baseState.mSavedData & BaseSavedState.ACCESSIBILITY_ID) != 0) {
+ mAccessibilityViewId = baseState.mAccessibilityViewId;
+ }
}
}
@@ -24408,11 +24426,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public static class BaseSavedState extends AbsSavedState {
static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1;
static final int IS_AUTOFILLED = 0b10;
+ static final int ACCESSIBILITY_ID = 0b100;
// Flags that describe what data in this state is valid
int mSavedData;
String mStartActivityRequestWhoSaved;
boolean mIsAutofilled;
+ int mAccessibilityViewId;
/**
* Constructor used when reading from a parcel. Reads the state of the superclass.
@@ -24435,6 +24455,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mSavedData = source.readInt();
mStartActivityRequestWhoSaved = source.readString();
mIsAutofilled = source.readBoolean();
+ mAccessibilityViewId = source.readInt();
}
/**
@@ -24453,6 +24474,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
out.writeInt(mSavedData);
out.writeString(mStartActivityRequestWhoSaved);
out.writeBoolean(mIsAutofilled);
+ out.writeInt(mAccessibilityViewId);
}
public static final Parcelable.Creator<BaseSavedState> CREATOR
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 6dd8ecfa12e3..a432d30f86ca 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -480,6 +480,13 @@ public abstract class Window {
public void onWindowFocusChanged(boolean hasFocus);
/**
+ * @hide
+ */
+ default void onBeforeAttachedToWindow() {
+ // empty
+ }
+
+ /**
* Called when the window has been attached to the window manager.
* See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
* for more information.
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 02c8945d9fce..7018529cd52a 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -109,6 +109,11 @@ public class WindowCallbackWrapper implements Window.Callback {
}
@Override
+ public void onBeforeAttachedToWindow() {
+ mWrapped.onBeforeAttachedToWindow();
+ }
+
+ @Override
public void onAttachedToWindow() {
mWrapped.onAttachedToWindow();
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 07fad60e5b6a..d429d379876f 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -37,6 +37,7 @@ import android.util.SparseArray;
import android.view.View;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -48,6 +49,8 @@ import java.util.Objects;
/**
* App entry point to the AutoFill Framework.
+ *
+ * <p>It is safe to call into this from any thread.
*/
// TODO(b/33197203): improve this javadoc
//TODO(b/33197203): restrict manager calls to activity
@@ -92,6 +95,7 @@ public final class AutofillManager {
*/
public static final String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
+ static final String SESSION_ID_TAG = "android:sessionId";
static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
// Public flags start from the lowest bit
@@ -108,17 +112,32 @@ public final class AutofillManager {
private final MetricsLogger mMetricsLogger = new MetricsLogger();
+ /**
+ * There is currently no session running.
+ * {@hide}
+ */
+ public static final int NO_SESSION = Integer.MIN_VALUE;
+
private final IAutoFillManager mService;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private IAutoFillManagerClient mServiceClient;
+ @GuardedBy("mLock")
private AutofillCallback mCallback;
- private Context mContext;
+ private final Context mContext;
- private boolean mHasSession;
+ @GuardedBy("mLock")
+ private int mSessionId = NO_SESSION;
+
+ @GuardedBy("mLock")
private boolean mEnabled;
/** If a view changes to this mapping the autofill operation was successful */
+ @GuardedBy("mLock")
@Nullable private ParcelableMap mLastAutofilledData;
/** @hide */
@@ -172,8 +191,63 @@ public final class AutofillManager {
*
* {@hide}
*/
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
+ public void onCreate(Bundle savedInstanceState) {
+ synchronized (mLock) {
+ mLastAutofilledData = savedInstanceState.getParcelable(LAST_AUTOFILLED_DATA_TAG);
+
+ if (mSessionId != NO_SESSION) {
+ Log.w(TAG, "New session was started before onCreate()");
+ return;
+ }
+
+ mSessionId = savedInstanceState.getInt(SESSION_ID_TAG, NO_SESSION);
+
+ if (mSessionId != NO_SESSION) {
+ ensureServiceClientAddedIfNeededLocked();
+
+ final AutofillClient client = getClientLocked();
+ if (client != null) {
+ try {
+ final boolean sessionWasRestored = mService.restoreSession(mSessionId,
+ mContext.getActivityToken(), mServiceClient.asBinder());
+
+ if (!sessionWasRestored) {
+ Log.w(TAG, "Session " + mSessionId + " could not be restored");
+ mSessionId = NO_SESSION;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "session " + mSessionId + " was restored");
+ }
+
+ client.autofillCallbackResetableStateAvailable();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not figure out if there was an autofill session", e);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Set window future popup windows should be attached to.
+ *
+ * @param windowToken The window the popup windows should be attached to
+ *
+ * {@hide}
+ */
+ public void onAttachedToWindow(@NonNull IBinder windowToken) {
+ synchronized (mLock) {
+ if (mSessionId == NO_SESSION) {
+ return;
+ }
+
+ try {
+ mService.setWindow(mSessionId, windowToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not attach window to session " + mSessionId);
+ }
+ }
}
/**
@@ -184,8 +258,14 @@ public final class AutofillManager {
* {@hide}
*/
public void onSaveInstanceState(Bundle outState) {
- if (mLastAutofilledData != null) {
- outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
+ synchronized (mLock) {
+ if (mSessionId != NO_SESSION) {
+ outState.putInt(SESSION_ID_TAG, mSessionId);
+ }
+
+ if (mLastAutofilledData != null) {
+ outState.putParcelable(LAST_AUTOFILLED_DATA_TAG, mLastAutofilledData);
+ }
}
}
@@ -198,8 +278,10 @@ public final class AutofillManager {
* @return whether autofill is enabled for the current user.
*/
public boolean isEnabled() {
- ensureServiceClientAddedIfNeeded();
- return mEnabled;
+ synchronized (mLock) {
+ ensureServiceClientAddedIfNeededLocked();
+ return mEnabled;
+ }
}
/**
@@ -212,16 +294,18 @@ public final class AutofillManager {
* @param view view requesting the new autofill context.
*/
public void requestAutofill(@NonNull View view) {
- ensureServiceClientAddedIfNeeded();
+ synchronized (mLock) {
+ ensureServiceClientAddedIfNeededLocked();
- if (!mEnabled) {
- return;
- }
+ if (!mEnabled) {
+ return;
+ }
- final AutofillId id = getAutofillId(view);
- final AutofillValue value = view.getAutofillValue();
+ final AutofillId id = getAutofillId(view);
+ final AutofillValue value = view.getAutofillValue();
- startSession(id, view.getWindowToken(), null, value, FLAG_MANUAL_REQUEST);
+ startSessionLocked(id, view.getWindowToken(), null, value, FLAG_MANUAL_REQUEST);
+ }
}
/**
@@ -236,14 +320,16 @@ public final class AutofillManager {
* @param bounds child boundaries, relative to the top window.
*/
public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
- ensureServiceClientAddedIfNeeded();
+ synchronized (mLock) {
+ ensureServiceClientAddedIfNeededLocked();
- if (!mEnabled) {
- return;
- }
+ if (!mEnabled) {
+ return;
+ }
- final AutofillId id = getAutofillId(view, childId);
- startSession(id, view.getWindowToken(), bounds, null, FLAG_MANUAL_REQUEST);
+ final AutofillId id = getAutofillId(view, childId);
+ startSessionLocked(id, view.getWindowToken(), bounds, null, FLAG_MANUAL_REQUEST);
+ }
}
@@ -253,24 +339,30 @@ public final class AutofillManager {
* @param view {@link View} that was entered.
*/
public void notifyViewEntered(@NonNull View view) {
- ensureServiceClientAddedIfNeeded();
+ AutofillCallback callback = null;
+ synchronized (mLock) {
+ ensureServiceClientAddedIfNeededLocked();
- if (!mEnabled) {
- if (mCallback != null) {
- mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+ if (!mEnabled) {
+ if (mCallback != null) {
+ callback = mCallback;
+ }
+ } else {
+ final AutofillId id = getAutofillId(view);
+ final AutofillValue value = view.getAutofillValue();
+
+ if (mSessionId == NO_SESSION) {
+ // Starts new session.
+ startSessionLocked(id, view.getWindowToken(), null, value, 0);
+ } else {
+ // Update focus on existing session.
+ updateSessionLocked(id, null, value, FLAG_VIEW_ENTERED);
+ }
}
- return;
}
- final AutofillId id = getAutofillId(view);
- final AutofillValue value = view.getAutofillValue();
-
- if (!mHasSession) {
- // Starts new session.
- startSession(id, view.getWindowToken(), null, value, 0);
- } else {
- // Update focus on existing session.
- updateSession(id, null, value, FLAG_VIEW_ENTERED);
+ if (callback != null) {
+ mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
}
}
@@ -280,13 +372,15 @@ public final class AutofillManager {
* @param view {@link View} that was exited.
*/
public void notifyViewExited(@NonNull View view) {
- ensureServiceClientAddedIfNeeded();
+ synchronized (mLock) {
+ ensureServiceClientAddedIfNeededLocked();
- if (mEnabled && mHasSession) {
- final AutofillId id = getAutofillId(view);
+ if (mEnabled && mSessionId != NO_SESSION) {
+ final AutofillId id = getAutofillId(view);
- // Update focus on existing session.
- updateSession(id, null, null, FLAG_VIEW_EXITED);
+ // Update focus on existing session.
+ updateSessionLocked(id, null, null, FLAG_VIEW_EXITED);
+ }
}
}
@@ -298,23 +392,30 @@ public final class AutofillManager {
* @param bounds child boundaries, relative to the top window.
*/
public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) {
- ensureServiceClientAddedIfNeeded();
+ AutofillCallback callback = null;
+ synchronized (mLock) {
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (!mEnabled) {
+ if (mCallback != null) {
+ callback = mCallback;
+ }
+ } else {
+ final AutofillId id = getAutofillId(view, childId);
- if (!mEnabled) {
- if (mCallback != null) {
- mCallback.onAutofillEvent(view, childId, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+ if (mSessionId == NO_SESSION) {
+ // Starts new session.
+ startSessionLocked(id, view.getWindowToken(), bounds, null, 0);
+ } else {
+ // Update focus on existing session.
+ updateSessionLocked(id, bounds, null, FLAG_VIEW_ENTERED);
+ }
}
- return;
}
- final AutofillId id = getAutofillId(view, childId);
-
- if (!mHasSession) {
- // Starts new session.
- startSession(id, view.getWindowToken(), bounds, null, 0);
- } else {
- // Update focus on existing session.
- updateSession(id, bounds, null, FLAG_VIEW_ENTERED);
+ if (callback != null) {
+ callback.onAutofillEvent(view, childId,
+ AutofillCallback.EVENT_INPUT_UNAVAILABLE);
}
}
@@ -325,13 +426,15 @@ public final class AutofillManager {
* @param childId id identifying the virtual child inside the view.
*/
public void notifyViewExited(@NonNull View view, int childId) {
- ensureServiceClientAddedIfNeeded();
+ synchronized (mLock) {
+ ensureServiceClientAddedIfNeededLocked();
- if (mEnabled && mHasSession) {
- final AutofillId id = getAutofillId(view, childId);
+ if (mEnabled && mSessionId != NO_SESSION) {
+ final AutofillId id = getAutofillId(view, childId);
- // Update focus on existing session.
- updateSession(id, null, null, FLAG_VIEW_EXITED);
+ // Update focus on existing session.
+ updateSessionLocked(id, null, null, FLAG_VIEW_EXITED);
+ }
}
}
@@ -345,40 +448,42 @@ public final class AutofillManager {
boolean valueWasRead = false;
AutofillValue value = null;
- // If the session is gone some fields might still be highlighted, hence we have to remove
- // the isAutofilled property even if no sessions are active.
- if (mLastAutofilledData == null) {
- view.setAutofilled(false);
- } else {
- id = getAutofillId(view);
- if (mLastAutofilledData.containsKey(id)) {
- value = view.getAutofillValue();
- valueWasRead = true;
-
- if (Objects.equals(mLastAutofilledData.get(id), value)) {
- view.setAutofilled(true);
+ synchronized (mLock) {
+ // If the session is gone some fields might still be highlighted, hence we have to
+ // remove the isAutofilled property even if no sessions are active.
+ if (mLastAutofilledData == null) {
+ view.setAutofilled(false);
+ } else {
+ id = getAutofillId(view);
+ if (mLastAutofilledData.containsKey(id)) {
+ value = view.getAutofillValue();
+ valueWasRead = true;
+
+ if (Objects.equals(mLastAutofilledData.get(id), value)) {
+ view.setAutofilled(true);
+ } else {
+ view.setAutofilled(false);
+ mLastAutofilledData.remove(id);
+ }
} else {
view.setAutofilled(false);
- mLastAutofilledData.remove(id);
}
- } else {
- view.setAutofilled(false);
}
- }
- if (!mEnabled || !mHasSession) {
- return;
- }
+ if (!mEnabled || mSessionId == NO_SESSION) {
+ return;
+ }
- if (id == null) {
- id = getAutofillId(view);
- }
+ if (id == null) {
+ id = getAutofillId(view);
+ }
- if (!valueWasRead) {
- value = view.getAutofillValue();
- }
+ if (!valueWasRead) {
+ value = view.getAutofillValue();
+ }
- updateSession(id, null, value, FLAG_VALUE_CHANGED);
+ updateSessionLocked(id, null, value, FLAG_VALUE_CHANGED);
+ }
}
@@ -390,12 +495,14 @@ public final class AutofillManager {
* @param value new value of the child.
*/
public void notifyValueChanged(View view, int childId, AutofillValue value) {
- if (!mEnabled || !mHasSession) {
- return;
- }
+ synchronized (mLock) {
+ if (!mEnabled || mSessionId == NO_SESSION) {
+ return;
+ }
- final AutofillId id = getAutofillId(view, childId);
- updateSession(id, null, value, FLAG_VALUE_CHANGED);
+ final AutofillId id = getAutofillId(view, childId);
+ updateSessionLocked(id, null, value, FLAG_VALUE_CHANGED);
+ }
}
/**
@@ -405,11 +512,13 @@ public final class AutofillManager {
* call this method after the form is submitted and another page is rendered.
*/
public void commit() {
- if (!mEnabled && !mHasSession) {
- return;
- }
+ synchronized (mLock) {
+ if (!mEnabled && mSessionId == NO_SESSION) {
+ return;
+ }
- finishSession();
+ finishSessionLocked();
+ }
}
/**
@@ -419,14 +528,16 @@ public final class AutofillManager {
* call this method if the user does not post the form but moves to another form in this page.
*/
public void cancel() {
- if (!mEnabled && !mHasSession) {
- return;
- }
+ synchronized (mLock) {
+ if (!mEnabled && mSessionId == NO_SESSION) {
+ return;
+ }
- cancelSession();
+ cancelSessionLocked();
+ }
}
- private AutofillClient getClient() {
+ private AutofillClient getClientLocked() {
if (mContext instanceof AutofillClient) {
return (AutofillClient) mContext;
}
@@ -442,21 +553,21 @@ public final class AutofillManager {
if (DEBUG) Log.d(TAG, "onAuthenticationResult(): d=" + data);
- if (data == null) {
- return;
- }
- final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
- final Bundle responseData = new Bundle();
- responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
- try {
- mService.setAuthenticationResult(responseData,
- mContext.getActivityToken(), mContext.getUserId());
- } catch (RemoteException e) {
- Log.e(TAG, "Error delivering authentication result", e);
+ synchronized (mLock) {
+ if (mSessionId == NO_SESSION || data == null) {
+ return;
+ }
+ final Parcelable result = data.getParcelableExtra(EXTRA_AUTHENTICATION_RESULT);
+ final Bundle responseData = new Bundle();
+ responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);
+ try {
+ mService.setAuthenticationResult(responseData, mSessionId, mContext.getUserId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error delivering authentication result", e);
+ }
}
}
-
private static AutofillId getAutofillId(View view) {
return new AutofillId(view.getAccessibilityViewId());
}
@@ -465,71 +576,74 @@ public final class AutofillManager {
return new AutofillId(parent.getAccessibilityViewId(), childId);
}
- private void startSession(@NonNull AutofillId id, @NonNull IBinder windowToken,
+ private void startSessionLocked(@NonNull AutofillId id, @NonNull IBinder windowToken,
@NonNull Rect bounds, @NonNull AutofillValue value, int flags) {
if (DEBUG) {
- Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ Log.d(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
+ ", flags=" + flags);
}
try {
- mService.startSession(mContext.getActivityToken(), windowToken,
+ mSessionId = mService.startSession(mContext.getActivityToken(), windowToken,
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, mContext.getOpPackageName());
- AutofillClient client = getClient();
+ AutofillClient client = getClientLocked();
if (client != null) {
client.autofillCallbackResetableStateAvailable();
}
- mHasSession = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- private void finishSession() {
+ private void finishSessionLocked() {
if (DEBUG) {
- Log.d(TAG, "finishSession()");
+ Log.d(TAG, "finishSessionLocked()");
}
- mHasSession = false;
+
try {
- mService.finishSession(mContext.getActivityToken(), mContext.getUserId());
+ mService.finishSession(mSessionId, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ mSessionId = NO_SESSION;
}
- private void cancelSession() {
+ private void cancelSessionLocked() {
if (DEBUG) {
- Log.d(TAG, "cancelSession()");
+ Log.d(TAG, "cancelSessionLocked()");
}
- mHasSession = false;
+
try {
- mService.cancelSession(mContext.getActivityToken(), mContext.getUserId());
+ mService.cancelSession(mSessionId, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ mSessionId = NO_SESSION;
}
- private void updateSession(AutofillId id, Rect bounds, AutofillValue value, int flags) {
+ private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int flags) {
if (DEBUG) {
if (VERBOSE || (flags & FLAG_VIEW_EXITED) != 0) {
- Log.d(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
- + ", flags=" + flags);
+ Log.d(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
+ + ", value=" + value + ", flags=" + flags);
}
}
try {
- mService.updateSession(mContext.getActivityToken(), id, bounds, value, flags,
- mContext.getUserId());
+ mService.updateSession(mSessionId, id, bounds, value, flags, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- private void ensureServiceClientAddedIfNeeded() {
- if (getClient() == null) {
+ private void ensureServiceClientAddedIfNeededLocked() {
+ if (getClientLocked() == null) {
return;
}
+
if (mServiceClient == null) {
mServiceClient = new AutofillManagerClient(this);
try {
@@ -546,16 +660,18 @@ public final class AutofillManager {
* @param callback callback to receive events.
*/
public void registerCallback(@Nullable AutofillCallback callback) {
- if (callback == null) return;
+ synchronized (mLock) {
+ if (callback == null) return;
- final boolean hadCallback = mCallback != null;
- mCallback = callback;
+ final boolean hadCallback = mCallback != null;
+ mCallback = callback;
- if (mHasSession && !hadCallback) {
- try {
- mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), true);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (!hadCallback) {
+ try {
+ mService.setHasCallback(mSessionId, mContext.getUserId(), true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
}
@@ -566,13 +682,13 @@ public final class AutofillManager {
* @param callback callback to stop receiving events.
*/
public void unregisterCallback(@Nullable AutofillCallback callback) {
- if (callback == null || mCallback == null || callback != mCallback) return;
+ synchronized (mLock) {
+ if (callback == null || mCallback == null || callback != mCallback) return;
- mCallback = null;
+ mCallback = null;
- if (mHasSession) {
try {
- mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), false);
+ mService.setHasCallback(mSessionId, mContext.getUserId(), false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -585,13 +701,21 @@ public final class AutofillManager {
if (anchor == null) {
return;
}
- if (getClient().autofillCallbackRequestShowFillUi(anchor, width, height,
- anchorBounds, presenter) && mCallback != null) {
+
+ AutofillCallback callback = null;
+ synchronized (mLock) {
+ if (getClientLocked().autofillCallbackRequestShowFillUi(anchor, width, height,
+ anchorBounds, presenter) && mCallback != null) {
+ callback = mCallback;
+ }
+ }
+
+ if (callback != null) {
if (id.isVirtual()) {
- mCallback.onAutofillEvent(anchor, id.getVirtualChildId(),
+ callback.onAutofillEvent(anchor, id.getVirtualChildId(),
AutofillCallback.EVENT_INPUT_SHOWN);
} else {
- mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
+ callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_SHOWN);
}
}
}
@@ -605,10 +729,12 @@ public final class AutofillManager {
private void setAutofilledIfValuesIs(@NonNull View view, @Nullable AutofillValue targetValue) {
AutofillValue currentValue = view.getAutofillValue();
if (Objects.equals(currentValue, targetValue)) {
- if (mLastAutofilledData == null) {
- mLastAutofilledData = new ParcelableMap(1);
+ synchronized (mLock) {
+ if (mLastAutofilledData == null) {
+ mLastAutofilledData = new ParcelableMap(1);
+ }
+ mLastAutofilledData.put(getAutofillId(view), targetValue);
}
- mLastAutofilledData.put(getAutofillId(view), targetValue);
view.setAutofilled(true);
}
}
@@ -646,11 +772,13 @@ public final class AutofillManager {
}
valuesByParent.put(id.getVirtualChildId(), value);
} else {
- // Mark the view as to be autofilled with 'value'
- if (mLastAutofilledData == null) {
- mLastAutofilledData = new ParcelableMap(itemCount - i);
+ synchronized (mLock) {
+ // Mark the view as to be autofilled with 'value'
+ if (mLastAutofilledData == null) {
+ mLastAutofilledData = new ParcelableMap(itemCount - i);
+ }
+ mLastAutofilledData.put(id, value);
}
- mLastAutofilledData.put(id, value);
view.autofill(value);
@@ -679,26 +807,41 @@ public final class AutofillManager {
}
private void requestHideFillUi(IBinder windowToken, AutofillId id) {
- if (getClient().autofillCallbackRequestHideFillUi() && mCallback != null) {
- final View anchor = findAchorView(windowToken, id);
+ final View anchor = findAchorView(windowToken, id);
+
+ AutofillCallback callback = null;
+ synchronized (mLock) {
+ if (getClientLocked().autofillCallbackRequestHideFillUi() && mCallback != null) {
+ callback = mCallback;
+ }
+ }
+
+ if (callback != null) {
if (id.isVirtual()) {
- mCallback.onAutofillEvent(anchor, id.getVirtualChildId(),
+ callback.onAutofillEvent(anchor, id.getVirtualChildId(),
AutofillCallback.EVENT_INPUT_HIDDEN);
} else {
- mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
+ callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_HIDDEN);
}
}
}
private void notifyNoFillUi(IBinder windowToken, AutofillId id) {
- if (mCallback != null) {
- final View anchor = findAchorView(windowToken, id);
+ final View anchor = findAchorView(windowToken, id);
+
+ AutofillCallback callback;
+ synchronized (mLock) {
+ callback = mCallback;
+ }
+
+ if (callback != null) {
if (id.isVirtual()) {
- mCallback.onAutofillEvent(anchor, id.getVirtualChildId(),
+ callback.onAutofillEvent(anchor, id.getVirtualChildId(),
AutofillCallback.EVENT_INPUT_UNAVAILABLE);
} else {
- mCallback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+ callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
}
+
}
}
@@ -787,7 +930,11 @@ public final class AutofillManager {
public void setState(boolean enabled) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(() -> afm.mEnabled = enabled);
+ afm.mContext.getMainThreadHandler().post(() -> {
+ synchronized (afm.mLock) {
+ afm.mEnabled = enabled;
+ }
+ });
}
}
@@ -808,8 +955,8 @@ public final class AutofillManager {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClient() != null) {
- afm.getClient().autofillCallbackAuthenticate(intent, fillInIntent);
+ if (afm.getClientLocked() != null) {
+ afm.getClientLocked().autofillCallbackAuthenticate(intent, fillInIntent);
}
});
}
@@ -821,7 +968,7 @@ public final class AutofillManager {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClient() != null) {
+ if (afm.getClientLocked() != null) {
afm.requestShowFillUi(windowToken, id, width,
height, anchorBounds, presenter);
}
@@ -834,7 +981,7 @@ public final class AutofillManager {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClient() != null) {
+ if (afm.getClientLocked() != null) {
afm.requestHideFillUi(windowToken, id);
}
});
@@ -846,7 +993,7 @@ public final class AutofillManager {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClient() != null) {
+ if (afm.getClientLocked() != null) {
afm.notifyNoFillUi(windowToken, id);
}
});
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 97210cc919a5..20d09ae13a60 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -30,14 +30,15 @@ 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,
+ int startSession(IBinder activityToken, IBinder windowToken, in IBinder appCallback,
in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
boolean hasCallback, int flags, String packageName);
- oneway void updateSession(in IBinder activityToken, in AutofillId id, in Rect bounds,
+ boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
+ void setWindow(int sessionId, in IBinder windowToken);
+ oneway void updateSession(int sessionId, in AutofillId id, in Rect bounds,
in AutofillValue value, int flags, int userId);
- oneway void finishSession(in IBinder activityToken, int userId);
- oneway void cancelSession(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);
+ void finishSession(int sessionId, int userId);
+ void cancelSession(int sessionId, int userId);
+ void setAuthenticationResult(in Bundle data, int sessionId, int userId);
+ oneway void setHasCallback(int sessionId, int userId, boolean hasIt);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a8e16c96acfa..9760f8111ec3 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1474,6 +1474,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
final Window.Callback cb = mWindow.getCallback();
if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
+ cb.onBeforeAttachedToWindow();
cb.onAttachedToWindow();
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 2bcc260b1c70..89645f457a55 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -333,23 +333,23 @@ public final class AutofillManagerService extends SystemService {
}
@Override
- public void setAuthenticationResult(Bundle data, IBinder activityToken, int userId) {
+ public void setAuthenticationResult(Bundle data, int sessionId, int userId) {
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
- service.setAuthenticationResultLocked(data, activityToken);
+ service.setAuthenticationResultLocked(data, sessionId, getCallingUid());
}
}
@Override
- public void setHasCallback(IBinder activityToken, int userId, boolean hasIt) {
+ public void setHasCallback(int sessionId, int userId, boolean hasIt) {
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
- service.setHasCallback(activityToken, hasIt);
+ service.setHasCallback(sessionId, getCallingUid(), hasIt);
}
}
@Override
- public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
+ public int startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
AutofillId autofillId, Rect bounds, AutofillValue value, int userId,
boolean hasCallback, int flags, String packageName) {
// TODO(b/33197203): make sure it's called by resumed / focused activity
@@ -369,41 +369,73 @@ public final class AutofillManagerService extends SystemService {
synchronized (mLock) {
final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
- service.startSessionLocked(activityToken, windowToken, appCallback,
- autofillId, bounds, value, hasCallback, flags, packageName);
+ return service.startSessionLocked(activityToken, getCallingUid(), windowToken,
+ appCallback, autofillId, bounds, value, hasCallback, flags, packageName);
}
}
@Override
- public void updateSession(IBinder activityToken, AutofillId id, Rect bounds,
+ public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
+ throws RemoteException {
+ activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
+ appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = mServicesCache.get(
+ UserHandle.getCallingUserId());
+ if (service != null) {
+ return service.restoreSession(sessionId, getCallingUid(), activityToken,
+ appCallback);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setWindow(int sessionId, IBinder windowToken) throws RemoteException {
+ windowToken = Preconditions.checkNotNull(windowToken, "windowToken");
+
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = mServicesCache.get(
+ UserHandle.getCallingUserId());
+ if (service != null) {
+ service.setWindow(sessionId, getCallingUid(), windowToken);
+ }
+ }
+ }
+
+ @Override
+ public void updateSession(int sessionId, AutofillId id, Rect bounds,
AutofillValue value, int flags, int userId) {
synchronized (mLock) {
final AutofillManagerServiceImpl service = mServicesCache.get(
UserHandle.getCallingUserId());
if (service != null) {
- service.updateSessionLocked(activityToken, id, bounds, value, flags);
+ service.updateSessionLocked(sessionId, getCallingUid(), id, bounds, value,
+ flags);
}
}
}
@Override
- public void finishSession(IBinder activityToken, int userId) {
+ public void finishSession(int sessionId, int userId) {
synchronized (mLock) {
final AutofillManagerServiceImpl service = mServicesCache.get(
UserHandle.getCallingUserId());
if (service != null) {
- service.finishSessionLocked(activityToken);
+ service.finishSessionLocked(sessionId, getCallingUid());
}
}
}
@Override
- public void cancelSession(IBinder activityToken, int userId) {
+ public void cancelSession(int sessionId, int userId) {
synchronized (mLock) {
final AutofillManagerServiceImpl service = mServicesCache.get(
UserHandle.getCallingUserId());
if (service != null) {
- service.cancelSessionLocked(activityToken);
+ service.cancelSessionLocked(sessionId, getCallingUid());
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index c8a5780fedf1..94d534a3d7fe 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -16,10 +16,11 @@
package com.android.server.autofill;
-import static android.service.autofill.AutofillService.EXTRA_ACTIVITY_TOKEN;
+import static android.service.autofill.AutofillService.EXTRA_SESSION_ID;
import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS;
import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
import static android.view.autofill.AutofillManager.FLAG_START_SESSION;
+import static android.view.autofill.AutofillManager.NO_SESSION;
import static com.android.server.autofill.Helper.DEBUG;
import static com.android.server.autofill.Helper.VERBOSE;
@@ -48,11 +49,11 @@ import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
@@ -65,6 +66,7 @@ import com.android.server.autofill.ui.AutoFillUI;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Random;
/**
* Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
@@ -74,6 +76,7 @@ import java.util.ArrayList;
final class AutofillManagerServiceImpl {
private static final String TAG = "AutofillManagerServiceImpl";
+ private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
static final int MSG_SERVICE_SAVE = 1;
@@ -85,6 +88,8 @@ final class AutofillManagerServiceImpl {
private RemoteCallbackList<IAutoFillManagerClient> mClients;
private AutofillServiceInfo mInfo;
+ private static final Random sRandom = new Random();
+
private final LocalLog mRequestsHistory;
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
@@ -94,7 +99,7 @@ final class AutofillManagerServiceImpl {
private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
switch (msg.what) {
case MSG_SERVICE_SAVE:
- handleSessionSave((IBinder) msg.obj);
+ handleSessionSave(msg.arg1);
break;
default:
Slog.w(TAG, "invalid msg on handler: " + msg);
@@ -113,7 +118,7 @@ final class AutofillManagerServiceImpl {
// TODO(b/33197203): need to make sure service is bound while callback is pending and/or
// use WeakReference
@GuardedBy("mLock")
- private final ArrayMap<IBinder, Session> mSessions = new ArrayMap<>();
+ private final SparseArray<Session> mSessions = new SparseArray<>();
/**
* Receiver of assist data from the app's {@link Activity}.
@@ -137,12 +142,12 @@ final class AutofillManagerServiceImpl {
return;
}
- final IBinder activityToken = receiverExtras.getBinder(EXTRA_ACTIVITY_TOKEN);
+ final int sessionId = receiverExtras.getInt(EXTRA_SESSION_ID);
final Session session;
synchronized (mLock) {
- session = mSessions.get(activityToken);
+ session = mSessions.get(sessionId);
if (session == null) {
- Slog.w(TAG, "no server session for activityToken " + activityToken);
+ Slog.w(TAG, "no server session for " + sessionId);
return;
}
// TODO(b/33197203): since service is fetching the data (to use for save later),
@@ -244,13 +249,17 @@ final class AutofillManagerServiceImpl {
if (!isEnabled()) {
return;
}
- final Session session = mSessions.get(activityToken);
- if (session == null) {
- Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
- return;
+
+ final int numSessions = mSessions.size();
+ for (int i = 0; i < numSessions; i++) {
+ final Session session = mSessions.valueAt(i);
+ if (session.getActivityTokenLocked().equals(activityToken)) {
+ session.callSaveLocked();
+ return;
+ }
}
- session.callSaveLocked();
+ Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
}
boolean addClientLocked(IAutoFillManagerClient client) {
@@ -261,61 +270,59 @@ final class AutofillManagerServiceImpl {
return isEnabled();
}
- void setAuthenticationResultLocked(Bundle data, IBinder activityToken) {
+ void setAuthenticationResultLocked(Bundle data, int sessionId, int uid) {
if (!isEnabled()) {
return;
}
- final Session session = mSessions.get(activityToken);
- if (session != null) {
+ final Session session = mSessions.get(sessionId);
+ if (session != null && uid == session.uid) {
session.setAuthenticationResultLocked(data);
}
}
- void setHasCallback(IBinder activityToken, boolean hasIt) {
+ void setHasCallback(int sessionId, int uid, boolean hasIt) {
if (!isEnabled()) {
return;
}
- final Session session = mSessions.get(activityToken);
- if (session != null) {
+ final Session session = mSessions.get(sessionId);
+ if (session != null && uid == session.uid) {
session.setHasCallback(hasIt);
}
}
- void startSessionLocked(@NonNull IBinder activityToken, @Nullable IBinder windowToken,
+ int startSessionLocked(@NonNull IBinder activityToken, int uid, @Nullable IBinder windowToken,
@NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
int flags, @NonNull String packageName) {
if (!isEnabled()) {
- return;
+ return 0;
}
- final String historyItem = "s=" + mInfo.getServiceInfo().packageName
- + " u=" + mUserId + " a=" + activityToken
- + " i=" + autofillId + " b=" + virtualBounds + " hc=" + hasCallback
- + " f=" + flags;
- mRequestsHistory.log(historyItem);
-
- // TODO(b/33197203): Handle scenario when user forced autofill after app was already
- // autofilled.
- final Session session = mSessions.get(activityToken);
- if (session != null) {
- // Already started...
- return;
+ final Session newSession = createSessionByTokenLocked(activityToken, uid, windowToken,
+ appCallbackToken, hasCallback, flags, packageName);
+ if (newSession == null) {
+ return NO_SESSION;
}
- final Session newSession = createSessionByTokenLocked(activityToken,
- windowToken, appCallbackToken, hasCallback, flags, packageName);
+ final String historyItem =
+ "id=" + newSession.id + " uid=" + uid + " s=" + mInfo.getServiceInfo().packageName
+ + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds + " hc=" +
+ hasCallback + " f=" + flags;
+ mRequestsHistory.log(historyItem);
+
newSession.updateLocked(autofillId, virtualBounds, value, FLAG_START_SESSION);
+
+ return newSession.id;
}
- void finishSessionLocked(IBinder activityToken) {
+ void finishSessionLocked(int sessionId, int uid) {
if (!isEnabled()) {
return;
}
- final Session session = mSessions.get(activityToken);
- if (session == null) {
- Slog.w(TAG, "finishSessionLocked(): no session for " + activityToken);
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.w(TAG, "finishSessionLocked(): no session for " + sessionId + "(" + uid + ")");
return;
}
@@ -328,26 +335,39 @@ final class AutofillManagerServiceImpl {
}
}
- void cancelSessionLocked(IBinder activityToken) {
+ void cancelSessionLocked(int sessionId, int uid) {
if (!isEnabled()) {
return;
}
- final Session session = mSessions.get(activityToken);
- if (session == null) {
- Slog.w(TAG, "cancelSessionLocked(): no session for " + activityToken);
+ final Session session = mSessions.get(sessionId);
+ if (session == null || uid != session.uid) {
+ Slog.w(TAG, "cancelSessionLocked(): no session for " + sessionId + "(" + uid + ")");
return;
}
session.removeSelfLocked();
}
- private Session createSessionByTokenLocked(@NonNull IBinder activityToken,
+ private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
@Nullable IBinder windowToken, @NonNull IBinder appCallbackToken, boolean hasCallback,
int flags, @NonNull String packageName) {
+ // use random ids so that one app cannot know that another app creates sessions
+ int sessionId;
+ int tries = 0;
+ do {
+ tries++;
+ if (tries > MAX_SESSION_ID_CREATE_TRIES) {
+ Log.w(TAG, "Cannot create session in " + MAX_SESSION_ID_CREATE_TRIES + " tries");
+ return null;
+ }
+
+ sessionId = sRandom.nextInt();
+ } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
+
final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
- activityToken, windowToken, appCallbackToken, hasCallback, flags,
+ sessionId, uid, activityToken, windowToken, appCallbackToken, hasCallback, flags,
mInfo.getServiceInfo().getComponentName(), packageName);
- mSessions.put(activityToken, newSession);
+ mSessions.put(newSession.id, newSession);
/*
* TODO(b/33197203): apply security checks below:
@@ -358,7 +378,7 @@ final class AutofillManagerServiceImpl {
*/
try {
final Bundle receiverExtras = new Bundle();
- receiverExtras.putBinder(EXTRA_ACTIVITY_TOKEN, activityToken);
+ receiverExtras.putInt(EXTRA_SESSION_ID, sessionId);
final long identity = Binder.clearCallingIdentity();
try {
if (!ActivityManager.getService().requestAutofillData(mAssistReceiver,
@@ -374,12 +394,51 @@ final class AutofillManagerServiceImpl {
return newSession;
}
- void updateSessionLocked(IBinder activityToken, AutofillId autofillId, Rect virtualBounds,
+ /**
+ * Restores a session after an activity was temporarily destroyed.
+ *
+ * @param sessionId The id of the session to restore
+ * @param uid UID of the process that tries to restore the session
+ * @param activityToken The new instance of the activity
+ * @param appCallback The callbacks to the activity
+ */
+ boolean restoreSession(int sessionId, int uid, @NonNull IBinder activityToken,
+ @NonNull IBinder appCallback) {
+ final Session session = mSessions.get(sessionId);
+
+ if (session == null || uid != session.uid) {
+ return false;
+ } else {
+ session.switchActivity(activityToken, appCallback);
+ return true;
+ }
+ }
+
+ /**
+ * Set the window the UI should get attached to
+ *
+ * @param sessionId The id of the session to restore
+ * @param uid UID of the process that tries to restore the session
+ * @param windowToken The window the activity is now in
+ */
+ boolean setWindow(int sessionId, int uid, @NonNull IBinder windowToken) {
+ final Session session = mSessions.get(sessionId);
+
+ if (session == null || uid != session.uid) {
+ return false;
+ } else {
+ session.switchWindow(windowToken);
+ return true;
+ }
+ }
+
+ void updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
AutofillValue value, int flags) {
- final Session session = mSessions.get(activityToken);
- if (session == null) {
+ final Session session = mSessions.get(sessionId);
+ if (session == null || session.uid != uid) {
if (VERBOSE) {
- Slog.v(TAG, "updateSessionLocked(): session gone for " + activityToken);
+ Slog.v(TAG, "updateSessionLocked(): session gone for " + sessionId + "(" + uid
+ + ")");
}
return;
}
@@ -387,15 +446,15 @@ final class AutofillManagerServiceImpl {
session.updateLocked(autofillId, virtualBounds, value, flags);
}
- void removeSessionLocked(IBinder activityToken) {
- mSessions.remove(activityToken);
+ void removeSessionLocked(int sessionId) {
+ mSessions.remove(sessionId);
}
- private void handleSessionSave(IBinder activityToken) {
+ private void handleSessionSave(int sessionId) {
synchronized (mLock) {
- final Session session = mSessions.get(activityToken);
+ final Session session = mSessions.get(sessionId);
if (session == null) {
- Slog.w(TAG, "handleSessionSave(): already gone: " + activityToken);
+ Slog.w(TAG, "handleSessionSave(): already gone: " + sessionId);
return;
}
@@ -408,8 +467,9 @@ final class AutofillManagerServiceImpl {
Slog.v(TAG, "destroyLocked()");
}
- for (Session session : mSessions.values()) {
- session.destroyLocked();
+ final int numSessions = mSessions.size();
+ for (int i = 0; i < numSessions; i++) {
+ mSessions.valueAt(i).destroyLocked();
}
mSessions.clear();
}
@@ -463,15 +523,16 @@ final class AutofillManagerServiceImpl {
}
void destroySessionsLocked() {
- for (Session session : mSessions.values()) {
- session.removeSelf();
+ while (mSessions.size() > 0) {
+ mSessions.valueAt(0).removeSelf();
}
}
void listSessionsLocked(ArrayList<String> output) {
- for (IBinder activityToken : mSessions.keySet()) {
+ final int numSessions = mSessions.size();
+ for (int i = 0; i < numSessions; i++) {
output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName()
- : null) + ":" + activityToken);
+ : null) + ":" + mSessions.keyAt(i));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index c3cbf18135fc..3ef9cd52762d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -38,12 +38,10 @@ import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
import android.metrics.LogMaker;
-import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
@@ -90,14 +88,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private static final String TAG = "AutofillSession";
private final AutofillManagerServiceImpl mService;
- private final IBinder mActivityToken;
- private final IBinder mWindowToken;
private final HandlerCaller mHandlerCaller;
private final Object mLock;
private final AutoFillUI mUi;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
+ /** Id of the session */
+ public final int id;
+
+ /** uid the session is for */
+ public final int uid;
+
+ @GuardedBy("mLock")
+ @NonNull private IBinder mActivityToken;
+
+ @GuardedBy("mLock")
+ @NonNull private IBinder mWindowToken;
+
/** Package name of the app that is auto-filled */
@NonNull private final String mPackageName;
@@ -110,7 +118,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
@Nullable private AutofillId mCurrentViewId;
- private final IAutoFillManagerClient mClient;
+ @GuardedBy("mLock")
+ private IAutoFillManagerClient mClient;
@GuardedBy("mLock")
RemoteFillService mRemoteFillService;
@@ -156,9 +165,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
@NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
- @NonNull Object lock, @NonNull IBinder activityToken,
+ @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
@Nullable IBinder windowToken, @NonNull IBinder client, boolean hasCallback,
int flags, @NonNull ComponentName componentName, @NonNull String packageName) {
+ id = sessionId;
+ this.uid = uid;
mService = service;
mLock = lock;
mUi = ui;
@@ -169,21 +180,43 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mHasCallback = hasCallback;
mPackageName = packageName;
mFlags = flags;
-
mClient = IAutoFillManagerClient.Stub.asInterface(client);
- try {
- client.linkToDeath(() -> {
- if (VERBOSE) {
- Slog.v(TAG, "app binder died");
- }
- removeSelf();
- }, 0);
- } catch (RemoteException e) {
- Slog.w(TAG, "linkToDeath() on mClient failed: " + e);
+ mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName);
+ }
+
+ /**
+ * Gets the currently registered activity token
+ *
+ * @return The activity token
+ */
+ public IBinder getActivityTokenLocked() {
+ return mActivityToken;
+ }
+
+ /**
+ * Sets new window for this session.
+ *
+ * @param newWindow The window the Ui should be attached to. Can be {@code null} if no
+ * further UI is needed.
+ */
+ void switchWindow(@NonNull IBinder newWindow) {
+ synchronized (mLock) {
+ mWindowToken = newWindow;
}
+ }
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName);
+ /**
+ * Sets new activity and client for this session.
+ *
+ * @param newActivity The token of the new activity
+ * @param newClient The client receiving autofill callbacks
+ */
+ void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) {
+ synchronized (mLock) {
+ mActivityToken = newActivity;
+ mClient = IAutoFillManagerClient.Stub.asInterface(newClient);
+ }
}
// FillServiceCallbacks
@@ -301,7 +334,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void save() {
mHandlerCaller.getHandler()
- .obtainMessage(AutofillManagerServiceImpl.MSG_SERVICE_SAVE, mActivityToken)
+ .obtainMessage(AutofillManagerServiceImpl.MSG_SERVICE_SAVE, id, 0)
.sendToTarget();
}
@@ -337,10 +370,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// AutoFillUiCallback
@Override
public void requestHideFillUi(AutofillId id) {
- try {
- mClient.requestHideFillUi(mWindowToken, id);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error requesting to hide fill UI", e);
+ synchronized (mLock) {
+ try {
+ mClient.requestHideFillUi(mWindowToken, id);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error requesting to hide fill UI", e);
+ }
}
}
@@ -402,7 +437,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final SaveInfo saveInfo = response.getSaveInfo();
if (DEBUG) {
- Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo);
+ Slog.d(TAG,
+ "showSaveLocked(): mResponses=" + mResponses + ", mViewStates=" + mViewStates);
}
/*
@@ -664,17 +700,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
private void notifyUnavailableToClient() {
- if (mCurrentViewId == null) {
- // TODO(b/33197203): temporary sanity check; should never happen
- Slog.w(TAG, "notifyUnavailable(): mCurrentViewId is null");
- return;
- }
- if (!mHasCallback) return;
- try {
- mClient.notifyNoFillUi(mWindowToken, mCurrentViewId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken
- + " id=" + mCurrentViewId, e);
+ synchronized (mLock) {
+ if (mCurrentViewId == null) {
+ // TODO(b/33197203): temporary sanity check; should never happen
+ Slog.w(TAG, "notifyUnavailable(): mCurrentViewId is null");
+ return;
+ }
+ if (!mHasCallback) return;
+ try {
+ mClient.notifyNoFillUi(mWindowToken, mCurrentViewId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken
+ + " id=" + mCurrentViewId, e);
+ }
}
}
@@ -683,12 +721,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.d(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response);
}
- if (mCurrentViewId == null) {
- // TODO(b/33197203): temporary sanity check; should never happen
- Slog.w(TAG, "processResponseLocked(): mCurrentViewId is null");
- return;
- }
-
if (mResponses == null) {
mResponses = new ArrayList<>(4);
}
@@ -699,6 +731,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
setViewStatesLocked(response, ViewState.STATE_FILLABLE);
+ if (mCurrentViewId == null) {
+ return;
+ }
+
if ((mFlags & FLAG_MANUAL_REQUEST) != 0 && response.getDatasets() != null
&& response.getDatasets().size() == 1) {
Slog.d(TAG, "autofilling manual request directly");
@@ -807,13 +843,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private void startAuthentication(IntentSender intent, Intent fillInIntent) {
try {
- mClient.authenticate(intent, fillInIntent);
+ synchronized (mLock) {
+ mClient.authenticate(intent, fillInIntent);
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Error launching auth intent", e);
}
}
void dumpLocked(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("id: "); pw.println(id);
+ pw.print(prefix); pw.print("uid: "); pw.println(uid);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags);
pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses);
@@ -912,6 +952,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.v(TAG, "removeSelfLocked()");
}
destroyLocked();
- mService.removeSessionLocked(mActivityToken);
+ mService.removeSessionLocked(id);
}
}