Moar AutoFill Framework refactoring...

* Encapsulated application-level auto-fill logic on AutoFillSession.
  Currently, Activity.java directly manages the IAutoFillCallback binder
  object used to auto-fill its views, but this logic need to be
  extended so Views can use it to draw the auto-fill bar.

* Pass auto-fill id and boundaries to requests
  So AutoFillUI can display its affordance in the right places.

* Uses a new auto-fill id on View (instead of reusing accessibility's).
  That allows moving the logic on whether a new request should be made or
  the existing UI moved to the service side.

* Split service methods in 2, for shell cmd and app
  And applied the right permission check on both.

* Removed CancelationSignal from onSaveRequest()
  Since it's not really needed.

* Etc...
  ¯\_(ツ)_/¯

BUG: 34637800
BUG: 31001899
Test: CtsAutoFillServiceTestCases passes
Test: manual verification

Change-Id: Ibd0cb2cfff6d0f6bb6b697a423ed5e89df687b82
diff --git a/api/current.txt b/api/current.txt
index 56fedf6..3ee9155 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -35735,7 +35735,7 @@
     method public void onDisconnected();
     method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
-    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
     field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
@@ -44449,8 +44449,8 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
-    field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
-    field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
+    field public static final int AUTO_FILL_FLAG_TYPE_FILL = 268435456; // 0x10000000
+    field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 536870912; // 0x20000000
     field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
     field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
     field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/api/system-current.txt b/api/system-current.txt
index b4a374f..ccd2307f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -38759,7 +38759,7 @@
     method public void onDisconnected();
     method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
-    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
     field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
@@ -47848,8 +47848,8 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
-    field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
-    field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
+    field public static final int AUTO_FILL_FLAG_TYPE_FILL = 268435456; // 0x10000000
+    field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 536870912; // 0x20000000
     field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
     field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
     field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/api/test-current.txt b/api/test-current.txt
index 9b2bf2b..4e1e31f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -35869,7 +35869,7 @@
     method public void onDisconnected();
     method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public void onFillResponseAuthenticationRequest(android.os.Bundle, int);
-    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
+    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String EXTRA_DATASET_EXTRAS = "android.service.autofill.extra.DATASET_EXTRAS";
     field public static final java.lang.String EXTRA_RESPONSE_EXTRAS = "android.service.autofill.extra.RESPONSE_EXTRAS";
     field public static final int FLAG_AUTHENTICATION_ERROR = 4; // 0x4
@@ -44754,8 +44754,8 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
-    field public static final int AUTO_FILL_FLAG_TYPE_FILL = 1; // 0x1
-    field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 2; // 0x2
+    field public static final int AUTO_FILL_FLAG_TYPE_FILL = 268435456; // 0x10000000
+    field public static final int AUTO_FILL_FLAG_TYPE_SAVE = 536870912; // 0x20000000
     field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
     field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
     field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 580bb50..1d84ff5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -117,9 +117,8 @@
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.autofill.AutoFillId;
-import android.view.autofill.Dataset;
-import android.view.autofill.DatasetField;
-import android.view.autofill.VirtualViewDelegate;
+import android.view.autofill.AutoFillManager;
+import android.view.autofill.AutoFillSession;
 import android.widget.AdapterView;
 import android.widget.Toast;
 import android.widget.Toolbar;
@@ -848,10 +847,7 @@
     private boolean mHasCurrentPermissionsRequest;
 
     @GuardedBy("this")
-    private WeakReference<IAutoFillAppCallback> mAutoFillCallback;
-
-    @GuardedBy("this")
-    private VirtualViewDelegate.Callback mAutoFillDelegateCallback;
+    private AutoFillSession mAutoFillSession;
 
     private static native String getDlWarning();
 
@@ -1704,76 +1700,17 @@
     }
 
     /**
-     * Lazily sets the {@link #mAutoFillDelegateCallback}.
-     */
-    private void setAutoFillDelegateCallback() {
-        synchronized (this) {
-            if (mAutoFillDelegateCallback == null) {
-                mAutoFillDelegateCallback = new VirtualViewDelegate.Callback() {
-                    // TODO(b/33197203): implement
-                };
-            }
-        }
-    }
-
-    /**
      * Lazily gets the {@link IAutoFillAppCallback} for this activitity.
      *
      * <p>This callback is used by the {@link AutoFillService} app to auto-fill the activity fields.
      */
