AutoFill Framework refactoring.

The AutoFill Framework uses the same AssitStructure provided by the Assist API
and so far it was using the same methods as well, both internally and externally
(public API).

Sharing that internal code internally is fine, but the public APIs must distinguish between the 2 cases so they can fill the assist structures accordingly (although the initial implementation still shares the same logic).

This CL also splits the original 'auto-fill' request in 2 types of requests,
which are set by View flags:

- ASSIST_FLAG_SANITIZED_TEXT
- ASSIST_FLAG_NON_SANITIZED_TEXT

It also added new methods and callbacks to handle save requests.

Bug: 31001899
Test: manual verification

Change-Id:  I4eb09099dc19a43cb7e053e64d939aed3704b410
diff --git a/api/current.txt b/api/current.txt
index 5a2df57..cd83170 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34869,7 +34869,8 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
   }
 
@@ -34887,6 +34888,11 @@
     method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
   }
 
+  public final class SaveCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(int[]);
+  }
+
 }
 
 package android.service.carrier {
@@ -42932,7 +42938,8 @@
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void dispatchProvideStructure(android.view.ViewStructure);
+    method public deprecated void dispatchProvideStructure(android.view.ViewStructure);
+    method public void dispatchProvideStructure(android.view.ViewStructure, int);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSetActivated(boolean);
@@ -43198,8 +43205,10 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void onProvideStructure(android.view.ViewStructure);
-    method public void onProvideVirtualStructure(android.view.ViewStructure);
+    method public deprecated void onProvideStructure(android.view.ViewStructure);
+    method public void onProvideStructure(android.view.ViewStructure, int);
+    method public deprecated void onProvideVirtualStructure(android.view.ViewStructure);
+    method public void onProvideVirtualStructure(android.view.ViewStructure, int);
     method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
@@ -43401,6 +43410,8 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2
+    field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1
     field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
     field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
     field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/api/system-current.txt b/api/system-current.txt
index 7a287df..e8c9b40 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -37700,7 +37700,8 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
   }
 
@@ -37718,6 +37719,11 @@
     method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
   }
 
+  public final class SaveCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(int[]);
+  }
+
 }
 
 package android.service.carrier {
@@ -46118,7 +46124,8 @@
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void dispatchProvideStructure(android.view.ViewStructure);
+    method public deprecated void dispatchProvideStructure(android.view.ViewStructure);
+    method public void dispatchProvideStructure(android.view.ViewStructure, int);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSetActivated(boolean);
@@ -46384,8 +46391,10 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void onProvideStructure(android.view.ViewStructure);
-    method public void onProvideVirtualStructure(android.view.ViewStructure);
+    method public deprecated void onProvideStructure(android.view.ViewStructure);
+    method public void onProvideStructure(android.view.ViewStructure, int);
+    method public deprecated void onProvideVirtualStructure(android.view.ViewStructure);
+    method public void onProvideVirtualStructure(android.view.ViewStructure, int);
     method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
@@ -46587,6 +46596,8 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2
+    field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1
     field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
     field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
     field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
@@ -49956,6 +49967,7 @@
     method public abstract void onMeasure(int, int);
     method public abstract void onOverScrolled(int, int, boolean, boolean);
     method public abstract void onProvideVirtualStructure(android.view.ViewStructure);
+    method public default void onProvideVirtualStructure(android.view.ViewStructure, int);
     method public abstract void onScrollChanged(int, int, int, int);
     method public abstract void onSizeChanged(int, int, int, int);
     method public abstract void onStartTemporaryDetach();
diff --git a/api/test-current.txt b/api/test-current.txt
index 20b723d..258ef4b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34966,7 +34966,8 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
   }
 
@@ -34984,6 +34985,11 @@
     method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
   }
 
