Made Autofill Field Classification API public and documented it.

Test: mmm frameworks/base/:doc-comment-check-docs
Test: atest FrameworksCoreTests:SettingsBackupTest
Test: m -j 100 update-api

Fixes: 67867469

Change-Id: Iedf56a2bbcde3826eb22f5ed7a96d6ed70b968f4
diff --git a/api/current.txt b/api/current.txt
index 66a577c..139d555 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37513,6 +37513,25 @@
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
   }
 
+  public final class EditDistanceScorer implements android.os.Parcelable android.service.autofill.Scorer {
+    method public int describeContents();
+    method public static android.service.autofill.EditDistanceScorer getInstance();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
+  }
+
+  public final class FieldClassification implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
+  }
+
+  public static final class FieldClassification.Match {
+    method public java.lang.String getRemoteId();
+    method public float getScore();
+  }
+
   public final class FillCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess(android.service.autofill.FillResponse);
@@ -37538,6 +37557,7 @@
     method public java.util.Map<android.view.autofill.AutofillId, java.lang.String> getChangedFields();
     method public android.os.Bundle getClientState();
     method public java.lang.String getDatasetId();
+    method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
     method public java.util.Set<java.lang.String> getIgnoredDatasetIds();
     method public java.util.Map<android.view.autofill.AutofillId, java.util.Set<java.lang.String>> getManuallyEnteredField();
     method public java.util.Set<java.lang.String> getSelectedDatasetIds();
@@ -37575,6 +37595,7 @@
     method public android.service.autofill.FillResponse.Builder disableAutofill(long);
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setFlags(int);
     method public android.service.autofill.FillResponse.Builder setFooter(android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
@@ -37658,6 +37679,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public abstract interface Scorer {
+  }
+
   public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
     method public int describeContents();
@@ -37668,6 +37692,22 @@
   public abstract interface Transformation {
   }
 
+  public final class UserData implements android.os.Parcelable {
+    method public int describeContents();
+    method public static int getMaxFieldClassificationIdsSize();
+    method public static int getMaxUserDataSize();
+    method public static int getMaxValueLength();
+    method public static int getMinValueLength();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
+  }
+
+  public static final class UserData.Builder {
+    ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
+    method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
+    method public android.service.autofill.UserData build();
+  }
+
   public abstract interface Validator {
   }
 
@@ -48755,9 +48795,11 @@
     method public void cancel();
     method public void commit();
     method public void disableAutofillServices();
+    method public android.service.autofill.UserData getUserData();
     method public boolean hasEnabledAutofillServices();
     method public boolean isAutofillSupported();
     method public boolean isEnabled();
+    method public boolean isFieldClassificationEnabled();
     method public void notifyValueChanged(android.view.View);
     method public void notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     method public void notifyViewEntered(android.view.View);
@@ -48769,6 +48811,7 @@
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
     method public void requestAutofill(android.view.View);
     method public void requestAutofill(android.view.View, int, android.graphics.Rect);
+    method public void setUserData(android.service.autofill.UserData);
     method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
     field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
diff --git a/api/system-current.txt b/api/system-current.txt
index 30e4cbc..227b483 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3531,6 +3531,11 @@
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+    field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
     field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 3fc5cd6..5635b56 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -463,34 +463,11 @@
   }
 
   public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
-    method public int describeContents();
-    method public static android.service.autofill.EditDistanceScorer getInstance();
     method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
   }
 
-  public final class FieldClassification implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
-  }
-
-  public static final class FieldClassification.Match implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.lang.String getRemoteId();
-    method public float getScore();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification.Match> CREATOR;
-  }
-
-  public static final class FillEventHistory.Event {
-    method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
-  }
-
-  public static final class FillResponse.Builder {
-    method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
+  public final class FillResponse implements android.os.Parcelable {
+    method public int getFlags();
   }
 
   public final class ImageTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -523,29 +500,10 @@
     method public boolean isValid(android.service.autofill.ValueFinder);
   }
 
-  public abstract interface Scorer {
-  }
-
   public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
   }
 