-    WeakReference<IAutoFillAppCallback> getAutoFillCallback() {
+    IAutoFillAppCallback getAutoFillCallback() {
         synchronized (this) {
-            if (mAutoFillCallback == null) {
-                final IAutoFillAppCallback cb = new IAutoFillAppCallback.Stub() {
-                    @Override
-                    public void autoFill(Dataset dataset) throws RemoteException {
-                        // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
-                        // dataset.extras to service
-                        runOnUiThread(() -> {
-                            final View root = getWindow().getDecorView().getRootView();
-                            for (DatasetField field : dataset.getFields()) {
-                                final AutoFillId id = field.getId();
-                                if (id == null) {
-                                    Log.w(TAG, "autoFill(): null id on " + field);
-                                    continue;
-                                }
-                                final int viewId = id.getViewId();
-                                final View view = root.findViewByAccessibilityIdTraversal(viewId);
-                                if (view == null) {
-                                    Log.w(TAG, "autoFill(): no View with id " + viewId);
-                                    continue;
-                                }
-
-                                // TODO(b/33197203): handle protected value (like credit card)
-                                if (id.isVirtual()) {
-                                    // Delegate virtual fields to provider.
-                                    setAutoFillDelegateCallback();
-                                    final VirtualViewDelegate mgr = view
-                                            .getAutoFillVirtualViewDelegate(
-                                                    mAutoFillDelegateCallback);
-                                    if (mgr == null) {
-                                        Log.w(TAG, "autoFill(): cannot fill virtual " + id
-                                                + "; no auto-fill provider for view "
-                                                + view.getClass());
-                                        continue;
-                                    }
-                                    if (DEBUG_AUTO_FILL) {
-                                        Log.d(TAG, "autoFill(): delegating " + id
-                                                + " to virtual manager  " + mgr);
-                                    }
-                                    mgr.autoFill(id.getVirtualChildId(), field.getValue());
-                                } else {
-                                    // Handle non-virtual fields itself.
-                                    view.autoFill(field.getValue());
-                                }
-                            }
-                        });
-                    }
-                };
-                mAutoFillCallback = new WeakReference<IAutoFillAppCallback>(cb);
+            if (mAutoFillSession == null) {
+                mAutoFillSession = new AutoFillSession(this);
             }
+            return mAutoFillSession.getCallback();
         }
-        return mAutoFillCallback;
     }
 
     /**
@@ -6067,9 +6004,9 @@
             getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
         }
 
-        if (mAutoFillCallback != null) {
-            writer.print(prefix); writer.print("mAutoFillCallback: " );
-                    writer.println(mAutoFillCallback.get());
+        if (mAutoFillSession!= null) {
+            writer.print(prefix); writer.print("mAutoFillSession: " );
+                    writer.println(mAutoFillSession);
         }
 
         mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 89510d9..e848080 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -154,6 +154,12 @@
     public abstract List<IBinder> getTopVisibleActivities();
 
     /**
+     * Returns the top, focused activity of the currently visible stack, but only if it belongs to
+     * the given UID.
+     */
+    public abstract IBinder getTopVisibleActivity(int uid);
+
+    /**
      * Callback for window manager to let activity manager know that docked stack changes its
      * minimized state.
      */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 90fab41..9a22125 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2967,7 +2967,7 @@
                 if (!forAutoFill) {
                     r.activity.onProvideAssistContent(content);
                 } else if (addAutoFillCallback) {
-                    IAutoFillAppCallback cb = r.activity.getAutoFillCallback().get();
+                    IAutoFillAppCallback cb = r.activity.getAutoFillCallback();
                     if (cb != null) {
                         data.putBinder(AutoFillService.KEY_CALLBACK, cb.asBinder());
                     } else {
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index b5cb8f8..1e4f90d 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -89,16 +89,14 @@
 
     /**
      * Key of the {@link Bundle} passed to methods such as
-     * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)}
-     * containing the extras set by
+     * {@link #onSaveRequest(AssistStructure, Bundle, SaveCallback)} containing the extras set by
      * {@link android.view.autofill.FillResponse.Builder#setExtras(Bundle)}.
      */
     public static final String EXTRA_RESPONSE_EXTRAS = KEY_PREFIX + "RESPONSE_EXTRAS";
 
     /**
      * Key of the {@link Bundle} passed to methods such as
-     * {@link #onSaveRequest(AssistStructure, Bundle, CancellationSignal, SaveCallback)}
-     * containing the extras set by
+     * {@link #onSaveRequest(AssistStructure, Bundle, SaveCallback)} containing the extras set by
      * {@link android.view.autofill.Dataset.Builder#setExtras(Bundle)}.
      */
     public static final String EXTRA_DATASET_EXTRAS = KEY_PREFIX + "DATASET_EXTRAS";
@@ -134,9 +132,9 @@
 
         @Override
         public void autoFill(AssistStructure structure, IAutoFillServerCallback callback,
-                Bundle extras, int flags) {
+                int flags) {
             mHandlerCaller
-                    .obtainMessageIOOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, extras, callback)
+                    .obtainMessageIOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, callback)
                     .sendToTarget();
         }
 
@@ -179,9 +177,8 @@
                     final SomeArgs args = (SomeArgs) msg.obj;
                     final int flags = msg.arg1;
                     final AssistStructure structure = (AssistStructure) args.arg1;
-                    final Bundle extras = (Bundle) args.arg2;
-                    final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg3;
-                    requestAutoFill(callback, structure, extras, flags);
+                    final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg2;
+                    requestAutoFill(callback, structure, flags);
                     break;
                 } case MSG_AUTHENTICATE_FILL_RESPONSE: {
                     final int flags = msg.arg1;
@@ -254,8 +251,8 @@
      * @param cancellationSignal signal for observing cancel requests.
      * @param callback object used to notify the result of the request.
      */
-    public abstract void onFillRequest(AssistStructure structure,
-            Bundle data, CancellationSignal cancellationSignal, FillCallback callback);
+    public abstract void onFillRequest(AssistStructure structure, Bundle data,
+            CancellationSignal cancellationSignal, FillCallback callback);
 
     /**
      * Called when user requests service to save the fields of an {@link Activity}.
@@ -267,20 +264,19 @@
      * @param structure {@link Activity}'s view structure.
      * @param data bundle containing additional arguments set by the Android system (currently none)
      * or data passed by the service in the {@link FillResponse} that originated this call.
-     * @param cancellationSignal signal for observing cancel requests.
      * @param callback object used to notify the result of the request.
      */
-    public abstract void onSaveRequest(AssistStructure structure,
-            Bundle data, CancellationSignal cancellationSignal, SaveCallback callback);
+    public abstract void onSaveRequest(AssistStructure structure, Bundle data,
+            SaveCallback callback);
 
     /**
      * Called as result of the user action for a {@link FillResponse} that required authentication.
      *
      * <p>When the {@link FillResponse} required authentication through
-     * {@link android.view.autofill.FillResponse.Builder#requiresCustomAuthentication(Bundle, int)}, this
-     * call indicates the user is requesting the service to authenticate him/her (and {@code flags}
-     * contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains the
-     * {@link Bundle} passed to that method.
+     * {@link android.view.autofill.FillResponse.Builder#requiresCustomAuthentication(Bundle, int)},
+     * this call indicates the user is requesting the service to authenticate him/her (and
+     * {@code flags} contains {@link #FLAG_AUTHENTICATION_REQUESTED}), and {@code extras} contains
+     * the {@link Bundle} passed to that method.
      *
      * <p>When the {@link FillResponse} required authentication through
      * {@link android.view.autofill.FillResponse.Builder#requiresFingerprintAuthentication(
@@ -336,27 +332,28 @@
     }
 
     private void requestAutoFill(IAutoFillServerCallback callback, AssistStructure structure,
-            Bundle data, int flags) {
-        switch (flags) {
-            case AUTO_FILL_FLAG_TYPE_FILL:
-                final FillCallback fillCallback = new FillCallback(callback);
-                if (DEBUG_PENDING_CALLBACKS) {
-                    addPendingCallback(fillCallback);
-                }
-                // TODO(b/33197203): hook up the cancelationSignal
-                onFillRequest(structure, data, new CancellationSignal(), fillCallback);
-                break;
-            case AUTO_FILL_FLAG_TYPE_SAVE:
-                final SaveCallback saveCallback = new SaveCallback(callback);
-                if (DEBUG_PENDING_CALLBACKS) {
-                    addPendingCallback(saveCallback);
-                }
-                // TODO(b/33197203): hook up the cancelationSignal
-                onSaveRequest(structure, data, new CancellationSignal(), saveCallback);
-                break;
-            default:
-                Log.w(TAG, "invalid flag on requestAutoFill(): " + flags);
+            int flags) {
+        if (DEBUG) Log.d(TAG, "requestAutoFill(): flags=" + flags);
+
+        if ((flags & AUTO_FILL_FLAG_TYPE_FILL) != 0) {
+            final FillCallback fillCallback = new FillCallback(callback);
+            if (DEBUG_PENDING_CALLBACKS) {
+                addPendingCallback(fillCallback);
+            }
+            // TODO(b/33197203): hook up the cancelationSignal
+            onFillRequest(structure, null, new CancellationSignal(), fillCallback);
+            return;
         }
+        if ((flags & AUTO_FILL_FLAG_TYPE_SAVE) != 0) {
+            final SaveCallback saveCallback = new SaveCallback(callback);
+            if (DEBUG_PENDING_CALLBACKS) {
+                addPendingCallback(saveCallback);
+            }
+            onSaveRequest(structure, null, saveCallback);
+            return;
+        }
+
+        Log.w(TAG, "invalid flags on requestAutoFill(): " + flags);
     }
 
     private void addPendingCallback(CallbackHelper.Dumpable callback) {
diff --git a/core/java/android/service/autofill/IAutoFillAppCallback.aidl b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
index cc83776..8c3898a 100644
--- a/core/java/android/service/autofill/IAutoFillAppCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
@@ -25,8 +25,7 @@
  *
  * @hide
  */
-// TODO(b/33197203): rename methods to make them more consistent with a callback, or rename class
-// itself
+// TODO(b/33197203): rename IAutoFillAppSession
 oneway interface IAutoFillAppCallback {
     /**
       * Auto-fills the activity with the contents of a dataset.
diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
index ce42107..ace5411 100644
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl
@@ -27,8 +27,9 @@
  */
 oneway interface IAutoFillManagerService {
 
-    void showAutoFillInput(in AutoFillId id, in Rect boundaries);
+    // Called by AutoFillManager (app).
+    void requestAutoFill(in AutoFillId id, in Rect bounds, int flags);
 
-    // TODO(b/33197203): remove it and refactor onShellCommand
-    void requestAutoFill(IBinder activityToken, int userId, in Bundle extras, int flags);
+    // Called by ShellCommand only.
+    void requestAutoFillForUser(int userId, int flags);
 }
diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
index 185c8f3..f7d5064 100644
--- a/core/java/android/service/autofill/IAutoFillServerCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
@@ -28,8 +28,7 @@
  *
  * @hide
  */
-// TODO(b/33197203): rename methods to make them more consistent with a callback, or rename class
-// itself
+// TODO(b/33197203): rename to IAutoFillServerSession
 oneway interface IAutoFillServerCallback {
     // TODO(b/33197203): document methods
     void showResponse(in FillResponse response);
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index fa9786a..3e8087b 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -27,8 +27,7 @@
 // TODO(b/33197203): document class and methods
 oneway interface IAutoFillService {
     // TODO(b/33197203): rename method to make them more consistent
-    void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback,
-                  in Bundle extras, int flags);
+    void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback, int flags);
     void authenticateFillResponse(in Bundle extras, int flags);
     void authenticateDataset(in Bundle extras, int flags);
     void onConnected();
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index d5022d8..e2fb588 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -58,7 +58,7 @@
     /**
      * Notifies the Android System that an
      * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
-     * android.os.CancellationSignal, SaveCallback)} was successfully fulfilled by the service.
+     * SaveCallback)} was successfully fulfilled by the service.
      *
      * @param ids ids ({@link ViewNode#getAutoFillId()}) of the fields that were saved.
      *
@@ -85,7 +85,7 @@
     /**
      * Notifies the Android System that an
      * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
-     * android.os.CancellationSignal, SaveCallback)} could not be fulfilled by the service.
+     * SaveCallback)} could not be fulfilled by the service.
      *
      * @param message error message to be displayed to the user.
      *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ba9bb67..0657bef 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1765,12 +1765,6 @@
      */
     int mAccessibilityViewId = NO_ID;
 
-    /**
-     * The stable ID of this view for auto-fill purposes.
-     */
-    private int mAutoFillId = NO_ID;
-
-
     private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
 
     SendViewStateChangedAccessibilityEvent mSendViewStateChangedAccessibilityEvent;
@@ -4045,9 +4039,9 @@
      * input fields and tags (like {@code id}).
      * </ul>
      */
-    // TODO(b/33197203) (b/34078930): improve documentation: mention all cases, show examples, etc.
-    // In particular, be more specific about webview restrictions
-    public static final int AUTO_FILL_FLAG_TYPE_FILL = 0x1;
+    // TODO(b/33197203): cannot conflict with flags defined on AutoFillManager until they're removed
+    // (when save is refactored).
+    public static final int AUTO_FILL_FLAG_TYPE_FILL = 0x10000000;
 
     /**
      * Set when the user explicitly asked a {@link android.service.autofill.AutoFillService} to save
@@ -4057,7 +4051,9 @@
      * (Personally Identifiable Information). For example, the text of password fields should be
      * included since that's what's typically saved.
      */
-    public static final int AUTO_FILL_FLAG_TYPE_SAVE = 0x2;
+    // TODO(b/33197203): cannot conflict with flags defined on AutoFillManager until they're removed
+    // (when save is refactored).
+    public static final int AUTO_FILL_FLAG_TYPE_SAVE = 0x20000000;
 
     /**
      * Set to true when drawing cache is enabled and cannot be created.
@@ -6940,8 +6936,7 @@
         if (forAutoFill) {
             // The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
             // reuse the accessibility id to save space.
-            mAutoFillId = getAccessibilityViewId();
-            structure.setAutoFillId(mAutoFillId);
+            structure.setAutoFillId(getAccessibilityViewId());
             structure.setAutoFillType(getAutoFillType());
         }
 
@@ -7568,20 +7563,6 @@
     }
 
     /**
-     * Gets the unique identifier of this view for auto-fill purposes.
-     *
-     * <p>It's only set after {@link #onProvideAutoFillStructure(ViewStructure, int)} is called.
-     *
-     * @return The view autofill id or {@link #NO_ID} if
-     * {@link #onProvideAutoFillStructure(ViewStructure, int)}  was not called yet.
-     *
-     * @hide
-     */
-    public int getAutoFillViewId() {
-        return mAutoFillId;
-    }
-
-    /**
      * Gets the unique identifier of the window in which this View reseides.
      *
      * @return The window accessibility id.
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index cd9842f..cf56e0e 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -36,12 +36,16 @@
     /**
      * Flag used to show the auto-fill UI affordance for a view.
      */
-    public static final int FLAG_UPDATE_UI_SHOW = 1 << 0;
+    // TODO(b/33197203): cannot conflict with flags defined on View until they're removed (when
+    // save is refactored).
+    public static final int FLAG_UPDATE_UI_SHOW = 0x1;
 
     /**
      * Flag used to hide the auto-fill UI affordance for a view.
      */
-    public static final int FLAG_UPDATE_UI_HIDE = 1 << 1;
+    // TODO(b/33197203): cannot conflict with flags defined on View until they're removed (when
+    // save is refactored).
+    public static final int FLAG_UPDATE_UI_HIDE = 0x2;
 
     private final IAutoFillManagerService mService;
 
@@ -64,11 +68,10 @@
      * {@link #FLAG_UPDATE_UI_HIDE}.
      */
     public void updateAutoFillInput(View view, int flags) {
-        if (DEBUG) {
-            Log.v(TAG, "updateAutoFillInput(" + view.getAutoFillViewId() + "): flags=" + flags);
-        }
+        final Rect bounds = new Rect();
+        view.getBoundsOnScreen(bounds);
 
-        updateAutoFillInput(view, false, View.NO_ID, null, flags);
+        requestAutoFill(new AutoFillId(view.getAccessibilityViewId()), bounds, flags);
     }
 
     /**
@@ -79,56 +82,22 @@
      *
      * @param parent parent view.
      * @param childId id identifying the virtual child inside the parent view.
-     * @param boundaries boundaries of the child (inside the parent; could be {@code null} when
+     * @param bounds absolute boundaries of the child in the window (could be {@code null} when
      * flag is {@link #FLAG_UPDATE_UI_HIDE}.
      * @param flags either {@link #FLAG_UPDATE_UI_SHOW} or
      * {@link #FLAG_UPDATE_UI_HIDE}.
      */
-    public void updateAutoFillInput(View parent, int childId, @Nullable Rect boundaries,
+    public void updateAutoFillInput(View parent, int childId, @Nullable Rect bounds,
             int flags) {
+        requestAutoFill(new AutoFillId(parent.getAccessibilityViewId(), childId), bounds, flags);
+    }
+
+    private void requestAutoFill(AutoFillId id, Rect bounds, int flags) {
         if (DEBUG) {
-            Log.v(TAG, "updateAutoFillInput(" + parent.getAutoFillViewId() + ", " + childId
-                    + "): boundaries=" + boundaries + ", flags=" + flags);
+            Log.v(TAG, "requestAutoFill(): id=" + id + ", bounds=" + bounds + ", flags=" + flags);
         }
-        updateAutoFillInput(parent, true, childId, boundaries, flags);
-    }
-
-    private void updateAutoFillInput(View view, boolean virtual, int childId, Rect boundaries,
-            int flags) {
-        if ((flags & FLAG_UPDATE_UI_SHOW) != 0) {
-            final int viewId = view.getAutoFillViewId();
-            final AutoFillId id = virtual
-                    ? new AutoFillId(viewId, childId)
-                    : new AutoFillId(viewId);
-            showAutoFillInput(id, boundaries);
-            return;
-        }
-        // TODO(b/33197203): handle FLAG_UPDATE_UI_HIDE
-    }
-
-    private void showAutoFillInput(AutoFillId id, Rect boundaries) {
-        final int autoFillViewId = id.getViewId();
-        /*
-         * TODO(b/33197203): currently SHOW_AUTO_FILL_BAR is only set once per activity (i.e, when
-         * the view does not have an auto-fill id), but it should be called again for views that
-         * were not part of the initial auto-fill dataset returned by the service. For example:
-         *
-         * 1.Activity has 4 fields, `first_name`, `last_name`, and `address`.
-         * 2.User taps `first_name`.
-         * 3.Service returns a dataset with ids for `first_name` and `last_name`.
-         * 4.When user taps `first_name` (again) or `last_name`, flag should not have
-         *   SHOW_AUTO_FILL_BAR set, but when user taps `address`, it should (since that field was
-         *   not part of the initial dataset).
-         *
-         * Similarly, once the activity is auto-filled, the flag logic should be reset (so if the
-         * user taps the view again, a new auto-fill request is made)
-         */
-        if (autoFillViewId != View.NO_ID) {
-            return;
-        }
-
         try {
-            mService.showAutoFillInput(id, boundaries);
+            mService.requestAutoFill(id, bounds, flags);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/autofill/AutoFillSession.java b/core/java/android/view/autofill/AutoFillSession.java
new file mode 100644
index 0000000..eec7a82
--- /dev/null
+++ b/core/java/android/view/autofill/AutoFillSession.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import static android.view.autofill.Helper.DEBUG;
+
+import android.app.Activity;
+import android.os.RemoteException;
+import android.service.autofill.IAutoFillAppCallback;
+import android.util.Log;
+import android.view.View;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * An auto-fill session associated with an activity.
+ *
+ * @hide
+ */
+public final class AutoFillSession {
+
+    private static final String TAG = "AutoFillSession";
+
+    private final IAutoFillAppCallback mCallback = new IAutoFillAppCallback.Stub() {
+        @Override
+        public void autoFill(Dataset dataset) throws RemoteException {
+            final Activity activity = mActivity.get();
+            if (activity == null) {
+                if (DEBUG) Log.d(TAG, "autoFill(): activity already GCed");
+                return;
+            }
+            // TODO(b/33197203): must keep the dataset so subsequent calls pass the same
+            // dataset.extras to service
+            activity.runOnUiThread(() -> {
+                final View root = activity.getWindow().getDecorView().getRootView();
+                for (DatasetField field : dataset.getFields()) {
+                    final AutoFillId id = field.getId();
+                    if (id == null) {
+                        Log.w(TAG, "autoFill(): null id on " + field);
+                        continue;
+                    }
+                    final int viewId = id.getViewId();
+                    final View view = root.findViewByAccessibilityIdTraversal(viewId);
+                    if (view == null) {
+                        Log.w(TAG, "autoFill(): no View with id " + viewId);
+                        continue;
+                    }
+
+                    // TODO(b/33197203): handle protected value (like credit card)
+                    if (id.isVirtual()) {
+                        // Delegate virtual fields.
+                        setAutoFillDelegateCallback();
+                        final VirtualViewDelegate delegate = view
+                                .getAutoFillVirtualViewDelegate(
+                                        mAutoFillDelegateCallback);
+                        if (delegate == null) {
+                            Log.w(TAG, "autoFill(): cannot fill virtual " + id
+                                    + "; no VirtualViewDelegate for view "
+                                    + view.getClass());
+                            continue;
+                        }
+                        if (DEBUG) {
+                            Log.d(TAG, "autoFill(): delegating " + id
+                                    + " to VirtualViewDelegate  " + delegate);
+                        }
+                        delegate.autoFill(id.getVirtualChildId(), field.getValue());
+                    } else {
+                        // Handle non-virtual fields itself.
+                        view.autoFill(field.getValue());
+                    }
+                }
+            });
+        }
+    };
+
+    private final WeakReference<Activity> mActivity;
+
+    @GuardedBy("this")
+    private VirtualViewDelegate.Callback mAutoFillDelegateCallback;
+
+    public AutoFillSession(Activity activity) {
+        mActivity = new WeakReference<>(activity);
+    }
+
+    public IAutoFillAppCallback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * Lazily sets the {@link #mAutoFillDelegateCallback}.
+     */
+    private void setAutoFillDelegateCallback() {
+        synchronized (this) {
+            if (mAutoFillDelegateCallback == null) {
+                mAutoFillDelegateCallback = new VirtualViewDelegate.Callback() {
+                    // TODO(b/33197203): implement
+                };
+            }
+        }
+    }
+
+}
diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/view/autofill/Dataset.java
index b11eecc..18a08f9 100644
--- a/core/java/android/view/autofill/Dataset.java
+++ b/core/java/android/view/autofill/Dataset.java
@@ -284,7 +284,7 @@
          * Sets a {@link Bundle} that will be passed to subsequent calls to
          * {@link android.service.autofill.AutoFillService} methods such as
  * {@link android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
-         * Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback)}, using
+         * Bundle, android.service.autofill.SaveCallback)}, using
          * {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} as the key.
          *
          * <p>It can be used to keep service state in between calls.
diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/view/autofill/FillResponse.java
index 67eb85a..48dbb84 100644
--- a/core/java/android/view/autofill/FillResponse.java
+++ b/core/java/android/view/autofill/FillResponse.java
@@ -149,9 +149,9 @@
  * Dataset.Builder#setExtras(Bundle)} methods to pass {@link Bundle}s with service-specific data use
  * to identify this response on future calls (like {@link
  * android.service.autofill.AutoFillService#onSaveRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback)}) - such bundles
- * will be available as the {@link android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS}
- * and {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} extras in that method's
+ * Bundle, android.service.autofill.SaveCallback)}) - such bundles will be available as the
+ * {@link android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS} and
+ * {@link android.service.autofill.AutoFillService#EXTRA_DATASET_EXTRAS} extras in that method's
  * {@code extras} argument.
  */
 public final class FillResponse implements Parcelable {
@@ -369,9 +369,8 @@
         /**
          * Adds ids of additional fields that the service would be interested to save (through
          * {@link android.service.autofill.AutoFillService#onSaveRequest(
-         * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
-         * android.service.autofill.SaveCallback)}) but were not indirectly set through {@link
-         * #addDataset(Dataset)}.
+         * android.app.assist.AssistStructure, Bundle, android.service.autofill.SaveCallback)})
+         * but were not indirectly set through {@link #addDataset(Dataset)}.
          *
          * <p>See {@link FillResponse} for examples.
          */
@@ -386,8 +385,8 @@
          * Sets a {@link Bundle} that will be passed to subsequent calls to {@link
          * android.service.autofill.AutoFillService} methods such as {@link
          * android.service.autofill.AutoFillService#onSaveRequest(
-         * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
-         * android.service.autofill.SaveCallback)}, using {@link
+         * android.app.assist.AssistStructure, Bundle, android.service.autofill.SaveCallback)},
+         * using {@link
          * android.service.autofill.AutoFillService#EXTRA_RESPONSE_EXTRAS} as the key.
          *
          * <p>It can be used when to keep service state in between calls.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 072fe4a..c0bec69 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9030,8 +9030,7 @@
             final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
             if (afm != null) {
                 if (DEBUG_AUTOFILL) {
-                    Log.v(LOG_TAG, "onFocusChanged(): id=" + getAutoFillViewId() + ", focused= "
-                            + focused);
+                    Log.v(LOG_TAG, "onFocusChanged(false): id=" + getAccessibilityViewId());
                 }
                 afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_HIDE);
             }
@@ -10615,7 +10614,7 @@
     protected void viewClicked(InputMethodManager imm) {
         final AutoFillManager afm = mContext.getSystemService(AutoFillManager.class);
         if (afm != null) {
-            if (DEBUG_AUTOFILL) Log.v(LOG_TAG, "viewClicked(): id=" + getAutoFillViewId());
+            if (DEBUG_AUTOFILL) Log.v(LOG_TAG, "viewClicked(): id=" + getAccessibilityViewId());
 
             // TODO(b/33197203): integrate with onFocus and/or move to view?
             afm.updateAutoFillInput(this, AutoFillManager.FLAG_UPDATE_UI_SHOW);
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 47ac1ce..637656c 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -21,6 +21,7 @@
 import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
 
 import android.Manifest;
+import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -31,7 +32,6 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -39,16 +39,15 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.autofill.IAutoFillManagerService;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
+import android.util.LocalLog;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 import android.view.autofill.AutoFillId;
 
 import com.android.internal.annotations.GuardedBy;
@@ -56,10 +55,12 @@
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 import com.android.server.FgThread;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 
 /**
  * Entry point service for auto-fill management.
@@ -76,7 +77,8 @@
     private static final long SERVICE_BINDING_LIFETIME_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
 
     protected static final int MSG_UNBIND = 1;
-    protected static final int MSG_SHOW_AUTO_FILL = 2;
+    protected static final int MSG_REQUEST_AUTO_FILL_FOR_USER = 2;
+    protected static final int MSG_REQUEST_AUTO_FILL = 3;
 
     private final AutoFillManagerServiceStub mServiceStub;
     private final AutoFillUI mUi;
@@ -91,11 +93,23 @@
         public void executeMessage(Message msg) {
             switch (msg.what) {
                 case MSG_UNBIND: {
-                    removeStaleServiceForUser(msg.arg1);
+                    synchronized (mLock) {
+                        removeCachedServiceLocked(msg.arg1);
+                    }
                     return;
-                } case MSG_SHOW_AUTO_FILL: {
+                } case MSG_REQUEST_AUTO_FILL_FOR_USER: {
+                    final int userId = msg.arg1;
+                    final int flags = msg.arg2;
+                    handleAutoFillForUser(userId, flags);
+                    return;
+                } case MSG_REQUEST_AUTO_FILL: {
                     final SomeArgs args = (SomeArgs) msg.obj;
-                    showAutoFillInput(msg.arg1, (AutoFillId) args.arg1, (Rect) args.arg2);
+                    final int userId = msg.arg1;
+                    final int flags = msg.arg2;
+                    final IBinder activityToken = (IBinder) args.arg1;
+                    final AutoFillId autoFillId = (AutoFillId) args.arg2;
+                    final Rect bounds = (Rect) args.arg3;
+                    handleAutoFill(activityToken, userId, autoFillId, bounds, flags);
                     return;
                 } default: {
                     Slog.w(TAG, "Invalid message: " + msg);
@@ -123,6 +137,9 @@
     @GuardedBy("mLock")
     private SparseArray<AutoFillManagerServiceImpl> mServicesCache = new SparseArray<>();
 
+    // TODO(b/33197203): set a different max (or disable it) on low-memory devices.
+    private final LocalLog mRequestsHistory = new LocalLog(100);
+
     public AutoFillManagerService(Context context) {
         super(context);
 
@@ -166,11 +183,11 @@
         if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
                 + serviceComponent + ", info: " + serviceInfo);
         if (serviceInfo == null) {
-            Slog.w(TAG, "no service info for " + serviceComponent);
+            if (DEBUG) Slog.d(TAG, "no service info for " + serviceComponent);
             return null;
         }
-        return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, FgThread.getHandler(),
-                userId, serviceInfo.applicationInfo.uid, serviceComponent,
+        return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, mRequestsHistory,
+                FgThread.getHandler(), userId, serviceInfo.applicationInfo.uid, serviceComponent,
                 SERVICE_BINDING_LIFETIME_MS);
     }
 
@@ -198,87 +215,92 @@
         }
         // Keep service connection alive for a while, in case user needs to interact with it
         // (for example, to save the data that was inputted in)
+        if (mHandlerCaller.hasMessages(MSG_UNBIND)) {
+            mHandlerCaller.removeMessages(MSG_UNBIND);
+        }
         mHandlerCaller.sendMessageDelayed(mHandlerCaller.obtainMessageI(MSG_UNBIND, userId),
                 SERVICE_BINDING_LIFETIME_MS);
         return service;
     }
 
     /**
-     * Removes a cached service, but respecting its TTL.
+     * Removes a cached service for a given user.
      */
-    private void removeStaleServiceForUser(int userId) {
-        synchronized (mLock) {
-            removeCachedService(userId, false);
-        }
-    }
-
-    /**
-     * Removes a cached service, even if it has TTL.
-     */
-    void removeCachedServiceForUserLocked(int userId) {
-        removeCachedService(userId, true);
-    }
-
-    private void removeCachedService(int userId, boolean force) {
+    void removeCachedServiceLocked(int userId) {
         if (DEBUG) Log.d(TAG, "removing cached service for userId " + userId);
         final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
         if (service == null) {
-            Log.w(TAG, "removeCachedServiceForUser(): no cached service for userId " + userId);
-            return;
-        }
-        if (!force) {
-            // Check TTL first.
-            final long now = SystemClock.uptimeMillis();
-            if (service.mEstimateTimeOfDeath > now) {
-                if (DEBUG) {
-                    final StringBuilder msg = new StringBuilder("service has some TTL left: ");
-                    TimeUtils.formatDuration(service.mEstimateTimeOfDeath - now, msg);
-                    Log.d(TAG, msg.toString());
-                }
-                return;
+            if (DEBUG) {
+                Log.d(TAG, "removeCachedServiceForUser(): no cached service for userId " + userId);
             }
+            return;
         }
         mServicesCache.delete(userId);
         service.stopLocked();
-
     }
 
-
-    private void requestAutoFillLocked(IBinder activityToken, int userId, Bundle extras,
-            int flags) {
-        final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
-        if (service != null) {
-            service.requestAutoFill(activityToken, extras, flags);
+    private void handleAutoFill(IBinder activityToken, int userId, AutoFillId autoFillId,
+            Rect bounds, int flags) {
+        synchronized (mLock) {
+            final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+            if (service != null) {
+                // TODO(b/33197203): must pass AUTO_FILL_FLAG_TYPE_FILL because AM is expecting
+                // either that flag or AUTO_FILL_FLAG_TYPE_SAVE; should go away once save is
+                // refactored
+                flags |= AUTO_FILL_FLAG_TYPE_FILL;
+                service.requestAutoFillLocked(activityToken, autoFillId, bounds, flags);
+            }
         }
     }
 
-    private void showAutoFillInput(int userId, AutoFillId id, Rect rect) {
-        if (DEBUG) Slog.d(TAG, "handler.showAutoFillInput(): id=" + id + ", rect=" + rect);
-
+    private void handleAutoFillForUser(int userId, int flags) {
+        if (DEBUG) {
+            Slog.d(TAG, "handler.requestAutoFillForUser(): id=" + userId + ", flags=" + flags);
+        }
+        final List<IBinder> topActivities = LocalServices
+                .getService(ActivityManagerInternal.class).getTopVisibleActivities();
+        if (DEBUG)
+            Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
+        if (topActivities.isEmpty()) {
+            Slog.w(TAG, "Could not get top activity");
+            return;
+        }
+        final IBinder activityToken = topActivities.get(0);
         synchronized (mLock) {
-            requestAutoFillLocked(null, userId, null, AUTO_FILL_FLAG_TYPE_FILL);
+            final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+            if (service == null) {
+                Slog.w(TAG, "no service for user " + userId);
+                return;
+            }
+            service.requestAutoFillLocked(activityToken, null, null, flags);
         }
     }
 
     final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
 
         @Override
-        public void showAutoFillInput(AutoFillId id, Rect boundaries) {
-            if (DEBUG) Slog.d(TAG, "showAutoFillInput(): id=" + id + ", boundaries=" + boundaries);
+        public void requestAutoFill(AutoFillId id, Rect bounds, int flags) {
+            if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", autoFillId=" + id
+                    + ", bounds=" + bounds);
 
-            // TODO(b/33197203): fail if it's not called by same uid as the top activity
-            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW_AUTO_FILL,
-                    UserHandle.getCallingUserId(), id, boundaries));
+            // Make sure its called by the top activity.
+            final int uid = Binder.getCallingUid();
+            final IBinder activityToken = LocalServices.getService(ActivityManagerInternal.class)
+                        .getTopVisibleActivity(uid);
+            if (activityToken == null) {
+                throw new SecurityException("uid " + uid + " does not own the top activity");
+            }
+
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIIOOO(MSG_REQUEST_AUTO_FILL,
+                    UserHandle.getCallingUserId(), flags, activityToken, id, bounds));
         }
 
         @Override
-        public void requestAutoFill(IBinder activityToken, int userId, Bundle extras, int flags) {
-            if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", userId=" + userId);
+        public void requestAutoFillForUser(int userId, int flags) {
             mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
-            synchronized (mLock) {
-                requestAutoFillLocked(activityToken, userId, extras, flags);
-            }
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageII(
+                    MSG_REQUEST_AUTO_FILL_FOR_USER, userId, flags));
         }
 
         @Override
@@ -304,6 +326,8 @@
                     }
                 }
             }
+            pw.println("Requests history:");
+            mRequestsHistory.reverseDump(fd, pw, args);
         }
 
         @Override
@@ -326,7 +350,7 @@
         public void onChange(boolean selfChange, Uri uri, int userId) {
             if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
             synchronized (mLock) {
-                removeCachedServiceForUserLocked(userId);
+                removeCachedServiceLocked(userId);
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 77e7b31..ad5cc20 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -16,16 +16,18 @@
 
 package com.android.server.autofill;
 
-import static com.android.server.autofill.Helper.DEBUG;
-import static com.android.server.autofill.Helper.bundleToString;
 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_SAVE;
+import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_HIDE;
+
+import static com.android.server.autofill.Helper.DEBUG;
+import static com.android.server.autofill.Helper.bundleToString;
 
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
 import android.app.IActivityManager;
 import android.app.assist.AssistStructure;
 import android.content.BroadcastReceiver;
@@ -35,6 +37,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.graphics.Rect;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -53,6 +56,7 @@
 import android.service.autofill.IAutoFillServerCallback;
 import android.service.autofill.IAutoFillService;
 import android.service.voice.VoiceInteractionSession;
+import android.util.LocalLog;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -64,9 +68,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
-import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
@@ -86,6 +90,7 @@
     private final int mUserId;
     private final int mUid;
     private final ComponentName mComponent;
+    private final String mComponentName;
     private final Context mContext;
     private final IActivityManager mAm;
     private final Object mLock;
@@ -93,14 +98,15 @@
     private final AutoFillManagerService mManagerService;
     private final AutoFillUI mUi;
 
-    // TODO(b/33197203): improve its usage
-    // - set maximum number of entries
-    // - disable on low-memory devices.
-    private final List<String> mRequestHistory = new LinkedList<>();
+    // Token used for fingerprint authentication
+    // TODO(b/33197203): create on demand?
+    private final IBinder mAuthToken = new Binder();
 
     @GuardedBy("mLock")
     private final List<QueuedRequest> mQueuedRequests = new LinkedList<>();
 
+    private final LocalLog mRequestsHistory;
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -140,9 +146,10 @@
                     if (DEBUG) Slog.d(TAG, "queued requests:" + mQueuedRequests.size());
                 }
                 for (final QueuedRequest request: mQueuedRequests) {
-                    requestAutoFillLocked(request.activityToken, request.extras, request.flags,
-                            false);
+                    requestAutoFillLocked(request.activityToken, request.autoFillId,
+                            request.bounds, request.flags, false);
                 }
+                mQueuedRequests.clear();
             }
         }
 
@@ -151,7 +158,7 @@
             if (DEBUG) Slog.d(TAG, name + " disconnected");
             synchronized (mLock) {
                 mService = null;
-                mManagerService.removeCachedServiceForUserLocked(mUserId);
+                mManagerService.removeCachedServiceLocked(mUserId);
             }
         }
     };
