Add API to mark sanitized fields and implement it on TextView.

When sending a ViewNode to the auto-fill service, it's necessary to sanitize
it so it does not contain PII (Personally Identifiable Information).

A good rule of thumb is to mark as sanitized nodes whose value were statically
set from resources. Ideally, that logic should belong to View, but View does
not know how its contexts are set, so the decision must be made by the
subclasses, and this CL implements that logic on TextView (which is the main
auto-fill type - others will come soon...)

Bug: 33269702

Test: CtsAutoFillServiceTestCases pass
Test: manual verification

Change-Id: I4dcdd8419a35812bd2f15a63a5be85f8cd97bb4e
diff --git a/api/current.txt b/api/current.txt
index 85e153a..f8c5f2c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -45639,6 +45639,7 @@
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
     method public abstract void setLongClickable(boolean);
+    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -45647,7 +45648,6 @@
     method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setUrl(java.lang.String);
     method public abstract void setVisibility(int);
-    field public static final int AUTO_FILL_FLAG_SANITIZED = 1; // 0x1
   }
 
   public final class ViewStub extends android.view.View {
diff --git a/api/system-current.txt b/api/system-current.txt
index e0e2c35..7ac97c4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -49144,6 +49144,7 @@
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
     method public abstract void setLongClickable(boolean);
+    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -49152,7 +49153,6 @@
     method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setUrl(java.lang.String);
     method public abstract void setVisibility(int);
-    field public static final int AUTO_FILL_FLAG_SANITIZED = 1; // 0x1
   }
 
   public final class ViewStub extends android.view.View {
diff --git a/api/test-current.txt b/api/test-current.txt
index 5e08f1a..7f34ad3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -46001,6 +46001,7 @@
     method public abstract void setHint(java.lang.CharSequence);
     method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
     method public abstract void setLongClickable(boolean);
+    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -46009,7 +46010,6 @@
     method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setUrl(java.lang.String);
     method public abstract void setVisibility(int);
-    field public static final int AUTO_FILL_FLAG_SANITIZED = 1; // 0x1
   }
 
   public final class ViewStub extends android.view.View {
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 8d385db..13d0f03 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -419,7 +419,7 @@
             mDisplayId = root.getDisplayId();
             mRoot = new ViewNode();
 
-            ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false, 0);
+            ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
             if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
                 if (forAutoFill) {
                     // NOTE: flags are currently not supported, hence 0
@@ -1187,11 +1187,10 @@
         final ViewNode mNode;
         final boolean mAsync;
 
-        ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async, int flags) {
+        ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
             mAssist = assist;
             mNode = node;
             mAsync = async;
-            mNode.mSanitized = (flags & AUTO_FILL_FLAG_SANITIZED) != 0;
         }
 
         @Override
@@ -1429,16 +1428,15 @@
             ViewNode node = new ViewNode();
             setAutoFillId(node, forAutoFill, virtualId);
             mNode.mChildren[index] = node;
-            return new ViewNodeBuilder(mAssist, node, false, flags);
+            return new ViewNodeBuilder(mAssist, node, false);
         }
 
-        private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId,
-                int flags) {
+        private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId) {
             synchronized (mAssist) {
                 ViewNode node = new ViewNode();
                 setAutoFillId(node, forAutoFill, virtualId);
                 mNode.mChildren[index] = node;
-                ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true, flags);
+                ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true);
                 mAssist.mPendingAsyncChildren.add(builder);
                 return builder;
             }
