OSDN Git Service

Refactor AutoFill server code.
authorJason Long <jasonlong@google.com>
Sat, 28 Jan 2017 05:21:06 +0000 (21:21 -0800)
committerJason Long <jasonlong@google.com>
Tue, 31 Jan 2017 08:02:31 +0000 (00:02 -0800)
With this change, data flows strictly like so:

AFManagerService -> AFManagerServiceImpl -> Session -> ViewSession

A ViewSession is introduced to 1) know when we can show the fill UI,
and 2) know when we need to create or update the current fill UI.

Each class has its own responsibilities:

* AFManagerService - interface to app process
* AFManagerServiceImpl - per user id
* Session - per activity
** AutoFillUI - per activity (can consider moving to ViewSession)
* ViewSession - per view

Bug: 34633695
Change-Id: Ia197a5c40219379ab6a95d0cf5c39ac4f5470d0e
Test: CTS, Manual with GMS Core

services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
services/autofill/java/com/android/server/autofill/AutoFillUI.java

index 8ce5278..392290c 100644 (file)
@@ -81,7 +81,6 @@ public final class AutoFillManagerService extends SystemService {
     protected static final int MSG_REQUEST_AUTO_FILL = 3;
 
     private final AutoFillManagerServiceStub mServiceStub;
-    private final AutoFillUI mUi;
     private final Context mContext;
     private final ContentResolver mResolver;
 
@@ -146,7 +145,7 @@ public final class AutoFillManagerService extends SystemService {
         mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
 
         mContext = context;
-        mUi = new AutoFillUI(context, this, mLock);
+
         mResolver = context.getContentResolver();
         mServiceStub = new AutoFillManagerServiceStub();
     }
@@ -186,7 +185,7 @@ public final class AutoFillManagerService extends SystemService {
             if (DEBUG) Slog.d(TAG, "no service info for " + serviceComponent);
             return null;
         }
-        return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, mRequestsHistory,
+        return new AutoFillManagerServiceImpl(this, mContext, mLock, mRequestsHistory,
                 FgThread.getHandler(), userId, serviceInfo.applicationInfo.uid, serviceComponent,
                 SERVICE_BINDING_LIFETIME_MS);
     }
@@ -326,7 +325,6 @@ public final class AutoFillManagerService extends SystemService {
                     }
                 }
             }
-            mUi.dump(pw);
             pw.println("Requests history:");
             mRequestsHistory.reverseDump(fd, pw, args);
         }
index 0dd891c..2dcb31c 100644 (file)
@@ -19,7 +19,9 @@ package com.android.server.autofill;
 import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_ERROR;
 import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_REQUESTED;
 import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_SUCCESS;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
 import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
+import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_SHOW;
 import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_HIDE;
 
 import static com.android.server.autofill.Helper.DEBUG;
@@ -56,6 +58,7 @@ import android.service.autofill.IAutoFillAppCallback;
 import android.service.autofill.IAutoFillServerCallback;
 import android.service.autofill.IAutoFillService;
 import android.service.voice.VoiceInteractionSession;
+import android.util.ArrayMap;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
@@ -63,6 +66,7 @@ import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
 import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 