@@ -181,9 +188,9 @@
                     Slog.w(TAG, "no server callback for id " + resultCode);
                     return;
                 }
-                session.mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
+                session.setAppCallback(appBinder);
             }
-            mService.autoFill(structure, session.mServerCallback, session.mExtras, flags);
+            mService.autoFill(structure, session.mServerCallback, flags);
         }
     };
 
@@ -198,15 +205,17 @@
     long mEstimateTimeOfDeath;
 
     AutoFillManagerServiceImpl(AutoFillManagerService managerService, AutoFillUI ui,
-            Context context, Object lock, Handler handler, int userId, int uid,
-            ComponentName component, long ttl) {
+            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;
         mUserId = userId;
         mUid = uid;
         mComponent = component;
+        mComponentName = mComponent.flattenToShortString();
         mAm = ActivityManager.getService();
         setLifeExpectancy(ttl);
 
@@ -254,60 +263,86 @@
     /**
      * Asks service to auto-fill an activity.
      *
-     * @param activityToken activity token
-     * @param extras bundle to be passed to the {@link AutoFillService} method.
+     * @param activityToken activity token.
+     * @param autoFillId id of the view that requested auto-fill.
      * @param flags optional flags.
      */
-    void requestAutoFill(@Nullable IBinder activityToken, @Nullable Bundle extras, int flags) {
-        synchronized (mLock) {
-            if (!mBound) {
-                Slog.w(TAG, "requestAutoFill() failed because it's not bound to service");
-                return;
-            }
+    void requestAutoFillLocked(IBinder activityToken, @Nullable AutoFillId autoFillId,
+            @Nullable Rect bounds, int flags) {
+        if (!mBound) {
+            Slog.w(TAG, "requestAutoFill() failed because it's not bound to service");
+            return;
         }
 
-        // TODO(b/33197203): activityToken should probably not be null, but we need to wait until
-        // the UI is triggering the call (for now it's trough 'adb shell cmd autofill request'
-        if (activityToken == null) {
-            // Let's get top activities from all visible stacks.
-
-            // TODO(b/33197203): overload getTopVisibleActivities() to take userId, otherwise it
-            // could return activities for different users when a work profile app is displayed in
-            // another window (in a multi-window environment).
-            final List<IBinder> topActivities = LocalServices
-                    .getService(ActivityManagerInternal.class).getTopVisibleActivities();
-            if (DEBUG)
-                Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
-            if (topActivities.isEmpty()) {
-                Slog.w(TAG, "Could not get top activity");
-                return;
-            }
-            activityToken = topActivities.get(0);
-        }
-
-        final String historyItem = TimeUtils.formatForLogging(System.currentTimeMillis())
-                + " - " + activityToken;
-        synchronized (mLock) {
-            mRequestHistory.add(historyItem);
-            requestAutoFillLocked(activityToken, extras, flags, true);
-        }
+        requestAutoFillLocked(activityToken, autoFillId, bounds, flags, true);
     }
 
-    private void requestAutoFillLocked(IBinder activityToken, @Nullable Bundle extras, int flags,
-            boolean queueIfNecessary) {
+    private void requestAutoFillLocked(IBinder activityToken, AutoFillId autoFillId, Rect bounds,
+            int flags, boolean queueIfNecessary) {
         if (mService == null) {
             if (!queueIfNecessary) {
                 Slog.w(TAG, "requestAutoFillLocked(): service is null");
                 return;
             }
-            if (DEBUG) Slog.d(TAG, "requestAutoFill(): service not set yet, queuing it");
-            mQueuedRequests.add(new QueuedRequest(activityToken, extras, flags));
+            if (DEBUG) Slog.d(TAG, "requestAutoFillLocked(): service not set yet, queuing it");
+            mQueuedRequests.add(new QueuedRequest(activityToken, autoFillId, bounds, flags));
+            return;
+        }
+        if (activityToken == null) {
+            // Sanity check
+            Slog.wtf(TAG, "requestAutoFillLocked(): null activityToken");
+            return;
+        }
+
+        final Session session = getSessionByTokenLocked(activityToken);
+
+        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);
+            }
+
+            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);
+
+                return;
+            }
+
+            session.mCurrentAutoFillId = autoFillId;
+            session.mCurrentBounds = bounds;
+            mUi.showResponse(mUserId, session.mId, autoFillId, bounds, session.mCurrentResponse);
             return;
         }
 
         final int sessionId = ++sSessionIdCounter;