+  public final class SaveCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(int[]);
+  }
+
 }
 
 package android.service.carrier {
@@ -43200,7 +43206,8 @@
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void dispatchProvideStructure(android.view.ViewStructure);
+    method public deprecated void dispatchProvideStructure(android.view.ViewStructure);
+    method public void dispatchProvideStructure(android.view.ViewStructure, int);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSetActivated(boolean);
@@ -43467,8 +43474,10 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void onProvideStructure(android.view.ViewStructure);
-    method public void onProvideVirtualStructure(android.view.ViewStructure);
+    method public deprecated void onProvideStructure(android.view.ViewStructure);
+    method public void onProvideStructure(android.view.ViewStructure, int);
+    method public deprecated void onProvideVirtualStructure(android.view.ViewStructure);
+    method public void onProvideVirtualStructure(android.view.ViewStructure, int);
     method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
@@ -43670,6 +43679,8 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+    field public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 2; // 0x2
+    field public static final int ASSIST_FLAG_SANITIZED_TEXT = 1; // 0x1
     field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
     field public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 64; // 0x40
     field public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6dd488f..bbbedb3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1737,8 +1737,8 @@
                                 FillableInputField autoFillField = (FillableInputField) field;
                                 final int viewId = autoFillField.getId();
                                 final View view = root.findViewByAccessibilityIdTraversal(viewId);
-                                // TODO: should handle other types of view as well, but that will
-                                // require:
+                                // TODO(b/33197203): should handle other types of view as well, but
+                                // that will require:
                                 // - a new interface like AutoFillable
                                 // - a way for the views to define the type of the autofield value
                                 if ((view instanceof EditText)) {
@@ -1751,7 +1751,7 @@
                     @Override
                     public void showError(String message) {
                         runOnUiThread(() -> {
-                            // TODO: temporary show a toast until it uses the Snack bar.
+                            // TODO(b/33197203): temporary show a toast until it uses the Snack bar.
                             Toast.makeText(Activity.this, "Auto-fill request failed: " + message,
                                     Toast.LENGTH_LONG).show();
                         });
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 312d3a5..f6f5472 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -450,9 +450,6 @@
     /** @hide requestType for assist context: generate full AssistStructure. */
     public static final int ASSIST_CONTEXT_FULL = 1;
 
-    /** @hide requestType for assist context: generate full AssistStructure for auto-fill. */
-    public static final int ASSIST_CONTEXT_AUTOFILL = 2;
-
     /** @hide Flag for registerUidObserver: report changes in process state. */
     public static final int UID_OBSERVER_PROCSTATE = 1<<0;
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f052bf7..9a92764 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -88,6 +88,7 @@
 import android.provider.Settings;
 import android.security.NetworkSecurityPolicy;
 import android.security.net.config.NetworkSecurityConfigProvider;
+import android.service.autofill.AutoFillService;
 import android.service.autofill.IAutoFillCallback;
 import android.service.voice.VoiceInteractionSession;
 import android.util.AndroidRuntimeException;
@@ -640,6 +641,7 @@
         IBinder requestToken;
         int requestType;
         int sessionId;
+        int flags;
     }
 
     static final class ActivityConfigChangeData {
@@ -1245,12 +1247,13 @@
 
         @Override
         public void requestAssistContextExtras(IBinder activityToken, IBinder requestToken,
-                int requestType, int sessionId) {
+                int requestType, int sessionId, int flags) {
             RequestAssistContextExtras cmd = new RequestAssistContextExtras();
             cmd.activityToken = activityToken;
             cmd.requestToken = requestToken;
             cmd.requestType = requestType;
             cmd.sessionId = sessionId;
+            cmd.flags = flags;
             sendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd);
         }
 
@@ -2883,6 +2886,16 @@
     }
 
     public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
+        // Filling for auto-fill has a few differences:
+        // - it does not need an AssistContent
+        // - it does not call onProvideAssistData()
+        // - it needs an IAutoFillCallback
+        // - it sets the flags so views can provide autofill-specific data (such as passwords)
+        boolean forAutoFill = (cmd.flags
+                & (View.ASSIST_FLAG_SANITIZED_TEXT
+                        | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
+
+        // TODO(b/33197203): decide if lastSessionId logic applies to auto-fill sessions
         if (mLastSessionId != cmd.sessionId) {
             // Clear the existing structures
             mLastSessionId = cmd.sessionId;
@@ -2894,46 +2907,45 @@
                 mLastAssistStructures.remove(i);
             }
         }
-        // Filling for auto-fill has a few differences:
-        // - it does not need an AssistContent
-        // - it does not call onProvideAssistData()
-        // - it needs an IAutoFillCallback
-        boolean forAutofill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTOFILL;
 
         Bundle data = new Bundle();
         AssistStructure structure = null;
-        AssistContent content = forAutofill ? null : new AssistContent();
+        AssistContent content = forAutoFill ? null : new AssistContent();
         ActivityClientRecord r = mActivities.get(cmd.activityToken);
         Uri referrer = null;
         if (r != null) {
-            r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
-            if (!forAutofill) {
+            if (!forAutoFill) {
+                r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
                 r.activity.onProvideAssistData(data);
             }
             referrer = r.activity.onProvideReferrer();
-            if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutofill) {
-                structure = new AssistStructure(r.activity);
+            if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutoFill) {
+                structure = new AssistStructure(r.activity, cmd.flags);
                 Intent activityIntent = r.activity.getIntent();
+                if (cmd.flags > 0) {
+                    data.putInt(VoiceInteractionSession.KEY_FLAGS, cmd.flags);
+                }
+                // TODO(b/33197203): re-evaluate conditions below for auto-fill. In particular,
+                // FLAG_SECURE might be allowed on AUTO_FILL but not on AUTO_FILL_SAVE)
                 if (activityIntent != null && (r.window == null ||
                         (r.window.getAttributes().flags
                                 & WindowManager.LayoutParams.FLAG_SECURE) == 0)) {
-                    Intent intent = new Intent(activityIntent);
-                    intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
-                    intent.removeUnsafeExtras();
-                    if (forAutofill) {
+                    if (forAutoFill) {
                         IAutoFillCallback autoFillCallback = r.activity.getAutoFillCallback();
-                        data.putBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK,
-                                autoFillCallback.asBinder());
+                        data.putBinder(AutoFillService.KEY_CALLBACK, autoFillCallback.asBinder());
                     } else {
+                        Intent intent = new Intent(activityIntent);
+                        intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+                        intent.removeUnsafeExtras();
                         content.setDefaultIntent(intent);
                     }
                 } else {
-                    if (!forAutofill) {
+                    if (!forAutoFill) {
                         content.setDefaultIntent(new Intent());
                     }
                 }
-                if (!forAutofill) {
+                if (!forAutoFill) {
                     r.activity.onProvideAssistContent(content);
                 }
             }
@@ -2941,6 +2953,7 @@
         if (structure == null) {
             structure = new AssistStructure();
         }
+        // TODO(b/33197203): decide if lastSessionId logic applies to auto-fill sessions
         mLastAssistStructures.add(new WeakReference<>(structure));
         IActivityManager mgr = ActivityManager.getService();
         try {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 62b3977..1e6f6c6 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -573,7 +573,7 @@
     void unregisterTaskStackListener(ITaskStackListener listener);
     void moveStackToDisplay(int stackId, int displayId);
     boolean requestAutoFillData(in IResultReceiver receiver, in Bundle receiverExtras,
-            in IBinder activityToken);
+            in IBinder activityToken, int flags);
     void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback);
 
     // WARNING: when these transactions are updated, check if they are any callers on the native
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 6b962b9..7f168c9 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -134,7 +134,7 @@
     void dumpDbInfo(in ParcelFileDescriptor fd, in String[] args);
     void unstableProviderDied(IBinder provider);
     void requestAssistContextExtras(IBinder activityToken, IBinder requestToken,
-            int requestType, int sessionId);
+            int requestType, int sessionId, int flags);
     void scheduleTranslucentConversionComplete(IBinder token, boolean timeout);
     void setProcessState(int state);
     void scheduleInstallProvider(in ProviderInfo provider);
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index a6f2366..1988e42 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -400,7 +400,7 @@
         final int mDisplayId;
         final ViewNode mRoot;
 
-        WindowNode(AssistStructure assist, ViewRootImpl root) {
+        WindowNode(AssistStructure assist, ViewRootImpl root, int flags) {
             View view = root.getView();
             Rect rect = new Rect();
             view.getBoundsOnScreen(rect);
@@ -415,11 +415,22 @@
             if ((root.getWindowFlags()& WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                 // This is a secure window, so it doesn't want a screenshot, and that
                 // means we should also not copy out its view hierarchy.
-                view.onProvideStructure(builder);
+
+                // Must explicitly set which method to calls since View subclasses might
+                // have implemented the deprecated method.
+                if (flags == 0) {
+                    view.onProvideStructure(builder);
+                } else {
+                    view.onProvideStructure(builder, flags);
+                }
                 builder.setAssistBlocked(true);
                 return;
             }
-            view.dispatchProvideStructure(builder);
+            if (flags == 0) {
+                view.dispatchProvideStructure(builder);
+            } else {
+                view.dispatchProvideStructure(builder, flags);
+            }
         }
 
         WindowNode(ParcelTransferReader reader) {
@@ -1351,14 +1362,14 @@
     }
 
     /** @hide */
-    public AssistStructure(Activity activity) {
+    public AssistStructure(Activity activity, int flags) {
         mHaveData = true;
         mActivityComponent = activity.getComponentName();
         ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
                 activity.getActivityToken());
         for (int i=0; i<views.size(); i++) {
             ViewRootImpl root = views.get(i);
-            mWindowNodes.add(new WindowNode(this, root));
+            mWindowNodes.add(new WindowNode(this, root, flags));
         }
     }
 
diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java
index 5f27e34..a7941c7 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -15,6 +15,11 @@
  */
 package android.service.autofill;
 
+import static android.service.voice.VoiceInteractionSession.KEY_FLAGS;
+import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
+import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT;
+import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT;
+
 import android.annotation.SdkConstant;
 import android.app.Activity;
 import android.app.Service;
@@ -26,13 +31,14 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
 
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
 
+// TODO(b/33197203): improve javadoc (class and methods)
+
 /**
  * Top-level service of the current auto-fill service for a given user.
  *
@@ -52,6 +58,11 @@
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
 
+    // Bundle keys.
+    /** @hide */
+    public static final String KEY_CALLBACK = "callback";
+
+    // Handler messages.
     private static final int MSG_CONNECT = 1;
     private static final int MSG_AUTO_FILL_ACTIVITY = 2;
     private static final int MSG_DISCONNECT = 3;
@@ -59,14 +70,12 @@
     private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
         @Override
         public void send(int resultCode, Bundle resultData) throws RemoteException {
-            final AssistStructure structure = resultData
-                    .getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
-
-            final IBinder binder = resultData
-                    .getBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK);
+            final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
+            final IBinder binder = resultData.getBinder(KEY_CALLBACK);
+            final int flags = resultData.getInt(KEY_FLAGS, 0);
 
             mHandlerCaller
-                .obtainMessageOO(MSG_AUTO_FILL_ACTIVITY, structure, binder).sendToTarget();
+                .obtainMessageIOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, binder).sendToTarget();
         }
 
     };
@@ -100,7 +109,8 @@
                     final SomeArgs args = (SomeArgs) msg.obj;
                     final AssistStructure structure = (AssistStructure) args.arg1;
                     final IBinder binder = (IBinder) args.arg2;
-                    requestAutoFill(structure, binder);
+                    final int flags = msg.arg1;
+                    requestAutoFill(structure, flags, binder);
                     break;
                 } case MSG_DISCONNECT: {
                     onDisconnected();
@@ -145,19 +155,46 @@
     }
 
     /**
-     * Handles an auto-fill request.
+     * Called when user requests service to auto-fill an {@link Activity}.
      *
      * @param structure {@link Activity}'s view structure .
+     * @param data bundle with optional parameters (currently none) which is passed along on
+     * subsequent calls (so it can be used by the service to share data).
      * @param cancellationSignal signal for observing cancel requests.
-     * @param callback object used to fulllfill the request.
      */
     public abstract void onFillRequest(AssistStructure structure,
-            CancellationSignal cancellationSignal, FillCallback callback);
+            Bundle data, CancellationSignal cancellationSignal, FillCallback callback);
 