-  public final class UserData implements android.os.Parcelable {
-    method public int describeContents();
-    method public static int getMaxFieldClassificationIdsSize();
-    method public static int getMaxUserDataSize();
-    method public static int getMaxValueLength();
-    method public static int getMinValueLength();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
-  }
-
-  public static final class UserData.Builder {
-    ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
-    method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
-    method public android.service.autofill.UserData build();
-  }
-
   public abstract interface ValueFinder {
     method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
   }
@@ -993,12 +951,6 @@
     ctor public AutofillId(int);
   }
 
-  public final class AutofillManager {
-    method public android.service.autofill.UserData getUserData();
-    method public boolean isFieldClassificationEnabled();
-    method public void setUserData(android.service.autofill.UserData);
-  }
-
 }
 
 package android.widget {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index abc16ce..d0c1003 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5328,48 +5328,51 @@
         public static final String AUTOFILL_SERVICE = "autofill_service";
 
         /**
-         * Experimental autofill feature.
+         * Boolean indicating if Autofill supports field classification.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
+         * @see android.service.autofill.AutofillService
+         *
          * @hide
          */
+        @SystemApi
         @TestApi
         public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
                 "autofill_field_classification";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
                 "autofill_user_data_max_user_data_size";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by
+         * {@link android.service.autofill.UserData#getMaxFieldClassificationIdsSize()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
                 "autofill_user_data_max_field_classification_size";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
                 "autofill_user_data_max_value_length";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by {@link android.service.autofill.UserData#getMinValueLength()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
                 "autofill_user_data_min_value_length";
 
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 97fdcef..2600f8a 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -453,13 +453,46 @@
  * email address), the service should only use it locally (i.e., in the app's process) for
  * heuristics purposes, but it should not be sent to external servers.
  *
- * <a name="FieldsClassification"></a>
- * <h3>Metrics and fields classification</h3
+ * <a name="FieldClassification"></a>
+ * <h3>Metrics and field classification</h3
  *
- * <p>TODO(b/67867469): document it or remove this section; in particular, document the relationship
- * between set/getUserData(), FillResponse.setFieldClassificationIds(), and
- * FillEventHistory.getFieldsClassification.
+ * <p>The service can call {@link #getFillEventHistory()} to get metrics representing the user
+ * actions, and then use these metrics to improve its heuristics.
+ *
+ * <p>Prior to Android {@link android.os.Build.VERSION_CODES#P}, the metrics covered just the
+ * scenarios where the service knew how to autofill an activity, but Android
+ * {@link android.os.Build.VERSION_CODES#P} introduced a new mechanism called field classification,
+ * which allows the service to dinamically classify the meaning of fields based on the existing user
+ * data known by the service.
+ *
+ * <p>Typically, field classification can be used to detect fields that can be autofilled with
+ * user data that is not associated with a specific app&mdash;such as email and physical
+ * address. Once the service identifies that a such field was manually filled by the user, the
+ * service could use this signal to improve its heuristics, either locally (i.e., in the same
+ * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can
+ * be used by other users).
+ *
+ * <p>The field classification workflow involves 4 steps:
+ *
+ * <ol>
+ *   <li>Set the user data through {@link AutofillManager#setUserData(UserData)}. This data is
+ *   cached until the system restarts (or the service is disabled), so it doesn't need to be set for
+ *   all requests.
+ *   <li>Identify which fields should be analysed by calling
+ *   {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}.
+ *   <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}.
+ *   <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future
+ *   requests.
+ * </ol>
+ *
+ * <p>The field classification is an expensive operation and should be used carefully, otherwise it
+ * can reach its rate limit and get blocked by the Android System. Ideally, it should be used just
+ * in cases where the service could not determine how an activity can be autofilled, but it has a
+ * strong suspicious that it could. For example, if an activity has four or more fields and one of
+ * them is a list, chances are that these are address fields (like address, city, state, and
+ * zip code).
  */
+// TODO(b/70407264): add code snippets above???
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
 
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
index e25cd04..0706b37 100644
--- a/core/java/android/service/autofill/EditDistanceScorer.java
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -16,7 +16,6 @@
 package android.service.autofill;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.AutofillValue;
@@ -24,14 +23,8 @@
 /**
  * Helper used to calculate the classification score between an actual {@link AutofillValue} filled
  * by the user and the expected value predicted by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - document algorithm / copy from InternalScorer
- * - unhide / remove testApi
- * @hide
  */
-@TestApi
+// TODO(b/70291841): explain algorithm once it's fully implemented
 public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
 
     private static final EditDistanceScorer sInstance = new EditDistanceScorer();
@@ -46,10 +39,11 @@
     private EditDistanceScorer() {
     }
 
+    /** @hide */
     @Override
     public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
         if (actualValue == null || !actualValue.isText() || userData == null) return 0;
-        // TODO(b/67867469): implement edit distance - currently it's returning either 0, 100%, or
+        // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or
         // partial match when number of chars match
         final String textValue = actualValue.getTextValue().toString();
         final int total = textValue.length();
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 0a60208..b28c6f8 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -19,7 +19,6 @@
 import static android.view.autofill.Helper.sDebug;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.Helper;
@@ -31,15 +30,10 @@
 import java.util.List;
 
 /**
- * Gets the <a href="#FieldsClassification">fields classification</a> results for a given field.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * Represents the <a href="AutofillService.html#FieldClassification">field classification</a>
+ * results for a given field.
  */
-@TestApi
+// TODO(b/70291841): let caller handle Parcelable...
 public final class FieldClassification implements Parcelable {
 
     private final Match mMatch;
@@ -79,7 +73,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mMatch, flags);
+        mMatch.writeToParcel(parcel);
     }
 
     public static final Parcelable.Creator<FieldClassification> CREATOR =
@@ -87,7 +81,7 @@
 
         @Override
         public FieldClassification createFromParcel(Parcel parcel) {
-            return new FieldClassification(parcel.readParcelable(null));
+            return new FieldClassification(Match.readFromParcel(parcel));
         }
 
         @Override
@@ -97,16 +91,12 @@
     };
 
     /**
-     * Gets the score of a {@link UserData} entry for the field.
+     * Represents the score of a {@link UserData} entry for the field.
      *
-     * TODO(b/67867469):
-     * - improve javadoc
-     * - unhide / remove testApi
-     *
-     * @hide
+     * <p>The score is defined by {@link #getScore()} and the entry is identified by
+     * {@link #getRemoteId()}.
      */
-    @TestApi
-    public static final class Match implements Parcelable {
+    public static final class Match {
 
         private final String mRemoteId;
         private final float mScore;
@@ -126,26 +116,19 @@
         }
 
         /**
-         * Gets a score between the value of this field and the value of the {@link UserData} entry.
+         * Gets a classification score for the value of this field compared to the value of the
+         * {@link UserData} entry.
          *
-         * <p>The score is based in a case-insensitive comparisson of all characters from both the
-         * field value and the user data entry, and it ranges from {@code 0} to {@code 1000000}:
+         * <p>The score is based in a comparison of the field value and the user data entry, and it
+         * ranges from {@code 0.0F} to {@code 1.0F}:
          * <ul>
-         *   <li>{@code 1.0} represents a full match ({@code 100%}).
-         *   <li>{@code 0.0} represents a full mismatch ({@code 0%}).
+         *   <li>{@code 1.0F} represents a full match ({@code 100%}).
+         *   <li>{@code 0.0F} represents a full mismatch ({@code 0%}).
          *   <li>Any other value is a partial match.
          * </ul>
          *
-         * <p>How the score is calculated depends on the algorithm used by the Android System.
-         * For example, if the user  data is {@code "abc"} and the field value us {@code " abc"},
-         * the result could be:
-         * <ul>
-         *   <li>{@code 1.0} if the algorithm trims the values.
-         *   <li>{@code 0.0} if the algorithm compares the values sequentially.
-         *   <li>{@code 0.75} if the algorithm consideres that 3/4 (75%) of the characters match.
-         * </ul>
-         *
-         * <p>Currently, the autofill service cannot configure the algorithm.
+         * <p>How the score is calculated depends on the algorithm used by the {@link Scorer}
+         * implementation.
          */
         public float getScore() {
             return mScore;
@@ -160,33 +143,31 @@
             return string.append(", score=").append(mScore).toString();
         }
 
-        /////////////////////////////////////
-        // Parcelable "contract" methods. //
-        /////////////////////////////////////
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int flags) {
+        private void writeToParcel(@NonNull Parcel parcel) {
             parcel.writeString(mRemoteId);
             parcel.writeFloat(mScore);
         }
 
-        @SuppressWarnings("hiding")
-        public static final Parcelable.Creator<Match> CREATOR = new Parcelable.Creator<Match>() {
+        private static Match readFromParcel(@NonNull Parcel parcel) {
+            return new Match(parcel.readString(), parcel.readFloat());
+        }
 
-            @Override
-            public Match createFromParcel(Parcel parcel) {
-                return new Match(parcel.readString(), parcel.readFloat());
+        /** @hide */
+        public static Match[] readArrayFromParcel(@NonNull Parcel parcel) {
+            final int length = parcel.readInt();
+            final Match[] matches = new Match[length];
+            for (int i = 0; i < length; i++) {
+                matches[i] = readFromParcel(parcel);
             }
+            return matches;
+        }
 
-            @Override
-            public Match[] newArray(int size) {
-                return new Match[size];
+        /** @hide */
+        public static void writeArrayToParcel(@NonNull Parcel parcel, @NonNull Match[] matches) {
+            parcel.writeInt(matches.length);
+            for (int i = 0; i < matches.length; i++) {
+                matches[i].writeToParcel(parcel);
             }
-        };
+        }
     }
 }
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index a527f16..07fab61 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -21,7 +21,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -158,7 +157,7 @@
                 final AutofillId[] detectedFields = event.mDetectedFieldIds;
                 parcel.writeParcelableArray(detectedFields, flags);
                 if (detectedFields != null) {
-                    parcel.writeParcelableArray(event.mDetectedMatches, flags);
+                    Match.writeArrayToParcel(parcel, event.mDetectedMatches);
                 }
             }
         }
@@ -208,6 +207,7 @@
          *       ({@link #getIgnoredDatasetIds()}).
          *   <li>Which fields in the selected datasets were changed by the user after the dataset
          *       was selected ({@link #getChangedFields()}.
+         *   <li>Which fields match the {@link UserData} set by the service.
          * </ul>
          *
          * <p><b>Note: </b>This event is only generated when:
@@ -222,7 +222,6 @@
          * <p>See {@link android.view.autofill.AutofillManager} for more information about autofill
          * contexts.
          */
-        // TODO(b/67867469): update with field detection behavior
         public static final int TYPE_CONTEXT_COMMITTED = 4;
 
         /** @hide */
@@ -356,19 +355,13 @@
         }
 
         /**
-         * Gets the <a href="#FieldsClassification">fields classification</a> results.
+         * Gets the <a href="AutofillService.html#FieldClassification">field classification</a>
+         * results.
          *
          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
          * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
-         * fields classification}.
-         *
-         * TODO(b/67867469):
-         *  - improve javadoc
-         *  - unhide / remove testApi
-         *
-         * @hide
+         * field classification}.
          */
-        @TestApi
         @NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
             if (mDetectedFieldIds == null) {
                 return Collections.emptyMap();
@@ -462,6 +455,8 @@
          * and belonged to datasets.
          * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
          * respective entry on {@code manuallyFilledFieldIds}.
+         * @param detectedMatches the field classification matches.
+         *
          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
          * {@code changedDatasetIds} doesn't match.
          * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
@@ -469,7 +464,6 @@
          *
          * @hide
          */
-        // TODO(b/67867469): document field classification parameters once stable
         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
                 @Nullable List<String> selectedDatasetIds,
                 @Nullable ArraySet<String> ignoredDatasetIds,
@@ -477,7 +471,7 @@
                 @Nullable ArrayList<String> changedDatasetIds,
                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-                @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMaches) {
+                @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMatches) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
                     "eventType");
             mDatasetId = datasetId;
@@ -502,7 +496,7 @@
             mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
 
             mDetectedFieldIds = detectedFieldIds;
-            mDetectedMatches = detectedMaches;
+            mDetectedMatches = detectedMatches;
         }
 
         @Override
@@ -555,7 +549,7 @@
                         final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
                                 AutofillId.class);
                         final Match[] detectedMatches = (detectedFieldIds != null)
-                                ? parcel.readParcelableArray(null, Match.class)
+                                ? Match.readArrayFromParcel(parcel)
                                 : null;
 
                         selection.addEvent(new Event(eventType, datasetId, clientState,
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 014d3e8..3a4b6bb 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -41,7 +41,7 @@
 import java.util.List;
 
 /**
- * Response for a {@link
+ * Response for an {@link
  * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
  *
  * <p>See the main {@link AutofillService} documentation for more details and examples.
@@ -156,6 +156,7 @@
     }
 
     /** @hide */
+    @TestApi
     public int getFlags() {
         return mFlags;
     }
@@ -352,22 +353,18 @@
         }
 
         /**
-         * Sets which fields are used for <a href="#FieldsClassification">fields classification</a>
+         * Sets which fields are used for
+         * <a href="AutofillService.html#FieldClassification">field classification</a>
          *
+         * <p><b>Note:</b> This method automatically adds the
+         * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
+
          * @throws IllegalArgumentException is length of {@code ids} args is more than
          * {@link UserData#getMaxFieldClassificationIdsSize()}.
          * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
          * already called.
          * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
-         *
-         * TODO(b/67867469):
-         *  - improve javadoc: explain relationship with UserData and how to check results
-         *  - unhide / remove testApi
-         *  - implement multiple ids
-         *
-         * @hide
          */
-        @TestApi
         public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
             throwIfDestroyed();
             throwIfDisableAutofillCalled();
@@ -375,6 +372,7 @@
             Preconditions.checkArgumentInRange(ids.length, 1,
                     UserData.getMaxFieldClassificationIdsSize(), "ids length");
             mFieldClassificationIds = ids;
+            mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
             return this;
         }
 
@@ -423,9 +421,8 @@
          * @throws IllegalStateException if either {@link #addDataset(Dataset)},
          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
          *       {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
-         *       {link #setFieldClassificationIds(AutofillId...)} was already called.
+         *       {@link #setFieldClassificationIds(AutofillId...)} was already called.
          */
-        // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
         public Builder disableAutofill(long duration) {
             throwIfDestroyed();
             if (duration <= 0) {
@@ -506,14 +503,13 @@
          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
          *       {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
          *       {@link #setClientState(Bundle)},
-         *       or {link #setFieldClassificationIds(AutofillId...)}.
+         *       or {@link #setFieldClassificationIds(AutofillId...)}.
          *   <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
          *       without any previous calls to {@link #addDataset(Dataset)}.
          * </ol>
          *
          * @return A built response.
          */
-        // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
         public FillResponse build() {
             throwIfDestroyed();
             if (mAuthentication == null && mDatasets == null && mSaveInfo == null
diff --git a/core/java/android/service/autofill/Scorer.java b/core/java/android/service/autofill/Scorer.java
index f6a802a..c401855 100644
--- a/core/java/android/service/autofill/Scorer.java
+++ b/core/java/android/service/autofill/Scorer.java
@@ -15,21 +15,14 @@
  */
 package android.service.autofill;
 
-import android.annotation.TestApi;
-
 /**
  * Helper class used to calculate a score.
  *
- * <p>Typically used to calculate the field classification score between an actual
- * {@link android.view.autofill.AutofillValue}  filled by the user and the expected value predicted
- * by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- * @hide
+ * <p>Typically used to calculate the
+ * <a href="AutofillService.html#FieldClassification">field classification</a> score between an
+ * actual {@link android.view.autofill.AutofillValue} filled by the user and the expected value
+ * predicted by an autofill service.
  */
-@TestApi
 public interface Scorer {
 
 }
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 0d37815..f0cc360 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -23,7 +23,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.content.ContentResolver;
 import android.os.Parcel;
@@ -38,15 +37,9 @@
 import java.util.ArrayList;
 
 /**
- * Class used by service to improve autofillable fields detection by tracking the meaning of fields
- * manually edited by the user (when they match values provided by the service).
- *
- * TODO(b/67867469):
- *  - improve javadoc / add link to section on AutofillService
- *  - unhide / remove testApi
- * @hide
+ * Defines the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
  */
-@TestApi
 public final class UserData implements Parcelable {
 
     private static final String TAG = "UserData";
@@ -110,12 +103,7 @@
 
     /**
      * A builder for {@link UserData} objects.
-     *
-     * TODO(b/67867469): unhide / remove testApi
-     *
-     * @hide
      */
-    @TestApi
     public static final class Builder {
         private final InternalScorer mScorer;
         private final ArrayList<String> mRemoteIds;
@@ -123,7 +111,7 @@
         private boolean mDestroyed;
 
         /**
-         * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
+         * Creates a new builder for the user data used for <a href="#FieldClassification">field
          * classification</a>.
          *
          * @throws IllegalArgumentException if any of the following occurs:
@@ -288,14 +276,16 @@
     }
 
     /**
-     * Gets the minimum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+     * Gets the minimum length of values passed to the builder's constructor or
+     * or {@link Builder#add(String, String)}.
      */
     public static int getMinValueLength() {
         return getInt(AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, DEFAULT_MIN_VALUE_LENGTH);
     }
 
     /**
-     * Gets the maximum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+     * Gets the maximum length of values passed to the builder's constructor or
+     * or {@link Builder#add(String, String)}.
      */
     public static int getMaxValueLength() {
         return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 6d84aa0..d0dbff0e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1010,20 +1009,14 @@
     }
 
     /**
-     * Gets the user data used for <a href="#FieldsClassification">fields classification</a>.
+     * Gets the user data used for
+     * <a href="AutofillService.html#FieldClassification">field classification</a>.
      *
      * <p><b>Note:</b> This method should only be called by an app providing an autofill service.
      *
-     * TODO(b/67867469):
-     *  - proper javadoc
-     *  - unhide / remove testApi
-     *
      * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
      * reset or if the caller currently does not have an enabled autofill service for the user.
-     *
-     * @hide
      */
-    @TestApi
     @Nullable public UserData getUserData() {
         try {
             return mService.getUserData();
@@ -1034,21 +1027,13 @@
     }
 
     /**
-     * Sets the user data used for <a href="#FieldsClassification">fields classification</a>.
+     * Sets the user data used for
+     * <a href="AutofillService.html#FieldClassification">field classification</a>
      *
      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
      * and it's ignored if the caller currently doesn't have an enabled autofill service for
      * the user.
-     *
-     * TODO(b/67867469):
-     *  - proper javadoc
-     *  - unhide / remove testApi
-     *  - add unit tests:
-     *    - call set / get / verify
-     *
-     * @hide
      */
-    @TestApi
     public void setUserData(@Nullable UserData userData) {
         try {
             mService.setUserData(userData);
@@ -1058,13 +1043,17 @@
     }
 
     /**
-     * TODO(b/67867469):
-     * - proper javadoc
-     * - mention this method in other places
-     * - unhide / remove testApi
-     * @hide
+     * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
+     * enabled.
+     *
+     * <p>As field classification is an expensive operation, it could be disabled, either
+     * temporarily (for example, because the service exceeded a rate-limit threshold) or
+     * permanently (for example, because the device is a low-level device).
+     *
+     * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+     * and it's ignored if the caller currently doesn't have an enabled autofill service for
+     * the user.
      */
-    @TestApi
     public boolean isFieldClassificationEnabled() {
         try {
             return mService.isFieldClassificationEnabled();
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 907a1b8..1002453 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -421,13 +421,6 @@
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                 // TODO(b/67867469): Move autofill settings below to
-                 // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
-                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
-                 Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
-                 Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
-                 Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
-                 Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
                  Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
                  Settings.Secure.ALWAYS_ON_VPN_APP,
                  Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
@@ -437,6 +430,11 @@
                  Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
+                 Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
                  Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 56f5f64..ceae93c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -512,7 +512,6 @@
         }
 
         final AutofillId[] fieldClassificationIds = response.getFieldClassificationIds();
-        // TODO(b/67867469): remove once feature is finished (or use method from AFM to check)
         if (fieldClassificationIds != null && !mService.isFieldClassificationEnabled()) {
             Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
             processNullResponseLocked(requestFlags);
@@ -1662,7 +1661,7 @@
                         isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
                 mViewStates.put(id, viewState);
 
-                // TODO(b/67867469): for optimization purposes, should also ignore if change is
+                // TODO(b/70407264): for optimization purposes, should also ignore if change is
                 // detectable, and batch-send them when the session is finished (but that will
                 // require tracking detectable fields on AutofillManager)
                 if (isIgnored) {