-        final Session session = new Session(sessionId, extras);
-        mSessions.put(sessionId, session);
+        if (DEBUG) {
+            Slog.d(TAG, "requestAutoFillLocked(): new session (id=" + sessionId + " for token "
+                    + activityToken + " and autoFillId " + autoFillId);
+        }
+
+        final Session newSession = new Session(sessionId, activityToken, autoFillId, bounds);
+        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:
@@ -324,10 +359,79 @@
                 Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
             }
         } catch (RemoteException e) {
-            // Should happen, it's a local call.
+            // 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;
+    }
+
     void stopLocked() {
         if (DEBUG) Slog.d(TAG, "stopLocked()");
 
@@ -443,12 +547,13 @@
 
         pw.print(prefix); pw.print("mUserId="); pw.println(mUserId);
         pw.print(prefix); pw.print("mUid="); pw.println(mUid);
-        pw.print(prefix); pw.print("mComponent="); pw.println(mComponent.flattenToShortString());
+        pw.print(prefix); pw.print("mComponent="); pw.println(mComponentName);
         pw.print(prefix); pw.print("mService: "); pw.println(mService);
         pw.print(prefix); pw.print("mBound="); pw.println(mBound);
         pw.print(prefix); pw.print("mEstimateTimeOfDeath=");
             TimeUtils.formatDuration(mEstimateTimeOfDeath, SystemClock.uptimeMillis(), pw);
-        pw.println();
+            pw.println();
+        pw.print(prefix); pw.print("mAuthToken: "); pw.println(mAuthToken);
 
         if (DEBUG) {
             // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
@@ -456,14 +561,6 @@
             mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix);
         }
 
