Merge "Refactoring of auto fill - lifecycle, auth, improvements"
diff --git a/Android.mk b/Android.mk
index 22323c5..1da4783 100644
--- a/Android.mk
+++ b/Android.mk
@@ -265,8 +265,9 @@
 	core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \
 	core/java/android/service/autofill/IAutoFillAppCallback.aidl \
 	core/java/android/service/autofill/IAutoFillManagerService.aidl \
-	core/java/android/service/autofill/IAutoFillServerCallback.aidl \
 	core/java/android/service/autofill/IAutoFillService.aidl \
+	core/java/android/service/autofill/IFillCallback.aidl \
+	core/java/android/service/autofill/ISaveCallback.aidl \
 	core/java/android/service/carrier/ICarrierService.aidl \
 	core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
 	core/java/android/service/carrier/ICarrierMessagingService.aidl \
diff --git a/api/current.txt b/api/current.txt
index eca3c27..adc32ee 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9092,6 +9092,10 @@
     field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+    field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+    field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+    field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+    field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -36096,26 +36100,19 @@
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
-    method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
     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.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
-    field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
-    field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
-    field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class FillCallback {
-    method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
+  public final class FillCallback implements android.os.Parcelable {
+    method public int describeContents();
     method public void onFailure(java.lang.CharSequence);
-    method public void onFillResponseAuthentication(int);
     method public void onSuccess(android.view.autofill.FillResponse);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
   }
 
   public final class SaveCallback {
@@ -46755,10 +46752,9 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.CharSequence);
+    ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
     method public android.view.autofill.Dataset build();
-    method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
-    method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+    method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
     method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
     method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
@@ -46770,12 +46766,11 @@
   }
 
   public static final class FillResponse.Builder {
-    ctor public FillResponse.Builder();
+    ctor public FillResponse.Builder(java.lang.String);
     method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
     method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
     method public android.view.autofill.FillResponse build();
-    method public android.view.autofill.FillResponse.Builder requiresCustomAuthentication(android.os.Bundle, int);
-    method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+    method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
     method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
@@ -47201,7 +47196,7 @@
 
   public final class TextClassificationManager {
     method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+    method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
   }
 
   public final class TextClassificationResult {
diff --git a/api/system-current.txt b/api/system-current.txt
index 39c6fbd..4a16b839 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9509,6 +9509,10 @@
     field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+    field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+    field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+    field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+    field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -39133,26 +39137,19 @@
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
-    method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
     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.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
-    field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
-    field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
-    field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class FillCallback {
-    method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
+  public final class FillCallback implements android.os.Parcelable {
+    method public int describeContents();
     method public void onFailure(java.lang.CharSequence);
-    method public void onFillResponseAuthentication(int);
     method public void onSuccess(android.view.autofill.FillResponse);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
   }
 
   public final class SaveCallback {
@@ -50165,10 +50162,9 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.CharSequence);
+    ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
     method public android.view.autofill.Dataset build();
-    method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
-    method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+    method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
     method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
     method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
@@ -50180,12 +50176,11 @@
   }
 
   public static final class FillResponse.Builder {
-    ctor public FillResponse.Builder();
+    ctor public FillResponse.Builder(java.lang.String);
     method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
     method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
     method public android.view.autofill.FillResponse build();
-    method public android.view.autofill.FillResponse.Builder requiresCustomAuthentication(android.os.Bundle, int);
-    method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+    method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
     method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
@@ -50611,7 +50606,7 @@
 
   public final class TextClassificationManager {
     method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+    method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
   }
 
   public final class TextClassificationResult {
diff --git a/api/test-current.txt b/api/test-current.txt
index bc7a001..d36b8de 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -9118,6 +9118,10 @@
     field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+    field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+    field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+    field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+    field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
     field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
     field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC";
@@ -36231,26 +36235,19 @@
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
-    method public void onDatasetAuthenticationRequest(android.os.Bundle, int);
     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.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
-    field public static final int FLAG_AUTHENTICATION_REQUESTED = 1; // 0x1
-    field public static final int FLAG_AUTHENTICATION_SUCCESS = 2; // 0x2
-    field public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE = 8; // 0x8
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class FillCallback {
-    method public void onDatasetAuthentication(android.view.autofill.Dataset, int);
+  public final class FillCallback implements android.os.Parcelable {
+    method public int describeContents();
     method public void onFailure(java.lang.CharSequence);
-    method public void onFillResponseAuthentication(int);
     method public void onSuccess(android.view.autofill.FillResponse);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR;
   }
 
   public final class SaveCallback {
@@ -47068,10 +47065,9 @@
   }
 
   public static final class Dataset.Builder {
-    ctor public Dataset.Builder(java.lang.CharSequence);
+    ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence);
     method public android.view.autofill.Dataset build();
-    method public android.view.autofill.Dataset.Builder requiresCustomAuthentication(android.os.Bundle, int);
-    method public android.view.autofill.Dataset.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+    method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
     method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle);
     method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue);
   }
@@ -47083,12 +47079,11 @@
   }
 
   public static final class FillResponse.Builder {
-    ctor public FillResponse.Builder();
+    ctor public FillResponse.Builder(java.lang.String);
     method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset);
     method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...);
     method public android.view.autofill.FillResponse build();
-    method public android.view.autofill.FillResponse.Builder requiresCustomAuthentication(android.os.Bundle, int);
-    method public android.view.autofill.FillResponse.Builder requiresFingerprintAuthentication(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.Bundle, int);
+    method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender);
     method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle);
   }
 