@@ -1457,12 +1455,12 @@
 
         @Override
         public ViewStructure asyncNewChild(int index) {
-            return asyncNewChild(index, false, 0, 0);
+            return asyncNewChild(index, false, 0);
         }
 
         @Override
         public ViewStructure asyncNewChild(int index, int virtualId, int flags) {
-            return asyncNewChild(index, true, virtualId, flags);
+            return asyncNewChild(index, true, virtualId);
         }
 
         @Override
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 9ce23e6..bc2725f 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -31,16 +31,6 @@
 public abstract class ViewStructure {
 
     /**
-     * Flag used when adding virtual views for auto-fill, it indicates the contents of the view
-     * (such as * {@link android.app.assist.AssistStructure.ViewNode#getText()} and
-     * {@link android.app.assist.AssistStructure.ViewNode#getAutoFillValue()})
-     * can be passed to the {@link
-     * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
-     * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} call.
-     */
-    public static final int AUTO_FILL_FLAG_SANITIZED = 0x1;
-
-    /**
      * Set the identifier for this view.
      *
      * @param id The view's identifier, as per {@link View#getId View.getId()}.
@@ -278,7 +268,7 @@
      *
      * @param index child index
      * @param virtualId id identifying the virtual child inside the custom view.
-     * @param flags currently {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}.
+     * @param flags currently {@code 0}.
      */
     // TODO(b/33197203, b/33802548): add CTS/unit test
     public abstract ViewStructure newChild(int index, int virtualId, int flags);
@@ -299,7 +289,7 @@
      *
      * @param index child index
      * @param virtualId id identifying the virtual child inside the custom view.
-     * @param flags currently {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}.
+     * @param flags currently {@code 0}.
      */
     // TODO(b/33197203, b/33802548): add CTS/unit test
     public abstract ViewStructure asyncNewChild(int index, int virtualId, int flags);
@@ -317,12 +307,19 @@
     public abstract void setAutoFillValue(AutoFillValue value);
 
     /**
-     * @hide
+     * Marks this node as sanitized so its content are sent on {@link
+     * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure,
+     * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
      *
-     * TODO(b/33197203, b/33269702): temporary set it as not sanitized until
-     * AssistStructure automaticaly sets sanitization based on text coming from resources
+     * <p>Only nodes that does not have PII (Personally Identifiable Information - sensitive data
+     * such as email addresses, credit card numbers, passwords, etc...) should be marked
+     * as sanitized; a good rule of thumb is to mark as sanitized nodes whose value were statically
+     * set from resources.
+     *
+     * <p>Should only be set when the node is used for AutoFill purposes - it will be ignored
+     * when used for Assist.
      */
-    public abstract void setSanitized(boolean sensitive);
+    public abstract void setSanitized(boolean sanitized);
 
     /**
      * Call when done populating a {@link ViewStructure} returned by
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 051ffee..bfaddaf 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -730,6 +730,10 @@
     // Watcher used to notify changes to auto-fill manager.
     private AutoFillChangeWatcher mAutoFillChangeWatcher;
 
+    // Indicates whether the text was set from resources or dynamically, so it can be used to
+    // sanitize auto-fill request.
+    private boolean mTextFromResource = false;
+
     /**
      * Kick-start the font cache for the zygote process (to pay the cost of
      * initializing freetype for our default font only once).
@@ -948,6 +952,8 @@
                     attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
 
         int n = a.getIndexCount();
+
+        boolean fromResourceId = false;
         for (int i = 0; i < n; i++) {
             int attr = a.getIndex(i);
 
@@ -1089,6 +1095,7 @@
                     break;
 
                 case com.android.internal.R.styleable.TextView_text:
+                    fromResourceId = true;
                     text = a.getText(attr);
                     break;
 
@@ -1567,6 +1574,10 @@
         }
 
         setText(text, bufferType);
+        if (fromResourceId) {
+            mTextFromResource = true;
+        }
+
         if (hint != null) setHint(hint);
 
         /*
@@ -5067,6 +5078,7 @@
 
     private void setText(CharSequence text, BufferType type,
                          boolean notifyBefore, int oldlen) {
+        mTextFromResource = false;
         if (text == null) {
             text = "";
         }
@@ -5301,6 +5313,7 @@
     @android.view.RemotableViewMethod
     public final void setText(@StringRes int resid) {
         setText(getContext().getResources().getText(resid));
+        mTextFromResource = true;
     }
 
     /**
@@ -5327,6 +5340,7 @@
      */
     public final void setText(@StringRes int resid, BufferType type) {
         setText(getContext().getResources().getText(resid), type);
+        mTextFromResource = true;
     }
 
     /**
@@ -9872,9 +9886,7 @@
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
         if (forAutoFill) {
-            // TODO(b/33197203, b/33269702): temporary set it as not sanitized until
-            // AssistStructure automaticaly sets sanitization based on text coming from resources
-            structure.setSanitized(!isPassword);
+            structure.setSanitized(mTextFromResource);
             if (mAutoFillChangeWatcher == null && isTextEditable()) {
                 mAutoFillChangeWatcher = new AutoFillChangeWatcher();
                 addTextChangedListener(mAutoFillChangeWatcher);