-        if (mRequestHistory.isEmpty()) {
-            pw.print(prefix); pw.println("No history");
-        } else {
-            pw.print(prefix); pw.println("History:");
-            for (int i = 0; i < mRequestHistory.size(); i++) {
-                pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mRequestHistory.get(i));
-            }
-        }
         if (mQueuedRequests.isEmpty()) {
             pw.print(prefix); pw.println("No queued requests");
         } else {
@@ -480,38 +577,37 @@
         } else {
             pw.print(prefix); pw.print(size); pw.println(" sessions:");
             for (int i = 0; i < size; i++) {
-                pw.print(prefix2); pw.print(mSessions.keyAt(i));
-                final Session session = mSessions.valueAt(i);
-                if (session.mAppCallback == null) {
-                    pw.println("(no appCallback)");
-                } else {
-                    pw.print(" (app callback: "); pw.print(session.mAppCallback) ; pw.println(")");
-                }
+                pw.print(prefix); pw.print("#"); pw.println(i + 1);
+                mSessions.valueAt(i).dumpLocked(prefix2, pw);
             }
-            pw.println();
         }
     }
 
     @Override
     public String toString() {
         return "AutoFillManagerServiceImpl: [userId=" + mUserId + ", uid=" + mUid
-                + ", component=" + mComponent.flattenToShortString() + "]";
+                + ", component=" + mComponentName + "]";
     }
 
     private static final class QueuedRequest {
         final IBinder activityToken;
-        final Bundle extras;
+        final AutoFillId autoFillId;
+        final Rect bounds;
         final int flags;
 
-        QueuedRequest(IBinder activityToken, Bundle extras, int flags) {
+        QueuedRequest(IBinder activityToken, AutoFillId autoFillId, Rect bounds, int flags) {
             this.activityToken = activityToken;
-            this.extras = extras;
+            this.autoFillId = autoFillId;
+            this.bounds = bounds;
             this.flags = flags;
         }
 
         @Override
         public String toString() {
-            return "flags: " + flags + " token: " + activityToken;
+            if (!DEBUG) return super.toString();
+
+            return "QueuedRequest: [flags=" + flags + ", token=" + activityToken
+                    + ", id=" + autoFillId + ", bounds=" + bounds;
         }
     }
 
@@ -532,11 +628,17 @@
     private final class Session {
 
         private final int mId;
-        private final Bundle mExtras;
+        private final WeakReference<IBinder> mActivityToken;
+
         private IAutoFillAppCallback mAppCallback;
 
-        // Token used on fingerprint authentication
-        private final IBinder mToken = new Binder();
+        // Current view where the auto-fill bar is displayed
+        @GuardedBy("mLock")
+        private AutoFillId mCurrentAutoFillId;
+        @GuardedBy("mLock")
+        private Rect mCurrentBounds;
+        @GuardedBy("mLock")
+        private FillResponse mCurrentResponse;
 
         private final IFingerprintService mFingerprintService;
 
@@ -710,13 +812,28 @@
             }
         };
 