@@ -47514,7 +47509,7 @@
 
   public final class TextClassificationManager {
     method public java.util.List<android.view.textclassifier.TextLanguage> detectLanguages(java.lang.CharSequence);
-    method public android.view.textclassifier.TextClassifier getDefaultTextClassifier();
+    method public synchronized android.view.textclassifier.TextClassifier getDefaultTextClassifier();
   }
 
   public final class TextClassificationResult {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1130327..6a8141f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1806,6 +1806,41 @@
     @SystemApi
     public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
 
+    /**
+     * Intent extra: An id if an autofill item ({@link
+     * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}).
+     * <p>
+     * Type: String
+     * </p>
+     */
+    public static final String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID";
+
+    /**
+     * Intent extra: The assist structure which captures the filled screen.
+     * <p>
+     * Type: {@link android.app.assist.AssistStructure}
+     * </p>
+     */
+    public static final String EXTRA_AUTO_FILL_ASSIST_STRUCTURE =
+            "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE";
+
+    /**
+     * Intent extra: The metadata associated with the authenticated entity ({@link
+     * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}).
+     * <p>
+     * Type: {@link android.os.Bundle}
+     * </p>
+     */
+    public static final String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS";
+
+    /**
+     * Intent extra: A callback to report an authentication result.
+     * <p>
+     * Type: {@link android.view.autofill.FillResponse}
+     * </p>
+     */
+    public static final String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index e99d303..f94e89a 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.annotation.IntegerRes;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -1339,7 +1340,7 @@
     }
 
     /**
-     * Flatten a heterogeneous array containing a particular object type into
+     * Flatten a homogeneous array containing a particular object type into
      * the parcel, at
      * the current dataPosition() and growing dataCapacity() if needed.  The
      * type of the objects in the array must be one that implements Parcelable.
@@ -1361,7 +1362,7 @@
         if (val != null) {
             int N = val.length;
             writeInt(N);
-            for (int i=0; i<N; i++) {
+            for (int i = 0; i < N; i++) {
                 T item = val[i];
                 if (item != null) {
                     writeInt(1);
@@ -1376,6 +1377,146 @@
     }
 
     /**
+     * Write a uniform (all items are null or the same class) array list of
+     * parcelables.
+     *
+     * @param list The list to write.
+     *
+     * @hide
+     */
+    public final <T extends Parcelable> void writeTypedArrayList(@Nullable ArrayList<T> list,
+            int parcelableFlags) {
+        if (list != null) {
+            int N = list.size();
+            writeInt(N);
+            boolean wroteCreator = false;
+            for (int i = 0; i < N; i++) {
+                T item = list.get(i);
+                if (item != null) {
+                    writeInt(1);
+                    if (!wroteCreator) {
+                        writeParcelableCreator(item);
+                        wroteCreator = true;
+                    }
+                    item.writeToParcel(this, parcelableFlags);
+                } else {
+                    writeInt(0);
+                }
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * Reads a uniform (all items are null or the same class) array list of
+     * parcelables.
+     *
+     * @return The list or null.
+     *
+     * @hide
+     */
+    public final @Nullable <T> ArrayList<T> readTypedArrayList(@Nullable ClassLoader loader) {
+        int N = readInt();
+        if (N <= 0) {
+            return null;
+        }
+        Parcelable.Creator<?> creator = null;
+        ArrayList<T> result = new ArrayList<T>(N);
+        for (int i = 0; i < N; i++) {
+            if (readInt() != 0) {
+                if (creator == null) {
+                    creator = readParcelableCreator(loader);
+                    if (creator == null) {
+                        return null;
+                    }
+                }
+                final T parcelable;
+                if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+                    Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+                            (Parcelable.ClassLoaderCreator<?>) creator;
+                    parcelable = (T) classLoaderCreator.createFromParcel(this, loader);
+                } else {
+                    parcelable = (T) creator.createFromParcel(this);
+                }
+                result.add(parcelable);
+            } else {
+                result.add(null);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Write a uniform (all items are null or the same class) array set of
+     * parcelables.
+     *
+     * @param set The set to write.
+     *
+     * @hide
+     */
+    public final <T extends Parcelable> void writeTypedArraySet(@Nullable ArraySet<T> set,
+            int parcelableFlags) {
+        if (set != null) {
+            int N = set.size();
+            writeInt(N);
+            boolean wroteCreator = false;
+            for (int i = 0; i < N; i++) {
+                T item = set.valueAt(i);
+                if (item != null) {
+                    writeInt(1);
+                    if (!wroteCreator) {
+                        writeParcelableCreator(item);
+                        wroteCreator = true;
+                    }
+                    item.writeToParcel(this, parcelableFlags);
+                } else {
+                    writeInt(0);
+                }
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
+     * Reads a uniform (all items are null or the same class) array set of
+     * parcelables.
+     *
+     * @return The set or null.
+     *
+     * @hide
+     */
+    public final @Nullable <T> ArraySet<T> readTypedArraySet(@Nullable ClassLoader loader) {
+        int N = readInt();
+        if (N <= 0) {
+            return null;
+        }
+        Parcelable.Creator<?> creator = null;
+        ArraySet<T> result = new ArraySet<T>(N);
+        for (int i = 0; i < N; i++) {
+            T parcelable = null;
+            if (readInt() != 0) {
+                if (creator == null) {
+                    creator = readParcelableCreator(loader);
+                    if (creator == null) {
+                        return null;
+                    }
+                }
+                if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+                    Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+                            (Parcelable.ClassLoaderCreator<?>) creator;
+                    parcelable = (T) classLoaderCreator.createFromParcel(this, loader);
+                } else {
+                    parcelable = (T) creator.createFromParcel(this);
+                }
+            }
+            result.append(parcelable);
+        }
+        return result;
+    }
+
+    /**
      * Flatten the Parcelable object into the parcel.
      *
      * @param val The Parcelable object to be written.
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index bfaf23c..aa4d26c 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -15,7 +15,10 @@
  */
 package android.service.autofill;
 
-import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import com.android.internal.os.HandlerCaller;
 import android.annotation.SdkConstant;
 import android.app.Activity;
 import android.app.Service;
@@ -24,21 +27,13 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
 import android.util.Log;
-import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 
-import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
 //TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the
 //life-cycle (and how state could be maintained on server-side) is well documented.
 
@@ -48,12 +43,7 @@
  * <p>Apps providing auto-fill capabilities must extend this service.
  */
 public abstract class AutoFillService extends Service {
-
     private static final String TAG = "AutoFillService";
-    static final boolean DEBUG = true; // TODO(b/33197203): set to false once stable
-
-    // TODO(b/33197203): check for device's memory size instead of DEBUG?
-    static final boolean DEBUG_PENDING_CALLBACKS = DEBUG;
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
@@ -79,85 +69,33 @@
 
     // Internal bundle keys.
     /** @hide */ public static final String KEY_CALLBACK = "callback";
-    /** @hide */ public static final String KEY_SAVABLE_IDS = "savable_ids";
-    /** @hide */ public static final String EXTRA_CRYPTO_OBJECT_ID = "crypto_object_id";
-
-    // Prefix for public bundle keys.
-    private static final String KEY_PREFIX = "android.service.autofill.extra.";
-
-    /**
-     * Key of the {@link Bundle} passed to methods such as
-     * {@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, 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";
-
-    /**
-     * Used to indicate the user selected an action that requires authentication.
-     */
-    public static final int FLAG_AUTHENTICATION_REQUESTED = 1 << 0;
-
-    /**
-     * Used to indicate the user authentication succeeded.
-     */
-    public static final int FLAG_AUTHENTICATION_SUCCESS = 1 << 1;
-
-    /**
-     * Used to indicate the user authentication failed.
-     */
-    public static final int FLAG_AUTHENTICATION_ERROR = 1 << 2;
-
-    /**
-     * Used when the service requested Fingerprint authentication but such option is not available.
-     */
-    public static final int FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE  = 1 << 3;
 
     // Handler messages.
     private static final int MSG_CONNECT = 1;
     private static final int MSG_DISCONNECT = 2;
-    private static final int MSG_AUTO_FILL_ACTIVITY = 3;
-    private static final int MSG_AUTHENTICATE_FILL_RESPONSE = 4;
-    private static final int MSG_AUTHENTICATE_DATASET = 5;
-    private static final int MSG_SAVE = 6;
+    private static final int MSG_ON_FILL_REQUEST = 3;
+    private static final int MSG_ON_SAVE_REQUEST = 4;
 
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
-
         @Override
-        public void autoFill(AssistStructure structure, IAutoFillServerCallback callback) {
-            mHandlerCaller
-                    .obtainMessageOO(MSG_AUTO_FILL_ACTIVITY, structure, callback)
+        public void onFillRequest(AssistStructure structure, Bundle extras,
+                IFillCallback callback) {
+            ICancellationSignal transport = CancellationSignal.createTransport();
+            try {
+                callback.onCancellable(transport);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+            mHandlerCaller.obtainMessageOOOO(MSG_ON_FILL_REQUEST, structure,
+                    CancellationSignal.fromTransport(transport), extras, callback)
                     .sendToTarget();
         }
 
         @Override
-        public void save(AssistStructure structure, IAutoFillServerCallback callback,
-                Bundle extras) throws RemoteException {
-            mHandlerCaller
-                    .obtainMessageOOO(MSG_SAVE, structure, callback, extras)
-                    .sendToTarget();
-        }
-
-        @Override
-        public void authenticateFillResponse(Bundle extras, int flags) {
-            final Message msg = mHandlerCaller.obtainMessage(MSG_AUTHENTICATE_FILL_RESPONSE);
-            msg.arg1 = flags;
-            msg.obj = extras;
-            mHandlerCaller.sendMessage(msg);
-        }
-
-        @Override
-        public void authenticateDataset(Bundle extras, int flags) {
-            final Message msg = mHandlerCaller.obtainMessage(MSG_AUTHENTICATE_DATASET);
-            msg.arg1 = flags;
-            msg.obj = extras;
-            mHandlerCaller.sendMessage(msg);
+        public void onSaveRequest(AssistStructure structure, Bundle extras,
+                ISaveCallback callback) {
+            mHandlerCaller.obtainMessageOOO(MSG_ON_SAVE_REQUEST, structure,
+                    extras, callback).sendToTarget();
         }
 
         @Override
@@ -171,63 +109,41 @@
         }
     };
 
-    private final HandlerCaller.Callback mHandlerCallback = new HandlerCaller.Callback() {
-
-        @Override
-        public void executeMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_CONNECT: {
-                    onConnected();
-                    break;
-                } case MSG_AUTO_FILL_ACTIVITY: {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        final AssistStructure structure = (AssistStructure) args.arg1;
-                        final IAutoFillServerCallback callback =
-                                (IAutoFillServerCallback) args.arg2;
-                        handleAutoFill(structure, callback);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                } case MSG_SAVE: {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        final AssistStructure structure = (AssistStructure) args.arg1;
-                        final IAutoFillServerCallback callback =
-                                (IAutoFillServerCallback) args.arg2;
-                        final Bundle extras = (Bundle) args.arg3;
-                        handleSave(structure, callback, extras);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-                } case MSG_AUTHENTICATE_FILL_RESPONSE: {
-                    final int flags = msg.arg1;
-                    final Bundle extras = (Bundle) msg.obj;
-                    onFillResponseAuthenticationRequest(extras, flags);
-                    break;
-                } case MSG_AUTHENTICATE_DATASET: {
-                    final int flags = msg.arg1;
-                    final Bundle extras = (Bundle) msg.obj;
-                    onDatasetAuthenticationRequest(extras, flags);
-                    break;
-                } case MSG_DISCONNECT: {
-                    onDisconnected();
-                    break;
-                } default: {
-                    Log.w(TAG, "MyCallbacks received invalid message type: " + msg);
-                }
+    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
+        switch (msg.what) {
+            case MSG_CONNECT: {
+                onConnected();
+                break;
+            } case MSG_ON_FILL_REQUEST: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                final AssistStructure structure = (AssistStructure) args.arg1;
+                final CancellationSignal cancellation = (CancellationSignal) args.arg2;
+                final Bundle extras = (Bundle) args.arg3;
+                final IFillCallback callback = (IFillCallback) args.arg4;
+                final FillCallback fillCallback = new FillCallback(callback);
+                args.recycle();
+                onFillRequest(structure, extras, cancellation, fillCallback);
+                break;
+            } case MSG_ON_SAVE_REQUEST: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                final AssistStructure structure = (AssistStructure) args.arg1;
+                final Bundle extras = (Bundle) args.arg2;
+                final ISaveCallback callback = (ISaveCallback) args.arg3;
+                final SaveCallback saveCallback = new SaveCallback(callback);
+                args.recycle();
+                onSaveRequest(structure, extras, saveCallback);
+                break;
+            } case MSG_DISCONNECT: {
+                onDisconnected();
+                break;
+            } default: {
+                Log.w(TAG, "MyCallbacks received invalid message type: " + msg);
             }
         }
     };
 
     private HandlerCaller mHandlerCaller;
 
-    // User for debugging purposes
-    private final List<CallbackHelper.Dumpable> mPendingCallbacks =
-            DEBUG_PENDING_CALLBACKS ? new ArrayList<>() : null;
-
     /**
      * {@inheritDoc}
      *
@@ -236,7 +152,6 @@
     @Override
     public void onCreate() {
         super.onCreate();
-
         mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
     }
 
@@ -255,7 +170,7 @@
      * <p>You should generally do initialization here rather than in {@link #onCreate}.
      */
     public void onConnected() {
-        if (DEBUG) Log.d(TAG, "onConnected()");
+
     }
 
     /**
@@ -267,14 +182,18 @@
      * to notify the result of the request.
      *
      * @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 on previous calls to fullfill other sections of this activity
-     * (see {@link FillResponse} Javadoc for examples of multiple-sections requests).
-     * @param cancellationSignal signal for observing cancel requests.
+     * @param data bundle containing data passed by the service on previous calls to fill.
+     *     This bundle allows your service to keep state between fill and save requests
+     *     as well as when filling different sections of the UI as the system will try to
+     *     aggressively unbind from the service to conserve resources. See {@link FillResponse}
+     *     Javadoc for examples of multiple-sections requests.
+     * @param cancellationSignal signal for observing cancellation requests. The system will use
+     *     this to notify you that the fill result is no longer needed and you should stop
+     *     handling this fill request in order to save resources.
      * @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(@NonNull AssistStructure structure, @Nullable Bundle data,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
 
     /**
      * Called when user requests service to save the fields of an {@link Activity}.
@@ -284,108 +203,15 @@
      * to notify the result of the request.
      *
      * @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 data bundle containing data passed by the service on previous calls to fill.
+     *     This bundle allows your service to keep state between fill and save requests
+     *     as well as when filling different sections of the UI as the system will try to
+     *     aggressively unbind from the service to conserve resources. See {@link FillResponse}
+     *     Javadoc for examples of multiple-sections requests.
      * @param callback object used to notify the result of the request.
      */
-    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.
-     *
-     * <p>When the {@link FillResponse} required authentication through
-     * {@link android.view.autofill.FillResponse.Builder#requiresFingerprintAuthentication(
-     * android.hardware.fingerprint.FingerprintManager.CryptoObject, Bundle, int)},
-     * {@code flags} this call contains the result of the fingerprint authentication (such as
-     * {@link #FLAG_AUTHENTICATION_SUCCESS}, {@link #FLAG_AUTHENTICATION_ERROR}, and
-     * {@link #FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}) and {@code extras} contains the
-     * {@link Bundle} passed to that method.
-     */
-    public void onFillResponseAuthenticationRequest(@SuppressWarnings("unused") Bundle extras,
-            int flags) {
-        if (DEBUG) Log.d(TAG, "onFillResponseAuthenticationRequest(): flags=" + flags);
-    }
-
-    /**
-     * Called as result of the user action for a {@link Dataset} that required authentication.
-     *
-     * <p>When the {@link Dataset} required authentication through
-     * {@link android.view.autofill.Dataset.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 Dataset} required authentication through
-     * {@link android.view.autofill.Dataset.Builder#requiresFingerprintAuthentication(
-     * android.hardware.fingerprint.FingerprintManager.CryptoObject, Bundle, int)},
-     * {@code flags} this call contains the result of the fingerprint authentication (such as
-     * {@link #FLAG_AUTHENTICATION_SUCCESS}, {@link #FLAG_AUTHENTICATION_ERROR}, and
-     * {@link #FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}) and {@code extras} contains the
-     * {@link Bundle} passed to that method.
-     */
-    public void onDatasetAuthenticationRequest(@SuppressWarnings("unused") Bundle extras,
-            int flags) {
-        if (DEBUG) Log.d(TAG, "onDatasetAuthenticationRequest(): flags=" + flags);
-    }
-
-    @Override
-    @CallSuper
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mPendingCallbacks != null) {
-            pw.print("Number of pending callbacks: "); pw.println(mPendingCallbacks.size());
-            final String prefix = "  ";
-            for (int i = 0; i < mPendingCallbacks.size(); i++) {
-                final CallbackHelper.Dumpable cb = mPendingCallbacks.get(i);
-                pw.print('#'); pw.print(i + 1); pw.println(':');
-                cb.dump(prefix, pw);
-            }
-            pw.println();
-        } else {
-            pw.println("Dumping disabled");
-        }
-    }
-
-    private void handleAutoFill(AssistStructure structure, IAutoFillServerCallback callback) {
-        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;
-    }
-
-    private void handleSave(AssistStructure structure, IAutoFillServerCallback callback,
-            Bundle extras) {
-        final SaveCallback saveCallback = new SaveCallback(callback);
-        if (DEBUG_PENDING_CALLBACKS) {
-            addPendingCallback(saveCallback);
-        }
-        onSaveRequest(structure, extras, saveCallback);
-    }
-
-    private void addPendingCallback(CallbackHelper.Dumpable callback) {
-        if (mPendingCallbacks == null) {
-            // Shouldn't happend since call is controlled by DEBUG_PENDING_CALLBACKS guard.
-            Log.wtf(TAG, "addPendingCallback(): mPendingCallbacks not set");
-            return;
-        }
-
-        if (DEBUG) Log.d(TAG, "Adding pending callback: " + callback);
-
-        callback.setFinalizer(() -> {
-            if (DEBUG) Log.d(TAG, "Removing pending callback: " + callback);
-            mPendingCallbacks.remove(callback);
-        });
-        mPendingCallbacks.add(callback);
-    }
+    public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
+            @NonNull SaveCallback callback);
 
     /**
      * Called when the Android system disconnects from the service.
@@ -393,6 +219,6 @@
      * <p> At this point this service may no longer be an active {@link AutoFillService}.
      */
     public void onDisconnected() {
-        if (DEBUG) Log.d(TAG, "onDisconnected()");
+
     }
 }
diff --git a/core/java/android/service/autofill/AutoFillServiceInfo.java b/core/java/android/service/autofill/AutoFillServiceInfo.java
index fd957f1..985e32f 100644
--- a/core/java/android/service/autofill/AutoFillServiceInfo.java
+++ b/core/java/android/service/autofill/AutoFillServiceInfo.java
@@ -16,6 +16,7 @@
 package android.service.autofill;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.ComponentName;
@@ -25,7 +26,6 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.os.RemoteException;
-import android.util.AndroidException;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
@@ -40,13 +40,10 @@
 /**
  * {@link ServiceInfo} and meta-data about an {@link AutoFillService}.
  *
- * <p>Upon construction, if {@link #getParseError()} is {@code null}, then the service is configured
- * correctly. Otherwise, {@link #getParseError()} indicates the parsing error.
- *
  * @hide
  */
 public final class AutoFillServiceInfo {
-    static final String TAG = "AutoFillServiceInfo";
+    private static final String TAG = "AutoFillServiceInfo";
 
     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
@@ -63,10 +60,9 @@
         throw new PackageManager.NameNotFoundException(comp.toString());
     }
 
-    @Nullable
-    private final String mParseError;
-
+    @NonNull
     private final ServiceInfo mServiceInfo;
+
     @Nullable
     private final String mSettingsActivity;
 
@@ -77,17 +73,7 @@
 
     public AutoFillServiceInfo(PackageManager pm, ServiceInfo si) {
         mServiceInfo = si;
-        TypedArray metaDataArray;
-        try {
-            metaDataArray = getMetaDataArray(pm, si);
-        } catch (AndroidException e) {
-            mParseError = e.getMessage();
-            mSettingsActivity = null;
-            Log.w(TAG, mParseError, e);
-            return;
-        }
-
-        mParseError = null;
+        final TypedArray metaDataArray = getMetaDataArray(pm, si);
         if (metaDataArray != null) {
             mSettingsActivity =
                     metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
@@ -101,12 +87,11 @@
      * Gets the meta-data as a TypedArray, or null if not provided, or throws if invalid.
      */
     @Nullable
-    private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si)
-            throws AndroidException {
+    private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
         // Check for permissions.
         if (!Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) {
-            throw new AndroidException(
-                "Service does not require permission " + Manifest.permission.BIND_AUTO_FILL);
+            Log.e(TAG, "Service does not require permission " + Manifest.permission.BIND_AUTO_FILL);
+            return null;
         }
 
         // Get the AutoFill metadata, if declared.
@@ -125,11 +110,13 @@
                         && type != XmlPullParser.START_TAG) {
                 }
             } catch (XmlPullParserException | IOException e) {
-                throw new AndroidException("Error parsing auto fill service meta-data: " + e, e);
+                Log.e(TAG, "Error parsing auto fill service meta-data", e);
+                return null;
             }
 
             if (!"autofill-service".equals(parser.getName())) {
-                throw new AndroidException("Meta-data does not start with autofill-service tag");
+                Log.e(TAG, "Meta-data does not start with autofill-service tag");
+                return null;
             }
             attrs = Xml.asAttributeSet(parser);
 
@@ -138,7 +125,8 @@
             try {
                 res = pm.getResourcesForApplication(si.applicationInfo);
             } catch (PackageManager.NameNotFoundException e) {
-                throw new AndroidException("Error getting application resources: " + e, e);
+                Log.e(TAG, "Error getting application resources", e);
+                return null;
             }
 
             return res.obtainAttributes(attrs, R.styleable.AutoFillService);
@@ -147,11 +135,6 @@
         }
     }
 
-    @Nullable
-    public String getParseError() {
-        return mParseError;
-    }
-
     public ServiceInfo getServiceInfo() {
         return mServiceInfo;
     }
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 7cab7ae..a306809 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -16,56 +16,32 @@
 
 package android.service.autofill;
 
-import static android.service.autofill.AutoFillService.DEBUG;
-import static android.util.DebugUtils.flagsToString;
-
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
-import android.service.autofill.CallbackHelper.Dumpable;
-import android.service.autofill.CallbackHelper.Finalizer;
-import android.util.Log;
-import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-
 /**
  * Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being
  * auto-filled.
- *
- * <p>This class is thread safe.
  */
-public final class FillCallback implements Dumpable {
-
-    private static final String TAG = "FillCallback";
-
-    // NOTE: constants below are public so they can be used by flagsToString()
-    /** @hide */ public static final int STATE_INITIAL = 1 << 0;
-    /** @hide */ public static final int STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE = 1 << 1;
-    /** @hide */ public static final int STATE_WAITING_DATASET_AUTH_RESPONSE = 1 << 2;
-    /** @hide */ public static final int STATE_FINISHED_OK = 1 << 3;
-    /** @hide */ public static final int STATE_FINISHED_FAILURE = 1 << 4;
-    /** @hide */ public static final int STATE_FINISHED_ERROR = 1 << 5;
-    /** @hide */ public static final int STATE_FINISHED_AUTHENTICATED = 1 << 6;
-
-    private final IAutoFillServerCallback mCallback;
-
-    @GuardedBy("mCallback")
-    private int mState = STATE_INITIAL;
-
-    @GuardedBy("mCallback")
-    private Finalizer mFinalizer;
+public final class FillCallback implements Parcelable {
+    private final IFillCallback mCallback;
+    private boolean mCalled;
 
     /** @hide */
-    FillCallback(IAutoFillServerCallback callback) {
+    public FillCallback(IFillCallback callback) {
         mCallback = callback;
     }
 
+    /** @hide */
+    private FillCallback(Parcel parcel) {
+        mCallback = IFillCallback.Stub.asInterface(parcel.readStrongBinder());
+    }
+
     /**
      * Notifies the Android System that an
      * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
@@ -76,43 +52,12 @@
      * {@link FillResponse} for examples.
      */
     public void onSuccess(@Nullable FillResponse response) {
-        final boolean authRequired = response != null && response.isAuthRequired();
-
-        if (DEBUG) Log.d(TAG, "onSuccess(): authReq= " + authRequired + ", resp=" + response);
-
-        synchronized (mCallback) {
-            if (authRequired) {
-                assertOnStateLocked(STATE_INITIAL);
-            } else {
-                assertOnStateLocked(STATE_INITIAL | STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE
-                        | STATE_WAITING_DATASET_AUTH_RESPONSE);
-            }
-
-            try {
-                mCallback.showResponse(response);
-                if (authRequired) {
-                    mState = STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE;
-                } else {
-                    // Check if at least one dataset requires authentication.
-                    boolean waitingAuth = false;
-                    if (response != null) {
-                        for (Dataset dataset : response.getDatasets()) {
-                            if (dataset.isAuthRequired()) {
-                                waitingAuth = true;
-                                break;
-                            }
-                        }
-                    }
-                    if (waitingAuth) {
-                        mState = STATE_WAITING_DATASET_AUTH_RESPONSE;
-                    } else {
-                        setFinalStateLocked(STATE_FINISHED_OK);
-                    }
-                }
-            } catch (RemoteException e) {
-                setFinalStateLocked(STATE_FINISHED_ERROR);
-                e.rethrowAsRuntimeException();
-            }
+        assertNotCalled();
+        mCalled = true;
+        try {
+            mCallback.onSuccess(response);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -124,118 +69,43 @@
      *
      * @param message error message to be displayed to the user.
      */
-    public void onFailure(CharSequence message) {
-        if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
-
-        Preconditions.checkArgument(message != null, "message cannot be null");
-
-        synchronized (mCallback) {
-            assertOnStateLocked(STATE_INITIAL | STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE
-                    | STATE_WAITING_DATASET_AUTH_RESPONSE);
-
-            try {
-                mCallback.showError(message);
-                setFinalStateLocked(STATE_FINISHED_FAILURE);
-            } catch (RemoteException e) {
-                setFinalStateLocked(STATE_FINISHED_ERROR);
-                e.rethrowAsRuntimeException();
-            }
+    public void onFailure(@Nullable CharSequence message) {
+        assertNotCalled();
+        mCalled = true;
+        try {
+            mCallback.onFailure(message);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
         }
     }
 
-    /**
-     * Notifies the Android System when the user authenticated a {@link FillResponse} previously
-     * passed to {@link #onSuccess(FillResponse)}.
-     *
-     * @param flags must contain either
-     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} or
-     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}.
-     */
-    public void onFillResponseAuthentication(int flags) {
-        if (DEBUG) Log.d(TAG, "onFillResponseAuthentication(): flags=" + flags);
-
-        synchronized (mCallback) {
-            assertOnStateLocked(STATE_WAITING_FILL_RESPONSE_AUTH_RESPONSE);
-
-            try {
-                mCallback.unlockFillResponse(flags);
-                setFinalStateLocked(STATE_FINISHED_AUTHENTICATED);
-            } catch (RemoteException e) {
-                setFinalStateLocked(STATE_FINISHED_ERROR);
-                e.rethrowAsRuntimeException();
-            }
-        }
-    }
-
-    /**
-     * Notifies the Android System when the user authenticated a {@link Dataset} previously passed
-     * to {@link #onSuccess(FillResponse)}.
-     *
-     * @param dataset values to fill the activity with in case of successful authentication of a
-     * previously locked (and empty) dataset).
-     * @param flags must contain either
-     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} or
-     * {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}.
-     */
-    public void onDatasetAuthentication(@Nullable Dataset dataset, int flags) {
-        if (DEBUG) Log.d(TAG, "onDatasetAuthentication(): dataset=" + dataset + ", flags=" + flags);
-
-        synchronized (mCallback) {
-            assertOnStateLocked(STATE_WAITING_DATASET_AUTH_RESPONSE);
-
-            try {
-                mCallback.unlockDataset(dataset, flags);
-                setFinalStateLocked(STATE_FINISHED_AUTHENTICATED);
-            } catch (RemoteException e) {
-                setFinalStateLocked(STATE_FINISHED_ERROR);
-                e.rethrowAsRuntimeException();
-            }
-        }
-    }
-
-    @Override
-    public String toString() {
-        if (!DEBUG) return super.toString();
-
-        return "FillCallback: [mState = " + mState + "]";
-    }
-
     /** @hide */
     @Override
-    public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("FillCallback: mState="); pw.println(mState);
+    public int describeContents() {
+        return 0;
     }
 
     /** @hide */
     @Override
-    public void setFinalizer(Finalizer f) {
-        synchronized (mCallback) {
-            mFinalizer = f;
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeStrongBinder(mCallback.asBinder());
+    }
+
+    private void assertNotCalled() {
+        if (mCalled) {
+            throw new IllegalStateException("Already called");
         }
     }
 
-    /**
-     * Sets a final state (where the callback cannot be used anymore) and notifies the
-     * {@link Finalizer} (if any).
-     */
-    private void setFinalStateLocked(int state) {
-        if (DEBUG) Log.d(TAG, "setFinalState(): " + state);
-        mState = state;
-
-        if (mFinalizer != null) {
-            mFinalizer.gone();
+    public static final Creator<FillCallback> CREATOR = new Creator<FillCallback>() {
+        @Override
+        public FillCallback createFromParcel(Parcel parcel) {
+            return new FillCallback(parcel);
         }
-    }
 
-    // TODO(b/33197203): move and/or re-add state check logic on server side to avoid malicious app
-    // calling the callback on wrong state.
-
-    // Make sure callback method is called during the proper lifecycle state.
-    private void assertOnStateLocked(int flags) {
-        if (DEBUG) Log.d(TAG, "assertOnState(): current=" + mState + ", required=" + flags);
-
-        Preconditions.checkState((flags & mState) != 0,
-                "invalid state: required " + flagsToString(FillCallback.class, "STATE_", flags)
-                + ", current is " + flagsToString(FillCallback.class, "STATE_", mState));
-    }
+        @Override
+        public FillCallback[] newArray(int size) {
+            return new FillCallback[size];
+        }
+    };
 }
diff --git a/core/java/android/service/autofill/IAutoFillAppCallback.aidl b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
index 8c3898a..d9c161c 100644
--- a/core/java/android/service/autofill/IAutoFillAppCallback.aidl
+++ b/core/java/android/service/autofill/IAutoFillAppCallback.aidl
@@ -18,6 +18,8 @@
 
 import java.util.List;
 
+import android.content.Intent;
+import android.content.IntentSender;
 import android.view.autofill.Dataset;
 
 /**
@@ -31,4 +33,9 @@
       * Auto-fills the activity with the contents of a dataset.
       */
     void autoFill(in Dataset dataset);
+
+    /**
+      * Start an intent sender from the context of the filled app
+      */
+    void startIntentSender(in IntentSender intent, in Intent fillInIntent);
 }
diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl
deleted file mode 100644
index 480438a..0000000
--- a/core/java/android/service/autofill/IAutoFillServerCallback.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 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.service.autofill;
-
-import java.util.List;
-
-import android.os.Bundle;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.Dataset;
-import android.view.autofill.FillResponse;
-
-/**
- * Object running in the AutoFillService process and used to communicate back with system_server.
- *
- * @hide
- */
-// TODO(b/33197203): rename to IAutoFillServerSession
-oneway interface IAutoFillServerCallback {
-    // TODO(b/33197203): document methods
-    void showResponse(in FillResponse response);
-    void showError(CharSequence message);
-    void onSaved();
-    void unlockFillResponse(int flags);
-    void unlockDataset(in Dataset dataset, int flags);
-}
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index a4e6ebc..fa1ea65 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -18,19 +18,20 @@
 
 import android.app.assist.AssistStructure;
 import android.os.Bundle;
-import android.service.autofill.IAutoFillServerCallback;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.ISaveCallback;
 import com.android.internal.os.IResultReceiver;
 
 /**
+ * Interface from the system to an auto fill service.
+ *
  * @hide
  */
-// 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);
-    void save(in AssistStructure structure, in IAutoFillServerCallback callback, in Bundle extras);
-    void authenticateFillResponse(in Bundle extras, int flags);
-    void authenticateDataset(in Bundle extras, int flags);
+    void onFillRequest(in AssistStructure structure, in Bundle extras,
+            in IFillCallback callback);
+    void onSaveRequest(in AssistStructure structure, in Bundle extras,
+            in ISaveCallback callback);
     void onConnected();
     void onDisconnected();
 }
diff --git a/core/java/android/service/autofill/CallbackHelper.java b/core/java/android/service/autofill/IFillCallback.aidl
similarity index 66%
copy from core/java/android/service/autofill/CallbackHelper.java
copy to core/java/android/service/autofill/IFillCallback.aidl
index ded8f97..537403e 100644
--- a/core/java/android/service/autofill/CallbackHelper.java
+++ b/core/java/android/service/autofill/IFillCallback.aidl
@@ -13,18 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.service.autofill;
 
-import java.io.PrintWriter;
+import android.os.ICancellationSignal;
 
-final class CallbackHelper {
+import android.view.autofill.FillResponse;
 
-    static interface Dumpable {
-        void dump(String prefix, PrintWriter pw);
-        void setFinalizer(Finalizer f);
-    }
-
-    static interface Finalizer {
-        void gone();
-    }
+/**
+ * Interface to receive the result of a save request.
+ *
+ * @hide
+ */
+interface IFillCallback {
+    void onCancellable(in ICancellationSignal cancellation);
+    void onSuccess(in FillResponse response);
+    void onFailure(CharSequence message);
 }
diff --git a/core/java/android/service/autofill/CallbackHelper.java b/core/java/android/service/autofill/ISaveCallback.aidl
similarity index 72%
rename from core/java/android/service/autofill/CallbackHelper.java
rename to core/java/android/service/autofill/ISaveCallback.aidl
index ded8f97..e260c73 100644
--- a/core/java/android/service/autofill/CallbackHelper.java
+++ b/core/java/android/service/autofill/ISaveCallback.aidl
@@ -13,18 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.service.autofill;
 
-import java.io.PrintWriter;
-
-final class CallbackHelper {
-
-    static interface Dumpable {
-        void dump(String prefix, PrintWriter pw);
-        void setFinalizer(Finalizer f);
-    }
-
-    static interface Finalizer {
-        void gone();
-    }
+/**
+ * Interface to receive the result of a save request.
+ *
+ * @hide
+ */
+interface ISaveCallback {
+    void onSuccess();
+    void onFailure(CharSequence message);
 }
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 9dd9795..46b3072 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -16,21 +16,9 @@
 
 package android.service.autofill;
 
-import static android.service.autofill.AutoFillService.DEBUG;
-
 import android.app.Activity;
-import android.app.assist.AssistStructure.ViewNode;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.service.autofill.CallbackHelper.Dumpable;
-import android.service.autofill.CallbackHelper.Finalizer;
-import android.util.Log;
-import android.view.autofill.AutoFillId;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
 
 /**
  * Handles save requests from the {@link AutoFillService} into the {@link Activity} being
@@ -38,20 +26,12 @@
  *
  * <p>This class is thread safe.
  */
-public final class SaveCallback implements Dumpable {
-
-    private static final String TAG = "SaveCallback";
-
-    private final IAutoFillServerCallback mCallback;
-
-    @GuardedBy("mCallback")
-    private boolean mReplied = false;
-
-    @GuardedBy("mCallback")
-    private Finalizer mFinalizer;
+public final class SaveCallback {
+    private final ISaveCallback mCallback;
+    private boolean mCalled;
 
     /** @hide */
-    SaveCallback(IAutoFillServerCallback callback) {
+    SaveCallback(ISaveCallback callback) {
         mCallback = callback;
     }
 
@@ -63,17 +43,12 @@
      * @throws RuntimeException if an error occurred while calling the Android System.
      */
     public void onSuccess() {
-        if (DEBUG) Log.d(TAG, "onSuccess()");
-
-        synchronized (mCallback) {
-            checkNotRepliedYetLocked();
-            try {
-                mCallback.onSaved();
-            } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
-            } finally {
-                setRepliedLocked();
-            }
+        assertNotCalled();
+        mCalled = true;
+        try {
+            mCallback.onSuccess();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
         }
     }
 
@@ -87,54 +62,18 @@
      * @throws RuntimeException if an error occurred while calling the Android System.
      */
     public void onFailure(CharSequence message) {
-        if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
-
-        synchronized (mCallback) {
-            checkNotRepliedYetLocked();
-
-            try {
-                mCallback.showError(message);
-            } catch (RemoteException e) {
-                e.rethrowAsRuntimeException();
-            } finally {
-                setRepliedLocked();
-            }
+        assertNotCalled();
+        mCalled = true;
+        try {
+            mCallback.onFailure(message);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
         }
     }
 
-    /** @hide */
-    @Override
-    public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("SaveCallback: mReplied="); pw.println(mReplied);
-    }
-
-    /** @hide */
-    @Override
-    public void setFinalizer(Finalizer f) {
-        synchronized (mCallback) {
-            mFinalizer = f;
-        }
-    }
-
-    @Override
-    public String toString() {
-        if (!DEBUG) return super.toString();
-
-        return "SaveCallback: [mReplied= " + mReplied + "]";
-    }
-
-    // There can be only one!!
-    private void checkNotRepliedYetLocked() {
-        Preconditions.checkState(!mReplied, "already replied");
-    }
-
-    private void setRepliedLocked() {
-        if (DEBUG) Log.d(TAG, "setReplied()");
-
-        mReplied = true;
-
-        if (mFinalizer != null) {
-            mFinalizer.gone();
+    private void assertNotCalled() {
+        if (mCalled) {
+            throw new IllegalStateException("Already called");
         }
     }
 }
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index f2f522d..147d72a 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -16,6 +16,8 @@
 
 package android.view.autofill;
 
+import static android.view.autofill.Helper.DEBUG;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
@@ -31,7 +33,6 @@
 public final class AutoFillManager {
 
     private static final String TAG = "AutoFillManager";
-    private static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
 
     /**
      * Flag used to show the auto-fill UI affordance for a view.
diff --git a/core/java/android/view/autofill/AutoFillSession.java b/core/java/android/view/autofill/AutoFillSession.java
index eec7a82..efc1df6 100644
--- a/core/java/android/view/autofill/AutoFillSession.java
+++ b/core/java/android/view/autofill/AutoFillSession.java
@@ -19,7 +19,8 @@
 import static android.view.autofill.Helper.DEBUG;
 
 import android.app.Activity;
-import android.os.RemoteException;
+import android.content.Intent;
+import android.content.IntentSender;
 import android.service.autofill.IAutoFillAppCallback;
 import android.util.Log;
 import android.view.View;
@@ -39,7 +40,7 @@
 
     private final IAutoFillAppCallback mCallback = new IAutoFillAppCallback.Stub() {
         @Override
-        public void autoFill(Dataset dataset) throws RemoteException {
+        public void autoFill(Dataset dataset) {
             final Activity activity = mActivity.get();
             if (activity == null) {
                 if (DEBUG) Log.d(TAG, "autoFill(): activity already GCed");
@@ -49,12 +50,10 @@
             // 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 itemCount = dataset.getFieldIds().size();
+                for (int i = 0; i < itemCount; i++) {
+                    final AutoFillId id = dataset.getFieldIds().get(i);
+                    final AutoFillValue value = dataset.getFieldValues().get(i);
                     final int viewId = id.getViewId();
                     final View view = root.findViewByAccessibilityIdTraversal(viewId);
                     if (view == null) {
@@ -79,14 +78,28 @@
                             Log.d(TAG, "autoFill(): delegating " + id
                                     + " to VirtualViewDelegate  " + delegate);
                         }
-                        delegate.autoFill(id.getVirtualChildId(), field.getValue());
+                        delegate.autoFill(id.getVirtualChildId(), value);
                     } else {
                         // Handle non-virtual fields itself.
-                        view.autoFill(field.getValue());
+                        view.autoFill(value);
                     }
                 }
             });
         }
+
+        @Override
+        public void startIntentSender(IntentSender intent, Intent fillInIntent) {
+            final Activity activity = mActivity.get();
+            if (activity != null) {
+                activity.runOnUiThread(() -> {
+                    try {
+                        activity.startIntentSender(intent, fillInIntent, 0, 0, 0);
+                    } catch (IntentSender.SendIntentException e) {
+                        Log.e(TAG, "startIntentSender() failed for intent:" + intent, e);
+                    }
+                });
+            }
+        }
     };
 
     private final WeakReference<Activity> mActivity;
@@ -114,5 +127,4 @@
             }
         }
     }
-
 }
diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/view/autofill/Dataset.java
index 18a08f9..2708358 100644
--- a/core/java/android/view/autofill/Dataset.java
+++ b/core/java/android/view/autofill/Dataset.java
@@ -17,12 +17,12 @@
 package android.view.autofill;
 
 import static android.view.autofill.Helper.DEBUG;
-import static android.view.autofill.Helper.append;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.assist.AssistStructure.ViewNode;
-import android.hardware.fingerprint.FingerprintManager.CryptoObject;
+import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -30,8 +30,6 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
 
 /**
  * A set of data that can be used to auto-fill an {@link Activity}.
@@ -44,221 +42,184 @@
  *   <li>An optional {@link Bundle} with extras (used only by the service creating it).
  * </ol>
  *
- * See {@link FillResponse} for examples.
+ * @see FillResponse for examples.
  */
 public final class Dataset implements Parcelable {
-
+    private final String mId;
     private final CharSequence mName;
-    private final ArrayList<DatasetField> mFields;
+    private final ArrayList<AutoFillId> mFieldIds;
+    private final ArrayList<AutoFillValue> mFieldValues;
     private final Bundle mExtras;
-    private final int mFlags;
-    private final boolean mRequiresAuth;
-    private final boolean mHasCryptoObject;
-    private final long mCryptoOpId;
+    private final IntentSender mAuthentication;
 
-    private Dataset(Dataset.Builder builder) {
+    private Dataset(Builder builder) {
+        mId = builder.mId;
         mName = builder.mName;
-        // TODO(b/33197203): make an immutable copy of mFields?
-        mFields = builder.mFields;
+        mFieldIds = builder.mFieldIds;
+        mFieldValues = builder.mFieldValues;
         mExtras = builder.mExtras;
-        mFlags = builder.mFlags;
-        mRequiresAuth = builder.mRequiresAuth;
-        mHasCryptoObject = builder.mHasCryptoObject;
-        mCryptoOpId = builder.mCryptoOpId;
+        mAuthentication = builder.mAuthentication;
     }
 
     /** @hide */
-    public CharSequence getName() {
+    public @NonNull String getId() {
+        return mId;
+    }
+
+    /** @hide */
+    public @NonNull CharSequence getName() {
         return mName;
     }
 
     /** @hide */
-    public List<DatasetField> getFields() {
-        return mFields;
+    public @Nullable ArrayList<AutoFillId> getFieldIds() {
+        return mFieldIds;
     }
 
     /** @hide */
-    public Bundle getExtras() {
+    public @Nullable ArrayList<AutoFillValue> getFieldValues() {
+        return mFieldValues;
+    }
+
+    /** @hide */
+    public @Nullable Bundle getExtras() {
         return mExtras;
     }
 
     /** @hide */
-    public int getFlags() {
-        return mFlags;
-    }
-
-    /** @hide */
-    public boolean isAuthRequired() {
-        return mRequiresAuth;
+    public @Nullable IntentSender getAuthentication() {
+        return mAuthentication;
     }
 
     /** @hide */
     public boolean isEmpty() {
-        return mFields.isEmpty();
-    }
-
-    /** @hide */
-    public boolean hasCryptoObject() {
-        return mHasCryptoObject;
-    }
-
-    /** @hide */
-    public long getCryptoObjectOpId() {
-        return mCryptoOpId;
+        return mFieldIds == null || mFieldIds.isEmpty();
     }
 
     @Override
     public String toString() {
         if (!DEBUG) return super.toString();
 
-        final StringBuilder builder = new StringBuilder("Dataset [name=").append(mName)
-                .append(", fields=").append(mFields).append(", extras=");
-        append(builder, mExtras)
-            .append(", flags=").append(mFlags)
-            .append(", requiresAuth: ").append(mRequiresAuth)
-            .append(", hasCrypto: ").append(mHasCryptoObject);
+        final StringBuilder builder = new StringBuilder("Dataset [id=").append(mId)
+                .append(", name=").append(mName)
+                .append(", fieldIds=").append(mFieldIds)
+                .append(", fieldValues=").append(mFieldValues)
+                .append(", hasAuthentication=").append(mAuthentication != null)
+                .append(", hasExtras=").append(mExtras != null);
         return builder.append(']').toString();
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final Dataset other = (Dataset) obj;
+        if (mId == null) {
+            if (other.mId != null) {
+                return false;
+            }
+        } else if (!mId.equals(other.mId)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return mId != null ? mId.hashCode() : 0;
+    }
+
     /**
-     * A builder for {@link Dataset} objects.
+     * A builder for {@link Dataset} objects. You must to provide at least
+     * one value for a field or set an authentication intent.
      */
     public static final class Builder {
+        private String mId;
         private CharSequence mName;
-        private final ArrayList<DatasetField> mFields = new ArrayList<>();
+        private ArrayList<AutoFillId> mFieldIds;
+        private ArrayList<AutoFillValue> mFieldValues;
         private Bundle mExtras;
-        private int mFlags;
-        private boolean mRequiresAuth;
-        private boolean mHasCryptoObject;
-        private long mCryptoOpId;
+        private IntentSender mAuthentication;
+        private boolean mDestroyed;
+
+        /** @hide */
+        // TODO(b/33197203): Remove once GCore migrates
+        public Builder(@NonNull CharSequence name) {
+            this(String.valueOf(System.currentTimeMillis()), name);
+        }
 
         /**
          * Creates a new builder.
          *
+         * @param id A required id to identify this dataset for future interactions related to it.
          * @param name Name used to identify the dataset in the UI. Typically it's the same value as
-         * the first field in the dataset (like username or email address) or an user-provided name
+         * the first field in the dataset (like username or email address) or a user-provided name
          * (like "My Work Address").
          */
-        public Builder(CharSequence name) {
+        public Builder(@NonNull String id, @NonNull CharSequence name) {
+            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null");
             mName = Preconditions.checkStringNotEmpty(name, "name cannot be empty or null");
         }
 
         /**
-         * Requires dataset authentication through the {@link
-         * android.service.autofill.AutoFillService} before auto-filling the activity with this
-         * dataset.
+         * Requires a dataset authentication before auto-filling the activity with this dataset.
          *
-         * <p>This method is typically called when the device (or the service) does not support
-         * fingerprint authentication (and hence it cannot use {@link
-         * #requiresFingerprintAuthentication(CryptoObject, Bundle, int)}) or when the service needs
-         * to use a custom authentication UI for the dataset. For example, when a dataset contains
-         * credit card information (such as number, expiration date, and verification code), the
-         * service displays an authentication dialog asking for the verification code to unlock the
-         * rest of the data).
+         * <p>This method is called when you need to provide an authentication
+         * UI for the dataset. For example, when a dataset contains credit card information
+         * (such as number, expiration date, and verification code), you can display UI
+         * asking for the verification code to before filing in the data). Even if the
+         * dataset is completely populated the system will launch the specified authentication
+         * intent and will need your approval to fill it in. Since the dataset is "locked"
+         * until the user authenticates it, typically this dataset name is masked
+         * (for example, "VISA....1234"). Typically you would want to store the dataset
+         * labels non-encypted and the actual sensitive data encrypted and not in memory.
+         * This allows showing the labels in the UI while involving the user if one of
+         * the items with these labels is chosen. Note that if you use sensitive data as
+         * a label, for example an email address, then it should also be encrypted.
+         *</p>
          *
-         * <p>Since the dataset is "locked" until the user authenticates it, typically this dataset
-         * name is masked (for example, "VISA....1234").
+         * <p>When a user selects this dataset, the system triggers the provided intent
+         * whose extras will have the {@link android.content.Intent#EXTRA_AUTO_FILL_ITEM_ID id}
+         * of the {@link android.view.autofill.Dataset dataset} to authenticate, the {@link
+         * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} associated with this
+         * dataset, and a {@link android.content.Intent#EXTRA_AUTO_FILL_CALLBACK callback}
+         * to dispatch the authentication result.</p>
          *
-         * <p>When the user selects this dataset, the Android System calls {@link
-         * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
-         * passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_REQUESTED} in
-         * the flags and the same {@code extras} passed to this method. The service can then
-         * displays its custom authentication UI, and then call the proper method on {@link
-         * android.service.autofill.FillCallback} depending on the authentication result and whether
-         * this dataset already contains the fields needed to auto-fill the activity:
+         * <p>Once you complete your authentication flow you should use the provided callback
+         * to notify for a failure or a success. In case of a success you need to provide
+         * only the fully populated dataset that is being authenticated. For example, if you
+         * provided a {@link FillResponse} with two {@link Dataset}s and marked that
+         * only the first dataset needs an authentication then in the provided response
+         * you need to provide only the fully populated dataset being authenticated instead
+         * of both of them.
+         * </p>
          *
-         * <ul>
-         *   <li>If authentication failed, call
-         *   {@link android.service.autofill.FillCallback#onDatasetAuthentication(Dataset,
-         *       int)} passing {@link
-         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the flags.
-         *   <li>If authentication succeeded and this datast is empty (no fields), call {@link
-         *       android.service.autofill.FillCallback#onSuccess(FillResponse)} with a new dataset
-         *       (with the proper fields).
-         *   <li>If authentication succeeded and this response is not empty, call {@link
-         *       android.service.autofill.FillCallback#onDatasetAuthentication(Dataset, int)}
-         *       passing
-         *       {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS} in the
-         *       {@code flags} and {@code null} as the {@code dataset}.
-         * </ul>
+         * <p>The indent sender mechanism allows you to have your authentication UI
+         * implemented as an activity or a service or a receiver. However, the recommended
+         * way is to do this is with an activity which the system will start in the
+         * filled activity's task meaning it will properly work with back, recent apps, and
+         * free-form multi-window, while avoiding the need for the "draw on top of other"
+         * apps special permission. You can still theme your authentication activity's
+         * UI to look like a dialog if desired.</p>
          *
-         * @param extras when set, will be passed back in the {@link
-         *     android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle,
-         *     int)}, call so it could be used by the service to handle state.
-         * @param flags optional parameters, currently ignored.
+         * <p></><strong>Note:</strong> Do not make the provided intent sender
+         * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
+         * platform needs to fill in the authentication arguments.</p>
+         *
+         * @param authentication Intent to trigger your authentication flow.
+         *
+         * @see android.app.PendingIntent#getIntentSender()
          */
-        public Builder requiresCustomAuthentication(@Nullable Bundle extras, int flags) {
-            return requiresAuthentication(null, extras, flags);
-        }
-
-        /**
-         * Requires dataset authentication through the Fingerprint sensor before auto-filling the
-         * activity with this dataset.
-         *
-         * <p>This method is typically called when the dataset contains sensitive information (for
-         * example, credit card information) and the provider requires the user to re-authenticate
-         * before using it.
-         *
-         * <p>Since the dataset is "locked" until the user authenticates it, typically this dataset
-         * name is masked (for example, "VISA....1234").
-         *
-         * <p>When the user selects this dataset, the Android System displays an UI affordance
-         * asking the user to use the fingerprint sensor unlock the dataset, and what happens after
-         * a successful fingerprint authentication depends on whether the dataset is empty (no
-         * fields, only the masked name) or not:
-         *
-         * <ul>
-         *   <li>If it's empty, the Android System will call {@link
-         *       android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle,
-         *       int)} passing {@link
-         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}} in the
-         *       flags.
-         *   <li>If it's not empty, the activity will be auto-filled with its data.
-         * </ul>
-         *
-         * <p>If the fingerprint authentication fails, the Android System will call {@link
-         * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
-         * passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the
-         * flags.
-         *
-         * <p><strong>NOTE: </note> the {@link android.service.autofill.AutoFillService} should use
-         * the {@link android.hardware.fingerprint.FingerprintManager} to check if fingerpint
-         * authentication is available before using this method, and use other alternatives (such as
-         * {@link #requiresCustomAuthentication(Bundle, int)}) if it is not: if this method is
-         * called when fingerprint is not available, Android System will call {@link
-         * android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
-         * passing {@link
-         * android.service.autofill.AutoFillService#FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}
-         * in the flags, but it would be wasting system resources (and worsening the user
-         * experience) in the process.
-         *
-         * @param crypto object that will be authenticated.
-         * @param extras when set, will be passed back in the {@link
-         *     android.service.autofill.AutoFillService#onDatasetAuthenticationRequest(Bundle, int)}
-         *     call so it could be used by the service to handle state.
-         * @param flags optional parameters, currently ignored.
-         */
-        public Builder requiresFingerprintAuthentication(CryptoObject crypto,
-                @Nullable Bundle extras, int flags) {
-            // TODO(b/33197203): should we allow crypto to be null?
-            Preconditions.checkArgument(crypto != null, "must pass a CryptoObject");
-            return requiresAuthentication(crypto, extras, flags);
-        }
-
-        private Builder requiresAuthentication(CryptoObject cryptoObject, Bundle extras,
-                int flags) {
-            // There can be only one!
-            Preconditions.checkState(!mRequiresAuth,
-                    "requires-authentication methods already called");
-            // TODO(b/33197203): make sure that either this method or setExtras() is called, but
-            // not both
-            mExtras = extras;
-            mFlags = flags;
-            mRequiresAuth = true;
-            if (cryptoObject != null) {
-                mHasCryptoObject = true;
-                mCryptoOpId = cryptoObject.getOpId();
-            }
+        public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) {
+            throwIfDestroyed();
+            mAuthentication = authentication;
             return this;
         }
 
@@ -268,41 +229,55 @@
          * @param id id returned by {@link ViewNode#getAutoFillId()}.
          * @param value value to be auto filled.
          */
-        public Dataset.Builder setValue(AutoFillId id, AutoFillValue value) {
-            putField(new DatasetField(id, value));
+        public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(id, "id cannot be null");
+            Preconditions.checkNotNull(value, "value cannot be null");
+            if (mFieldIds != null) {
+                final int existingIdx = mFieldIds.indexOf(id);
+                if (existingIdx >= 0) {
+                    mFieldValues.set(existingIdx, value);
+                    return this;
+                }
+            } else {
+                mFieldIds = new ArrayList<>();
+                mFieldValues = new ArrayList<>();
+            }
+            mFieldIds.add(id);
+            mFieldValues.add(value);
             return this;
         }
 
         /**
-         * Creates a new {@link Dataset} instance.
+         * Sets a {@link Bundle} that will be passed to subsequent APIs that
+         * manipulate this dataset. For example, they are passed in as {@link
+         * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your
+         * authentication flow.
          */
-        public Dataset build() {
+        public @NonNull Builder setExtras(@Nullable Bundle extras) {
+            throwIfDestroyed();
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Creates a new {@link Dataset} instance. You should not interact
+         * with this builder once this method is called.
+         */
+        public @NonNull Dataset build() {
+            throwIfDestroyed();
+            mDestroyed = true;
+            if (mFieldIds == null && mAuthentication == null) {
+                throw new IllegalArgumentException(
+                        "at least one value or an authentication must be set");
+            }
             return new Dataset(this);
         }
 
-        /**
-         * 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.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.
-         */
-        public Builder setExtras(Bundle extras) {
-            // TODO(b/33197203): make sure that either this method or the requires-Authentication
-            // ones are called, but not both
-            mExtras = Objects.requireNonNull(extras, "extras cannot be null");
-            return this;
-        }
-
-        /**
-         * Emulates {@code Map.put()} by adding a new field to the list if its id is not the yet,
-         * or replacing the existing one.
-         */
-        private void putField(DatasetField field) {
-            // TODO(b/33197203): check if already exists and replaces it if so
-            mFields.add(field);
+        private void throwIfDestroyed() {
+            if (mDestroyed) {
+                throw new IllegalStateException("Already called #build()");
+            }
         }
     }
 
@@ -317,32 +292,33 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mId);
         parcel.writeCharSequence(mName);
-        parcel.writeList(mFields);
+        parcel.writeTypedArrayList(mFieldIds, 0);
+        parcel.writeTypedArrayList(mFieldValues, 0);
         parcel.writeBundle(mExtras);
-        parcel.writeInt(mFlags);
-        parcel.writeInt(mRequiresAuth ? 1 : 0);
-        parcel.writeInt(mHasCryptoObject ? 1 : 0);
-        if (mHasCryptoObject) {
-            parcel.writeLong(mCryptoOpId);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private Dataset(Parcel parcel) {
-        mName = parcel.readCharSequence();
-        mFields = parcel.readArrayList(null);
-        mExtras = parcel.readBundle();
-        mFlags = parcel.readInt();
-        mRequiresAuth = parcel.readInt() == 1;
-        mHasCryptoObject = parcel.readInt() == 1;
-        mCryptoOpId = mHasCryptoObject ? parcel.readLong() : 0;
+        parcel.writeParcelable(mAuthentication, flags);
     }
 
     public static final Parcelable.Creator<Dataset> CREATOR = new Parcelable.Creator<Dataset>() {
         @Override
-        public Dataset createFromParcel(Parcel source) {
-            return new Dataset(source);
+        public Dataset createFromParcel(Parcel parcel) {
+            // Always go through the builder to ensure the data ingested by
+            // the system obeys the contract of the builder to avoid attacks
+            // using specially crafted parcels.
+            final Builder builder = new Builder(parcel.readString(), parcel.readCharSequence());
+            final ArrayList<AutoFillId> ids = parcel.readTypedArrayList(null);
+            final ArrayList<AutoFillValue> values = parcel.readTypedArrayList(null);
+            final int idCount = (ids != null) ? ids.size() : 0;
+            final int valueCount = (values != null) ? values.size() : 0;
+            for (int i = 0; i < idCount; i++) {
+                AutoFillId id = ids.get(i);
+                AutoFillValue value = (valueCount > i) ? values.get(i) : null;
+                builder.setValue(id, value);
+            }
+            builder.setExtras(parcel.readBundle());
+            builder.setAuthentication(parcel.readParcelable(null));
+            return builder.build();
         }
 
         @Override
diff --git a/core/java/android/view/autofill/DatasetField.java b/core/java/android/view/autofill/DatasetField.java
deleted file mode 100644
index c6b92ac..0000000
--- a/core/java/android/view/autofill/DatasetField.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 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.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public final class DatasetField implements Parcelable {
-
-    private final AutoFillId mId;
-    private final AutoFillValue mValue;
-
-    DatasetField(AutoFillId id, AutoFillValue value) {
-        mId = id;
-        mValue = value;
-    }
-
-    public AutoFillId getId() {
-        return mId;
-    }
-
-    public AutoFillValue getValue() {
-        return mValue;
-    }
-
-    /////////////////////////////////
-    //  Object "contract" methods. //
-    /////////////////////////////////
-
-    @Override
-    public String toString() {
-        if (!DEBUG) return super.toString();
-
-        return "DatasetField [id=" + mId + ", value=" + mValue + "]";
-    }
-
-    /////////////////////////////////////
-    //  Parcelable "contract" methods. //
-    /////////////////////////////////////
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mId, 0);
-        parcel.writeParcelable(mValue, 0);
-    }
-
-    private DatasetField(Parcel parcel) {
-        mId = parcel.readParcelable(null);
-        mValue = parcel.readParcelable(null);
-    }
-
-    public static final Parcelable.Creator<DatasetField> CREATOR =
-            new Parcelable.Creator<DatasetField>() {
-        @Override
-        public DatasetField createFromParcel(Parcel source) {
-            return new DatasetField(source);
-        }
-
-        @Override
-        public DatasetField[] newArray(int size) {
-            return new DatasetField[size];
-        }
-    };
-}
diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/view/autofill/FillResponse.java
index 48dbb84..596a06c 100644
--- a/core/java/android/view/autofill/FillResponse.java
+++ b/core/java/android/view/autofill/FillResponse.java
@@ -16,36 +16,30 @@
 package android.view.autofill;
 
 import static android.view.autofill.Helper.DEBUG;
-import static android.view.autofill.Helper.append;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
-import android.hardware.fingerprint.FingerprintManager.CryptoObject;
+import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.service.autofill.FillCallback;
+import android.util.ArraySet;
 
 import com.android.internal.util.Preconditions;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
 /**
  * Response for a {@link
  * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} request.
+ * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} and
+ * authentication requests.
  *
  * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
- * fields that can be auto-filled together, and the Android System displays a dataset picker UI
+ * fields that can be auto-filled together, and the Android system displays a dataset picker UI
  * affordance that the user must use before the {@link Activity} is filled with the dataset.
  *
  * <p>For example, for a login page with username/password where the user only has one account in
- * the service, the response could be:
+ * the response could be:
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
@@ -102,12 +96,17 @@
  * <p>Notice that the ids that are part of a dataset (ids 1 to 4, in this example) are automatically
  * added to the {@code savableIds} list.
  *
- * <p>If the service has multiple {@link Dataset}s with multiple options for some fields on each
- * dataset (for example, multiple accounts with both a home and work address), then it should
- * "partition" the {@link Activity} in sections and populate the response with just a subset of the
- * data that would fulfill the first section; then once the user fills the first section and taps a
- * field from the next section, the Android system would issue another request for that section, and
- * so on. For example, the first response could be:
+ * <p>If the service has multiple {@link Dataset}s for different sections of the activity,
+ * for example, a user section for which there are two datasets followed by an address
+ * section for which there are two datasets for each user user, then it should "partition"
+ * the activity in sections and populate the response with just a subset of the data that would
+ * fulfill the first section (the name in our example); then once the user fills the first
+ * section and taps a field from the next section (the address in our example), the Android
+ * system would issue another request for that section, and so on. Note that if the user
+ * chooses to populate the first section with a service provided dataset, the subsequent request
+ * would contain the populated values so you don't try to provide suggestions for the first
+ * section but ony for the second one based on the context of what was already filled. For
+ * example, the first response could be:
  *
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
@@ -132,236 +131,182 @@
  *          .setTextFieldValue(id4, "Springfield")
  *          .build())
  *      .add(new Dataset.Builder("Work")
- *          .setTextFieldValue(id3, "Springfield Nuclear Power Plant")
+ *          .setTextFieldValue(id3, "Springfield Power Plant")
  *          .setTextFieldValue(id4, "Springfield")
  *          .build())
  *      .build();
  * </pre>
  *
- * <p>The service could require user authentication, either at the {@link FillResponse} or {@link
- * Dataset} levels, prior to auto-filling the activity - see {@link
- * FillResponse.Builder#requiresFingerprintAuthentication(CryptoObject, Bundle, int)}, {@link
- * FillResponse.Builder#requiresCustomAuthentication(Bundle, int)}, {@link
- * Dataset.Builder#requiresFingerprintAuthentication(CryptoObject, Bundle, int)}, and {@link
- * Dataset.Builder#requiresCustomAuthentication(Bundle, int)} for details.
+ * <p>The service could require user authentication at the {@link FillResponse} or the
+ * {@link Dataset} level, prior to auto-filling an activity - see {@link FillResponse.Builder
+ * #setAuthentication(IntentSender)} and {@link Dataset.Builder#setAuthentication(IntentSender)}.
+ * It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
+ * which would allow you to provide the dataset names to the user and if they choose one
+ * them challenge the user to authenticate. For example, if the user has a home and a work
+ * address the Home and Work labels could be stored unencrypted as they don't have any sensitive
+ * data while the address data is in an encrypted storage. If the user chooses Home, then the
+ * platform will start your authentication flow. If you encrypt all data and require auth
+ * at the response level the user will have to interact with the fill UI to trigger a request
+ * for the datasets as they don't see Home and Work options which will trigger your auth
+ * flow and after successfully authenticating the user will be presented with the Home and
+ * Work options where they can pick one. Hence, you have flexibility how to implement your
+ * auth while storing labels non-encrypted and data encrypted provides a better user
+ * experience.</p>
  *
- * <p>Finally, the service can use the {@link FillResponse.Builder#setExtras(Bundle)} and/or {@link
- * 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.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.
+ * <p>Finally, the service can use {@link Dataset.Builder#setExtras(Bundle)} methods
+ * to pass {@link Bundle extras} provided to all future calls related to a dataset,
+ * for example during authentication and saving.</p>
  */
 public final class FillResponse implements Parcelable {
-
-    private final List<Dataset> mDatasets;
-    private final AutoFillId[] mSavableIds;
+    private final String mId;
+    private final ArraySet<Dataset> mDatasets;
+    private final ArraySet<AutoFillId> mSavableIds;
     private final Bundle mExtras;
-    private final int mFlags;
-    private final boolean mRequiresAuth;
-    private final boolean mHasCryptoObject;
-    private final long mCryptoOpId;
+    private final IntentSender mAuthentication;
 
-    private FillResponse(Builder builder) {
-        // TODO(b/33197203): make it immutable?
+    private FillResponse(@NonNull Builder builder) {
+        mId = builder.mId;
         mDatasets = builder.mDatasets;
-        final int size = builder.mSavableIds.size();
-        mSavableIds = new AutoFillId[size];
-        int i = 0;
-        for (AutoFillId id : builder.mSavableIds) {
-            mSavableIds[i++] = id;
-        }
+        mSavableIds = builder.mSavableIds;
         mExtras = builder.mExtras;
-        mFlags = builder.mFlags;
-        mRequiresAuth = builder.mRequiresAuth;
-        mHasCryptoObject = builder.mHasCryptoObject;
-        mCryptoOpId = builder.mCryptoOpId;
+        mAuthentication = builder.mAuthentication;
     }
 
     /** @hide */
-    public List<Dataset> getDatasets() {
-        return mDatasets;
+    public @NonNull String getId() {
+        return mId;
     }
 
     /** @hide */
-    public AutoFillId[] getSavableIds() {
-        return mSavableIds;
-    }
-
-    /** @hide */
-    public Bundle getExtras() {
+    public @Nullable Bundle getExtras() {
         return mExtras;
     }
 
     /** @hide */
-    public int getFlags() {
-        return mFlags;
+    public @Nullable ArraySet<Dataset> getDatasets() {
+        return mDatasets;
     }
 
     /** @hide */
-    public boolean isAuthRequired() {
-        return mRequiresAuth;
+    public @Nullable ArraySet<AutoFillId> getSavableIds() {
+        return mSavableIds;
     }
 
     /** @hide */
-    public boolean hasCryptoObject() {
-        return mHasCryptoObject;
-    }
-
-    /** @hide */
-    public long getCryptoObjectOpId() {
-        return mCryptoOpId;
+    public @Nullable IntentSender getAuthentication() {
+        return mAuthentication;
     }
 
     /**
-     * Builder for {@link FillResponse} objects.
+     * Builder for {@link FillResponse} objects. You must to provide at least
+     * one dataset or set an authentication intent.
      */
     public static final class Builder {
-        private final List<Dataset> mDatasets = new ArrayList<>();
-        private final Set<AutoFillId> mSavableIds = new HashSet<>();
+        private final String mId;
+        private ArraySet<Dataset> mDatasets;
+        private ArraySet<AutoFillId> mSavableIds;
         private Bundle mExtras;
-        private int mFlags;
-        private boolean mRequiresAuth;
-        private boolean mHasCryptoObject;
-        private long mCryptoOpId;
+        private IntentSender mAuthentication;
+        private boolean mDestroyed;
 
-        /**
-         * Requires user authentication through the {@link android.service.autofill.AutoFillService}
-         * before handling an auto-fill request.
-         *
-         * <p>This method is typically called when the device (or the service) does not support
-         * fingerprint authentication (and hence it cannot use {@link
-         * #requiresFingerprintAuthentication(CryptoObject, Bundle, int)}) or when the service needs
-         * to use a custom authentication UI and is used in 2 scenarios:
-         *
-         * <ol>
-         *   <li>When the user data is encrypted and the service must authenticate an object that
-         *       will be used to decrypt it.
-         *   <li>When the service already acquired the user data but wants to confirm the user's
-         *       identity before the activity is filled with it.
-         * </ol>
-         *
-         * <p>When this method is called, the Android System displays an UI affordance asking the
-         * user to tap it to auto-fill the activity; if the user taps it, the Android System calls
-         * {@link
-         * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
-         * int)} passing {@link
-         * android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_REQUESTED} in the flags and
-         * the same {@code extras} passed to this method. The service can then displays its custom
-         * authentication UI, and then call the proper method on {@link FillCallback} depending on
-         * the authentication result and whether this response already contains the {@link Dataset}s
-         * need to auto-fill the activity:
-         *
-         * <ul>
-         *   <li>If authentication failed, call {@link
-         *       FillCallback#onFillResponseAuthentication(int)} passing {@link
-         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR} in the flags.
-         *   <li>If authentication succeeded and this response is empty (no datasets), call {@link
-         *       FillCallback#onSuccess(FillResponse)} with a new dataset (that does not require
-         *       authentication).
-         *   <li>If authentication succeeded and this response is not empty, call {@link
-         *       FillCallback#onFillResponseAuthentication(int)} passing {@link
-         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS} in the flags.
-         * </ul>
-         *
-         * @param extras when set, will be passed back in the {@link
-         *     android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
-         *     int)} call so it could be used by the service to handle state.
-         * @param flags optional parameters, currently ignored.
-         */
-        public Builder requiresCustomAuthentication(@Nullable Bundle extras, int flags) {
-            return requiresAuthentication(null, extras, flags);
+        /** @hide */
+        // TODO(b/33197203): Remove once GCore migrates
+        public Builder() {
+            this(String.valueOf(System.currentTimeMillis()));
         }
 
         /**
-         * Requires user authentication through the Fingerprint sensor before handling an auto-fill
-         * request.
+         * Creates a new {@link FillResponse} builder.
          *
-         * <p>The {@link android.service.autofill.AutoFillService} typically uses this method in 2
-         * situations:
-         *
-         * <ol>
-         *   <li>When the user data is encrypted and the service must authenticate an object that
-         *       will be used to decrypt it.
-         *   <li>When the service already acquired the user data but wants to confirm the user's
-         *       identity before the activity is filled with it.
-         * </ol>
-         *
-         * <p>When this method is called, the Android System displays an UI affordance asking the
-         * user to use the fingerprint sensor to auto-fill the activity, and what happens after a
-         * successful fingerprint authentication depends on the number of {@link Dataset}s included
-         * in this response:
-         *
-         * <ul>
-         *   <li>If it's empty (scenario #1 above), the Android System will call {@link
-         *     android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
-         *       int)} passing {@link
-         *       android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_SUCCESS}} in the
-         *       flags.
-         *   <li>If it contains one dataset, the activity will be auto-filled right away.
-         *   <li>If it contains many datasets, the Android System will show dataset picker UI, and
-         *       then auto-fill the activity once the user select the proper datased.
-         * </ul>
-         *
-         * <p>If the fingerprint authentication fails, the Android System will call {@link
-         * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
-         * int)} passing {@link android.service.autofill.AutoFillService#FLAG_AUTHENTICATION_ERROR}
-         * in the flags.
-         *
-         * <p><strong>NOTE: </note> the {@link android.service.autofill.AutoFillService} should use
-         * the {@link android.hardware.fingerprint.FingerprintManager} to check if fingerpint
-         * authentication is available before using this method, and use other alternatives (such as
-         * {@link #requiresCustomAuthentication(Bundle, int)}) if it is not: if this method is
-         * called when fingerprint is not available, Android System will call {@link
-         * android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
-         * int)} passing {@link
-         * android.service.autofill.AutoFillService#FLAG_FINGERPRINT_AUTHENTICATION_NOT_AVAILABLE}
-         * in the flags, but it would be wasting system resources (and worsening the user
-         * experience) in the process.
-         *
-         * @param crypto object that will be authenticated.
-         * @param extras when set, will be passed back in the {@link
-         *     android.service.autofill.AutoFillService#onFillResponseAuthenticationRequest(Bundle,
-         *     int)} call so it could be used by the service to handle state.
-         * @param flags optional parameters, currently ignored.
+         * @param id A required id to identify this dataset for future interactions related to it.
          */
-        public Builder requiresFingerprintAuthentication(CryptoObject crypto,
-                @Nullable Bundle extras, int flags) {
-            // TODO(b/33197203): should we allow crypto to be null?
-            Preconditions.checkArgument(crypto != null, "must pass a CryptoObject");
-            return requiresAuthentication(crypto, extras, flags);
+        public Builder(@NonNull String id) {
+            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null");
         }
 
-        private Builder requiresAuthentication(CryptoObject cryptoObject, Bundle extras,
-                int flags) {
-            // There can be only one!
-            Preconditions.checkState(!mRequiresAuth,
-                    "requires-authentication methods already called");
-            // TODO(b/33197203): make sure that either this method or setExtras() is called, but
-            // not both
-            mExtras = extras;
-            mFlags = flags;
-            mRequiresAuth = true;
-            if (cryptoObject != null) {
-                mHasCryptoObject = true;
-                mCryptoOpId = cryptoObject.getOpId();
-            }
+        /**
+         * Requires a fill response authentication before auto-filling the activity with
+         * any dataset in this response. This is typically useful when a user interaction
+         * is required to unlock their data vault if you encrypt the dataset labels and
+         * dataset data. It is recommended to encrypt only the sensitive data and not the
+         * dataset labels which would allow auth on the dataset level leading to a better
+         * user experience. Note that if you use sensitive data as a label, for example an
+         * email address, then it should also be encrypted.
+         *
+         * <p>This method is called when you need to provide an authentication
+         * UI for the fill response. For example, when the user's data is stored
+         * encrypted and needs a user interaction to decrypt before offering fill
+         * suggestions.</p>
+         *
+         * <p>When a user initiates an auto fill, the system triggers the provided
+         * intent whose extras will have the {@link android.content.Intent
+         * #EXTRA_AUTO_FILL_ITEM_ID id} of the {@link android.view.autofill.FillResponse})
+         * to authenticate, the {@link android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras}
+         * associated with this response, and a {@link android.content.Intent
+         * #EXTRA_AUTO_FILL_CALLBACK callback} to dispatch the authentication result.</p>
+         *
+         * <p>Once you complete your authentication flow you should use the provided callback
+         * to notify for a failure or a success. In case of a success you need to provide
+         * the fully populated response that is being authenticated. For example, if you
+         * provided an empty {@link FillResponse} because the user's data was locked and
+         * marked that the response needs an authentication then in the response returned
+         * if authentication succeeds you need to provide all available datasets some of
+         * which may need to be further authenticated, for example a credit card whose
+         * CVV needs to be entered.</p>
+         *
+         * <p>The indent sender mechanism allows you to have your authentication UI
+         * implemented as an activity or a service or a receiver. However, the recommended
+         * way is to do this is with an activity which the system will start in the
+         * filled activity's task meaning it will properly work with back, recent apps, and
+         * free-form multi-window, while avoiding the need for the "draw on top of other"
+         * apps special permission. You can still theme your authentication activity's
+         * UI to look like a dialog if desired.</p>
+         *
+         * <p></><strong>Note:</strong> Do not make the provided intent sender
+         * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
+         * platform needs to fill in the authentication arguments.</p>
+         *
+         * @param authentication Intent to trigger your authentication flow.
+         *
+         * @see android.app.PendingIntent#getIntentSender()
+         */
+        public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) {
+            throwIfDestroyed();
+            mAuthentication = authentication;
             return this;
         }
 
         /**
-         * Adds a new {@link Dataset} to this response.
+         * Adds a new {@link Dataset} to this response. Adding a dataset with the
+         * same id updates the existing one.
          *
          * @throws IllegalArgumentException if a dataset with same {@code name} already exists.
          */
-        public Builder addDataset(Dataset dataset) {
-            Preconditions.checkNotNull(dataset, "dataset cannot be null");
-            // TODO(b/33197203): check if name already exists
-            mDatasets.add(dataset);
-            for (DatasetField field : dataset.getFields()) {
-                mSavableIds.add(field.getId());
+        public@NonNull Builder addDataset(@Nullable Dataset dataset) {
+            throwIfDestroyed();
+            if (dataset == null) {
+                return this;
+            }
+            if (mDatasets == null) {
+                mDatasets = new ArraySet<>();
+            }
+            final int datasetCount = mDatasets.size();
+            for (int i = 0; i < datasetCount; i++) {
+                if (mDatasets.valueAt(i).getName().equals(dataset.getName())) {
+                    throw new IllegalArgumentException("Duplicate dataset name: "
+                            + dataset.getName());
+                }
+            }
+            if (!mDatasets.add(dataset)) {
+                return this;
+            }
+            final int fieldCount = dataset.getFieldIds().size();
+            for (int i = 0; i < fieldCount; i++) {
+                final AutoFillId id = dataset.getFieldIds().get(i);
+                if (mSavableIds == null) {
+                    mSavableIds = new ArraySet<>();
+                }
+                mSavableIds.add(id);
             }
             return this;
         }
@@ -374,27 +319,35 @@
          *
          * <p>See {@link FillResponse} for examples.
          */
-        public Builder addSavableFields(AutoFillId... ids) {
+        public @NonNull Builder addSavableFields(@Nullable AutoFillId... ids) {
+            throwIfDestroyed();
+            if (ids == null) {
+                return this;
+            }
             for (AutoFillId id : ids) {
+                if (mSavableIds == null) {
+                    mSavableIds = new ArraySet<>();
+                }
                 mSavableIds.add(id);
             }
             return this;
         }
 
         /**
-         * Sets a {@link Bundle} that will be passed to subsequent calls to {@link
-         * android.service.autofill.AutoFillService} methods such as {@link
+         * Sets a {@link Bundle} that will be passed to subsequent APIs that
+         * manipulate this response. For example, they are passed in as {@link
+         * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your
+         * authentication flow and to subsequent calls to {@link
+         * android.service.autofill.AutoFillService#onFillRequest(
+         * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
+         * android.service.autofill.FillCallback)} and {@link
          * android.service.autofill.AutoFillService#onSaveRequest(
-         * 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.
+         * android.app.assist.AssistStructure, Bundle,
+         * android.service.autofill.SaveCallback)}.
          */
         public Builder setExtras(Bundle extras) {
-            // TODO(b/33197203): make sure that either this method or the requires-Authentication
-            // ones are called, but not both
-            mExtras = Objects.requireNonNull(extras, "extras cannot be null");
+            throwIfDestroyed();
+            mExtras = extras;
             return this;
         }
 
@@ -402,8 +355,16 @@
          * Builds a new {@link FillResponse} instance.
          */
         public FillResponse build() {
+            throwIfDestroyed();
+            mDestroyed = true;
             return new FillResponse(this);
         }
+
+        private void throwIfDestroyed() {
+            if (mDestroyed) {
+                throw new IllegalStateException("Already called #build()");
+            }
+        }
     }
 
     /////////////////////////////////////
@@ -412,14 +373,12 @@
     @Override
     public String toString() {
         if (!DEBUG) return super.toString();
-
-        final StringBuilder builder = new StringBuilder("FillResponse: [datasets=")
-                .append(mDatasets).append(", savableIds=").append(Arrays.toString(mSavableIds))
-                .append(", extras=");
-        append(builder, mExtras)
-            .append(", flags=").append(mFlags)
-            .append(", requiresAuth: ").append(mRequiresAuth)
-            .append(", hasCrypto: ").append(mHasCryptoObject);
+        final StringBuilder builder = new StringBuilder(
+                "FillResponse: [id=").append(mId)
+                .append(", datasets=").append(mDatasets)
+                .append(", savableIds=").append(mSavableIds)
+                .append(", hasExtras=").append(mExtras != null)
+                .append(", hasAuthentication=").append(mAuthentication != null);
         return builder.append(']').toString();
     }
 
@@ -434,33 +393,34 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeList(mDatasets);
-        parcel.writeParcelableArray(mSavableIds, 0);
-        parcel.writeBundle(mExtras);
-        parcel.writeInt(mFlags);
-        parcel.writeInt(mRequiresAuth ? 1 : 0);
-        parcel.writeInt(mHasCryptoObject ? 1 : 0);
-        if (mHasCryptoObject) {
-            parcel.writeLong(mCryptoOpId);
-        }
-    }
-
-    private FillResponse(Parcel parcel) {
-        mDatasets = new ArrayList<>();
-        parcel.readList(mDatasets, null);
-        mSavableIds = parcel.readParcelableArray(null, AutoFillId.class);
-        mExtras = parcel.readBundle();
-        mFlags = parcel.readInt();
-        mRequiresAuth = parcel.readInt() == 1;
-        mHasCryptoObject = parcel.readInt() == 1;
-        mCryptoOpId = mHasCryptoObject ? parcel.readLong() : 0;
+        parcel.writeString(mId);
+        parcel.writeTypedArraySet(mDatasets, 0);
+        parcel.writeTypedArraySet(mSavableIds, 0);
+        parcel.writeParcelable(mExtras, 0);
+        parcel.writeParcelable(mAuthentication, 0);
     }
 
     public static final Parcelable.Creator<FillResponse> CREATOR =
             new Parcelable.Creator<FillResponse>() {
         @Override
-        public FillResponse createFromParcel(Parcel source) {
-            return new FillResponse(source);
+        public FillResponse createFromParcel(Parcel parcel) {
+            // Always go through the builder to ensure the data ingested by
+            // the system obeys the contract of the builder to avoid attacks
+            // using specially crafted parcels.
+            final Builder builder = new Builder(parcel.readString());
+            final ArraySet<Dataset> datasets = parcel.readTypedArraySet(null);
+            final int datasetCount = (datasets != null) ? datasets.size() : 0;
+            for (int i = 0; i < datasetCount; i++) {
+                builder.addDataset(datasets.valueAt(i));
+            }
+            final ArraySet<AutoFillId> fillIds = parcel.readTypedArraySet(null);
+            final int fillIdCount = (fillIds != null) ? fillIds.size() : 0;
+            for (int i = 0; i < fillIdCount; i++) {
+                builder.addSavableFields(fillIds.valueAt(i));
+            }
+            builder.setExtras(parcel.readParcelable(null));
+            builder.setAuthentication(parcel.readParcelable(null));
+            return builder.build();
         }
 
         @Override
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 78436f7..178a697 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -34,7 +34,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -43,7 +42,6 @@
 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;
@@ -72,66 +70,47 @@
 public final class AutoFillManagerService extends SystemService {
 
     private static final String TAG = "AutoFillManagerService";
-    static final boolean DEBUG = true; // TODO(b/33197203): change to false once stable
+    static final boolean DEBUG = false;
 
-    private static final long SERVICE_BINDING_LIFETIME_MS = 5 * DateUtils.MINUTE_IN_MILLIS;
-
-    private static final int MSG_UNBIND = 1;
-    private static final int MSG_REQUEST_AUTO_FILL_FOR_USER = 2;
-    private static final int MSG_REQUEST_AUTO_FILL = 3;
+    protected static final int MSG_REQUEST_AUTO_FILL_FOR_USER = 1;
+    protected static final int MSG_REQUEST_AUTO_FILL = 2;
+    private static final int MSG_REQUEST_SAVE_FOR_USER = 3;
     private static final int MSG_ON_VALUE_CHANGED = 4;
-    private static final int MSG_REQUEST_SAVE_FOR_USER = 5;
 
-    private final AutoFillManagerServiceStub mServiceStub;
     private final Context mContext;
-    private final ContentResolver mResolver;
+    private final AutoFillUI mUi;
 
     private final Object mLock = new Object();
 
-    private final HandlerCaller.Callback mHandlerCallback = new HandlerCaller.Callback() {
-
-        @Override
-        public void executeMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UNBIND: {
-                    synchronized (mLock) {
-                        removeCachedServiceLocked(msg.arg1);
-                    }
-                    return;
-                } case MSG_REQUEST_AUTO_FILL_FOR_USER: {
-                    handleAutoFillForUser(msg.arg1);
-                    return;
-                } case MSG_REQUEST_SAVE_FOR_USER: {
-                    handleSaveForUser(msg.arg1);
-                    return;
-                } case MSG_REQUEST_AUTO_FILL: {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        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);
-                    } finally {
-                        args.recycle();
-                    }
-                    return;
-                } case MSG_ON_VALUE_CHANGED: {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        final int userId = msg.arg1;
-                        final IBinder activityToken = (IBinder) args.arg1;
-                        final AutoFillId autoFillId = (AutoFillId) args.arg2;
-                        final AutoFillValue newValue = (AutoFillValue) args.arg3;
-                        handleValueChanged(activityToken, userId, autoFillId, newValue);
-                    } finally {
-                        args.recycle();
-                    }
-                    return;
-                } default: {
-                    Slog.w(TAG, "Invalid message: " + msg);
-                }
+    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
+        switch (msg.what) {
+            case MSG_REQUEST_AUTO_FILL_FOR_USER: {
+                handleAutoFillForUser(msg.arg1);
+                return;
+            } case MSG_REQUEST_SAVE_FOR_USER: {
+                handleSaveForUser(msg.arg1);
+                return;
+            } case MSG_REQUEST_AUTO_FILL: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                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;
+                args.recycle();
+                handleAutoFill(activityToken, userId, autoFillId, bounds, flags);
+                return;
+            } case MSG_ON_VALUE_CHANGED: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                final int userId = msg.arg1;
+                final IBinder activityToken = (IBinder) args.arg1;
+                final AutoFillId autoFillId = (AutoFillId) args.arg2;
+                final AutoFillValue newValue = (AutoFillValue) args.arg3;
+                args.recycle();
+                handleValueChanged(activityToken, userId, autoFillId, newValue);
+                return;
+            } default: {
+                Slog.w(TAG, "Invalid message: " + msg);
             }
         }
     };
@@ -148,10 +127,10 @@
      * Entries on this cache are added on demand and removed when:
      * <ol>
      *   <li>An auto-fill service app is removed.
-     *   <li>The {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} for an user change.
-     *   <li>It has not been interacted with for {@link #SERVICE_BINDING_LIFETIME_MS} ms.
+     *   <li>The {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} for an user change.\
      * </ol>
      */
+    // TODO(b/33197203): Update the above comment
     @GuardedBy("mLock")
     private SparseArray<AutoFillManagerServiceImpl> mServicesCache = new SparseArray<>();
 
@@ -160,19 +139,14 @@
 
     public AutoFillManagerService(Context context) {
         super(context);
-
         mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
-
         mContext = context;
-
-        mResolver = context.getContentResolver();
-        mServiceStub = new AutoFillManagerServiceStub();
+        mUi = new AutoFillUI(mContext);
     }
 
     @Override
     public void onStart() {
-        if (DEBUG) Slog.d(TAG, "onStart(): binding as " + AUTO_FILL_MANAGER_SERVICE);
-        publishBinderService(AUTO_FILL_MANAGER_SERVICE, mServiceStub);
+        publishBinderService(AUTO_FILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
     }
 
     @Override
@@ -186,61 +160,41 @@
         ComponentName serviceComponent = null;
         ServiceInfo serviceInfo = null;
         final String componentName = Settings.Secure.getStringForUser(
-                mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId);
+                mContext.getContentResolver(), Settings.Secure.AUTO_FILL_SERVICE, userId);
         if (!TextUtils.isEmpty(componentName)) {
             try {
                 serviceComponent = ComponentName.unflattenFromString(componentName);
                 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0,
                         userId);
             } catch (RuntimeException | RemoteException e) {
-                Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e);
+                Slog.e(TAG, "Bad auto-fill service name " + componentName, e);
                 return null;
             }
         }
 
-        if (DEBUG) {
-            Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component="
-                    + serviceComponent + ", info: " + serviceInfo);
-        }
         if (serviceInfo == null) {
-            if (DEBUG) Slog.d(TAG, "no service info for " + serviceComponent);
             return null;
         }
-        return new AutoFillManagerServiceImpl(this, mContext, mLock, mRequestsHistory,
-                userId, serviceInfo.applicationInfo.uid, serviceComponent,
-                SERVICE_BINDING_LIFETIME_MS);
+
+        try {
+            return new AutoFillManagerServiceImpl(mContext, mLock, mRequestsHistory,
+                    userId, serviceComponent, mUi);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Auto-fill service not found: " + serviceComponent, e);
+        }
+
+        return null;
     }
 
     /**
      * Gets the service instance for an user.
-     * <p>
-     * First it tries to return the existing instance from the cache; if it's not cached, it creates
-     * a new instance and caches it.
      */
-    // TODO(b/33197203): make private once AutoFillUi does not uses notifications
-    AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
+    AutoFillManagerServiceImpl getOrCreateServiceForUserLocked(int userId) {
         AutoFillManagerServiceImpl service = mServicesCache.get(userId);
-        if (service != null) {
-            if (DEBUG)
-                Log.d(TAG, "reusing cached service for userId " + userId);
-            service.setLifeExpectancy(SERVICE_BINDING_LIFETIME_MS);
-        } else {
+        if (service == null) {
             service = newServiceForUser(userId);
-            if (service == null) {
-                // Already logged
-                return null;
-            }
-            if (DEBUG) Log.d(TAG, "creating new cached service for userId " + userId);
-            service.startLocked();
             mServicesCache.put(userId, service);
         }
-        // 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;
     }
 
@@ -248,22 +202,17 @@
      * Removes a cached service for a given user.
      */
     void removeCachedServiceLocked(int userId) {
-        if (DEBUG) Log.d(TAG, "removing cached service for userId " + userId);
         final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
-        if (service == null) {
-            if (DEBUG) {
-                Log.d(TAG, "removeCachedServiceForUser(): no cached service for userId " + userId);
-            }
-            return;
+        if (service != null) {
+            mServicesCache.delete(userId);
+            service.destroyLocked();
         }
-        mServicesCache.delete(userId);
-        service.stopLocked();
     }
 
     private void handleAutoFill(IBinder activityToken, int userId, AutoFillId autoFillId,
             Rect bounds, int flags) {
         synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+            final AutoFillManagerServiceImpl service = getOrCreateServiceForUserLocked(userId);
             if (service != null) {
                 service.requestAutoFillLocked(activityToken, autoFillId, bounds, flags);
             }
@@ -273,7 +222,7 @@
     private void handleValueChanged(IBinder activityToken, int userId, AutoFillId autoFillId,
             AutoFillValue newValue) {
         synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+            final AutoFillManagerServiceImpl service = getOrCreateServiceForUserLocked(userId);
             if (service != null) {
                 service.onValueChangeLocked(activityToken, autoFillId, newValue);
             }
@@ -283,45 +232,32 @@
     private IBinder getTopActivityForUser() {
         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 null;
         }
         return topActivities.get(0);
     }
 
     private void handleAutoFillForUser(int userId) {
-        if (DEBUG) Slog.d(TAG, "handler.requestAutoFillForUser(): id=" + userId);
         final IBinder activityToken = getTopActivityForUser();
-        if (activityToken == null) {
-            return;
+        if (activityToken != null) {
+            synchronized (mLock) {
+                final AutoFillManagerServiceImpl service =
+                        getOrCreateServiceForUserLocked(userId);
+                service.requestAutoFillLocked(activityToken, null, null, 0);
+            }
         }
 
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
-            if (service == null) {
-                Slog.w(TAG, "no service for user " + userId);
-                return;
-            }
-            service.requestAutoFillLocked(activityToken, null, null, 0);
-        }
     }
 
     private void handleSaveForUser(int userId) {
-        if (DEBUG) Slog.d(TAG, "handler.handleSaveForUser(): id=" + userId);
         final IBinder activityToken = getTopActivityForUser();
-        if (activityToken == null) {
-            return;
-        }
-
-        synchronized (mLock) {
-            final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
-            if (service == null) {
-                Slog.w(TAG, "no service for user " + userId);
-                return;
+        if (activityToken != null) {
+            synchronized (mLock) {
+                final AutoFillManagerServiceImpl service =
+                        getOrCreateServiceForUserLocked(userId);
+                service.requestSaveForUserLocked(activityToken);
             }
-            service.requestSaveForUserLocked(activityToken);
         }
     }
 
@@ -347,9 +283,6 @@
 
         @Override
         public void requestAutoFill(AutoFillId id, Rect bounds, int flags) {
-            if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", autoFillId=" + id
-                    + ", bounds=" + bounds);
-
             final IBinder activityToken = getTopActivity();
             if (activityToken != null) {
                 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIIOOO(MSG_REQUEST_AUTO_FILL,
@@ -360,25 +293,19 @@
         @Override
         public void requestAutoFillForUser(int userId) {
             mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-
-            mHandlerCaller.sendMessage(
-                    mHandlerCaller.obtainMessageI(MSG_REQUEST_AUTO_FILL_FOR_USER, userId));
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageI(
+                    MSG_REQUEST_AUTO_FILL_FOR_USER, userId));
         }
 
         @Override
         public void requestSaveForUser(int userId) {
             mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-
-            mHandlerCaller.sendMessage(
-                    mHandlerCaller.obtainMessageI(MSG_REQUEST_SAVE_FOR_USER, userId));
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageI(MSG_REQUEST_SAVE_FOR_USER, userId));
         }
 
         @Override
         public void onValueChanged(AutoFillId id, AutoFillValue value) {
-            if (DEBUG) Slog.d(TAG, "onValueChanged(): id=" + id + ", value=" + value);
-
             final IBinder activityToken = getTopActivity();
-
             if (activityToken != null) {
                 mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOOO(MSG_ON_VALUE_CHANGED,
                         UserHandle.getCallingUserId(), activityToken, id, value));
@@ -407,6 +334,7 @@
                         impl.dumpLocked("  ", pw);
                     }
                 }
+                mUi.dump(pw);
             }
             pw.println("Requests history:");
             mRequestsHistory.reverseDump(fd, pw, args);
@@ -430,7 +358,6 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId);
             synchronized (mLock) {
                 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 42e4fd3..e32e21d 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -16,15 +16,11 @@
 
 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.autofill.AutoFillManager.FLAG_UPDATE_UI_SHOW;
 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.VERBOSE;
-import static com.android.server.autofill.Helper.bundleToString;
 
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -38,33 +34,26 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.ServiceConnection;
+import android.content.IntentSender;
 import android.content.pm.PackageManager;
 import android.graphics.Rect;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.IFingerprintService;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.Binder;
 import android.os.Bundle;
-import android.os.DeadObjectException;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
 import android.service.autofill.AutoFillService;
 import android.service.autofill.AutoFillServiceInfo;
+import android.service.autofill.FillCallback;
 import android.service.autofill.IAutoFillAppCallback;
-import android.service.autofill.IAutoFillServerCallback;
 import android.service.autofill.IAutoFillService;
+import android.service.autofill.IFillCallback;
 import android.service.voice.VoiceInteractionSession;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.LocalLog;
-import android.util.Log;
 import android.util.PrintWriterPrinter;
 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;
@@ -77,8 +66,6 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -95,24 +82,13 @@
     private static int sSessionIdCounter = 0;
 
     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;
     private final AutoFillServiceInfo mInfo;
-    private final AutoFillManagerService mManagerService;
-
-    // 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<>();
+    private final AutoFillUI mUi;
 
     private final LocalLog mRequestsHistory;
 
@@ -122,15 +98,7 @@
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
                 final String reason = intent.getStringExtra("reason");
                 if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason);
-
-                synchronized (mLock) {
-                    final int size = mSessions.size();
-                    for (int i = 0; i < size; i++) {
-                        final Session session = mSessions.valueAt(i);
-                        // TODO(b/33197203): invalidate the sessions instead?
-                        session.mUi.closeAll();
-                    }
-                }
+                mUi.hideAll();
             }
         }
     };
@@ -144,40 +112,7 @@
     // TODO(b/33197203): need to make sure service is bound while callback is pending and/or
     // use WeakReference
     @GuardedBy("mLock")
-    private static final SparseArray<Session> mSessions = new SparseArray<>();
-
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (DEBUG) Slog.d(TAG, "onServiceConnected():" + name);
-            synchronized (mLock) {
-                mService = IAutoFillService.Stub.asInterface(service);
-                try {
-                    mService.onConnected();
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Exception on service.onConnected(): " + e);
-                    return;
-                }
-                if (!mQueuedRequests.isEmpty()) {
-                    if (DEBUG) Slog.d(TAG, "queued requests:" + mQueuedRequests.size());
-                }
-                for (final QueuedRequest request: mQueuedRequests) {
-                    requestAutoFillLocked(request.activityToken, request.autoFillId,
-                            request.bounds, request.flags, false);
-                }
-                mQueuedRequests.clear();
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            if (DEBUG) Slog.d(TAG, name + " disconnected");
-            synchronized (mLock) {
-                mService = null;
-                mManagerService.removeCachedServiceLocked(mUserId);
-            }
-        }
-    };
+    private final SparseArray<Session> mSessions = new SparseArray<>();
 
     /**
      * Receiver of assist data from the app's {@link Activity}, uses the {@code resultData} as
@@ -193,8 +128,13 @@
                 Slog.w(TAG, "no app callback on mAssistReceiver's resultData");
                 return;
             }
+
             final AssistStructure structure = resultData
                     .getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
+            if (structure == null) {
+                Slog.w(TAG, "no assist structure for id " + resultCode);
+                return;
+            }
 
             final Session session;
             synchronized (mLock) {
@@ -203,91 +143,55 @@
                     Slog.w(TAG, "no server callback for id " + resultCode);
                     return;
                 }
-                session.setAppCallbackLocked(appBinder);
-                // TODO(b/33197203): since service is fetching the data (to use for save later),
-                // we should optimize what's sent (for example, remove layout containers,
-                // color / font info, etc...)
-                session.mStructure = structure;
-
-                // TODO(b/33197203, b/33269702): Must fetch the data so it's available later on
-                // handleSave(), even if if the activity is gone by then, but structure.ensureData()
-                // gives a ONE_WAY warning because system_service could block on app calls.
-                // We need to change AssistStructure so it provides a "one-way" writeToParcel()
-                // method that sends all the data
-                structure.ensureData();
-
-                structure.sanitizeForParceling(true);
-                if (VERBOSE) {
-                    Slog.v(TAG, "Dumping " + structure + " before calling service.autoFill()");
-                    structure.dump();
-                }
-                mService.autoFill(structure, session.mServerCallback);
             }
+
+            // TODO(b/33197203): since service is fetching the data (to use for save later),
+            // we should optimize what's sent (for example, remove layout containers,
+            // color / font info, etc...)
+
+            // TODO(b/33197203, b/33269702): Must fetch the data so it's available later on
+            // handleSave(), even if if the activity is gone by then, but structure.ensureData()
+            // gives a ONE_WAY warning because system_service could block on app calls.
+            // We need to change AssistStructure so it provides a "one-way" writeToParcel()
+            // method that sends all the data
+            structure.ensureData();
+
+            structure.sanitizeForParceling(true);
+
+            if (VERBOSE) {
+                Slog.v(TAG, "Dumping " + structure + " before calling service.autoFill()");
+                structure.dump();
+            }
+
+            session.onApplicationDataAvailable(structure, appBinder);
         }
     };
 
-    @GuardedBy("mLock")
-    private IAutoFillService mService;
-    @GuardedBy("mLock")
-    private boolean mBound;
-    @GuardedBy("mLock")
-    private boolean mValid;
-
-    // Estimated time when the service will be evicted from the cache.
-    long mEstimateTimeOfDeath;
-
-    AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock,
-            LocalLog requestsHistory, int userId, int uid, ComponentName component, long ttl) {
-        mManagerService = managerService;
+    AutoFillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
+            int userId, ComponentName component, AutoFillUI ui)
+            throws PackageManager.NameNotFoundException {
         mContext = context;
         mLock = lock;
         mRequestsHistory = requestsHistory;
         mUserId = userId;
-        mUid = uid;
         mComponent = component;
         mComponentName = mComponent.flattenToShortString();
         mAm = ActivityManager.getService();
-        setLifeExpectancy(ttl);
+        mUi = ui;
+        mInfo = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId);
 
-        final AutoFillServiceInfo info;
-        try {
-            info = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.w(TAG, "Auto-fill service not found: " + component, e);
-            mInfo = null;
-            mValid = false;
-            return;
-        }
-        mInfo = info;
-        if (mInfo.getParseError() != null) {
-            Slog.w(TAG, "Bad auto-fill service: " + mInfo.getParseError());
-            mValid = false;
-            return;
-        }
-
-        mValid = true;
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
     }
 
-    void setLifeExpectancy(long ttl) {
-        mEstimateTimeOfDeath = SystemClock.uptimeMillis() + ttl;
-    }
 
-    void startLocked() {
-        if (DEBUG) Slog.d(TAG, "startLocked()");
-
-        final Intent intent = new Intent(AutoFillService.SERVICE_INTERFACE);
-        intent.setComponent(mComponent);
-        mBound = mContext.bindServiceAsUser(intent, mConnection,
-                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUserId));
-
-        if (!mBound) {
-            Slog.w(TAG, "Failed binding to auto-fill service " + mComponent);
-            return;
-        }
-        if (DEBUG) Slog.d(TAG, "Bound to " + mComponent);
+    /**
+     * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app.
+     */
+    void requestSaveForUserLocked(IBinder activityToken) {
+        final Session session = getOrCreateSessionByTokenLocked(activityToken);
+        session.onSaveLocked();
     }
 
     /**
@@ -299,66 +203,17 @@
      * @param flags optional flags.
      */
     void requestAutoFillLocked(IBinder activityToken, @Nullable AutoFillId autoFillId,
-            @Nullable Rect bounds, int flags) {
-        if (!mBound) {
-            Slog.w(TAG, "requestAutoFillLocked() failed because it's not bound to service");
-            return;
-        }
-
-        requestAutoFillLocked(activityToken, autoFillId, bounds, flags, true);
-    }
-
-    /**
-     * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app.
-     */
-    void requestSaveForUserLocked(IBinder activityToken) {
-        if (!mBound) {
-            Slog.w(TAG, "requestSaveForUserLocked() failed because it's not bound to service");
-            return;
-        }
-        if (mService == null) {
-            Slog.w(TAG, "requestSaveForUserLocked: service not set");
-            return;
-        }
-
-        final Session session = getSessionByTokenLocked(activityToken);
-        if (session == null) {
-            Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken);
-            return;
-        }
-
-        session.onSaveLocked();
-    }
-
-    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");
-                return;
-            }
-            if (DEBUG) Slog.d(TAG, "requestAutoFillLocked(): service not set yet, queuing it");
-            mQueuedRequests.add(new QueuedRequest(activityToken, autoFillId, bounds, flags));
-            return;
-        }
-
+          @Nullable Rect bounds, int flags) {
         final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags
                 + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds;
         mRequestsHistory.log(historyItem);
 
         // TODO(b/33197203): Handle partitioning
-        Session session = getSessionByTokenLocked(activityToken);
-
-        if (session == null) {
-            session = createSessionByTokenLocked(activityToken);
-        } else {
-            if (DEBUG) Slog.d(TAG, "reusing session for " + activityToken + ": " + session.mId);
-        }
-
+        Session session = getOrCreateSessionByTokenLocked(activityToken);
         session.updateAutoFillInput(flags, autoFillId, null, bounds);
     }
 
-    private Session getSessionByTokenLocked(IBinder activityToken) {
+    private Session getOrCreateSessionByTokenLocked(IBinder activityToken) {
         final int size = mSessions.size();
         for (int i = 0; i < size; i++) {
             final Session session = mSessions.valueAt(i);
@@ -366,14 +221,14 @@
                 return session;
             }
         }
-        return null;
+        return createSessionByTokenLocked(activityToken);
     }
 
     private Session createSessionByTokenLocked(IBinder activityToken) {
         final int sessionId = ++sSessionIdCounter;
         if (DEBUG) Slog.d(TAG, "creating session for " + activityToken + ": " + sessionId);
 
-        final Session newSession = new Session(sessionId, activityToken);
+        final Session newSession = new Session(mContext, activityToken, sessionId);
         mSessions.put(sessionId, newSession);
 
         /*
@@ -401,88 +256,33 @@
      */
     void onValueChangeLocked(IBinder activityToken, AutoFillId autoFillId, AutoFillValue newValue) {
         // TODO(b/33197203): add MetricsLogger call
-        final Session session = getSessionByTokenLocked(activityToken);
-        if (session == null) {
-            Slog.w(TAG, "onValueChangeLocked(): session gone for " + activityToken);
-            return;
-        }
-
+        final Session session = getOrCreateSessionByTokenLocked(activityToken);
         session.updateValueLocked(autoFillId, newValue);
     }
 
-    void stopLocked() {
-        if (DEBUG) Slog.d(TAG, "stopLocked()");
-
-        // Sanity check.
-        if (mService == null) {
-            Slog.w(TAG, "service already null on shutdown");
-            return;
-        }
-        try {
-            mService.onDisconnected();
-        } catch (RemoteException e) {
-            if (! (e instanceof DeadObjectException)) {
-                Slog.w(TAG, "Exception calling service.onDisconnected(): " + e);
-            }
-        } finally {
-            mService = null;
-        }
-
-        if (mBound) {
-            mContext.unbindService(mConnection);
-            mBound = false;
-        }
-        if (mValid) {
-            mContext.unregisterReceiver(mBroadcastReceiver);
-        }
-    }
-
     void removeSessionLocked(int id) {
         if (DEBUG) Slog.d(TAG, "Removing session " + id);
-        mSessions.remove(id);
+        mSessions.get(id);
+    }
 
-        // TODO(b/33197203): notify mService so it can invalidate the FillCallback / SaveCallback
-        // and cached AssistStructures
+    void destroyLocked() {
+        mContext.unregisterReceiver(mBroadcastReceiver);
+        final int sessionCount = mSessions.size();
+        for (int i = sessionCount - 1; i >= 0; i--) {
+            Session session = mSessions.valueAt(i);
+            session.destroy();
+            mSessions.removeAt(i);
+        }
     }
 
     void dumpLocked(String prefix, PrintWriter pw) {
-        if (!mValid) {
-            pw.print("  NOT VALID: ");
-            if (mInfo == null) {
-                pw.println("no info");
-            } else {
-                pw.println(mInfo.getParseError());
-            }
-            return;
-        }
-
         final String prefix2 = prefix + "  ";
-
-        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(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.print(prefix); pw.print("mAuthToken: "); pw.println(mAuthToken);
-
         if (DEBUG) {
             // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps)
             pw.print(prefix); pw.println("ServiceInfo:");
             mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix);
         }
 
-        if (mQueuedRequests.isEmpty()) {
-            pw.print(prefix); pw.println("No queued requests");
-        } else {
-            pw.print(prefix); pw.println("Queued requests:");
-            for (int i = 0; i < mQueuedRequests.size(); i++) {
-                pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mQueuedRequests.get(i));
-            }
-        }
-
         pw.print(prefix); pw.print("sSessionIdCounter="); pw.println(sSessionIdCounter);
         final int size = mSessions.size();
         if (size == 0) {
@@ -498,32 +298,10 @@
 
     @Override
     public String toString() {
-        return "AutoFillManagerServiceImpl: [userId=" + mUserId + ", uid=" + mUid
+        return "AutoFillManagerServiceImpl: [userId=" + mUserId
                 + ", component=" + mComponentName + "]";
     }
 
-    private static final class QueuedRequest {
-        final IBinder activityToken;
-        final AutoFillId autoFillId;
-        final Rect bounds;
-        final int flags;
-
-        QueuedRequest(IBinder activityToken, AutoFillId autoFillId, Rect bounds, int flags) {
-            this.activityToken = activityToken;
-            this.autoFillId = autoFillId;
-            this.bounds = bounds;
-            this.flags = flags;
-        }
-
-        @Override
-        public String toString() {
-            if (!DEBUG) return super.toString();
-
-            return "QueuedRequest: [flags=" + flags + ", token=" + activityToken
-                    + ", id=" + autoFillId + ", bounds=" + bounds;
-        }
-    }
-
     /**
      * State for a given view with a AutoFillId.
      *
@@ -553,12 +331,7 @@
          * 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();
         }
 
@@ -608,39 +381,27 @@
     // - On all authentication scenarios.
     // - When user does not interact back after a while.
     // - When service is unbound.
-    final class Session implements ViewState.Listener {
+    final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
+            AutoFillUI.AutoFillUiCallback {
+        private final int mId;
 
-        private final AutoFillUI mUi;
         private final WeakReference<IBinder> mActivityToken;
 
         @GuardedBy("mLock")
         private final Map<AutoFillId, ViewState> mViewStates = new ArrayMap<>();
+
         @GuardedBy("mLock")
         @Nullable
         private ViewState mCurrentViewState;
 
         private IAutoFillAppCallback mAppCallback;
 
+        @GuardedBy("mLock")
+        RemoteFillService mRemoteFillService;
+
         // TODO(b/33197203): Get a response per view instead of per activity.
         @GuardedBy("mLock")
         private FillResponse mCurrentResponse;
-        @GuardedBy("mLock")
-        private FillResponse mResponseRequiringAuth;
-        @GuardedBy("mLock")
-        private Dataset mDatasetRequiringAuth;
-
-        /**
-         * Used to auto-fill the activity directly when the FillCallback.onResponse() is called as
-         * the result of a successful user authentication on service's side.
-         */
-        @GuardedBy("mLock")
-        private boolean mAutoFillDirectly;
-
-        /**
-         * Used to remember which {@link Dataset} filled the session.
-         */
-        @GuardedBy("mLock")
-        private Dataset mAutoFilledDataset;
 
         /**
          * Map of ids that must be updated so they're send to {@link #onSaveLocked()}.
@@ -652,168 +413,77 @@
          * Assist structure sent by the app; it will be updated (sanitized, change values for save)
          * before sent to {@link AutoFillService}.
          */
+        @GuardedBy("mLock")
         private AssistStructure mStructure;
 
-        // TODO(b/33197203): use handler to handle results?
-        // TODO(b/33197203): handle all callback methods and/or cancelation?
-        private IFingerprintServiceReceiver mServiceReceiver =
-                new IFingerprintServiceReceiver.Stub() {
+        private Session(Context context, IBinder activityToken, int id) {
+            mActivityToken = new WeakReference<>(activityToken);
+            mRemoteFillService = new RemoteFillService(context, mComponent, mUserId, this);
+            mId = id;
+        }
 
-            @Override
-            public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
-                if (DEBUG) Slog.d(TAG, "onEnrollResult()");
+        // FillServiceCallbacks
+        @Override
+        public void onFillRequestSuccess(FillResponse response) {
+            // TODO(b/33197203): add MetricsLogger call
+            if (response == null) {
+                destroy();
+                return;
             }
-
-            @Override
-            public void onAcquired(long deviceId, int acquiredInfo, int vendorCode) {
-                if (DEBUG) Slog.d(TAG, "onAcquired()");
+            synchronized (mLock) {
+                processResponseLocked(response);
             }
+        }
 
-            @Override
-            public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
-                if (DEBUG) Slog.d(TAG, "onAuthenticationSucceeded(): " + fp.getGroupId());
+        // FillServiceCallbacks
+        @Override
+        public void onFillRequestFailure(CharSequence message) {
+            // TODO(b/33197203): add MetricsLogger call
+            getUiForShowing().showError(message);
+            destroy();
+        }
 
-                // First, check what was authenticated, a response or a dataset.
-                // Then, decide how to handle it:
-                // - If service provided data, handle them directly.
-                // - Otherwise, notify service.
+        // FillServiceCallbacks
+        @Override
+        public void onSaveRequestSuccess() {
+            // TODO: Implement
+        }
 
-                mAutoFillDirectly = true;
+        // FillServiceCallbacks
+        @Override
+        public void onSaveRequestFailure(CharSequence message) {
+            // TODO(b/33197203): add MetricsLogger call
+            getUiForShowing().showError(message);
+            destroy();
+        }
 
-                if (mDatasetRequiringAuth != null) {
-                    if (mDatasetRequiringAuth.isEmpty()) {
-                        notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
-                                FLAG_AUTHENTICATION_SUCCESS);
-                    } else {
-                        autoFillApp(mDatasetRequiringAuth);
-                    }
-                } else if (mResponseRequiringAuth != null) {
-                    final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
-                    if (datasets.isEmpty()) {
-                        notifyResponseAuthenticationResult(mResponseRequiringAuth.getExtras(),
-                                FLAG_AUTHENTICATION_SUCCESS);
-                    } else {
-                        showResponseLocked(mResponseRequiringAuth, true);
-                    }
-                } else {
-                    Slog.w(TAG, "onAuthenticationSucceeded(): no response or dataset");
-                }
+        // FillServiceCallbacks
+        @Override
+        public void authenticate(IntentSender intent, Intent fillInIntent) {
+            startAuthIntent(intent, fillInIntent);
+        }
 
-                mUi.dismissFingerprintRequest(true);
+        // FillServiceCallbacks
+        @Override
+        public void onServiceDied(RemoteFillService service) {
+            // TODO: Implement
+        }
+
+        // AutoFillUiCallback
+        @Override
+        public void fill(Dataset dataset) {
+            autoFill(dataset);
+        }
+
+        // AutoFillUiCallback
+        @Override
+        public void save() {
+            synchronized (mLock) {
+                onSaveLocked();
             }
-
-            @Override
-            public void onAuthenticationFailed(long deviceId) {
-                if (DEBUG) Slog.d(TAG, "onAuthenticationFailed()");
-                // Do nothing - onError() will be called after a few failures...
-            }
-
-            @Override
-            public void onError(long deviceId, int error, int vendorCode) {
-                if (DEBUG) Slog.d(TAG, "onError()");
-
-                // Notify service so it can fallback to its own authentication
-                if (mDatasetRequiringAuth != null) {
-                    notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
-                            FLAG_AUTHENTICATION_ERROR);
-                } else if (mResponseRequiringAuth != null) {
-                    notifyResponseAuthenticationResult(mResponseRequiringAuth.getExtras(),
-                            FLAG_AUTHENTICATION_ERROR);
-                } else {
-                    Slog.w(TAG, "onError(): no response or dataset");
-                }
-
-                mUi.dismissFingerprintRequest(false);
-            }
-
-            @Override
-            public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) {
-                if (DEBUG) Slog.d(TAG, "onRemoved()");
-            }
-
-            @Override
-            public void onEnumerated(long deviceId, int fingerId, int groupId, int remaining) {
-                if (DEBUG) Slog.d(TAG, "onEnumerated()");
-            }
-        };
-
-        private IAutoFillServerCallback mServerCallback = new IAutoFillServerCallback.Stub() {
-            @Override
-            public void showResponse(FillResponse response) {
-                // TODO(b/33197203): add MetricsLogger call
-                if (response == null) {
-                    if (DEBUG) Slog.d(TAG, "showResponse(): null response");
-
-                    removeSelf();
-                    return;
-                }
-
-                synchronized (mLock) {
-                    showResponseLocked(response, response.isAuthRequired());
-                }
-            }
-
-            @Override
-            public void showError(CharSequence message) {
-                // TODO(b/33197203): add MetricsLogger call
-                if (DEBUG) Slog.d(TAG, "showError(): " + message);
-
-                mUi.showError(message);
-                removeSelf();
-            }
-
-            @Override
-            public void onSaved() {
-                // TODO(b/33197203): add MetricsLogger call
-                if (DEBUG) Slog.d(TAG, "onSaved()");
-
-                removeSelf();
-            }
-
-            @Override
-            public void unlockFillResponse(int flags) {
-                // TODO(b/33197203): add proper MetricsLogger calls?
-                if (DEBUG) Log.d(TAG, "unlockUser(): flags=" + flags);
-
-                synchronized (mLock) {
-                    if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
-                        if (mResponseRequiringAuth == null) {
-                            Log.wtf(TAG, "unlockUser(): no mResponseRequiringAuth on flags "
-                                    + flags);
-                            removeSelf();
-                            return;
-                        }
-                        final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
-                        if (datasets.isEmpty()) {
-                            Log.w(TAG, "unlockUser(): no dataset on previous response: "
-                                    + mResponseRequiringAuth);
-                            removeSelf();
-                            return;
-                        }
-                        mAutoFillDirectly = true;
-                        showResponseLocked(mResponseRequiringAuth, false);
-                    }
-                    // TODO(b/33197203): show UI error on authentication failure?
-                    // Or let service handle it?
-                }
-            }
-
-            @Override
-            public void unlockDataset(Dataset dataset, int flags) {
-                // TODO(b/33197203): add proper MetricsLogger calls?
-                if (DEBUG) Log.d(TAG, "unlockDataset(): dataset=" + dataset + ", flags=" + flags);
-
-                if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
-                    autoFillApp(dataset != null ? dataset : mDatasetRequiringAuth);
-                    return;
-                }
-            }
-        };
-
-        final int mId;
+        }
 
         private Session(int id, IBinder activityToken) {
-            mUi = new AutoFillUI(mContext, this);
             mId = id;
             mActivityToken = new WeakReference<>(activityToken);
         }
@@ -826,7 +496,7 @@
 
           // TODO(b/33197203): ignore if not part of the savable ids.
           if (mUpdatedValues == null) {
-                // Lazy intializes it
+                // Lazy initializes it
                 mUpdatedValues = new HashMap<>();
             }
             mUpdatedValues.put(id, newValue);
@@ -847,32 +517,10 @@
             if (mUpdatedValues == null || mUpdatedValues.isEmpty()) {
                 // Nothing changed
                 if (DEBUG) Slog.d(TAG, "onSave(): when no changes, comes no responsibilities");
-
                 return;
             }
 
             // TODO(b/33197203): make sure the extras are tested by CTS
-            final Bundle responseExtras = mCurrentResponse == null ? null
-                    : mCurrentResponse.getExtras();
-            final Bundle datasetExtras = mAutoFilledDataset == null ? null
-                    : mAutoFilledDataset.getExtras();
-            final Bundle extras = (responseExtras == null && datasetExtras == null)
-                    ? null : new Bundle();
-            if (responseExtras != null) {
-                if (DEBUG) {
-                    Slog.d(TAG, "response extras on save extras: "
-                            + bundleToString(responseExtras));
-                }
-                extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras);
-            }
-            if (datasetExtras != null) {
-                if (DEBUG) {
-                    Slog.d(TAG, "dataset extras on save extras: " + bundleToString(datasetExtras));
-                }
-                extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
-            }
-
-
             for (Entry<AutoFillId, AutoFillValue> entry : mUpdatedValues.entrySet()) {
                 final AutoFillId id = entry.getKey();
                 final ViewNode node = findViewNodeByIdLocked(id);
@@ -891,20 +539,24 @@
                 Slog.v(TAG, "Dumping " + mStructure + " before calling service.save()");
                 mStructure.dump();
             }
-            try {
-                mService.save(mStructure, mServerCallback, extras);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Error calling save on service: " + e);
-                // TODO(b/33197203): invalidate session?
-            }
+
+            mRemoteFillService.onSaveRequest(mStructure, mCurrentResponse.getExtras());
         }
 
-        void setAppCallbackLocked(IBinder appBinder) {
+        void onApplicationDataAvailable(AssistStructure structure, IBinder appCallback) {
+            setAppCallback(appCallback);
+            mStructure = structure;
+            // TODO(b/33197203): Need to pipe the bundle
+            mRemoteFillService.onFillRequest(structure, null);
+        }
+
+        private void setAppCallback(IBinder appBinder) {
             try {
                 appBinder.linkToDeath(() -> {
                     if (DEBUG) Slog.d(TAG, "app callback died");
                     // TODO(b/33197203): more cleanup here?
                     mAppCallback = null;
+                    destroy();
                 }, 0);
             } catch (RemoteException e) {
                 Slog.w(TAG, "linkToDeath() failed: " + e);
@@ -957,78 +609,105 @@
                     filterText = text.toString();
                 }
             }
-            mUi.showFillUi(viewState, response.getDatasets(), bounds, filterText);
+            getUiForShowing().showFillUi(viewState, response.getDatasets(), bounds, filterText);
         }
 
-        private void showResponseLocked(FillResponse response, boolean authRequired) {
-            if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly
-                    + ", authRequired=" + authRequired +"):" + response);
+        private void processResponseLocked(FillResponse response) {
+            if (DEBUG) Slog.d(TAG, "showResponse(authRequired="
+                    + response.getAuthentication() +"):" + response);
 
-            if (mAutoFillDirectly && response != null) {
-                final List<Dataset> datasets = response.getDatasets();
-                if (datasets.size() == 1) {
-                    // User authenticated and provider returned just 1 dataset - auto-fill it now!
-                    final Dataset dataset = datasets.get(0);
-                    if (DEBUG) Slog.d(TAG, "auto-filling directly from auth: " + dataset);
+            // TODO(b/33197203): add MetricsLogger calls
 
-                    autoFillApp(dataset);
-                    return;
-                }
-            }
+            mCurrentResponse = response;
 
-            if (!authRequired) {
-                // TODO(b/33197203): add MetricsLogger call
-                mCurrentResponse = response;
+            if (mCurrentResponse.getAuthentication() != null) {
+                // ...or handle authentication.
+                Intent fillInIntent = createAuthFillInIntent(response.getId(), mStructure,
+                        new Bundle(), new FillCallback(new IFillCallback.Stub() {
+                            @Override
+                            public void onCancellable(ICancellationSignal cancellation) {
+                                // TODO(b/33197203): Handle cancellation
+                            }
+
+                            @Override
+                            public void onSuccess(FillResponse response) {
+                                mCurrentResponse = createAuthenticatedResponse(
+                                        mCurrentResponse, response);
+                                processResponseLocked(mCurrentResponse);
+                            }
+
+                            @Override
+                            public void onFailure(CharSequence message) {
+                                getUiForShowing().showError(message);
+                                destroy();
+                            }
+                        }));
+
+                 getUiForShowing().showFillResponseAuthRequest(
+                         mCurrentResponse.getAuthentication(), fillInIntent);
+            } else {
                 // TODO(b/33197203): Consider using mCurrentResponse, depends on partitioning design
                 if (mCurrentViewState != null) {
                     mCurrentViewState.setResponse(mCurrentResponse);
                 }
-                return;
             }
-
-            // Handles response that requires authentication.
-            // TODO(b/33197203): add MetricsLogger call, including if fingerprint requested
-
-            mResponseRequiringAuth = response;
-            final boolean requiresFingerprint = response.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.
-                // Start scanning for the fingerprint.
-                scanFingerprint(response.getCryptoObjectOpId());
-            }
-            // Displays the message asking the user to tap (or fingerprint) for AutoFill.
-            mUi.showFillResponseAuthenticationRequest(requiresFingerprint,
-                    response.getExtras(), response.getFlags());
         }
 
         void autoFill(Dataset dataset) {
             synchronized (mLock) {
                 // Autofill it directly...
-                if (!dataset.isAuthRequired()) {
+                if (dataset.getAuthentication() == null) {
                     autoFillApp(dataset);
+                    // For now just show this on every fill
+                    getUiForShowing().showSaveUi();
                     return;
                 }
 
                 // ...or handle authentication.
-
-                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.
-                    // Start scanning for the fingerprint.
-                    scanFingerprint(dataset.getCryptoObjectOpId());
-                    // Displays the message asking the user to tap (or fingerprint) for AutoFill.
-                    mUi.showDatasetFingerprintAuthenticationRequest(dataset);
-                } else {
-                    try {
-                        mService.authenticateDataset(dataset.getExtras(),
-                                FLAG_AUTHENTICATION_REQUESTED);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Error authenticating dataset: " + e);
+                Intent fillInIntent = createAuthFillInIntent(dataset.getId(), mStructure,
+                        new Bundle(), new FillCallback(new IFillCallback.Stub() {
+                    @Override
+                    public void onCancellable(ICancellationSignal cancellation) {
+                        // TODO(b/33197203): Handle cancellation
                     }
-                }
+
+                    @Override
+                    public void onSuccess(FillResponse response) {
+                        mCurrentResponse = createAuthenticatedResponse(
+                                mCurrentResponse, response);
+                        Dataset augmentedDataset = Helper.findDatasetById(dataset.getId(),
+                                mCurrentResponse);
+                        if (augmentedDataset != null) {
+                            autoFill(augmentedDataset);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(CharSequence message) {
+                        getUiForShowing().showError(message);
+                        destroy();
+                    }
+                }));
+
+                startAuthIntent(dataset.getAuthentication(), fillInIntent);
+            }
+        }
+
+        private Intent createAuthFillInIntent(String itemId, AssistStructure structure,
+                Bundle extras, FillCallback fillCallback) {
+            Intent fillInIntent = new Intent();
+            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ITEM_ID, itemId);
+            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ASSIST_STRUCTURE, structure);
+            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_EXTRAS, extras);
+            fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_CALLBACK, fillCallback);
+            return fillInIntent;
+        }
+
+        private void startAuthIntent(IntentSender intent, Intent fillInIntent) {
+            try {
+                mAppCallback.startIntentSender(intent, fillInIntent);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error launching auth intent", e);
             }
         }
 
@@ -1036,11 +715,6 @@
             pw.print(prefix); pw.print("mId: "); pw.println(mId);
             pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken.get());
             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 + "  ";
@@ -1057,42 +731,8 @@
             } else {
                 pw.println("null");
             }
-        }
 
-        /**
-         * 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);
-                }
-            }
+            mRemoteFillService.dump(prefix, pw);
         }
 
         void autoFillApp(Dataset dataset) {
@@ -1106,32 +746,9 @@
             }
         }
 
-        /**
-         * Called by UI to trigger a save request to the service.
-         */
-        void requestSave() {
-            synchronized (mLock) {
-                onSaveLocked();
-            }
-        }
-
-        private void scanFingerprint(long opId) {
-            // TODO(b/33197203): add MetricsLogger call
-            if (DEBUG) Slog.d(TAG, "Starting fingerprint scan for op id: " + opId);
-
-            // TODO(b/33197203): since we're clearing the AutoFillService's identity, make sure
-            // this method is only called at the proper times, otherwise a malicious provider could
-            // keep the callback refence to bypass the check
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // TODO(b/33197203): set a timeout?
-                mFingerprintService.authenticate(mAuthToken, opId, mUserId, mServiceReceiver, 0,
-                        null);
-            } catch (RemoteException e) {
-                // Local call, shouldn't happen.
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+        private AutoFillUI getUiForShowing() {
+            mUi.setCallback(this, mId);
+            return mUi;
         }
 
         private ViewNode findViewNodeByIdLocked(AutoFillId id) {
@@ -1167,10 +784,89 @@
             return null;
         }
 
-        private void removeSelf() {
+        private void destroy() {
             synchronized (mLock) {
+                mRemoteFillService.destroy();
+                mUi.hideAll();
+                mUi.setCallback(null, 0);
                 removeSessionLocked(mId);
             }
         }
+
+        /**
+         * Creates a response from the {@code original} and an {@code update} by
+         * replacing all items that needed authentication (response or datasets)
+         * with their updated version if the latter does not need authentication.
+         * New datasets that don't require auth are appended.
+         *
+         * @param original The original response requiring auth at some level.
+         * @param update An updated response with auth not needed anymore at some level.
+         * @return A new response with updated items where auth is not needed anymore.
+         */
+        // TODO(b/33197203) Unit test
+        FillResponse createAuthenticatedResponse(FillResponse original, FillResponse update) {
+            // Can update only if ids match
+            if (!original.getId().equals(update.getId())) {
+                return original;
+            }
+
+            // If the original required auth and the update doesn't, the update wins
+            // but only if none of the update's datasets requires authentication.
+            if (original.getAuthentication() != null && update.getAuthentication() == null) {
+                ArraySet<Dataset> updateDatasets = update.getDatasets();
+                final int udpateDatasetCount = updateDatasets.size();
+                for (int i = 0; i < udpateDatasetCount; i++) {
+                    Dataset updateDataset = updateDatasets.valueAt(i);
+                    if (updateDataset.getAuthentication() != null) {
+                        return original;
+                    }
+                }
+                return update;
+            }
+
+            // If no auth on response level we create a response that has all
+            // datasets from the original with the ones that required auth but
+            // not anymore updated and new ones not requiring auth appended.
+
+            // The update shouldn't require auth
+            if (update.getAuthentication() != null) {
+                return original;
+            }
+
+            final FillResponse.Builder builder = new FillResponse.Builder(original.getId());
+
+            // Update existing datasets
+            final ArraySet<Dataset> origDatasets = original.getDatasets();
+            final int origDatasetCount = origDatasets.size();
+            for (int i = 0; i < origDatasetCount; i++) {
+                Dataset origDataset = origDatasets.valueAt(i);
+                ArraySet<Dataset> updateDatasets = update.getDatasets();
+                final int updateDatasetCount = updateDatasets.size();
+                for (int j = 0; j < updateDatasetCount; j++) {
+                    Dataset updateDataset = updateDatasets.valueAt(j);
+                    if (origDataset.getId().equals(updateDataset.getId())) {
+                        // The update shouldn't require auth
+                        if (updateDataset.getAuthentication() == null) {
+                            origDataset = updateDataset;
+                            updateDatasets.removeAt(j);
+                        }
+                        break;
+                    }
+                }
+                builder.addDataset(origDataset);
+            }
+
+            // Add new datasets
+            final ArraySet<Dataset> updateDatasets = update.getDatasets();
+            final int updateDatasetCount = updateDatasets.size();
+            for (int i = 0; i < updateDatasetCount; i++) {
+                final Dataset updateDataset = updateDatasets.valueAt(i);
+                builder.addDataset(updateDataset);
+            }
+
+            // For now no extras and savable id updates.
+
+            return builder.build();
+        }
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index 76c2916..da54d85 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -18,10 +18,7 @@
 
 import static com.android.server.autofill.Helper.DEBUG;
 
-import android.annotation.Nullable;
-import android.app.Activity;
 import android.app.Notification;
-import android.app.Notification.Action;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
@@ -29,12 +26,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.graphics.Rect;
-import android.graphics.PixelFormat;
 import android.os.Binder;
-import android.os.Bundle;
+import android.util.ArraySet;
 import android.util.Slog;
-import android.view.autofill.AutoFillId;
 import android.view.autofill.Dataset;
 import android.view.autofill.FillResponse;
 import android.view.Gravity;
@@ -44,27 +40,28 @@
 import android.view.WindowManager.LayoutParams;
 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;
-import java.util.List;
 
 /**
  * Handles all auto-fill related UI tasks.
  */
 // TODO(b/33197203): document exactly what once the auto-fill bar is implemented
 final class AutoFillUI {
-
     private static final String TAG = "AutoFillUI";
 
+    private static final String EXTRA_AUTH_INTENT_SENDER =
+            "com.android.server.autofill.extra.AUTH_INTENT_SENDER";
+    private static final String EXTRA_AUTH_FILL_IN_INTENT =
+            "com.android.server.autofill.extra.AUTH_FILL_IN_INTENT";
+
     private final Context mContext;
-    private final Session mSession;
     private final WindowManager mWm;
 
+    // TODO(b/33197203) Fix locking - some state requires lock and some not - requires refactoring
+
     // Fill UI variables
     private AnchoredWindow mFillWindow;
     private DatasetPicker mFillView;
@@ -72,21 +69,39 @@
     private Rect mBounds;
     private String mFilterText;
 
+    private AutoFillUiCallback mCallback;
+    private int mClientId;
+
+    public interface AutoFillUiCallback {
+        void authenticate(IntentSender intent, Intent fillInIntent);
+        void fill(Dataset dataset);
+        void save();
+    }
+
     /**
      * Custom snackbar UI used for saving autofill or other informational messages.
      */
     private View mSnackbar;
 
-    AutoFillUI(Context context, Session session) {
+    AutoFillUI(Context context) {
         mContext = context;
-        mSession = session;
         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
     }
 
+    void setCallback(AutoFillUiCallback callback, int clientId) {
+        hideAll();
+        mCallback = callback;
+        mClientId = clientId;
+    }
+
     /**
      * Displays an error message to the user.
      */
     void showError(CharSequence message) {
+        if (!hasCallback()) {
+            return;
+        }
+        hideAll();
         // TODO(b/33197203): proper implementation
         UiThread.getHandler().runWithScissors(() -> {
             Toast.makeText(mContext, "AutoFill error: " + message, Toast.LENGTH_LONG).show();
@@ -95,21 +110,17 @@
 
     /**
      * Hides the fill UI.
-     * 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}).
      */
     void hideFillUi() {
         UiThread.getHandler().runWithScissors(() -> {
-            hideFillUiLocked();
+            hideFillUiUiThread();
         }, 0);
     }
 
-    // Must be called in inside UI Thread
-    private void hideFillUiLocked() {
+    @android.annotation.UiThread
+    private void hideFillUiUiThread() {
         if (mFillWindow != null) {
-            if (DEBUG) Slog.d(TAG, "hideFillUiLocked(): hide" + mFillWindow);
-
+            if (DEBUG) Slog.d(TAG, "hideFillUiUiThread(): hide" + mFillWindow);
             mFillWindow.hide();
         }
 
@@ -120,7 +131,6 @@
         mFillWindow = null;
     }
 
-
     /**
      * Shows the fill UI, removing the previous fill UI if the has changed.
      *
@@ -129,26 +139,31 @@
      * @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,
+    void showFillUi(ViewState viewState, ArraySet<Dataset> datasets, Rect bounds,
             String filterText) {
+        if (!hasCallback()) {
+            return;
+        }
+        hideAll();
         UiThread.getHandler().runWithScissors(() -> {
             if (mViewState != viewState) {
-                // new
-                hideFillUi();
-
                 mViewState = viewState;
 
                 mFillView = new DatasetPicker(mContext, datasets,
                         (dataset) -> {
-                            mSession.autoFillApp(dataset);
+                            final AutoFillUiCallback callback;
+                            synchronized (mLock) {
+                                callback = mCallback;
+                            }
+                            callback.fill(dataset);
                             hideFillUi();
                         });
+                // TODO: No magical numbers
                 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;
@@ -170,27 +185,14 @@
      * <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(boolean usesFingerprint,
-            Bundle extras, int flags) {
-        // TODO(b/33197203): proper implementation
-        showAuthNotification(usesFingerprint, extras, flags);
-    }
-
-    /**
-     * Shows an UI affordance asking indicating that user action is required before a
-     * {@link Dataset} can be used.
-     *
-     * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint to
-     * autofill".
-     */
-    void showDatasetFingerprintAuthenticationRequest(Dataset dataset) {
-        if (DEBUG) Slog.d(TAG, "showDatasetAuthenticationRequest(): dataset=" + dataset);
-
-        // TODO(b/33197203): proper implementation (either pop up a fingerprint dialog or replace
-        // the auto-fill bar with a new message.
+    void showFillResponseAuthRequest(IntentSender intent, Intent fillInIntent) {
+        if (!hasCallback()) {
+            return;
+        }
+        hideAll();
         UiThread.getHandler().runWithScissors(() -> {
-            Toast.makeText(mContext, "AutoFill: press fingerprint to unlock " + dataset.getName(),
-                    Toast.LENGTH_LONG).show();
+            // TODO(b/33197203): proper implementation
+            showFillResponseAuthUiUiThread(intent, fillInIntent);
         }, 0);
     }
 
@@ -198,46 +200,36 @@
      * Shows the UI asking the user to save for auto-fill.
      */
     void showSaveUi() {
-        showSnackbar(new SavePrompt(mContext, new SavePrompt.OnSaveListener() {
-            @Override
-            public void onSaveClick() {
-                hideSnackbar();
-
-                // TODO(b/33197203): add MetricsLogger call
-                mSession.requestSave();
-            }
-            @Override
-            public void onCancelClick() {
-                hideSnackbar();
-            }
-        }));
-    }
-
-    /**
-     * Called by service after the user user the fingerprint sensors to authenticate.
-     */
-    void dismissFingerprintRequest(boolean success) {
-        if (DEBUG) Slog.d(TAG, "dismissFingerprintRequest(): ok=" + success);
-
-        dismissAuthNotification();
-
-        if (!success) {
-            // TODO(b/33197203): proper implementation (snack bar / i18n string)
-            UiThread.getHandler().runWithScissors(() -> {
-                Toast.makeText(mContext, "AutoFill: fingerprint failed", Toast.LENGTH_LONG).show();
-            }, 0);
+        if (!hasCallback()) {
+            return;
         }
+        hideAll();
+        UiThread.getHandler().runWithScissors(() -> {
+            showSnackbarUiThread(new SavePrompt(mContext,
+                    new SavePrompt.OnSaveListener() {
+                @Override
+                public void onSaveClick() {
+                    hideSnackbarUiThread();
+                    // TODO(b/33197203): add MetricsLogger call
+                    mCallback.save();
+                }
+
+                @Override
+                public void onCancelClick() {
+                    hideSnackbarUiThread();
+                }
+            }));
+        }, 0);
     }
 
     /**
-     * Closes all UI affordances.
+     * Hides all UI affordances.
      */
-    void closeAll() {
-        if (DEBUG) Slog.d(TAG, "closeAll()");
-
+    void hideAll() {
         UiThread.getHandler().runWithScissors(() -> {
-            hideSnackbarLocked();
-            hideFillUiLocked();
+            hideSnackbarUiThread();
+            hideFillUiUiThread();
+            hideFillResponseAuthUiUiThread();
         }, 0);
     }
 
@@ -245,7 +237,7 @@
         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("mClientId: "); pw.println(mClientId);
         pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
         pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState);
         pw.print(prefix); pw.print("mBounds: "); pw.println(mBounds);
@@ -254,7 +246,7 @@
 
     //similar to a snackbar, but can be a bit custom since it is more than just text. This will
     //allow two buttons for saving or not saving the autofill for instance as well.
-    private void showSnackbar(View snackBar) {
+    private void showSnackbarUiThread(View snackBar) {
         final LayoutParams params = new LayoutParams();
         params.setTitle("AutoFill Save");
         params.type = LayoutParams.TYPE_PHONE; // TODO(b/33197203) use app window token
@@ -274,20 +266,19 @@
         }, 0);
     }
 
-    private void hideSnackbar() {
-        UiThread.getHandler().runWithScissors(() -> {
-            hideSnackbarLocked();
-        }, 0);
-    }
-
-    // Must be called in inside UI Thread
-    private void hideSnackbarLocked() {
+    private void hideSnackbarUiThread() {
         if (mSnackbar != null) {
             mWm.removeView(mSnackbar);
             mSnackbar = null;
         }
     }
 
+    private boolean hasCallback() {
+        synchronized (mLock) {
+            return mCallback != null;
+        }
+    }
+
     /////////////////////////////////////////////////////////////////////////////////
     // TODO(b/33197203): temporary code using a notification to request auto-fill. //
     // Will be removed once UX decide the right way to present it to the user.     //
@@ -297,25 +288,13 @@
     private static final String NOTIFICATION_AUTO_FILL_INTENT =
             "com.android.internal.autofill.action.REQUEST_AUTOFILL";
 
-    // Extras used in the notification intents
-    private static final String EXTRA_USER_ID = "user_id";
-    private static final String EXTRA_NOTIFICATION_TYPE = "notification_type";
-    private static final String EXTRA_SESSION_ID = "session_id";
-    private static final String EXTRA_FILL_RESPONSE = "fill_response";
-    private static final String EXTRA_DATASET = "dataset";
-    private static final String EXTRA_AUTH_REQUIRED_EXTRAS = "auth_required_extras";
-    private static final String EXTRA_FLAGS = "flags";
-
-    private static final String TYPE_OPTIONS = "options";
-    private static final String TYPE_AUTH_RESPONSE = "auth_response";
-
     private BroadcastReceiver mNotificationReceiver;
     private final Object mLock = new Object();
 
     // Hack used to generate unique pending intents
     static int sResultCode = 0;
 
-    private void setNotificationListener() {
+    private void ensureNotificationListener() {
         synchronized (mLock) {
             if (mNotificationReceiver == null) {
                 mNotificationReceiver = new NotificationReceiver();
@@ -328,83 +307,58 @@
     final class NotificationReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
-            if (type == null) {
-                Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
-                return;
-            }
-            final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
-            final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
-
-            if (DEBUG) Slog.d(TAG, "Notification received: type=" + type
-                    + ", sessionId=" + mSession.mId);
+            final AutoFillUiCallback callback;
             synchronized (mLock) {
-                switch (type) {
-                    case TYPE_AUTH_RESPONSE:
-                        mSession.notifyResponseAuthenticationResult(
-                                intent.getBundleExtra(EXTRA_AUTH_REQUIRED_EXTRAS), flags);
-                        break;
-                    default: {
-                        Slog.w(TAG, "Unknown notification type: " + type);
-                    }
-                }
+                callback = mCallback;
+            }
+            if (callback != null) {
+                IntentSender intentSender = intent.getParcelableExtra(EXTRA_AUTH_INTENT_SENDER);
+                Intent fillInIntent = intent.getParcelableExtra(EXTRA_AUTH_FILL_IN_INTENT);
+                callback.authenticate(intentSender, fillInIntent);
             }
             collapseStatusBar();
         }
     }
 
-    private static Intent newNotificationIntent(String type) {
-        final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
-        intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
-        return intent;
-    }
-
-    private void showAuthNotification(boolean usesFingerprint,
-            Bundle extras, int flags) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            showAuthNotificationAsSystem(usesFingerprint, extras, flags);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private void showAuthNotificationAsSystem(
-            boolean usesFingerprint, Bundle extras, int flags) {
+    @android.annotation.UiThread
+    private void showFillResponseAuthUiUiThread(IntentSender intent, Intent fillInIntent) {
         final String title = "AutoFill Authentication";
         final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n");
 
-        final Intent authIntent = newNotificationIntent(TYPE_AUTH_RESPONSE);
-        if (extras != null) {
-            authIntent.putExtra(EXTRA_AUTH_REQUIRED_EXTRAS, extras);
-        }
-        if (flags != 0) {
-            authIntent.putExtra(EXTRA_FLAGS, flags);
-        }
-        final PendingIntent authPendingIntent = PendingIntent.getBroadcast(mContext, ++sResultCode,
-                authIntent, PendingIntent.FLAG_ONE_SHOT);
+        final Intent authIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
+        authIntent.putExtra(EXTRA_AUTH_INTENT_SENDER, intent);
+        authIntent.putExtra(EXTRA_AUTH_FILL_IN_INTENT, fillInIntent);
 
-        if (usesFingerprint) {
-            subTitle.append("But kindly accepts your fingerprint instead"
-                    + "\n(tap fingerprint sensor to trigger it)");
+        final PendingIntent authPendingIntent = PendingIntent.getBroadcast(
+                mContext, ++sResultCode, authIntent, PendingIntent.FLAG_ONE_SHOT);
 
-        } else {
-            subTitle.append("Tap notification to launch its authentication UI.");
-        }
+        subTitle.append("Tap notification to launch its authentication UI.");
 
         final Notification.Builder notification = newNotificationBuilder()
                 .setAutoCancel(true)
                 .setOngoing(false)
                 .setContentTitle(title)
-                .setStyle(new Notification.BigTextStyle().bigText(subTitle.toString()));
-        if (authPendingIntent != null) {
-            notification.setContentIntent(authPendingIntent);
+                .setStyle(new Notification.BigTextStyle().bigText(subTitle.toString()))
+                .setContentIntent(authPendingIntent);
+
+        ensureNotificationListener();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            NotificationManager.from(mContext).notify(mClientId, notification.build());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        NotificationManager.from(mContext).notify(mSession.mId, notification.build());
     }
 
-    private void dismissAuthNotification() {
-        NotificationManager.from(mContext).cancel(mSession.mId);
+    @android.annotation.UiThread
+    private void hideFillResponseAuthUiUiThread() {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            NotificationManager.from(mContext).cancel(mClientId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     private Notification.Builder newNotificationBuilder() {
diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
index db516d8..8212cf1 100644
--- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java
+++ b/services/autofill/java/com/android/server/autofill/DatasetPicker.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.graphics.Color;
+import android.util.ArraySet;
 import android.view.autofill.Dataset;
 import android.view.View;
 import android.view.ViewGroup;
@@ -44,7 +45,7 @@
 
     private final Listener mListener;
 
-    DatasetPicker(Context context, List<Dataset> datasets, Listener listener) {
+    DatasetPicker(Context context, ArraySet<Dataset> datasets, Listener listener) {
         super(context);
         mListener = listener;
 
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 9171dac..7fff410 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -17,6 +17,9 @@
 package com.android.server.autofill;
 
 import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.autofill.Dataset;
+import android.view.autofill.FillResponse;
 
 import java.util.Arrays;
 import java.util.Objects;
@@ -55,4 +58,26 @@
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
+
+    /**
+     * Finds a data set by id in a response.
+     *
+     * @param id The dataset id.
+     * @param response The response to search.
+     * @return The dataset if found or null.
+     */
+    static Dataset findDatasetById(String id, FillResponse response) {
+        ArraySet<Dataset> datasets = response.getDatasets();
+        if (datasets == null || datasets.isEmpty()) {
+            return null;
+        }
+        final int datasetCount = datasets.size();
+        for (int i = 0; i < datasetCount; i++) {
+            Dataset dataset = datasets.valueAt(i);
+            if (dataset.getId().equals(id)) {
+                return dataset;
+            }
+        }
+        return null;
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
new file mode 100644
index 0000000..c070f77
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -0,0 +1,521 @@
+/*
+ * 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 com.android.server.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.assist.AssistStructure;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.ICancellationSignal;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.autofill.AutoFillService;
+import android.service.autofill.IAutoFillService;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.ISaveCallback;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.FillResponse;
+import com.android.internal.os.HandlerCaller;
+import com.android.server.FgThread;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * This class represents a remote fill service. It abstracts away the binding
+ * and unbinding from the remote implementation.
+ *
+ * <p>Clients can call methods of this class without worrying about when and
+ * how to bind/unbind/timeout. All state of this class is modified on a handler
+ * thread.
+ */
+final class RemoteFillService implements DeathRecipient {
+    private static final String LOG_TAG = "RemoteFillService";
+
+    private static final boolean DEBUG = Helper.DEBUG;
+
+    // How long after the last interaction with the service we would unbind
+    private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.MINUTE_IN_MILLIS;
+
+    private final Context mContext;
+
+    private final ComponentName mComponentName;
+
+    private final Intent mIntent;
+
+    private final FillServiceCallbacks mCallbacks;
+
+    private final int mUserId;
+
+    private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
+
+    private final HandlerCaller mHandler;
+
+    private IAutoFillService mAutoFillService;
+
+    private boolean mBinding;
+
+    private boolean mDestroyed;
+
+    private boolean mServiceDied;
+
+    private boolean mCompleted;
+
+    private PendingRequest mPendingRequest;
+
+    public interface FillServiceCallbacks {
+        void onFillRequestSuccess(FillResponse response);
+        void onFillRequestFailure(CharSequence message);
+        void onSaveRequestSuccess();
+        void onSaveRequestFailure(CharSequence message);
+        void onServiceDied(RemoteFillService service);
+    }
+
+    public RemoteFillService(Context context, ComponentName componentName,
+            int userId, FillServiceCallbacks callbacks) {
+        mContext = context;
+        mCallbacks = callbacks;
+        mComponentName = componentName;
+        mIntent = new Intent(AutoFillService.SERVICE_INTERFACE)
+                .setComponent(mComponentName);
+        mUserId = userId;
+        mHandler = new MyHandler(context);
+    }
+
+    public void destroy() {
+        mHandler.obtainMessage(MyHandler.MSG_DESTROY).sendToTarget();
+    }
+
+    private void handleDestroy() {
+        if (mPendingRequest != null) {
+            mPendingRequest.cancel();
+            mPendingRequest = null;
+        }
+        ensureUnbound();
+        mDestroyed = true;
+    }
+
+    @Override
+    public void binderDied() {
+        mHandler.obtainMessage(MyHandler.MSG_BINDER_DIED).sendToTarget();
+    }
+
+    private void handleBinderDied() {
+        if (mAutoFillService != null) {
+            mAutoFillService.asBinder().unlinkToDeath(this, 0);
+        }
+        mAutoFillService = null;
+        mServiceDied = true;
+        mCallbacks.onServiceDied(this);
+    }
+
+    public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+        cancelScheduledUnbind();
+        PendingFillRequest request = new PendingFillRequest(structure, extras, this);
+        mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+    }
+
+    public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+        cancelScheduledUnbind();
+        PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
+        mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+    }
+
+    // Note: we are dumping without a lock held so this is a bit racy but
+    // adding a lock to a class that offloads to a handler thread would
+    // mean adding a lock adding overhead to normal runtime operation.
+    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        String tab = "  ";
+        pw.append(prefix).append("service:").println();
+        pw.append(prefix).append(tab).append("userId=")
+                .append(String.valueOf(mUserId)).println();
+        pw.append(prefix).append(tab).append("componentName=")
+                .append(mComponentName.flattenToString()).println();
+        pw.append(prefix).append(tab).append("destroyed=")
+                .append(String.valueOf(mDestroyed)).println();
+        pw.append(prefix).append(tab).append("bound=")
+                .append(String.valueOf(isBound())).println();
+        pw.append(prefix).append(tab).append("hasPendingRequest=")
+                .append(String.valueOf(mPendingRequest != null)).println();
+        pw.println();
+    }
+
+    private void cancelScheduledUnbind() {
+        mHandler.removeMessages(MyHandler.MSG_UNBIND);
+    }
+
+    private void scheduleUnbind() {
+        cancelScheduledUnbind();
+        Message message = mHandler.obtainMessage(MyHandler.MSG_UNBIND);
+        mHandler.sendMessageDelayed(message, TIMEOUT_IDLE_BIND_MILLIS);
+    }
+
+    private void handleUnbind() {
+        ensureUnbound();
+    }
+
+    private void handlePendingRequest(PendingRequest pendingRequest) {
+        if (mDestroyed || mCompleted) {
+            return;
+        }
+        if (pendingRequest.isFinal()) {
+            mCompleted = true;
+        }
+        if (!isBound()) {
+            if (mPendingRequest != null) {
+                mPendingRequest.cancel();
+            }
+            mPendingRequest = pendingRequest;
+            ensureBound();
+        } else {
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "[user: " + mUserId + "] handleOnFillRequest()");
+            }
+            pendingRequest.run();
+        }
+    }
+
+    private boolean isBound() {
+        return mAutoFillService != null;
+    }
+
+    private void ensureBound() {
+        if (isBound() || mBinding) {
+            return;
+        }
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
+        }
+        mBinding = true;
+
+        boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection,
+                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                new UserHandle(mUserId));
+
+        if (!willBind) {
+            if (DEBUG) {
+                Slog.d(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent);
+            }
+            mBinding = false;
+
+            if (!mServiceDied) {
+                handleBinderDied();
+            }
+        }
+    }
+
+    private void ensureUnbound() {
+        if (!isBound() && !mBinding) {
+            return;
+        }
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
+        }
+        mBinding = false;
+        if (isBound()) {
+            mAutoFillService.asBinder().unlinkToDeath(this, 0);
+            mAutoFillService = null;
+        }
+        mContext.unbindService(mServiceConnection);
+    }
+
+    private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
+            FillResponse response) {
+        mHandler.getHandler().post(() -> {
+            if (handleResponseCallbackCommon(pendingRequest)) {
+                mCallbacks.onFillRequestSuccess(response);
+            }
+        });
+    }
+
+    private void dispatchOnFillRequestFailure(PendingRequest pendingRequest,
+            CharSequence message) {
+        mHandler.getHandler().post(() -> {
+            if (handleResponseCallbackCommon(pendingRequest)) {
+                mCallbacks.onFillRequestFailure(message);
+            }
+        });
+    }
+
+    private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest) {
+        mHandler.getHandler().post(() -> {
+            if (handleResponseCallbackCommon(pendingRequest)) {
+                mCallbacks.onSaveRequestSuccess();
+            }
+        });
+    }
+
+    private void dispatchOnSaveRequestFailure(PendingRequest pendingRequest,
+            CharSequence message) {
+        mHandler.getHandler().post(() -> {
+            if (handleResponseCallbackCommon(pendingRequest)) {
+                mCallbacks.onSaveRequestFailure(message);
+            }
+        });
+    }
+
+    private boolean handleResponseCallbackCommon(PendingRequest pendingRequest) {
+        if (mDestroyed) {
+            return false;
+        }
+        if (mPendingRequest == pendingRequest) {
+            mPendingRequest = null;
+        }
+        if (mPendingRequest == null) {
+            scheduleUnbind();
+        }
+        return true;
+    }
+
+    private class RemoteServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (mDestroyed || !mBinding) {
+                mContext.unbindService(mServiceConnection);
+                return;
+            }
+            mBinding = false;
+            mAutoFillService = IAutoFillService.Stub.asInterface(service);
+            try {
+                service.linkToDeath(RemoteFillService.this, 0);
+            } catch (RemoteException re) {
+                handleBinderDied();
+                return;
+            }
+
+            if (mPendingRequest != null) {
+                handlePendingRequest(mPendingRequest);
+            }
+
+            mServiceDied = false;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mBinding = true;
+            mAutoFillService = null;
+        }
+    }
+
+    private final class MyHandler extends HandlerCaller {
+        public static final int MSG_DESTROY = 1;
+        public static final int MSG_BINDER_DIED = 2;
+        public static final int MSG_UNBIND = 3;
+        public static final int MSG_ON_PENDING_REQUEST = 4;
+
+        public MyHandler(Context context) {
+            // Cannot use lambda - doesn't compile
+            super(context, FgThread.getHandler().getLooper(), new Callback() {
+                @Override
+                public void executeMessage(Message message) {
+                    if (mDestroyed) {
+                        Slog.w(LOG_TAG, "Not handling " + message + " as service for "
+                                + mComponentName + " is already destroyed");
+                        return;
+                    }
+                    switch (message.what) {
+                        case MSG_DESTROY: {
+                            handleDestroy();
+                        } break;
+
+                        case MSG_BINDER_DIED: {
+                            handleBinderDied();
+                        } break;
+
+                        case MSG_UNBIND: {
+                            handleUnbind();
+                        } break;
+
+                        case MSG_ON_PENDING_REQUEST: {
+                            handlePendingRequest((PendingRequest) message.obj);
+                        } break;
+                    }
+                }
+            }, false);
+        }
+    }
+
+    private static abstract class PendingRequest implements Runnable {
+        void cancel() {
+
+        }
+
+        /**
+         * @return whether this request leads to a final state where no
+         * other requests can be made.
+         */
+        boolean isFinal() {
+            return false;
+        }
+    }
+
+    private static final class PendingFillRequest extends PendingRequest {
+        private final Object mLock = new Object();
+        private final WeakReference<RemoteFillService> mWeakService;
+        private AssistStructure mStructure;
+        private Bundle mExtras;
+        private final IFillCallback mCallback;
+        private ICancellationSignal mCancellation;
+        private boolean mCancelled;
+
+        public PendingFillRequest(AssistStructure structure,
+                Bundle extras, RemoteFillService service) {
+            mStructure = structure;
+            mExtras = extras;
+            mWeakService = new WeakReference<>(service);
+            mCallback = new IFillCallback.Stub() {
+                @Override
+                public void onCancellable(ICancellationSignal cancellation) {
+                    synchronized (mLock) {
+                        final boolean cancelled;
+                        synchronized (mLock) {
+                            mCancellation = cancellation;
+                            cancelled = mCancelled;
+                        }
+                        if (cancelled) {
+                            try {
+                                cancellation.cancel();
+                            } catch (RemoteException e) {
+                                Slog.e(LOG_TAG, "Error requesting a cancellation", e);
+                            }
+                        }
+                    }
+                }
+
+                @Override
+                public void onSuccess(FillResponse response) {
+                    RemoteFillService remoteService = mWeakService.get();
+                    if (remoteService != null) {
+                        remoteService.dispatchOnFillRequestSuccess(
+                                PendingFillRequest.this, response);
+                    }
+                }
+
+                @Override
+                public void onFailure(CharSequence message) {
+                    RemoteFillService remoteService = mWeakService.get();
+                    if (remoteService != null) {
+                        remoteService.dispatchOnFillRequestFailure(
+                                PendingFillRequest.this, message);
+                    }
+                }
+            };
+        }
+
+        @Override
+        public void run() {
+            RemoteFillService remoteService = mWeakService.get();
+            if (remoteService != null) {
+                try {
+                    remoteService.mAutoFillService.onFillRequest(mStructure,
+                            mExtras, mCallback);
+                    synchronized (mLock) {
+                        mStructure = null;
+                        mExtras = null;
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Error calling on fill request", e);
+                    cancel();
+                }
+            }
+        }
+
+        @Override
+        public void cancel() {
+            final ICancellationSignal cancellation;
+            synchronized (mLock) {
+                if (mCancelled) {
+                    return;
+                }
+                mCancelled = true;
+                cancellation = mCancellation;
+            }
+            if (cancellation == null) {
+                return;
+            }
+            try {
+                cancellation.cancel();
+            } catch (RemoteException e) {
+                Slog.e(LOG_TAG, "Error cancelling a fill request", e);
+            }
+        }
+    }
+
+    private static final class PendingSaveRequest extends PendingRequest {
+        private final Object mLock = new Object();
+        private final WeakReference<RemoteFillService> mWeakService;
+        private AssistStructure mStructure;
+        private Bundle mExtras;
+        private final ISaveCallback mCallback;
+
+        public PendingSaveRequest(@NonNull AssistStructure structure,
+                @Nullable Bundle extras, @NonNull RemoteFillService service) {
+            mStructure = structure;
+            mExtras = extras;
+            mWeakService = new WeakReference<>(service);
+            mCallback = new ISaveCallback.Stub() {
+                @Override
+                public void onSuccess() {
+                    RemoteFillService service = mWeakService.get();
+                    if (service != null) {
+                        service.dispatchOnSaveRequestSuccess(
+                                PendingSaveRequest.this);
+                    }
+                }
+
+                @Override
+                public void onFailure(CharSequence message) {
+                    RemoteFillService service = mWeakService.get();
+                    if (service != null) {
+                        service.dispatchOnSaveRequestFailure(
+                                PendingSaveRequest.this, message);
+                    }
+                }
+            };
+        }
+
+        @Override
+        public void run() {
+            RemoteFillService service = mWeakService.get();
+            if (service != null) {
+                try {
+                    service.mAutoFillService.onSaveRequest(mStructure,
+                            mExtras, mCallback);
+                    synchronized (mLock) {
+                        mStructure = null;
+                        mExtras = null;
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(LOG_TAG, "Error calling on save request", e);
+                }
+            }
+        }
+
+        @Override
+        public boolean isFinal() {
+            return true;
+        }
+    }
+}