-    private void requestAutoFill(AssistStructure structure, IBinder binder) {
-        final FillCallback callback = new FillCallback(binder);
-        // TODO: hook up the cancelationSignal
-        onFillRequest(structure, new CancellationSignal(), callback);
+    /**
+     * Called when user requests service to save the fields of an {@link Activity}.
+     *
+     * @param structure {@link Activity}'s view structure.
+     * @param data same bundle passed to
+     * {@link #onFillRequest(AssistStructure, Bundle, CancellationSignal, FillCallback)};
+     * might also contain with optional parameters (currently none).
+     * @param cancellationSignal signal for observing cancel requests.
+     * @param callback object used to notify the result of the request.
+     */
+    public abstract void onSaveRequest(AssistStructure structure,
+            Bundle data, CancellationSignal cancellationSignal, SaveCallback callback);
+
+    private void requestAutoFill(AssistStructure structure, int flags, IBinder binder) {
+        // TODO(b/33197203): pass the Bundle received from mAssistReceiver instead?
+        final Bundle data = new Bundle();
+        switch (flags) {
+            case ASSIST_FLAG_SANITIZED_TEXT:
+                final FillCallback fillCallback = new FillCallback(binder);
+                // TODO(b/33197203): hook up the cancelationSignal
+                onFillRequest(structure, data, new CancellationSignal(), fillCallback);
+                break;
+            case ASSIST_FLAG_NON_SANITIZED_TEXT:
+                final SaveCallback saveCallback = new SaveCallback(binder);
+                // TODO(b/33197203): hook up the cancelationSignal
+                onSaveRequest(structure, null, new CancellationSignal(), saveCallback);
+                break;
+            default:
+                Log.w(TAG, "invalid flag on requestAutoFill(): " + flags);
+        }
     }
 
     /**
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 2308440..3284b90 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -17,7 +17,6 @@
 package android.service.autofill;
 
 import static android.service.autofill.AutoFillService.DEBUG;
-import static android.service.autofill.AutoFillService.TAG;
 
 import android.app.Activity;
 import android.app.assist.AssistStructure.ViewNode;
@@ -38,6 +37,8 @@
  */
 public final class FillCallback {
 
+    private static final String TAG = "FillCallback";
+
     private final IAutoFillCallback mCallback;
 
     /** @hide */
@@ -62,6 +63,13 @@
         }
     }
 