-        private Session(int id, Bundle extras) {
+        private Session(int id, IBinder activityToken, AutoFillId autoFillId, Rect bounds) {
             this.mId = id;
-            this.mExtras = extras;
+            this.mActivityToken = new WeakReference<>(activityToken);
+            this.mCurrentAutoFillId = autoFillId;
+            this.mCurrentBounds = bounds;
             this.mFingerprintService = IFingerprintService.Stub
                     .asInterface(ServiceManager.getService("fingerprint"));
         }
 
+        void setAppCallback(IBinder appBinder) {
+            try {
+                appBinder.linkToDeath(() -> {
+                    if (DEBUG) Slog.d(TAG, "app callback died");
+                    // TODO(b/33197203): more cleanup here?
+                    mAppCallback = null;
+                }, 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "linkToDeath() failed: " + e);
+            }
+            mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
+        }
+
         private void showResponseLocked(FillResponse response, boolean authRequired) {
             if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly
                     + ", authRequired=" + authRequired +"):" + response);
@@ -735,7 +852,8 @@
 
             if (!authRequired) {
                 // TODO(b/33197203): add MetricsLogger call
-                mUi.showOptions(mUserId, mId, response);
+                mCurrentResponse = response;
+                mUi.showResponse(mUserId, mId, mCurrentAutoFillId, mCurrentBounds, mCurrentResponse);
                 return;
             }
 
@@ -768,8 +886,8 @@
                 mDatasetRequiringAuth = dataset;
                 final boolean requiresFingerprint = dataset.hasCryptoObject();
                 if (requiresFingerprint) {
-                    // TODO(b/33197203): check if fingerprint is available first and call error callback
-                    // with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
+                    // TODO(b/33197203): check if fingerprint is available first and call error
+                    // callback with FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE if it's not.
                     // Start scanning for the fingerprint.
                     scanFingerprint(dataset.getCryptoObjectOpId());
                     // Displays the message asking the user to tap (or fingerprint) for AutoFill.
@@ -785,14 +903,27 @@
             }
         }
 