@@ -74,6 +78,7 @@ import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Bridge between the {@code system_server}'s {@link AutoFillManagerService} and the
@@ -96,12 +101,14 @@ final class AutoFillManagerServiceImpl {
     private final Object mLock;
     private final AutoFillServiceInfo mInfo;
     private final AutoFillManagerService mManagerService;
-    private final AutoFillUI mUi;
 
     // Token used for fingerprint authentication
     // TODO(b/33197203): create on demand?
     private final IBinder mAuthToken = new Binder();
 
+    private final IFingerprintService mFingerprintService =
+            IFingerprintService.Stub.asInterface(ServiceManager.getService("fingerprint"));
+
     @GuardedBy("mLock")
     private final List<QueuedRequest> mQueuedRequests = new LinkedList<>();
 
@@ -204,11 +211,10 @@ final class AutoFillManagerServiceImpl {
     // Estimated time when the service will be evicted from the cache.
     long mEstimateTimeOfDeath;
 
-    AutoFillManagerServiceImpl(AutoFillManagerService managerService, AutoFillUI ui,
-            Context context, Object lock, LocalLog requestsHistory, Handler handler, int userId,
-            int uid, ComponentName component, long ttl) {
+    AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock,
+            LocalLog requestsHistory, Handler handler, int userId, int uid, ComponentName component,
+            long ttl) {
         mManagerService = managerService;
-        mUi = ui;
         mContext = context;
         mLock = lock;
         mRequestsHistory = requestsHistory;
@@ -277,8 +283,8 @@ final class AutoFillManagerServiceImpl {
         requestAutoFillLocked(activityToken, autoFillId, bounds, flags, true);
     }
 
-    private void requestAutoFillLocked(IBinder activityToken, AutoFillId autoFillId, Rect bounds,
-            int flags, boolean queueIfNecessary) {
+    private void requestAutoFillLocked(IBinder activityToken, @Nullable AutoFillId autoFillId,
+          @Nullable Rect bounds, int flags, boolean queueIfNecessary) {
         if (mService == null) {
             if (!queueIfNecessary) {
                 Slog.w(TAG, "requestAutoFillLocked(): service is null");
@@ -294,56 +300,35 @@ final class AutoFillManagerServiceImpl {
             return;
         }
 
-        final Session session = getSessionByTokenLocked(activityToken);
+        final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags
+                + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds;
+        mRequestsHistory.log(historyItem);
 
-        if (session != null) {
-            // Session already exist, update UI instead...
-            /*
-             * TODO(b/33197203): currently, it's always reusing the session, regardless of the
-             * requested autoFillId, but it should start a new session 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`, session should be reused, but
-             * when user taps `address`, it should start a new session (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 (DEBUG) {
-                Slog.d(TAG, "requestAutoFillLocked(): reusing session for token "
-                        + activityToken + ", id " + autoFillId + " and flags " + flags);
-            }
+        // TODO(b/33197203): Handle partitioning
+        Session session = getOrCreateSessionByTokenLocked(activityToken);
+        if (DEBUG) Slog.d(TAG, "using Session: " + session.mId);
 
-            if ((flags & FLAG_UPDATE_UI_HIDE) != 0) {
-                // TODO(b/33197203): handle it?
-                if (DEBUG) Slog.d(TAG, "ignoring FLAG_UPDATE_UI_HIDE request for " + autoFillId);
+        session.updateAutoFillInput(flags, autoFillId, null, bounds);
+    }
 
-                return;
+    private Session getOrCreateSessionByTokenLocked(IBinder activityToken) {
+        final int size = mSessions.size();
+        for (int i = 0; i < size; i++) {
+            final Session session = mSessions.valueAt(i);
+            if (activityToken.equals(session.mActivityToken.get())) {
+                return session;
             }
-
-            session.mCurrentAutoFillId = autoFillId;
-            session.mCurrentBounds = bounds;
-            mUi.showResponse(mUserId, session.mId, autoFillId, bounds, session.mCurrentResponse);
-            return;
         }
+        return createSessionByTokenLocked(activityToken);
+    }
 
+    private Session createSessionByTokenLocked(IBinder activityToken) {
         final int sessionId = ++sSessionIdCounter;
-        if (DEBUG) {
-            Slog.d(TAG, "requestAutoFillLocked(): new session (id=" + sessionId + " for token "
-                    + activityToken + " and autoFillId " + autoFillId);
-        }
+        if (DEBUG) Slog.d(TAG, "creating Session: " + sessionId);
 
-        final Session newSession = new Session(sessionId, activityToken, autoFillId, bounds);
+        final Session newSession = new Session(sessionId, activityToken);
         mSessions.put(sessionId, newSession);
 
-        final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags
-                + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds;
-        mRequestsHistory.log(historyItem);
-
         /*
          * TODO(b/33197203): apply security checks below:
          * - checks if disabled by secure settings / device policy
@@ -353,7 +338,8 @@ final class AutoFillManagerServiceImpl {
          */
         try {
             // TODO(b/33197203): add MetricsLogger call
-            if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken, flags)) {
+            if (!mAm.requestAutoFillData(
+                    mAssistReceiver, null, sessionId, activityToken, AUTO_FILL_FLAG_TYPE_FILL)) {
                 // TODO(b/33197203): might need a way to warn user (perhaps a new method on
                 // AutoFillService).
                 Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
@@ -361,75 +347,7 @@ final class AutoFillManagerServiceImpl {
         } catch (RemoteException e) {
             // Should not happen, it's a local call.
         }
-    }
-
-    /**
-     * Called by UI to trigger a save request to the service.
-     */
-    void requestSaveLocked(int sessionId) {
-        // TODO(b/33197203): add MetricsLogger call
-        // TODO(b/33197203): use handler?
-        // TODO(b/33197203): show error on UI on Slog.w situations below???
-
-        if (mService == null) {
-            Slog.w(TAG, "requestSave(): service is null");
-            return;
-        }
-        final Session session = mSessions.get(sessionId);
-        if (session == null) {
-            Slog.w(TAG, "requestSave(): no session with id " + sessionId);
-            return;
-        }
-        final IBinder activityToken = session.mActivityToken.get();
-        if (activityToken == null) {
-            Slog.w(TAG, "activity token for session " + sessionId + " already GCed");
-            return;
-        }
-
-        /*
-         * TODO(b/33197203): apply security checks below:
-         * - checks if disabled by secure settings / device policy
-         * - log operation using noteOp()
-         * - check flags
-         * - display disclosure if needed
-         */
-        try {
-            /* TODO(b/33197203): refactor save logic so it uses a cached AssistStructure, and get
-               the extras to be sent to the service based on the response / dataset in the session.
-               Something like:
-           final Bundle extras = (responseExtras == null && datasetExtras == null)
-                  ? null : new Bundle();
-            if (responseExtras != null) {
-                if (DEBUG) Slog.d(TAG, "response extras on save notification: " +
-                        bundleToString(responseExtras));
-                extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras);
-            }
-            if (datasetExtras != null) {
-                if (DEBUG) Slog.d(TAG, "dataset extras on save notification: " +
-                        bundleToString(datasetExtras));
-                extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
-            }
-
-             */
-
-            if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken,
-                    AUTO_FILL_FLAG_TYPE_SAVE)) {
-                Slog.w(TAG, "failed to save for " + activityToken);
-            }
-        } catch (RemoteException e) {
-            // Should not happen, it's a local call.
-        }
-    }
-
-    private Session getSessionByTokenLocked(IBinder activityToken) {
-        final int size = mSessions.size();
-        for (int i = 0; i < size; i++) {
-            final Session session = mSessions.valueAt(i);
-            if (activityToken.equals(session.mActivityToken.get())) {
-                return session;
-            }
-        }
-        return null;
+        return newSession;
     }
 
     void stopLocked() {
@@ -459,35 +377,6 @@ final class AutoFillManagerServiceImpl {
         }
     }
 
-    /**
-     * Called by {@link AutoFillUI} to fill an activity after the user selected a dataset.
-     */
-    void autoFillApp(int sessionId, Dataset dataset) {
-        // TODO(b/33197203): add MetricsLogger call
-
-        if (dataset == null) {
-            Slog.w(TAG, "autoFillApp(): no dataset for callback id " + sessionId);
-            return;
-        }
-
-
-        final Session session;
-        synchronized (mLock) {
-            session = mSessions.get(sessionId);
-            if (session == null) {
-                Slog.w(TAG, "autoFillApp(): no session with id " + sessionId);
-                return;
-            }
-            if (session.mAppCallback == null) {
-                Slog.w(TAG, "autoFillApp(): no app callback for session " + sessionId);
-                return;
-            }
-
-            // TODO(b/33197203): use a handler?
-            session.autoFill(dataset);
-        }
-    }
-
     void removeSessionLocked(int id) {
         if (DEBUG) Slog.d(TAG, "Removing session " + id);
         mSessions.remove(id);
@@ -495,43 +384,6 @@ final class AutoFillManagerServiceImpl {
         // TODO(b/33197203): notify mService so it can invalidate the FillCallback / SaveCallback?
     }
 
-    /**
-     * Notifies the result of a {@link FillResponse} authentication request to the service.
-     *
-     * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after user
-     * used the fingerprint sensors to authenticate.
-     */
-    void notifyResponseAuthenticationResult(Bundle extras, int flags) {
-        if (DEBUG) Slog.d(TAG, "notifyResponseAuthenticationResult(): flags=" + flags
-                + ", extras=" + bundleToString(extras));
-
-        synchronized (mLock) {
-            try {
-                mService.authenticateFillResponse(extras, flags);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Error sending authentication result back to service: " + e);
-            }
-        }
-    }
-
-    /**
-     * Notifies the result of a {@link Dataset} authentication request to the service.
-     *
-     * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after
-     * it gets the results from a fingerprint authentication.
-     */
-    void notifyDatasetAuthenticationResult(Bundle extras, int flags) {
-        if (DEBUG) Slog.d(TAG, "notifyDatasetAuthenticationResult(): flags=" + flags
-                + ", extras=" + bundleToString(extras));
-        synchronized (mLock) {
-            try {
-                mService.authenticateDataset(extras, flags);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Error sending authentication result back to service: " + e);
-            }
-        }
-    }
-
     void dumpLocked(String prefix, PrintWriter pw) {
         if (!mValid) {
             pw.print("  NOT VALID: ");
@@ -612,8 +464,78 @@ final class AutoFillManagerServiceImpl {
     }
 
     /**
-     * A bridge between the {@link AutoFillService} implementation and the activity being
-     * auto-filled (represented through the {@link IAutoFillAppCallback}).
+     * State for a given view with a AutoFillId.
+     *
+     * <p>This class holds state about a view and calls its listener when the fill UI is ready to
+     * be displayed for the view.
+     */
+    static final class ViewState {
+        interface Listener {
+            /**
+             * Called when the fill UI is ready to be shown for this view.
+             */
+            void onFillReady(ViewState viewState, FillResponse fillResponse, Rect bounds,
+                    @Nullable AutoFillValue value);
+        }
+
+        private final Listener mListener;
+        @Nullable
+        private FillResponse mResponse;
+        private AutoFillValue mAutoFillValue;
+        private Rect mBounds;
+
+        ViewState(Listener listener) {
+            mListener = listener;
+        }
+
+        /**
+         * Response should only be set once.
+         */
+        void setResponse(FillResponse response) {
+            if (mResponse != null) {
+                Slog.e(TAG, "ViewState response set more than once");
+                return;
+            }
+            mResponse = response;
+
+            maybeCallOnFillReady();
+        }
+
+        void update(@Nullable AutoFillValue autoFillValue, @Nullable Rect bounds) {
+            if (autoFillValue != null) {
+                mAutoFillValue = autoFillValue;
+            }
+            if (bounds != null) {
+                mBounds = bounds;
+            }
+
+            maybeCallOnFillReady();
+        }
+
+        /**
+         * Calls {@link Listener#onFillReady(ViewState, FillResponse, Rect, AutoFillValue)} if the
+         * fill UI is ready to be displayed (i.e. when response and bounds are set).
+         */
+        void maybeCallOnFillReady() {
+            if (mResponse != null && mBounds != null) {
+                mListener.onFillReady(this, mResponse, mBounds, mAutoFillValue);
+            }
+        }
+
+        @Override
+        public String toString() {
+            if (!DEBUG) return super.toString();
+
+            return "ViewState: [response=" + mResponse + ", value=" + mAutoFillValue
+                    + ", bounds=" + mBounds + "]";
+        }
+    }
+
+    /**
+     * A session for a given activity.
+     *
+     * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track
+     * of the current view session to display the appropriate UI.
      *
      * <p>Although the auto-fill requests and callbacks are stateless from the service's point of
      * view, we need to keep state in the framework side for cases such as authentication. For
@@ -625,23 +547,23 @@ final class AutoFillManagerServiceImpl {
     // - On all authentication scenarios.
     // - When user does not interact back after a while.
     // - When service is unbound.
-    private final class Session {
+    final class Session implements ViewState.Listener {
 
-        private final int mId;
+        private final AutoFillUI mUi;
+        final int mId;
         private final WeakReference<IBinder> mActivityToken;
 
-        private IAutoFillAppCallback mAppCallback;
-
-        // Current view where the auto-fill bar is displayed
         @GuardedBy("mLock")
-        private AutoFillId mCurrentAutoFillId;
+        private final Map<AutoFillId, ViewState> mViewStates = new ArrayMap<>();
         @GuardedBy("mLock")
-        private Rect mCurrentBounds;
-        @GuardedBy("mLock")
-        private FillResponse mCurrentResponse;
+        @Nullable
+        private ViewState mCurrentViewState;
 
-        private final IFingerprintService mFingerprintService;
+        private IAutoFillAppCallback mAppCallback;
 
+        // TODO(b/33197203): Get a response per view instead of per activity.
+        @GuardedBy("mLock")
+        private FillResponse mCurrentResponse;
         @GuardedBy("mLock")
         private FillResponse mResponseRequiringAuth;
         @GuardedBy("mLock")
@@ -683,7 +605,7 @@ final class AutoFillManagerServiceImpl {
                         notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
                                 FLAG_AUTHENTICATION_SUCCESS);
                     } else {
-                        autoFillAppLocked(mDatasetRequiringAuth, true);
+                        autoFillApp(mDatasetRequiringAuth);
                     }
                 } else if (mResponseRequiringAuth != null) {
                     final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
@@ -697,7 +619,7 @@ final class AutoFillManagerServiceImpl {
                     Slog.w(TAG, "onAuthenticationSucceeded(): no response or dataset");
                 }
 
-                mUi.dismissFingerprintRequest(mUserId, true);
+                mUi.dismissFingerprintRequest(true);
             }
 
             @Override
@@ -721,7 +643,7 @@ final class AutoFillManagerServiceImpl {
                     Slog.w(TAG, "onError(): no response or dataset");
                 }
 
-                mUi.dismissFingerprintRequest(mUserId, false);
+                mUi.dismissFingerprintRequest(false);
             }
 
             @Override
@@ -741,7 +663,6 @@ final class AutoFillManagerServiceImpl {
                 // TODO(b/33197203): add MetricsLogger call
                 if (response == null) {
                     if (DEBUG) Slog.d(TAG, "showResponse(): null response");
-
                     removeSelf();
                     return;
                 }
@@ -805,20 +726,16 @@ final class AutoFillManagerServiceImpl {
                 if (DEBUG) Log.d(TAG, "unlockDataset(): dataset=" + dataset + ", flags=" + flags);
 
                 if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
-                    autoFillAppLocked(dataset != null ? dataset : mDatasetRequiringAuth, true);
+                    autoFillApp(dataset != null ? dataset : mDatasetRequiringAuth);
                     return;
                 }
-                removeSelf();
             }
         };
 
-        private Session(int id, IBinder activityToken, AutoFillId autoFillId, Rect bounds) {
-            this.mId = id;
-            this.mActivityToken = new WeakReference<>(activityToken);
-            this.mCurrentAutoFillId = autoFillId;
-            this.mCurrentBounds = bounds;
-            this.mFingerprintService = IFingerprintService.Stub
-                    .asInterface(ServiceManager.getService("fingerprint"));
+        private Session(int id, IBinder activityToken) {
+            mUi = new AutoFillUI(mContext, this);
+            mId = id;
+            mActivityToken = new WeakReference<>(activityToken);
         }
 
         void setAppCallback(IBinder appBinder) {
@@ -834,6 +751,54 @@ final class AutoFillManagerServiceImpl {
             mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
         }
 
+        void updateAutoFillInput(int flags, AutoFillId autoFillId,
+                @Nullable AutoFillValue autoFillValue, @Nullable Rect bounds) {
+            synchronized (mLock) {
+                ViewState viewState = mViewStates.get(autoFillId);
+                if (viewState == null) {
+                    viewState = new ViewState(this);
+                    mViewStates.put(autoFillId, viewState);
+                }
+
+                if ((flags & FLAG_UPDATE_UI_SHOW) != 0) {
+                    // Remove the UI if the ViewState has changed.
+                    if (mCurrentViewState != viewState) {
+                        mUi.hideFillUi();
+                        mCurrentViewState = viewState;
+                    }
+
+                    // If the ViewState is ready to be displayed, onReady() will be called.
+                    viewState.update(autoFillValue, bounds);
+
+                    // TODO(b/33197203): Remove when there is a response per activity.
+                    if (mCurrentResponse != null) {
+                        viewState.setResponse(mCurrentResponse);
+                    }
+                } else if ((flags & FLAG_UPDATE_UI_HIDE) != 0) {
+                    if (mCurrentViewState == viewState) {
+                        mUi.hideFillUi();
+                        mCurrentViewState = null;
+                    }
+                } else {
+                    Slog.w(TAG, "unknown flags " + flags);
+                }
+            }
+        }
+
+        @Override
+        public void onFillReady(ViewState viewState, FillResponse response, Rect bounds,
+                @Nullable AutoFillValue value) {
+            String filterText = "";
+            if (value != null) {
+                // TODO(b/33197203): Handle other AutoFillValue types
+                final CharSequence text = value.getTextValue();
+                if (text != null) {
+                    filterText = text.toString();
+                }
+            }
+            mUi.showFillUi(viewState, response.getDatasets(), bounds, filterText);
+        }
+
         private void showResponseLocked(FillResponse response, boolean authRequired) {
             if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly
                     + ", authRequired=" + authRequired +"):" + response);
@@ -845,7 +810,7 @@ final class AutoFillManagerServiceImpl {
                     final Dataset dataset = datasets.get(0);
                     if (DEBUG) Slog.d(TAG, "auto-filling directly from auth: " + dataset);
 
-                    autoFillAppLocked(dataset, true);
+                    autoFillApp(dataset);
                     return;
                 }
             }
@@ -853,7 +818,10 @@ final class AutoFillManagerServiceImpl {
             if (!authRequired) {
                 // TODO(b/33197203): add MetricsLogger call
                 mCurrentResponse = response;
-                mUi.showResponse(mUserId, mId, mCurrentAutoFillId, mCurrentBounds, mCurrentResponse);
+                // TODO(b/33197203): Consider using mCurrentResponse, depends on partitioning design
+                if (mCurrentViewState != null) {
+                    mCurrentViewState.setResponse(mCurrentResponse);
+                }
                 return;
             }
 
@@ -869,7 +837,7 @@ final class AutoFillManagerServiceImpl {
                 scanFingerprint(response.getCryptoObjectOpId());
             }
             // Displays the message asking the user to tap (or fingerprint) for AutoFill.
-            mUi.showFillResponseAuthenticationRequest(mUserId, mId, requiresFingerprint,
+            mUi.showFillResponseAuthenticationRequest(requiresFingerprint,
                     response.getExtras(), response.getFlags());
         }
 
@@ -877,7 +845,7 @@ final class AutoFillManagerServiceImpl {
             synchronized (mLock) {
                 // Autofill it directly...
                 if (!dataset.isAuthRequired()) {
-                    autoFillAppLocked(dataset, true);
+                    autoFillApp(dataset);
                     return;
                 }
 
@@ -906,30 +874,114 @@ final class AutoFillManagerServiceImpl {
         void dumpLocked(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("mId: "); pw.println(mId);
             pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken.get());
-            pw.print(prefix); pw.print("mCurrentAutoFillId: "); pw.println(mCurrentAutoFillId);
-            pw.print(prefix); pw.print("mCurrentBounds: "); pw.println(mCurrentBounds);
             pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse);
             pw.print(prefix);
                 pw.print("mResponseRequiringAuth: "); pw.println(mResponseRequiringAuth);
             pw.print(prefix);
                 pw.print("mDatasetRequiringAuth: "); pw.println(mDatasetRequiringAuth);
             pw.print(prefix); pw.print("mAutoFillDirectly: "); pw.println(mAutoFillDirectly);
+            pw.print(prefix); pw.print("mCurrentViewStates: "); pw.println(mCurrentViewState);
+            pw.print(prefix); pw.print("mViewStates: "); pw.println(mViewStates.size());
+            final String prefix2 = prefix + "  ";
+            for (Map.Entry<AutoFillId, ViewState> entry : mViewStates.entrySet()) {
+                pw.print(prefix2);
+                pw.print(entry.getKey()); pw.print(": " ); pw.println(entry.getValue());
+            }
         }
 
-        private void autoFillAppLocked(Dataset dataset, boolean removeSelf) {
-            try {
-                if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
-                mAppCallback.autoFill(dataset);
+        /**
+         * Notifies the result of a {@link FillResponse} authentication request to the service.
+         *
+         * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after user
+         * used the fingerprint sensors to authenticate.
+         */
+        void notifyResponseAuthenticationResult(Bundle extras, int flags) {
+            if (DEBUG) Slog.d(TAG, "notifyResponseAuthenticationResult(): flags=" + flags
+                    + ", extras=" + bundleToString(extras));
+            synchronized (mLock) {
+                try {
+                    mService.authenticateFillResponse(extras, flags);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error sending authentication result back to service: " + e);
+                }
+            }
+        }
 
-                // TODO(b/33197203): temporarily hack: show the save notification after autofilled,
-                // since save is not automatically detected yet.
-                mUi.showSaveNotification(mUserId, mId); removeSelf = false;
+        /**
+         * Notifies the result of a {@link Dataset} authentication request to the service.
+         *
+         * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after
+         * it gets the results from a fingerprint authentication.
+         */
+        void notifyDatasetAuthenticationResult(Bundle extras, int flags) {
+            if (DEBUG) Slog.d(TAG, "notifyDatasetAuthenticationResult(): flags=" + flags
+                    + ", extras=" + bundleToString(extras));
+            synchronized (mLock) {
+                try {
+                    mService.authenticateDataset(extras, flags);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error sending authentication result back to service: " + e);
+                }
+            }
+        }
 
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Error auto-filling activity: " + e);
+        void autoFillApp(Dataset dataset) {
+            synchronized (mLock) {
+                try {
+                    if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+                    mAppCallback.autoFill(dataset);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error auto-filling activity: " + e);
+                }
             }
-            if (removeSelf) {
-                removeSelf();
+        }
+
+        void requestSave() {
+            synchronized (mLock) {
+                requestSaveLocked(mId);
+            }
+        }
+
+        /**
+         * Called by UI to trigger a save request to the service.
+         */
+        void requestSaveLocked(int sessionId) {
+            // TODO(b/33197203): add MetricsLogger call
+            // TODO(b/33197203): use handler?
+            // TODO(b/33197203): show error on UI on Slog.w situations below???
+
+            if (mService == null) {
+                Slog.w(TAG, "requestSave(): service is null");
+                return;
+            }
+            final Session session = mSessions.get(sessionId);
+            if (session == null) {
+                Slog.w(TAG, "requestSave(): no session with id " + sessionId);
+                return;
+            }
+            final IBinder activityToken = session.mActivityToken.get();
+            if (activityToken == null) {
+                Slog.w(TAG, "activity token for session " + sessionId + " already GCed");
+                return;
+            }
+
+            /*
+             * TODO(b/33197203): apply security checks below:
+             * - checks if disabled by secure settings / device policy
+             * - log operation using noteOp()
+             * - check flags
+             * - display disclosure if needed
+             */
+            try {
+                /* TODO(b/33197203): refactor save logic so it uses a cached AssistStructure, and
+                   get the extras to be sent to the service based on the response / dataset in the
+                   session. */
+                if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken,
+                    AUTO_FILL_FLAG_TYPE_SAVE)) {
+                    Slog.w(TAG, "failed to save for " + activityToken);
+                }
+            } catch (RemoteException e) {
+                // Should not happen, it's a local call.
             }
         }
 
index 96f3408..86e04cc 100644 (file)
@@ -45,6 +45,8 @@ import android.widget.Toast;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.UiThread;
+import com.android.server.autofill.AutoFillManagerServiceImpl.Session;
+import com.android.server.autofill.AutoFillManagerServiceImpl.ViewState;
 
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -59,24 +61,25 @@ final class AutoFillUI {
     private static final String TAG = "AutoFillUI";
 
     private final Context mContext;
-
+    private final Session mSession;
     private final WindowManager mWm;
 
-    @Nullable
+    // Fill UI variables
     private AnchoredWindow mFillWindow;
+    private DatasetPicker mFillView;
+    private ViewState mViewState;
+    private Rect mBounds;
+    private String mFilterText;
 
     /**
      * Custom snackbar UI used for saving autofill or other informational messages.
      */
     private View mSnackbar;
 
-    AutoFillUI(Context context, AutoFillManagerService service, Object lock) {
+    AutoFillUI(Context context, Session session) {
         mContext = context;
+        mSession = session;
         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mService = service;
-        mLock = lock;
-
-        setNotificationListener();
     }
 
     /**
@@ -101,29 +104,63 @@ final class AutoFillUI {
     }
 
     /**
-     * Shows the options from a {@link FillResponse} so the user can pick up the proper
-     * {@link Dataset} (when the response has one) for a given view (identified by
-     * {@code autoFillId}).
+     * Hides the fill UI.
      */
-    void showResponse(int userId, int sessionId, AutoFillId autoFillId, Rect bounds,
-            FillResponse response) {
-        if (DEBUG) Slog.d(TAG, "showResponse: id=" + autoFillId +  ", bounds=" + bounds);
-
+    void hideFillUi() {
         UiThread.getHandler().runWithScissors(() -> {
             if (mFillWindow != null) {
+                if (DEBUG) Slog.d(TAG, "remove FillUi remove " + mFillWindow);
                 mFillWindow.hide();
             }
 
-            final DatasetPicker fillView = new DatasetPicker(mContext, response.getDatasets(),
-                    (dataset) -> {
-                        mFillWindow.hide();
-                        onDatasetPicked(userId, dataset, sessionId);
-                    });
+            mViewState = null;
+            mBounds = null;
+            mFilterText = null;
+            mFillView = null;
+            mFillWindow = null;
+        }, 0);
+    }
+
+    /**
+     * Shows the fill UI, removing the previous fill UI if the has changed.
+     *
+     * @param viewState the view state, compared by reference to know if new UI should be shown
+     * @param response the response to show, not used if viewState is the same
+     * @param bounds bounds of the view to be filled, used if changed
+     * @param filterText text of the view to be filled, used if changed
+     */
+    void showFillUi(ViewState viewState, List<Dataset> datasets, Rect bounds,
+            String filterText) {
+        UiThread.getHandler().runWithScissors(() -> {
+            if (mViewState != viewState) {
+                // new
+                hideFillUi();
+
+                mViewState = viewState;
+
+                mFillView = new DatasetPicker(mContext, datasets,
+                        (dataset) -> {
+                            mSession.autoFillApp(dataset);
+                            hideFillUi();
+                            showSaveUi();
+                        });
+                mFillWindow = new AnchoredWindow(
+                        mWm, mFillView, 800, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+                if (DEBUG) Slog.d(TAG, "show FillUi");
+            }
+
+            if (!bounds.equals(mBounds)) {
+                if (DEBUG) Slog.d(TAG, "update FillUi bounds: " + mBounds);
+                mBounds = bounds;
+                mFillWindow.show(mBounds);
+            }
 
-            // TODO(b/33197203): request width/height properly.
-            mFillWindow = new AnchoredWindow(mWm, fillView, 800,
-                    ViewGroup.LayoutParams.WRAP_CONTENT);
-            mFillWindow.show(bounds != null ? bounds : new Rect());
+            if (!filterText.equals(mFilterText)) {
+                if (DEBUG) Slog.d(TAG, "update FillUi filter text: " + mFilterText);
+                mFilterText = filterText;
+                mFillView.update(mFilterText);
+            }
         }, 0);
     }
 
@@ -134,10 +171,10 @@ final class AutoFillUI {
      * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint or tap to
      * autofill" or "Tap to autofill", depending on the value of {@code usesFingerprint}.
      */
-    void showFillResponseAuthenticationRequest(int userId, int sessionId, boolean usesFingerprint,
+    void showFillResponseAuthenticationRequest(boolean usesFingerprint,
             Bundle extras, int flags) {
         // TODO(b/33197203): proper implementation
-        showAuthNotification(userId, sessionId, usesFingerprint, extras, flags);
+        showAuthNotification(usesFingerprint, extras, flags);
     }
 
     /**
@@ -161,15 +198,13 @@ final class AutoFillUI {
     /**
      * Shows the UI asking the user to save for auto-fill.
      */
-    void showSaveUI(int userId, int sessionId) {
+    void showSaveUi() {
         showSnackbar(new SavePrompt(mContext, new SavePrompt.OnSaveListener() {
             @Override
             public void onSaveClick() {
                 hideSnackbar();
-                synchronized (mLock) {
-                    final AutoFillManagerServiceImpl service = getServiceLocked(userId);
-                    service.requestSaveLocked(sessionId);
-                }
+
+                mSession.requestSave();
             }
             @Override
             public void onCancelClick() {
@@ -181,10 +216,10 @@ final class AutoFillUI {
     /**
      * Called by service after the user user the fingerprint sensors to authenticate.
      */
-    void dismissFingerprintRequest(int userId, boolean success) {
+    void dismissFingerprintRequest(boolean success) {
         if (DEBUG) Slog.d(TAG, "dismissFingerprintRequest(): ok=" + success);
 
-        dismissAuthNotification(userId);
+        dismissAuthNotification();
 
         if (!success) {
             // TODO(b/33197203): proper implementation (snack bar / i18n string)
@@ -198,48 +233,11 @@ final class AutoFillUI {
         pw.println("AufoFill UI");
         final String prefix = "  ";
         pw.print(prefix); pw.print("sResultCode: "); pw.println(sResultCode);
+        pw.print(prefix); pw.print("mSessionId: "); pw.println(mSession.mId);
         pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
-        mFillWindow.dump(pw);
-    }
-
-    private AutoFillManagerServiceImpl getServiceLocked(int userId) {
-        final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
-        if (service == null) {
-            Slog.w(TAG, "no auto-fill service for user " + userId);
-        }
-        return service;
-    }
-
-    private void onSaveRequested(int userId, int sessionId) {
-        // TODO(b/33197203): displays the snack bar, until save notification is refactored
-        showSaveUI(userId, sessionId);
-    }
-
-    private void onDatasetPicked(int userId, Dataset dataset, int sessionId) {
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
-            if (service == null) return;
-
-            service.autoFillApp(sessionId, dataset);
-        }
-    }
-
-    private void onSessionDone(int userId, int sessionId) {
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
-            if (service == null) return;
-
-            service.removeSessionLocked(sessionId);
-        }
-    }
-
-    private void onResponseAuthenticationRequested(int userId, Bundle extras, int flags) {
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceLocked(userId);
-            if (service == null) return;
-
-            service.notifyResponseAuthenticationResult(extras, flags);
-        }
+        pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState);
+        pw.print(prefix); pw.print("mBounds: "); pw.println(mBounds);
+        pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText);
     }
 
     //similar to a snackbar, but can be a bit custom since it is more than just text. This will
@@ -289,16 +287,10 @@ final class AutoFillUI {
     private static final String EXTRA_FLAGS = "flags";
 
     private static final String TYPE_OPTIONS = "options";
-    private static final String TYPE_FINISH_SESSION = "finish_session";
-    private static final String TYPE_PICK_DATASET = "pick_dataset";
-    private static final String TYPE_SAVE = "save";
     private static final String TYPE_AUTH_RESPONSE = "auth_response";
 
-    @GuardedBy("mServiceLock")
     private BroadcastReceiver mNotificationReceiver;
-    @GuardedBy("mServiceLock")
-    private final AutoFillManagerService mService;
-    private final Object mLock;
+    private final Object mLock = new Object();
 
     // Hack used to generate unique pending intents
     static int sResultCode = 0;
@@ -316,8 +308,6 @@ final class AutoFillUI {
     final class NotificationReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
-            final int sessionId = intent.getIntExtra(EXTRA_SESSION_ID, -1);
             final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
             if (type == null) {
                 Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
@@ -326,26 +316,12 @@ final class AutoFillUI {
             final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
             final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
 
-            if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
-                    + ", sessionId=" + sessionId);
+            if (DEBUG) Slog.d(TAG, "Notification received: type=" + type
+                    + ", sessionId=" + mSession.mId);
             synchronized (mLock) {
                 switch (type) {
-                    case TYPE_SAVE:
-                        onSaveRequested(userId, sessionId);
-                        break;
-                    case TYPE_FINISH_SESSION:
-                        onSessionDone(userId, sessionId);
-                        break;
-                    case TYPE_PICK_DATASET:
-                        onDatasetPicked(userId, dataset, sessionId);
-
-                        // Must cancel notification because it might be comming from action
-                        if (DEBUG) Slog.d(TAG, "Cancelling notification");
-                        NotificationManager.from(mContext).cancel(TYPE_OPTIONS, userId);
-
-                        break;
                     case TYPE_AUTH_RESPONSE:
-                        onResponseAuthenticationRequested(userId,
+                        mSession.notifyResponseAuthenticationResult(
                                 intent.getBundleExtra(EXTRA_AUTH_REQUIRED_EXTRAS), flags);
                         break;
                     default: {
@@ -357,161 +333,28 @@ final class AutoFillUI {
         }
     }
 
-    private static Intent newNotificationIntent(int userId, String type) {
+    private static Intent newNotificationIntent(String type) {
         final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
-        intent.putExtra(EXTRA_USER_ID, userId);
         intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
         return intent;
     }
 
-    private PendingIntent newPickDatasetPI(int userId, int sessionId, FillResponse response,
-            Dataset dataset) {
-        final int resultCode = ++ sResultCode;
-        if (DEBUG) Slog.d(TAG, "newPickDatasetPI: userId=" + userId + ", sessionId=" + sessionId
-                + ", resultCode=" + resultCode);
-
-        final Intent intent = newNotificationIntent(userId, TYPE_PICK_DATASET);
-        intent.putExtra(EXTRA_SESSION_ID, sessionId);
-        intent.putExtra(EXTRA_FILL_RESPONSE, response);
-        intent.putExtra(EXTRA_DATASET, dataset);
-        return PendingIntent.getBroadcast(mContext, resultCode, intent,
-                PendingIntent.FLAG_ONE_SHOT);
-    }
-
-    /**
-     * 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.
-     */
-    private void showOptionsNotification(int userId, int callbackId, AutoFillId autoFillId,
-            FillResponse response) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            showOptionsNotificationAsSystem(userId, callbackId, autoFillId, response);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private void showOptionsNotificationAsSystem(int userId, int sessionId,
-            AutoFillId autoFillId, FillResponse response) {
-        // Make sure server callback is removed from cache if user cancels the notification.
-        final Intent deleteIntent = newNotificationIntent(userId, TYPE_FINISH_SESSION)
-                .putExtra(EXTRA_SESSION_ID, sessionId);
-        final PendingIntent deletePendingIntent = PendingIntent.getBroadcast(mContext,
-                ++sResultCode, deleteIntent, PendingIntent.FLAG_ONE_SHOT);
-
-        final String title = "AutoFill Options";
-
-        final Notification.Builder notification = newNotificationBuilder()
-                .setOngoing(false)
-                .setDeleteIntent(deletePendingIntent)
-                .setContentTitle(title);
-
-        boolean autoCancel = true;
-        final String subTitle;
-        final List<Dataset> datasets;
-        final AutoFillId[] savableIds;
-        if (response != null) {
-            datasets = response.getDatasets();
-            savableIds = response.getSavableIds();
-        } else {
-            datasets = null;
-            savableIds = null;
-        }
-        boolean showSave = false;
-        if (datasets == null ) {
-            subTitle = "No options to auto-fill " + autoFillId;
-        } else if (datasets.isEmpty()) {
-            if (savableIds.length == 0) {
-                subTitle = "No options to auto-fill " + autoFillId;
-            } else {
-                subTitle = "No options to auto-fill " + autoFillId
-                        + ", but provider can save ids:\n" + Arrays.toString(savableIds);
-                showSave = true;
-            }
-        } else {
-            final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
-            if (service == null) {
-                subTitle = "No auto-fill service for user " + userId;
-                Slog.w(TAG, subTitle);
-            } else {
-                autoCancel = false;
-                final int size = datasets.size();
-                subTitle = "There are " + size + " option(s) to fill " + autoFillId + ".\n"
-                        + "Use the notification action(s) to select the proper one."
-                        + "Actions with (F) require fingerprint unlock, and with (P) require"
-                        + "provider authentication to unlock";
-                for (Dataset dataset : datasets) {
-                    final StringBuilder name = new StringBuilder(dataset.getName());
-                    if (dataset.isAuthRequired()) {
-                        if (dataset.hasCryptoObject()) {
-                            name.append("(F)");
-                        } else {
-                            name.append("(P)");
-                        }
-                    }
-                    final PendingIntent pi = newPickDatasetPI(userId, sessionId, response, dataset);
-                    notification.addAction(new Action.Builder(null, name, pi).build());
-                }
-            }
-        }
-
-        notification.setAutoCancel(autoCancel);
-        notification.setStyle(new Notification.BigTextStyle().bigText(subTitle));
-
-        NotificationManager.from(mContext).notify(TYPE_OPTIONS, userId, notification.build());
-
-        if (showSave) {
-            showSaveNotification(userId, sessionId);
-        }
-    }
-
-    void showSaveNotification(int userId, int sessionId) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            showSaveNotificationAsSystem(userId, sessionId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private void showSaveNotificationAsSystem(int userId, int sessionId) {
-        final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE)
-                .putExtra(EXTRA_SESSION_ID, sessionId);
-
-        final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext,
-                ++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT);
-
-        final String title = "AutoFill Save Emulation";
-        final String subTitle = "Tap notification to launch the save snackbar.";
-
-        final Notification notification = newNotificationBuilder()
-                .setAutoCancel(true)
-                .setOngoing(false)
-                .setContentTitle(title)
-                .setContentIntent(savePendingIntent)
-                .setStyle(new Notification.BigTextStyle().bigText(subTitle))
-                .build();
-        NotificationManager.from(mContext).notify(TYPE_SAVE, userId, notification);
-    }
-
-    private void showAuthNotification(int userId, int sessionId, boolean usesFingerprint,
+    private void showAuthNotification(boolean usesFingerprint,
             Bundle extras, int flags) {
         final long token = Binder.clearCallingIdentity();
         try {
-            showAuthNotificationAsSystem(userId, sessionId, usesFingerprint, extras, flags);
+            showAuthNotificationAsSystem(usesFingerprint, extras, flags);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    private void showAuthNotificationAsSystem(int userId, int sessionId,
+    private void showAuthNotificationAsSystem(
             boolean usesFingerprint, Bundle extras, int flags) {
         final String title = "AutoFill Authentication";
         final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n");
 
-        final Intent authIntent = newNotificationIntent(userId, TYPE_AUTH_RESPONSE)
-                .putExtra(EXTRA_SESSION_ID, sessionId);
+        final Intent authIntent = newNotificationIntent(TYPE_AUTH_RESPONSE);
         if (extras != null) {
             authIntent.putExtra(EXTRA_AUTH_REQUIRED_EXTRAS, extras);
         }
@@ -537,11 +380,11 @@ final class AutoFillUI {
         if (authPendingIntent != null) {
             notification.setContentIntent(authPendingIntent);
         }
-        NotificationManager.from(mContext).notify(TYPE_AUTH_RESPONSE, userId, notification.build());
+        NotificationManager.from(mContext).notify(mSession.mId, notification.build());
     }
 
-    private void dismissAuthNotification(int userId) {
-        NotificationManager.from(mContext).cancel(TYPE_AUTH_RESPONSE, userId);
+    private void dismissAuthNotification() {
+        NotificationManager.from(mContext).cancel(mSession.mId);
     }
 
     private Notification.Builder newNotificationBuilder() {