+    /**
+     * Notifies the {@link Activity} that the auto-fill request failed.
+     *
+     * @param message error message to be displayed.
+     *
+     * @throws RuntimeException if an error occurred while notifying the activity.
+     */
     public void onFailure(CharSequence message) {
         if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
 
diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
index 76a2561..f1251c0 100644
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl
@@ -25,11 +25,5 @@
  */
 oneway interface IAutoFillManagerService {
 
-    /**
-     * Request auto-fill on the top activity of a given user.
-     *
-     * @param userId user handle.
-     * @param activityToken optional token of activity that needs to be on top.
-     */
-    void requestAutoFill(int userId, IBinder activityToken);
+    void requestAutoFill(IBinder activityToken, int userId, int flags);
 }
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
new file mode 100644
index 0000000..4dc7392
--- /dev/null
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -0,0 +1,79 @@
+/*
+ * 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 static android.service.autofill.AutoFillService.DEBUG;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure.ViewNode;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Handles save requests from the {@link AutoFillService} into the {@link Activity} being
+ * auto-filled.
+ */
+public final class SaveCallback {
+
+    private static final String TAG = "SaveCallback";
+
+    private final IAutoFillCallback mCallback;
+
+    /** @hide */
+    SaveCallback(IBinder binder) {
+        mCallback = IAutoFillCallback.Stub.asInterface(binder);
+    }
+
+    /**
+     * Notifies the {@link Activity} that the save request succeeded.
+     *
+     * @param ids ids ({@link ViewNode#getAutoFillId()}) of the fields that were saved.
+     *
+     * @throws RuntimeException if an error occurred while saving the data.
+     */
+    public void onSuccess(int[] ids) {
+        Preconditions.checkArgument(ids != null, "ids cannot be null");
+
+        Preconditions.checkArgument(ids.length > 0, "ids cannot be empty");
+
+        if (DEBUG) Log.d(TAG, "onSuccess(): ids=" + ids.length);
+
+        // TODO(b/33197203): display which ids were saved
+    }
+
+    /**
+     * Notifies the {@link Activity} that the save request failed.
+     *
+     * @param message error message to be displayed.
+     *
+     * @throws RuntimeException if an error occurred while notifying the activity.
+     */
+    public void onFailure(CharSequence message) {
+        if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
+
+        Preconditions.checkArgument(message != null, "message cannot be null");
+
+        try {
+            mCallback.showError(message.toString());
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 12aed25..48f3ac3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -120,7 +120,7 @@
     /** @hide */
     public static final String KEY_RECEIVER_EXTRAS = "receiverExtras";
     /** @hide */
-    public static final String KEY_AUTO_FILL_CALLBACK = "autoFillCallback";
+    public static final String KEY_FLAGS = "flags";
 
     final Context mContext;
     final HandlerCaller mHandlerCaller;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f9a03c0..06f10c7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3940,6 +3940,21 @@
     int mLayerType = LAYER_TYPE_NONE;
     Paint mLayerPaint;
 
+
+    /**
+     * <p>When setting a {@link android.app.assist.AssistStructure}, its nodes should not contain
+     * PII (Personally Identifiable Information).
+     */
+    // TODO(b/33197203) (b/33269702): improve documentation: mention all cases, show examples, etc.
+    public static final int ASSIST_FLAG_SANITIZED_TEXT = 0x1;
+
+    /**
+     * <p>When setting a {@link android.app.assist.AssistStructure}, its nodes should contain all
+     * type of data, even sensitive PII (Personally Identifiable Information) like passwords or
+     * credit card numbers.
+     */
+    public static final int ASSIST_FLAG_NON_SANITIZED_TEXT = 0x2;
+
     /**
      * Set to true when drawing cache is enabled and cannot be created.
      *
@@ -6757,8 +6772,35 @@
      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
      * @param structure Fill in with structured view data.  The default implementation
      * fills in all data that can be inferred from the view itself.
+     *
+     * @deprecated As of API O sub-classes should override
+     * {@link #onProvideStructure(ViewStructure, int)} instead.
      */
+    // TODO(b/33197203): set proper API above
+    @Deprecated
     public void onProvideStructure(ViewStructure structure) {
+        onProvideStructure(structure, 0);
+    }
+
+    /**
+     * Called when assist structure is being retrieved from a view as part of
+     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} or as part
+     * of an auto-fill request.
+     *
+     * <p>The default implementation fills in all data that can be inferred from the view itself.
+     *
+     * <p>The structure must be filled according to the request type, which is set in the
+     * {@code flags} parameter - see the documentation on each flag for more details.
+     *
+     * @param structure Fill in with structured view data. The default implementation
+     * fills in all data that can be inferred from the view itself.
+     * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and
+     * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info).
+     */
+    public void onProvideStructure(ViewStructure structure, int flags) {
+        boolean forAutoFill = (flags
+                & (View.ASSIST_FLAG_SANITIZED_TEXT
+                        | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
         final int id = mID;
         if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0
                 && (id&0x0000ffff) != 0) {
@@ -6776,9 +6818,11 @@
             structure.setId(id, null, null, null);
         }
 
-        // The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
-        // reuse the accessibility id to save space.
-        structure.setAutoFillId(getAccessibilityViewId());
+        if (forAutoFill) {
+            // The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
+            // reuse the accessibility id to save space.
+            structure.setAutoFillId(getAccessibilityViewId());
+        }
 
         structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
         if (!hasIdentityMatrix()) {
@@ -6828,20 +6872,52 @@
      * uses {@link #getAccessibilityNodeProvider()} to try to generate this from the
      * view's virtual accessibility nodes, if any.  You can override this for a more
      * optimal implementation providing this data.
+     *
+     * @deprecated As of API O, sub-classes should override
+     * {@link #onProvideVirtualStructure(ViewStructure, int)} instead.
      */
+    // TODO(b/33197203): set proper API above
+    @Deprecated
     public void onProvideVirtualStructure(ViewStructure structure) {
+        onProvideVirtualStructure(structure, 0);
+    }
+
+    /**
+     * Called when assist structure is being retrieved from a view as part of
+     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} or as part
+     * of an auto-fill request to generate additional virtual structure under this view.
+     *
+     * <p>The defaullt implementation uses {@link #getAccessibilityNodeProvider()} to try to
+     * generate this from the view's virtual accessibility nodes, if any.  You can override this
+     * for a more optimal implementation providing this data.
+     *
+     * <p>The structure must be filled according to the request type, which is set in the
+     * {@code flags} parameter - see the documentation on each flag for more details.
+     *
+     * @param structure Fill in with structured view data.
+     * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and
+     * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info).
+     */
+    public void onProvideVirtualStructure(ViewStructure structure, int flags) {
+        boolean sanitize = (flags & View.ASSIST_FLAG_SANITIZED_TEXT) != 0;
+
+        if (sanitize) {
+            // TODO(b/33197203): change populateVirtualStructure so it sanitizes data in this case.
+            return;
+        }
+
         AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
         if (provider != null) {
             AccessibilityNodeInfo info = createAccessibilityNodeInfo();
             structure.setChildCount(1);
             ViewStructure root = structure.newChild(0);
-            populateVirtualStructure(root, provider, info);
+            populateVirtualStructure(root, provider, info, flags);
             info.recycle();
         }
     }
 
     private void populateVirtualStructure(ViewStructure structure,
-            AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
+            AccessibilityNodeProvider provider, AccessibilityNodeInfo info, int flags) {
         structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
                 null, null, null);
         Rect rect = structure.getTempRect();
@@ -6890,7 +6966,7 @@
                 AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo(
                         AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
                 ViewStructure child = structure.newChild(i);
-                populateVirtualStructure(child, provider, cinfo);
+                populateVirtualStructure(child, provider, cinfo, flags);
                 cinfo.recycle();
             }
         }
@@ -6900,11 +6976,38 @@
      * Dispatch creation of {@link ViewStructure} down the hierarchy.  The default
      * implementation calls {@link #onProvideStructure} and
      * {@link #onProvideVirtualStructure}.
+     *
+     * @deprecated As of API O,  sub-classes should override
+     * {@link #dispatchProvideStructure(ViewStructure, int)} instead.
      */
+    // TODO(b/33197203): set proper API above
+    @Deprecated
     public void dispatchProvideStructure(ViewStructure structure) {
-        if (!isAssistBlocked()) {
-            onProvideStructure(structure);
-            onProvideVirtualStructure(structure);
+        dispatchProvideStructure(structure, 0);
+    }
+
+    /**
+     * Dispatch creation of {@link ViewStructure} down the hierarchy.
+     *
+     * <p>The structure must be filled according to the request type, which is set in the
+     * {@code flags} parameter - see the documentation on each flag for more details.
+     *
+     * <p>The default implementation calls {@link #onProvideStructure(ViewStructure, int)} and
+     * {@link #onProvideVirtualStructure(ViewStructure, int)}.
+     *
+     * @param structure Fill in with structured view data.
+     * @param flags optional flags (see {@link #ASSIST_FLAG_SANITIZED_TEXT} and
+     * {@link #ASSIST_FLAG_NON_SANITIZED_TEXT} for more info).
+     */
+    public void dispatchProvideStructure(ViewStructure structure, int flags) {
+        boolean forAutoFill = (flags
+                & (View.ASSIST_FLAG_SANITIZED_TEXT
+                        | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
+
+        boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked();
+        if (!blocked) {
+            onProvideStructure(structure, flags);
+            onProvideVirtualStructure(structure, flags);
         } else {
             structure.setClassName(getAccessibilityClassName().toString());
             structure.setAssistBlocked(true);
@@ -8643,6 +8746,25 @@
 
     /**
      * @hide
+     * Indicates whether this view will participate in data collection through
+     * {@link ViewStructure} for auto-fill purposes.
+     *
+     * <p>If {@code true}, it will not provide any data for itself or its children.
+     * <p>If {@code false}, the normal data collection will be allowed.
+     *
+     * @return Returns {@code false} if assist data collection for auto-fill is not blocked,
+     * else {@code true}.
+     *
+     * TODO(b/33197203): update / remove javadoc tags below
+     * @see #setAssistBlocked(boolean)
+     * @attr ref android.R.styleable#View_assistBlocked
+     */
+    public boolean isAutoFillBlocked() {
+        return false; // TODO(b/33197203): properly implement it
+    }
+
+    /**
+     * @hide
      * Controls whether assist data collection from this view and its children is enabled
      * (that is, whether {@link #onProvideStructure} and
      * {@link #onProvideVirtualStructure} will be called).  The default value is false,
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0f8200d..ff797d1 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3083,14 +3083,22 @@
     }
 
     /**
-     * Dispatch creation of {@link ViewStructure} down the hierarchy.  This implementation
-     * adds in all child views of the view group, in addition to calling the default View
-     * implementation.
+     * {@inheritDoc}
+     *
+     * <p>This implementation adds in all child views of the view group, in addition to calling the
+     * default {@link View} implementation.
      */
     @Override
-    public void dispatchProvideStructure(ViewStructure structure) {
-        super.dispatchProvideStructure(structure);
-        if (!isAssistBlocked()) {
+    public void dispatchProvideStructure(ViewStructure structure, int flags) {
+        super.dispatchProvideStructure(structure, flags);
+
+        boolean forAutoFill = (flags
+                & (View.ASSIST_FLAG_SANITIZED_TEXT
+                        | View.ASSIST_FLAG_NON_SANITIZED_TEXT)) != 0;
+
+        boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked();
+
+        if (!blocked) {
             if (structure.getChildCount() == 0) {
                 final int childrenCount = getChildCount();
                 if (childrenCount > 0) {
@@ -3151,7 +3159,14 @@
                         final View child = getAndVerifyPreorderedView(
                                 preorderedList, children, childIndex);
                         final ViewStructure cstructure = structure.newChild(i);
-                        child.dispatchProvideStructure(cstructure);
+
+                        // Must explicitly check which recursive method to call because child might
+                        // not be overriding the new, flags-based version
+                        if (flags == 0) {
+                            child.dispatchProvideStructure(cstructure);
+                        } else {
+                            child.dispatchProvideStructure(cstructure, flags);
+                        }
                     }
                     if (preorderedList != null) preorderedList.clear();
                 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b840f4a..8ecc42d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2504,6 +2504,7 @@
         return mProvider.getViewDelegate().shouldDelayChildPressedState();
     }
 
+    @Override
     public CharSequence getAccessibilityClassName() {
         return WebView.class.getName();
     }
@@ -2513,6 +2514,11 @@
         mProvider.getViewDelegate().onProvideVirtualStructure(structure);
     }
 
+    @Override
+    public void onProvideVirtualStructure(ViewStructure structure, int flags) {
+        mProvider.getViewDelegate().onProvideVirtualStructure(structure, flags);
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 95ec179..7b95180 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -311,6 +311,11 @@
 
         public void onProvideVirtualStructure(android.view.ViewStructure structure);
 
+        @SuppressWarnings("unused")
+        public default void onProvideVirtualStructure(android.view.ViewStructure structure,
+                int flags) {
+        }
+
         public AccessibilityNodeProvider getAccessibilityNodeProvider();
 
         public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index fcc1667..e629df9 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1404,8 +1404,10 @@
     }
 
     @Override
-    public void onProvideStructure(ViewStructure structure) {
-        super.onProvideStructure(structure);
+    public void onProvideStructure(ViewStructure structure, int flags) {
+        super.onProvideStructure(structure, flags);
+
+        // NOTE: current there is no difference for Assist (flags=0) or AutoFill (flags>0);
         CharSequence switchText = isChecked() ? mTextOn : mTextOff;
         if (!TextUtils.isEmpty(switchText)) {
             CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 44655f1..3d35819 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9385,11 +9385,14 @@
     }
 
     @Override
-    public void onProvideStructure(ViewStructure structure) {
-        super.onProvideStructure(structure);
+    public void onProvideStructure(ViewStructure structure, int flags) {
+        super.onProvideStructure(structure, flags);
+
+        final boolean forAutoFillSave =
+                (flags & ASSIST_FLAG_NON_SANITIZED_TEXT) != 0;
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
-        if (!isPassword) {
+        if (!isPassword || forAutoFillSave) {
             if (mLayout == null) {
                 assumeLayout();
             }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index d70c439..6a16131 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -18,10 +18,13 @@
 
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTO_FILL_MANAGER_SERVICE;
+import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT;
+import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT;
 
 import android.Manifest;
 import android.app.AppGlobals;
 import android.app.Notification;
+import android.app.Notification.Action;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -248,13 +251,14 @@
     final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
 
         @Override
-        public void requestAutoFill(int userId, IBinder activityToken) {
+        public void requestAutoFill(IBinder activityToken, int userId, int flags) {
+            if (DEBUG) Slog.d(TAG, "requestAutoFill: flags=" + flags + ", userId=" + userId);
             mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
             synchronized (mLock) {
                 final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
                 if (service != null) {
-                    service.requestAutoFill(activityToken);
+                    service.requestAutoFill(activityToken, flags);
                 }
             }
         }
@@ -307,7 +311,7 @@
             synchronized (mLock) {
                 removeCachedServiceForUserLocked(userId);
                 final ComponentName serviceComponent = getProviderForUser(userId);
-                if (serviceComponent== null) {
+                if (serviceComponent == null) {
                     cancelNotificationLocked(userId);
                 } else {
                     showNotification(serviceComponent, userId);
@@ -322,9 +326,10 @@
     ////////////////////////////////////////////////////////////////////////////
 
     // TODO: remove from frameworks/base/core/res/AndroidManifest.xml once it's not used anymore
-    private static final String NOTIFICATION_INTENT =
+    private static final String NOTIFICATION_AUTO_FILL_INTENT =
             "com.android.internal.autofill.action.REQUEST_AUTOFILL";
     private static final String EXTRA_USER_ID = "user_id";
+    private static final String EXTRA_FLAGS = "flags";
 
     private static final int MSG_SHOW_ALL_NOTIFICATIONS = 42;
     private static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000;
@@ -335,13 +340,14 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
+            final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
             if (DEBUG) Slog.d(TAG, "Requesting autofill by notification for user " + userId);
             synchronized (mLock) {
                 final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
                 if (service == null) {
                     Slog.w(TAG, "no auto-fill service for user " + userId);
                 } else {
-                    service.requestAutoFill(null);
+                    service.requestAutoFill(null, flags);
                 }
             }
         }
@@ -393,14 +399,23 @@
             if (mNotificationReceiver == null) {
                 mNotificationReceiver = new NotificationReceiver();
                 mContext.registerReceiver(mNotificationReceiver,
-                        new IntentFilter(NOTIFICATION_INTENT));
+                        new IntentFilter(NOTIFICATION_AUTO_FILL_INTENT));
             }
         }
 
-        final Intent intent = new Intent(NOTIFICATION_INTENT);
-        intent.putExtra(EXTRA_USER_ID, userId);
-        final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent,
-                PendingIntent.FLAG_UPDATE_CURRENT);
+        final Intent fillIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
+        fillIntent.putExtra(EXTRA_USER_ID, userId);
+        fillIntent.putExtra(EXTRA_FLAGS, ASSIST_FLAG_SANITIZED_TEXT);
+        final PendingIntent fillPendingIntent = PendingIntent.getBroadcast(mContext,
+                ASSIST_FLAG_SANITIZED_TEXT, fillIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+        final Action fillAction = new Action.Builder(null, "FILL", fillPendingIntent).build();
+
+        final Intent saveIntent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
+        saveIntent.putExtra(EXTRA_USER_ID, userId);
+        saveIntent.putExtra(EXTRA_FLAGS, ASSIST_FLAG_NON_SANITIZED_TEXT);
+        final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext,
+                ASSIST_FLAG_NON_SANITIZED_TEXT, saveIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+        final Action saveAction = new Action.Builder(null, "SAVE", savePendingIntent).build();
 
         final String packageName = serviceComponent.getPackageName();
         String providerName = null;
@@ -413,8 +428,8 @@
         } catch (Exception e) {
             providerName = packageName;
         }
-        final String title = "AutoFill by '" + providerName + "'";
-        final String subTitle = "Tap notification to auto-fill top activity for user " + userId;
+        final String title = "AutoFill actions";
+        final String subTitle = "Provider: " + providerName + "\n" + "User: " + userId;
 
         final Notification notification = new Notification.Builder(mContext)
                 .setCategory(Notification.CATEGORY_SYSTEM)
@@ -425,7 +440,7 @@
                         com.android.internal.R.color.system_notification_accent_color))
                 .setContentTitle(title)
                 .setStyle(new Notification.BigTextStyle().bigText(subTitle))
-                .setContentIntent(pi)
+                .setActions(fillAction, saveAction)
                 .build();
         NotificationManager.from(mContext).notify(userId, notification);
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index e409cb0..82356c8 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -47,7 +47,6 @@
 import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
@@ -70,13 +69,13 @@
     private final AutoFillServiceInfo mInfo;
     private final AutoFillManagerService mManagerService;
 
-    // TODO: improve its usage
+    // TODO(b/33197203): improve its usage
     // - set maximum number of entries
     // - disable on low-memory devices.
     private final List<String> mRequestHistory = new LinkedList<>();
 
     @GuardedBy("mLock")
-    private final List<IBinder> mQueuedRequests = new LinkedList<>();
+    private final List<QueuedRequest> mQueuedRequests = new LinkedList<>();
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -84,7 +83,8 @@
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
                 final String reason = intent.getStringExtra("reason");
                 if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason);
-                // TODO: close any pending UI like account selection (or remove this receiver)
+                // TODO(b/33197203): close any pending UI like account selection (or remove this
+                // receiver)
             }
         }
     };
@@ -104,8 +104,8 @@
                 if (!mQueuedRequests.isEmpty()) {
                     if (DEBUG) Log.d(TAG, "queued requests:" + mQueuedRequests.size());
                 }
-                for (IBinder activityToken : mQueuedRequests) {
-                    requestAutoFillLocked(activityToken, false);
+                for (final QueuedRequest request: mQueuedRequests) {
+                    requestAutoFillLocked(request.activityToken, request.flags, false);
                 }
             }
         }
@@ -180,7 +180,7 @@
         if (DEBUG) Slog.d(TAG, "Bound to " + mComponent);
     }
 
-    void requestAutoFill(IBinder activityToken) {
+    void requestAutoFill(IBinder activityToken, int flags) {
         synchronized (mLock) {
             if (!mBound) {
                 Slog.w(TAG, "requestAutoFill() failed because it's not bound to service");
@@ -188,14 +188,14 @@
             }
         }
 
-        // TODO: activityToken should probably not be null, but we need to wait until the UI is
-        // triggering the call (for now it's trough 'adb shell cmd autofill request'
+        // TODO(b/33197203): activityToken should probably not be null, but we need to wait until
+        // the UI is triggering the call (for now it's trough 'adb shell cmd autofill request'
         if (activityToken == null) {
             // Let's get top activities from all visible stacks.
 
-            // TODO: overload getTopVisibleActivities() to take userId, otherwise it could return
-            // activities for different users when a work profile app is displayed in another
-            // window (in a multi-window environment).
+            // TODO(b/33197203): overload getTopVisibleActivities() to take userId, otherwise it
+            // could return activities for different users when a work profile app is displayed in
+            // another window (in a multi-window environment).
             final List<IBinder> topActivities = LocalServices
                     .getService(ActivityManagerInternal.class).getTopVisibleActivities();
             if (DEBUG)
@@ -211,32 +211,34 @@
                 DateFormat.getDateTimeInstance().format(new Date()) + " - " + activityToken;
         synchronized (mLock) {
             mRequestHistory.add(historyItem);
-            requestAutoFillLocked(activityToken, true);
+            requestAutoFillLocked(activityToken, flags, true);
         }
     }
 
-    private void requestAutoFillLocked(IBinder activityToken, boolean queueIfNecessary) {
+    private void requestAutoFillLocked(IBinder activityToken, int flags, boolean queueIfNecessary) {
         if (mService == null) {
             if (!queueIfNecessary) {
                 Slog.w(TAG, "requestAutoFillLocked(): service is null");
                 return;
             }
             if (DEBUG) Slog.d(TAG, "requestAutoFill(): service not set yet, queuing it");
-            mQueuedRequests.add(activityToken);
+            mQueuedRequests.add(new QueuedRequest(activityToken, flags));
             return;
         }
 
         /*
-         * TODO: apply security checks below:
+         * TODO(b/33197203): apply security checks below:
          * - checks if disabled by secure settings / device policy
          * - log operation using noteOp()
          * - check flags
          * - display disclosure if needed
          */
         try {
-            // TODO: add MetricsLogger call
-            if (!mAm.requestAutoFillData(mService.getAssistReceiver(), null, activityToken)) {
-                // TODO: might need a way to warn user (perhaps a new method on AutoFillService).
+            // TODO(b/33197203): add MetricsLogger call
+            if (!mAm.requestAutoFillData(mService.getAssistReceiver(), null, activityToken,
+                    flags)) {
+                // TODO(b/33197203): might need a way to warn user (perhaps a new method on
+                // AutoFillService).
                 Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
             }
         } catch (RemoteException e) {
@@ -322,4 +324,19 @@
         return "[AutoFillManagerServiceImpl: userId=" + mUserId + ", uid=" + mUid
                 + ", component=" + mComponent.flattenToShortString() + "]";
     }
+
+    private static final class QueuedRequest {
+        final IBinder activityToken;
+        final int flags;
+
+        QueuedRequest(IBinder activityToken, int flags) {
+            this.activityToken = activityToken;
+            this.flags = flags;
+        }
+
+        @Override
+        public String toString() {
+            return "flags: " + flags + " token: " + activityToken;
+        }
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
index 6406b8a..aa3503b 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
@@ -16,6 +16,9 @@
 
 package com.android.server.autofill;
 
+import static android.view.View.ASSIST_FLAG_SANITIZED_TEXT;
+import static android.view.View.ASSIST_FLAG_NON_SANITIZED_TEXT;
+
 import android.app.ActivityManager;
 import android.os.RemoteException;
 import android.os.ShellCommand;
@@ -40,8 +43,10 @@
         final PrintWriter pw = getOutPrintWriter();
         try {
             switch (cmd) {
-                case "request":
-                    return requestAutoFill();
+                case "fill":
+                    return requestAutoFill(ASSIST_FLAG_SANITIZED_TEXT);
+                case "save":
+                    return requestAutoFill(ASSIST_FLAG_NON_SANITIZED_TEXT);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -58,15 +63,17 @@
             pw.println("  help");
             pw.println("    Prints this help text.");
             pw.println("");
-            pw.println("  request [--user USER_ID]");
-            pw.println("    Request auto-fill on the top activity. ");
+            pw.println("  fill [--user USER_ID]");
+            pw.println("    Request provider to auto-fill the top activity. ");
+            pw.println("  save [--user USER_ID]");
+            pw.println("    Request provider to save contents of the top activity. ");
             pw.println("");
         }
     }
 
-    private int requestAutoFill() throws RemoteException {
+    private int requestAutoFill(int flags) throws RemoteException {
         final int userId = getUserIdFromArgs();
-        mService.requestAutoFill(userId, null);
+        mService.requestAutoFill(null, userId, flags);
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e6e4b2d..2890964 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -202,6 +202,7 @@
 import android.os.storage.StorageManager;
 import android.provider.Downloads;
 import android.provider.Settings;
+import android.service.autofill.AutoFillService;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.service.voice.VoiceInteractionSession;
@@ -507,6 +508,9 @@
     // on getting this result before starting to launch its UI).
     static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
 
+    // How long to wait in getAutoFillAssistStructure() for the activity to respond with the result.
+    static final int PENDING_AUTO_FILL_ASSIST_STRUCTURE_TIMEOUT = 2000;
+
     // Maximum number of persisted Uri grants a package is allowed
     static final int MAX_PERSISTED_URI_GRANTS = 128;
 
@@ -683,15 +687,18 @@
         public AssistStructure structure = null;
         public AssistContent content = null;
         public Bundle receiverExtras;
+        public int flags;
 
         public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
-                String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _userHandle) {
+                String _hint, IResultReceiver _receiver, Bundle _receiverExtras, int _flags,
+                int _userHandle) {
             activity = _activity;
             extras = _extras;
             intent = _intent;
             hint = _hint;
             receiver = _receiver;
             receiverExtras = _receiverExtras;
+            flags = _flags;
             userHandle = _userHandle;
         }
         @Override
@@ -12167,7 +12174,7 @@
     public Bundle getAssistContextExtras(int requestType) {
         PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
                 null, null, true /* focused */, true /* newSessionId */,
-                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT);
+                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
         if (pae == null) {
             return null;
         }
@@ -12235,22 +12242,23 @@
             IBinder activityToken, boolean focused, boolean newSessionId) {
         return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
                 activityToken, focused, newSessionId,
-                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT)
+                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0)
                 != null;
     }
 
     @Override
     public boolean requestAutoFillData(IResultReceiver receiver, Bundle receiverExtras,
-            IBinder activityToken) {
-        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null, receiver,
+            IBinder activityToken, int flags) {
+        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_FULL, null, null, receiver,
                 receiverExtras, activityToken, true, true,
-                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT)
-                != null;
+                UserHandle.getCallingUserId(), null, PENDING_AUTO_FILL_ASSIST_STRUCTURE_TIMEOUT,
+                flags) != null;
     }
 
     private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
             IResultReceiver receiver, Bundle receiverExtras, IBinder activityToken,
-            boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout) {
+            boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
+            int flags) {
         enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
                 "enqueueAssistContext()");
         synchronized (this) {
@@ -12289,14 +12297,14 @@
             extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
             extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
             pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
-                    userHandle);
+                    flags, userHandle);
             // Increment the sessionId if necessary
             if (newSessionId) {
                 mViSessionId++;
             }
             try {
                 activity.app.thread.requestAssistContextExtras(activity.appToken, pae,
-                        requestType, mViSessionId);
+                        requestType, mViSessionId, flags);
                 mPendingAssistExtras.add(pae);
                 mUiHandler.postDelayed(pae, timeout);
             } catch (RemoteException e) {
@@ -12372,10 +12380,13 @@
                 sendBundle.putParcelable(VoiceInteractionSession.KEY_CONTENT, pae.content);
                 sendBundle.putBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS,
                         pae.receiverExtras);
+                if (pae.flags > 0) {
+                    sendBundle.putInt(VoiceInteractionSession.KEY_FLAGS, pae.flags);
+                }
                 IBinder autoFillCallback =
-                        extras.getBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK);
+                        extras.getBinder(AutoFillService.KEY_CALLBACK);
                 if (autoFillCallback != null) {
-                    sendBundle.putBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK,
+                    sendBundle.putBinder(AutoFillService.KEY_CALLBACK,
                             autoFillCallback);
                 }
             }
@@ -12409,7 +12420,7 @@
             Bundle args) {
         return enqueueAssistContext(requestType, intent, hint, null, null, null,
                 true /* focused */, true /* newSessionId */,
-                userHandle, args, PENDING_ASSIST_EXTRAS_TIMEOUT) != null;
+                userHandle, args, PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
     }
 
     public void registerProcessObserver(IProcessObserver observer) {