+        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);
+        }
+
         private void autoFillAppLocked(Dataset dataset, boolean removeSelf) {
             try {
                 if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
                 mAppCallback.autoFill(dataset);
 
-                // TODO(b/33197203): temporarily hack: show the save notification, since save is
-                // not integrated with IME yet.
-                mUi.showSaveNotification(mUserId, null, dataset);
+                // TODO(b/33197203): temporarily hack: show the save notification after autofilled,
+                // since save is not automatically detected yet.
+                mUi.showSaveUI(mUserId, mId);
 
             } catch (RemoteException e) {
                 Slog.w(TAG, "Error auto-filling activity: " + e);
@@ -812,7 +943,8 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 // TODO(b/33197203): set a timeout?
-                mFingerprintService.authenticate(mToken, opId, mUserId, mServiceReceiver, 0, null);
+                mFingerprintService.authenticate(mAuthToken, opId, mUserId, mServiceReceiver, 0,
+                        null);
             } catch (RemoteException e) {
                 // Local call, shouldn't happen.
             } finally {
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
index 26f2451..4998e3f 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
@@ -74,7 +74,7 @@
 
     private int requestAutoFill(int flags) throws RemoteException {
         final int userId = getUserIdFromArgs();
-        mService.requestAutoFill(null, userId, null, flags);
+        mService.requestAutoFillForUser(userId, flags);
         return 0;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index ad525d4..6618a27 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -15,11 +15,8 @@
  */
 package com.android.server.autofill;
 
-import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
-import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
 
 import static com.android.server.autofill.Helper.DEBUG;
-import static com.android.server.autofill.Helper.bundleToString;
 
 import android.app.Activity;
 import android.app.Notification;
@@ -31,9 +28,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
-import android.service.autofill.AutoFillService;
 import android.util.Slog;
 import android.view.autofill.AutoFillId;
 import android.view.autofill.Dataset;
@@ -87,12 +84,15 @@
 
     /**
      * Shows the options from a {@link FillResponse} so the user can pick up the proper
-     * {@link Dataset} (when the response has one).
+     * {@link Dataset} (when the response has one) for a given view (identified by
+     * {@code autoFillId}).
      */
-    void showOptions(int userId, int sessionId, FillResponse response) {
+    void showResponse(int userId, int sessionId, AutoFillId autoFillId, Rect bounds,
+            FillResponse response) {
+        if (DEBUG) Slog.d(TAG, "showResponse: id=" + autoFillId +  ", bounds=" + bounds);
         // TODO(b/33197203): proper implementation
-        // TODO(b/33197203): make sure if removes the callback from cache
-        showOptionsNotification(userId, sessionId, response);
+        // TODO(b/33197203): make sure if removes the session from cache
+        showOptionsNotification(userId, sessionId, autoFillId, response);
     }
 
     /**
@@ -127,6 +127,13 @@
     }
 
     /**
+     * Shows the UI asking the user to save for auto-fill.
+     */
+    void showSaveUI(int userId, int sessionId) {
+        showSaveNotification(userId, sessionId);
+    }
+
+    /**
      * Called by service after the user user the fingerprint sensors to authenticate.
      */
     void dismissFingerprintRequest(int userId, boolean success) {
@@ -150,26 +157,10 @@
         return service;
     }
 
-    private void onSaveRequested(int userId, Bundle responseExtras, Bundle datasetExtras) {
+    private void onSaveRequested(int userId, int sessionId) {
         synchronized (mLock) {
             final AutoFillManagerServiceImpl service = getServiceLocked(userId);
-            if (service == null) return;
-
-            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 notificataion: " +
-                        bundleToString(datasetExtras));
-                extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
-            }
-
-            service.requestAutoFill(null, extras, AUTO_FILL_FLAG_TYPE_SAVE);
+            service.requestSaveLocked(sessionId);
         }
     }
 
@@ -253,10 +244,7 @@
                 Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
                 return;
             }
-            final FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE);
             final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
-            final Bundle responseExtras = response == null ? null : response.getExtras();
-            final Bundle datasetExtras = dataset == null ? null : dataset.getExtras();
             final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
 
             if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
@@ -264,7 +252,7 @@
             synchronized (mLock) {
                 switch (type) {
                     case TYPE_SAVE:
-                        onSaveRequested(userId, responseExtras, datasetExtras);
+                        onSaveRequested(userId, sessionId);
                         break;
                     case TYPE_FINISH_SESSION:
                         onSessionDone(userId, sessionId);
@@ -315,17 +303,18 @@
      * 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 sessionId, FillResponse response) {
+    private void showOptionsNotification(int userId, int callbackId, AutoFillId autoFillId,
+            FillResponse response) {
         final long token = Binder.clearCallingIdentity();
         try {
-            showOptionsNotificationAsSystem(userId, sessionId, response);
+            showOptionsNotificationAsSystem(userId, callbackId, autoFillId, response);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
     private void showOptionsNotificationAsSystem(int userId, int sessionId,
-            FillResponse response) {
+            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);
@@ -352,13 +341,13 @@
         }
         boolean showSave = false;
         if (datasets == null ) {
-            subTitle = "No options to auto-fill this activity.";
+            subTitle = "No options to auto-fill " + autoFillId;
         } else if (datasets.isEmpty()) {
             if (savableIds.length == 0) {
-                subTitle = "No options to auto-fill this activity.";
+                subTitle = "No options to auto-fill " + autoFillId;
             } else {
-                subTitle = "No options to auto-fill this activity, but provider can save ids:\n"
-                        + Arrays.toString(savableIds);
+                subTitle = "No options to auto-fill " + autoFillId
+                        + ", but provider can save ids:\n" + Arrays.toString(savableIds);
                 showSave = true;
             }
         } else {
@@ -369,7 +358,7 @@
             } else {
                 autoCancel = false;
                 final int size = datasets.size();
-                subTitle = "There are " + size + " option(s).\n"
+                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";
@@ -394,36 +383,28 @@
         NotificationManager.from(mContext).notify(TYPE_OPTIONS, userId, notification.build());
 
         if (showSave) {
-            showSaveNotification(userId, response, null);
+            showSaveNotification(userId, sessionId);
         }
     }
 
-    void showSaveNotification(int userId, FillResponse response, Dataset dataset) {
+    void showSaveNotification(int userId, int sessionId) {
         final long token = Binder.clearCallingIdentity();
         try {
-            showSaveNotificationAsSystem(userId, response, dataset);
+            showSaveNotificationAsSystem(userId, sessionId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    private void showSaveNotificationAsSystem(int userId, FillResponse response, Dataset dataset) {
-        final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE);
-        if (response != null) {
-            saveIntent.putExtra(EXTRA_FILL_RESPONSE, response);
-        }
-        if (dataset != null) {
-            saveIntent.putExtra(EXTRA_DATASET, dataset);
-        }
+    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";
-        // Response is not set after fillign an authenticated dataset...
-        final String subTitle = response == null
-                ? "Tap notification to ask provider to save fields."
-                : "Tap notification to ask provider to save fields: \n"
-                        + Arrays.toString(response.getSavableIds());
+        final String subTitle = "Tap notification to ask provider to save fields.";
 
         final Notification notification = newNotificationBuilder()
                 .setAutoCancel(true)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 42d036c..f116a4a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22598,6 +22598,13 @@
         }
 
         @Override
+        public IBinder getTopVisibleActivity(int uid) {
+            synchronized (ActivityManagerService.this) {
+                return mStackSupervisor.getTopVisibleActivity(uid);
+            }
+        }
+
+        @Override
         public void notifyDockedStackMinimizedChanged(boolean minimized) {
             synchronized (ActivityManagerService.this) {
                 mStackSupervisor.setDockedStackMinimized(minimized);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 389fb8b..b2f8b92 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4902,4 +4902,24 @@
         }
         return topActivityTokens;
     }
+
+    public IBinder getTopVisibleActivity(int uid) {
+        // TODO(b/33197203): get rid of DEFAULT_DISPLAY here?. Used in
+        // VoiceInteractionManagerServiceImpl#showSessionLocked.
+        final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY);
+        if (display == null) {
+            return null;
+        }
+        final ArrayList<ActivityStack> stacks = display.mStacks;
+        for (int i = stacks.size() - 1; i >= 0; i--) {
+            ActivityStack stack = stacks.get(i);
+            if (stack.getStackVisibilityLocked(null) == ActivityStack.STACK_VISIBLE) {
+                ActivityRecord top = stack.topActivity();
+                if (top != null && stack == mFocusedStack && top.app.uid == uid) {
+                    return top.appToken;
+                }
+            }
+        }
+        return null;
+    }
 }