summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/test-current.txt32
-rw-r--r--core/java/android/provider/Settings.java36
-rw-r--r--core/java/android/service/autofill/AutofillService.java8
-rw-r--r--core/java/android/service/autofill/FieldsDetection.java127
-rw-r--r--core/java/android/service/autofill/FillEventHistory.java35
-rw-r--r--core/java/android/service/autofill/FillResponse.java58
-rw-r--r--core/java/android/service/autofill/UserData.aidl20
-rw-r--r--core/java/android/service/autofill/UserData.java288
-rw-r--r--core/java/android/view/autofill/AutofillManager.java50
-rw-r--r--core/java/android/view/autofill/AutofillValue.java2
-rw-r--r--core/java/android/view/autofill/Helper.java31
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl3
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java6
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java33
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java73
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java10
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java27
17 files changed, 631 insertions, 208 deletions
diff --git a/api/test-current.txt b/api/test-current.txt
index b18153885867..de0945b511da 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -457,19 +457,12 @@ package android.service.autofill {
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
}
- public final class FieldsDetection implements android.os.Parcelable {
- ctor public FieldsDetection(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.autofill.FieldsDetection> CREATOR;
- }
-
public static final class FillEventHistory.Event {
- method public java.util.Map<java.lang.String, java.lang.Integer> getDetectedFields();
+ method public java.util.Map<java.lang.String, java.lang.Integer> getFieldsClassification();
}
public static final class FillResponse.Builder {
- method public android.service.autofill.FillResponse.Builder setFieldsDetection(android.service.autofill.FieldsDetection);
+ method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
}
public final class ImageTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -501,6 +494,22 @@ package android.service.autofill {
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(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);
}
@@ -983,6 +992,11 @@ package android.view.autofill {
ctor public AutofillId(int);
}
+ public final class AutofillManager {
+ method public android.service.autofill.UserData getUserData();
+ 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 1e0948a46198..775b822c39aa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5333,6 +5333,42 @@ public final class Settings {
public static final String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
/**
+ * Experimental autofill feature.
+ *
+ * <p>TODO(b/67867469): document (or remove) once feature is finished
+ * @hide
+ */
+ public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
+ "autofill_user_data_max_user_data_size";
+
+ /**
+ * Experimental autofill feature.
+ *
+ * <p>TODO(b/67867469): document (or remove) once feature is finished
+ * @hide
+ */
+ public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
+ "autofill_user_data_max_field_classification_size";
+
+ /**
+ * Experimental autofill feature.
+ *
+ * <p>TODO(b/67867469): document (or remove) once feature is finished
+ * @hide
+ */
+ public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
+ "autofill_user_data_max_value_length";
+
+ /**
+ * Experimental autofill feature.
+ *
+ * <p>TODO(b/67867469): document (or remove) once feature is finished
+ * @hide
+ */
+ public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
+ "autofill_user_data_min_value_length";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
*/
@Deprecated
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index cd362c712b3f..1afa8b3eb4dd 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -440,7 +440,6 @@ import com.android.internal.os.SomeArgs;
* save(username, password);
* </pre>
*
- *
* <a name="Privacy"></a>
* <h3>Privacy</h3>
*
@@ -453,6 +452,13 @@ import com.android.internal.os.SomeArgs;
* <p>Because this data could contain PII (Personally Identifiable Information, such as username or
* 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
+ *
+ * <p>TODO(b/67867469): document it or remove this section; in particular, document the relationship
+ * between set/getUserData(), FillResponse.setFieldClassificationIds(), and
+ * FillEventHistory.getFieldsClassification.
*/
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/FieldsDetection.java b/core/java/android/service/autofill/FieldsDetection.java
deleted file mode 100644
index 550ecf687349..000000000000
--- a/core/java/android/service/autofill/FieldsDetection.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.service.autofill;
-
-import android.annotation.TestApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.autofill.AutofillId;
-
-/**
- * Class 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):
- * - proper javadoc
- * - unhide / remove testApi
- * - add FieldsDetection management so service can set it just once and reference it in further
- * calls to improve performance (and also API to refresh it)
- * - rename to FieldsDetectionInfo or FieldClassification? (same for CTS tests)
- * - add FieldsDetectionUnitTest once API is well-defined
- * @hide
- */
-@TestApi
-public final class FieldsDetection implements Parcelable {
-
- private final AutofillId mFieldId;
- private final String mRemoteId;
- private final String mValue;
-
- /**
- * Creates a field detection for just one field / value pair.
- *
- * @param fieldId autofill id of the field in the screen.
- * @param remoteId id used by the service to identify the field later.
- * @param value field value known to the service.
- *
- * TODO(b/67867469):
- * - proper javadoc
- * - change signature to allow more fields / values / match methods
- * - might also need to use a builder, where the constructor is the id for the fieldsdetector
- * - might need id for values as well
- * - add @NonNull / check it / add unit tests
- * - make 'value' input more generic so it can accept distance-based match and other matches
- * - throw exception if field value is less than X characters (somewhere between 7-10)
- * - make sure to limit total number of fields to around 10 or so
- * - use AutofillValue instead of String (so it can compare dates, for example)
- */
- public FieldsDetection(AutofillId fieldId, String remoteId, String value) {
- mFieldId = fieldId;
- mRemoteId = remoteId;
- mValue = value;
- }
-
- /** @hide */
- public AutofillId getFieldId() {
- return mFieldId;
- }
-
- /** @hide */
- public String getRemoteId() {
- return mRemoteId;
- }
-
- /** @hide */
- public String getValue() {
- return mValue;
- }
-
- /////////////////////////////////////
- // Object "contract" methods. //
- /////////////////////////////////////
- @Override
- public String toString() {
- // Cannot disclose remoteId or value because they could contain PII
- return new StringBuilder("FieldsDetection: [field=").append(mFieldId)
- .append(", remoteId_length=").append(mRemoteId.length())
- .append(", value_length=").append(mValue.length())
- .append("]").toString();
- }
-
- /////////////////////////////////////
- // Parcelable "contract" methods. //
- /////////////////////////////////////
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mFieldId, flags);
- parcel.writeString(mRemoteId);
- parcel.writeString(mValue);
- }
-
- public static final Parcelable.Creator<FieldsDetection> CREATOR =
- new Parcelable.Creator<FieldsDetection>() {
- @Override
- public FieldsDetection createFromParcel(Parcel parcel) {
- // TODO(b/67867469): remove comment below if it does not use a builder at the end
- // Always go through the builder to ensure the data ingested by
- // the system obeys the contract of the builder to avoid attacks
- // using specially crafted parcels.
- return new FieldsDetection(parcel.readParcelable(null), parcel.readString(),
- parcel.readString());
- }
-
- @Override
- public FieldsDetection[] newArray(int size) {
- return new FieldsDetection[size];
- }
- };
-}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 736d9ef48d04..eedb972e4ea6 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -58,11 +58,6 @@ import java.util.Set;
*/
public final class FillEventHistory implements Parcelable {
/**
- * Not in parcel. The UID of the {@link AutofillService} that created the {@link FillResponse}.
- */
- private final int mServiceUid;
-
- /**
* Not in parcel. The ID of the autofill session that created the {@link FillResponse}.
*/
private final int mSessionId;
@@ -70,17 +65,6 @@ public final class FillEventHistory implements Parcelable {
@Nullable private final Bundle mClientState;
@Nullable List<Event> mEvents;
- /**
- * Gets the UID of the {@link AutofillService} that created the {@link FillResponse}.
- *
- * @return The UID of the {@link AutofillService}
- *
- * @hide
- */
- public int getServiceUid() {
- return mServiceUid;
- }
-
/** @hide */
public int getSessionId() {
return mSessionId;
@@ -123,9 +107,8 @@ public final class FillEventHistory implements Parcelable {
/**
* @hide
*/
- public FillEventHistory(int serviceUid, int sessionId, @Nullable Bundle clientState) {
+ public FillEventHistory(int sessionId, @Nullable Bundle clientState) {
mClientState = clientState;
- mServiceUid = serviceUid;
mSessionId = sessionId;
}
@@ -364,16 +347,17 @@ public final class FillEventHistory implements Parcelable {
}
/**
- * Gets the results of the last {@link FieldsDetection} request.
+ * Gets the results of the last fields classification request.
*
* @return map of edit-distance match ({@code 0} means full match,
- * {@code 1} means 1 character different, etc...) by remote id (as set in the
- * {@link FieldsDetection} constructor), or {@code null} if none of the user-input values
+ * {@code 1} means 1 character different, etc...) by remote id (as set on
+ * {@link UserData.Builder#add(String, android.view.autofill.AutofillValue)}),
+ * or {@code null} if none of the user-input values
* matched the requested detection.
*
* <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
- * service requested {@link FillResponse.Builder#setFieldsDetection(FieldsDetection) fields
- * detection}.
+ * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
+ * fields detection}.
*
* TODO(b/67867469):
* - improve javadoc
@@ -382,11 +366,12 @@ public final class FillEventHistory implements Parcelable {
* - unhide
* - unhide / remove testApi
* - add @NonNull / check it / add unit tests
+ * - add link to AutofillService #FieldsClassification anchor
*
* @hide
*/
@TestApi
- @NonNull public Map<String, Integer> getDetectedFields() {
+ @NonNull public Map<String, Integer> getFieldsClassification() {
if (mDetectedRemoteId == null || mDetectedFieldScore == -1) {
return Collections.emptyMap();
}
@@ -534,7 +519,7 @@ public final class FillEventHistory implements Parcelable {
new Parcelable.Creator<FillEventHistory>() {
@Override
public FillEventHistory createFromParcel(Parcel parcel) {
- FillEventHistory selection = new FillEventHistory(0, 0, parcel.readBundle());
+ FillEventHistory selection = new FillEventHistory(0, parcel.readBundle());
final int numEvents = parcel.readInt();
for (int i = 0; i < numEvents; i++) {
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 84a0974d11cd..dff40ffc3e18 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -76,7 +76,7 @@ public final class FillResponse implements Parcelable {
private final @Nullable AutofillId[] mAuthenticationIds;
private final @Nullable AutofillId[] mIgnoredIds;
private final long mDisableDuration;
- private final @Nullable FieldsDetection mFieldsDetection;
+ private final @Nullable AutofillId[] mFieldClassificationIds;
private final int mFlags;
private int mRequestId;
@@ -89,7 +89,7 @@ public final class FillResponse implements Parcelable {
mAuthenticationIds = builder.mAuthenticationIds;
mIgnoredIds = builder.mIgnoredIds;
mDisableDuration = builder.mDisableDuration;
- mFieldsDetection = builder.mFieldsDetection;
+ mFieldClassificationIds = builder.mFieldClassificationIds;
mFlags = builder.mFlags;
mRequestId = INVALID_REQUEST_ID;
}
@@ -135,8 +135,8 @@ public final class FillResponse implements Parcelable {
}
/** @hide */
- public @Nullable FieldsDetection getFieldsDetection() {
- return mFieldsDetection;
+ public @Nullable AutofillId[] getFieldClassificationIds() {
+ return mFieldClassificationIds;
}
/** @hide */
@@ -175,7 +175,7 @@ public final class FillResponse implements Parcelable {
private AutofillId[] mAuthenticationIds;
private AutofillId[] mIgnoredIds;
private long mDisableDuration;
- private FieldsDetection mFieldsDetection;
+ private AutofillId[] mFieldClassificationIds;
private int mFlags;
private boolean mDestroyed;
@@ -329,21 +329,29 @@ public final class FillResponse implements Parcelable {
}
/**
+ * Sets which fields are used for <a href="#FieldsClassification">fields classification</a>
+ *
+ * @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):
- * - javadoc it
- * - javadoc how to check results
- * - unhide
+ * - improve javadoc: explain relationship with UserData and how to check results
* - unhide / remove testApi
- * - throw exception (and document) if response has datasets or saveinfo
- * - throw exception (and document) if id on fieldsDetection is ignored
+ * - implement multiple ids
*
* @hide
*/
@TestApi
- public Builder setFieldsDetection(@NonNull FieldsDetection fieldsDetection) {
+ public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
throwIfDestroyed();
throwIfDisableAutofillCalled();
- mFieldsDetection = Preconditions.checkNotNull(fieldsDetection);
+ Preconditions.checkArrayElementsNotNull(ids, "ids");
+ Preconditions.checkArgumentInRange(ids.length, 1,
+ UserData.getMaxFieldClassificationIdsSize(), "ids length");
+ mFieldClassificationIds = ids;
return this;
}
@@ -391,16 +399,17 @@ public final class FillResponse implements Parcelable {
* @throws IllegalArgumentException if {@code duration} is not a positive number.
* @throws IllegalStateException if either {@link #addDataset(Dataset)},
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
- * {@link #setSaveInfo(SaveInfo)}, or {@link #setClientState(Bundle)}
- * was already called.
+ * {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
+ * {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) {
throw new IllegalArgumentException("duration must be greater than 0");
}
if (mAuthentication != null || mDatasets != null || mSaveInfo != null
- || mFieldsDetection != null || mClientState != null) {
+ || mFieldClassificationIds != null || mClientState != null) {
throw new IllegalStateException("disableAutofill() must be the only method called");
}
@@ -417,15 +426,18 @@ public final class FillResponse implements Parcelable {
* <li>No call was made to {@link #addDataset(Dataset)},
* {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
* {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
- * or {@link #setClientState(Bundle)}.
+ * {@link #setClientState(Bundle)},
+ * or {link #setFieldClassificationIds(AutofillId...)}.
* </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
- && mDisableDuration == 0 && mFieldsDetection == null && mClientState == null) {
+ && mDisableDuration == 0 && mFieldClassificationIds == null
+ && mClientState == null) {
throw new IllegalStateException("need to provide: at least one DataSet, or a "
+ "SaveInfo, or an authentication with a presentation, "
+ "or a FieldsDetection, or a client state, or disable autofill");
@@ -466,7 +478,8 @@ public final class FillResponse implements Parcelable {
.append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
.append(", disableDuration=").append(mDisableDuration)
.append(", flags=").append(mFlags)
- .append(", fieldDetection=").append(mFieldsDetection)
+ .append(", fieldClassificationIds=")
+ .append(Arrays.toString(mFieldClassificationIds))
.append("]")
.toString();
}
@@ -490,7 +503,7 @@ public final class FillResponse implements Parcelable {
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelableArray(mIgnoredIds, flags);
parcel.writeLong(mDisableDuration);
- parcel.writeParcelable(mFieldsDetection, flags);
+ parcel.writeParcelableArray(mFieldClassificationIds, flags);
parcel.writeInt(mFlags);
parcel.writeInt(mRequestId);
}
@@ -526,9 +539,10 @@ public final class FillResponse implements Parcelable {
if (disableDuration > 0) {
builder.disableAutofill(disableDuration);
}
- final FieldsDetection fieldsDetection = parcel.readParcelable(null);
- if (fieldsDetection != null) {
- builder.setFieldsDetection(fieldsDetection);
+ final AutofillId[] fieldClassifactionIds =
+ parcel.readParcelableArray(null, AutofillId.class);
+ if (fieldClassifactionIds != null) {
+ builder.setFieldClassificationIds(fieldClassifactionIds);
}
builder.setFlags(parcel.readInt());
diff --git a/core/java/android/service/autofill/UserData.aidl b/core/java/android/service/autofill/UserData.aidl
new file mode 100644
index 000000000000..76016ded424a
--- /dev/null
+++ b/core/java/android/service/autofill/UserData.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+parcelable UserData;
+parcelable UserData.Constraints;
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
new file mode 100644
index 000000000000..16d8d4ad3d86
--- /dev/null
+++ b/core/java/android/service/autofill/UserData.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.autofill.Helper;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+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
+ */
+@TestApi
+public final class UserData implements Parcelable {
+
+ private static final String TAG = "UserData";
+
+ private static final int DEFAULT_MAX_USER_DATA_SIZE = 10;
+ private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10;
+ private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
+ private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
+
+ private final String[] mRemoteIds;
+ private final String[] mValues;
+
+ private UserData(Builder builder) {
+ mRemoteIds = new String[builder.mRemoteIds.size()];
+ builder.mRemoteIds.toArray(mRemoteIds);
+ mValues = new String[builder.mValues.size()];
+ builder.mValues.toArray(mValues);
+ }
+
+ /** @hide */
+ public String[] getRemoteIds() {
+ return mRemoteIds;
+ }
+
+ /** @hide */
+ public String[] getValues() {
+ return mValues;
+ }
+
+ /** @hide */
+ public void dump(String prefix, PrintWriter pw) {
+ // Cannot disclose remote ids because they could contain PII
+ pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
+ for (int i = 0; i < mValues.length; i++) {
+ pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": "); pw.println(mValues[i]);
+ }
+ }
+
+ /** @hide */
+ public static void dumpConstraints(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("maxUserDataSize: "); pw.println(getMaxUserDataSize());
+ pw.print(prefix); pw.print("maxFieldClassificationIdsSize: ");
+ pw.println(getMaxFieldClassificationIdsSize());
+ pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength());
+ pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength());
+ }
+
+ /**
+ * A builder for {@link UserData} objects.
+ *
+ * TODO(b/67867469): unhide / remove testApi
+ *
+ * @hide
+ */
+ @TestApi
+ public static final class Builder {
+ private final ArraySet<String> mRemoteIds;
+ private final ArrayList<String> mValues;
+ private boolean mDestroyed;
+
+ /**
+ * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
+ * classification</a>.
+ *
+ * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
+ * length of {@code value} is lower than {@link UserData#getMinValueLength()}
+ * or higher than {@link UserData#getMaxValueLength()}.
+ */
+ public Builder(@NonNull String remoteId, @NonNull String value) {
+ checkValidRemoteId(remoteId);
+ checkValidValue(value);
+ final int capacity = getMaxUserDataSize();
+ mRemoteIds = new ArraySet<>(capacity);
+ mValues = new ArrayList<>(capacity);
+ mRemoteIds.add(remoteId);
+ mValues.add(value);
+ }
+
+ /**
+ * Adds a new value for user data.
+ *
+ * @param remoteId unique string used to identify the user data.
+ * @param value value of the user data.
+ *
+ * @throws IllegalStateException if {@link #build()} or
+ * {@link #add(String, String)} with the same {@code remoteId} has already
+ * been called, or if the number of values add (i.e., calls made to this method plus
+ * constructor) is more than {@link UserData#getMaxUserDataSize()}.
+ *
+ * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
+ * length of {@code value} is lower than {@link UserData#getMinValueLength()}
+ * or higher than {@link UserData#getMaxValueLength()}.
+ */
+ public Builder add(@NonNull String remoteId, @NonNull String value) {
+ throwIfDestroyed();
+ checkValidRemoteId(remoteId);
+ checkValidValue(value);
+
+ Preconditions.checkState(!mRemoteIds.contains(remoteId),
+ // Don't include remoteId on message because it could contain PII
+ "already has entry with same remoteId");
+ Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
+ "already added " + mRemoteIds.size() + " elements");
+ mRemoteIds.add(remoteId);
+ mValues.add(value);
+ return this;
+ }
+
+ private void checkValidRemoteId(@Nullable String remoteId) {
+ Preconditions.checkNotNull(remoteId);
+ Preconditions.checkArgument(!remoteId.isEmpty(), "remoteId cannot be empty");
+ }
+
+ private void checkValidValue(@Nullable String value) {
+ Preconditions.checkNotNull(value);
+ final int length = value.length();
+ Preconditions.checkArgumentInRange(length, getMinValueLength(),
+ getMaxValueLength(), "value length (" + length + ")");
+ }
+
+ /**
+ * Creates a new {@link UserData} instance.
+ *
+ * <p>You should not interact with this builder once this method is called.
+ *
+ * @throws IllegalStateException if {@link #build()} was already called.
+ *
+ * @return The built dataset.
+ */
+ public UserData build() {
+ throwIfDestroyed();
+ mDestroyed = true;
+ return new UserData(this);
+ }
+
+ private void throwIfDestroyed() {
+ if (mDestroyed) {
+ throw new IllegalStateException("Already called #build()");
+ }
+ }
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ if (!sDebug) return super.toString();
+
+ // Cannot disclose keys or values because they could contain PII
+ final StringBuilder builder = new StringBuilder("UserData: [remoteIds=");
+ Helper.appendRedacted(builder, mRemoteIds);
+ builder.append(", values=");
+ Helper.appendRedacted(builder, mValues);
+ return builder.append("]").toString();
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeStringArray(mRemoteIds);
+ parcel.writeStringArray(mValues);
+ }
+
+ public static final Parcelable.Creator<UserData> CREATOR =
+ new Parcelable.Creator<UserData>() {
+ @Override
+ public UserData createFromParcel(Parcel parcel) {
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ final String[] remoteIds = parcel.readStringArray();
+ final String[] values = parcel.readStringArray();
+ final Builder builder = new Builder(remoteIds[0], values[0]);
+ for (int i = 1; i < remoteIds.length; i++) {
+ builder.add(remoteIds[i], values[i]);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public UserData[] newArray(int size) {
+ return new UserData[size];
+ }
+ };
+
+ /**
+ * Gets the maximum number of values that can be added to a {@link UserData}.
+ */
+ public static int getMaxUserDataSize() {
+ return getInt(AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE, DEFAULT_MAX_USER_DATA_SIZE);
+ }
+
+ /**
+ * Gets the maximum number of ids that can be passed to {@link
+ * FillResponse.Builder#setFieldClassificationIds(android.view.autofill.AutofillId...)}.
+ */
+ public static int getMaxFieldClassificationIdsSize() {
+ return getInt(AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
+ DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE);
+ }
+
+ /**
+ * Gets the minimum length of values passed to {@link Builder#Builder(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(String, String)}.
+ */
+ public static int getMaxValueLength() {
+ return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
+ }
+
+ private static int getInt(String settings, int defaultValue) {
+ ContentResolver cr = null;
+ final ActivityThread at = ActivityThread.currentActivityThread();
+ if (at != null) {
+ cr = at.getApplication().getContentResolver();
+ }
+
+ if (cr == null) {
+ Log.w(TAG, "Could not read from " + settings + "; hardcoding " + defaultValue);
+ return defaultValue;
+ }
+ return Settings.Secure.getInt(cr, settings, defaultValue);
+ }
+}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 547e0db9e841..9a99e5398c8e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -24,6 +24,7 @@ import android.annotation.IntDef;
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;
@@ -36,6 +37,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.UserData;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -1007,6 +1009,54 @@ public final class AutofillManager {
}
/**
+ * Gets the user data used for <a href="#FieldsClassification">fields 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();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ /**
+ * Sets the user data used for <a href="#FieldsClassification">fields 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);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns {@code true} if autofill is supported by the current device and
* is supported for this user.
*
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 3beae11cf38c..8e649de52c97 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -177,7 +177,7 @@ public final class AutofillValue implements Parcelable {
.append("[type=").append(mType)
.append(", value=");
if (isText()) {
- string.append(((CharSequence) mValue).length()).append("_chars");
+ Helper.appendRedacted(string, (CharSequence) mValue);
} else {
string.append(mValue);
}
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index 829e7f3aa5ac..b95704af7d44 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -16,6 +16,8 @@
package android.view.autofill;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Bundle;
import java.util.Arrays;
@@ -50,6 +52,35 @@ public final class Helper {
return builder;
}
+ /**
+ * Appends {@code value} to the {@code builder} redacting its contents.
+ */
+ public static void appendRedacted(@NonNull StringBuilder builder,
+ @Nullable CharSequence value) {
+ if (value == null) {
+ builder.append("null");
+ } else {
+ builder.append(value.length()).append("_chars");
+ }
+ }
+
+ /**
+ * Appends {@code values} to the {@code builder} redacting its contents.
+ */
+ public static void appendRedacted(@NonNull StringBuilder builder, @Nullable String[] values) {
+ if (values == null) {
+ builder.append("N/A");
+ return;
+ }
+ builder.append("[");
+ for (String value : values) {
+ builder.append(" '");
+ appendRedacted(builder, value);
+ builder.append("'");
+ }
+ builder.append(" ]");
+ }
+
private Helper() {
throw new UnsupportedOperationException("contains static members only");
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index d6db3fe573f5..7d6a19f529ce 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -21,6 +21,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.UserData;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
@@ -53,4 +54,6 @@ interface IAutoFillManager {
boolean isServiceSupported(int userId);
boolean isServiceEnabled(int userId, String packageName);
void onPendingSaveUi(int operation, IBinder token);
+ UserData getUserData();
+ void setUserData(in UserData userData);
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 4ce60294e615..2e1c0a1d1fde 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -419,7 +419,13 @@ public class SettingsBackupTest {
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_DETECTION,
+ 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,
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 23e4f504735b..02912763509f 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -52,6 +52,7 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.UserData;
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -581,6 +582,34 @@ public final class AutofillManagerService extends SystemService {
}
@Override
+ public UserData getUserData() throws RemoteException {
+ UserHandle user = getCallingUserHandle();
+ int uid = getCallingUid();
+
+ synchronized (mLock) {
+ AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+ if (service != null) {
+ return service.getUserData(uid);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void setUserData(UserData userData) throws RemoteException {
+ UserHandle user = getCallingUserHandle();
+ int uid = getCallingUid();
+
+ synchronized (mLock) {
+ AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+ if (service != null) {
+ service.setUserData(uid, userData);
+ }
+ }
+ }
+
+ @Override
public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
throws RemoteException {
activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
@@ -723,6 +752,7 @@ public final class AutofillManagerService extends SystemService {
}
boolean oldDebug = sDebug;
+ final String prefix = " ";
try {
synchronized (mLock) {
oldDebug = sDebug;
@@ -731,6 +761,7 @@ public final class AutofillManagerService extends SystemService {
pw.print("Verbose mode: "); pw.println(sVerbose);
pw.print("Disabled users: "); pw.println(mDisabledUsers);
pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
+ pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw);
final int size = mServicesCache.size();
pw.print("Cached services: ");
if (size == 0) {
@@ -740,7 +771,7 @@ public final class AutofillManagerService extends SystemService {
for (int i = 0; i < size; i++) {
pw.print("\nService at index "); pw.println(i);
final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
- impl.dumpLocked(" ", pw);
+ impl.dumpLocked(prefix, pw);
}
}
mUi.dump(pw);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 21e27220b621..8b6dc2028b91 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -51,6 +51,7 @@ import android.service.autofill.FillEventHistory;
import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
+import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -121,6 +122,11 @@ final class AutofillManagerServiceImpl {
private boolean mDisabled;
/**
+ * Data used for field classification.
+ */
+ private UserData mUserData;
+
+ /**
* Caches whether the setup completed for the current user.
*/
@GuardedBy("mLock")
@@ -183,6 +189,14 @@ final class AutofillManagerServiceImpl {
}
}
+ private int getServiceUidLocked() {
+ if (mInfo == null) {
+ Slog.w(TAG, "getServiceUidLocked(): no mInfo");
+ return -1;
+ }
+ return mInfo.getServiceInfo().applicationInfo.uid;
+ }
+
@Nullable
String getServicePackageName() {
final ComponentName serviceComponent = getServiceComponentName();
@@ -574,9 +588,9 @@ final class AutofillManagerServiceImpl {
* Initializes the last fill selection after an autofill service returned a new
* {@link FillResponse}.
*/
- void setLastResponse(int serviceUid, int sessionId, @NonNull FillResponse response) {
+ void setLastResponse(int sessionId, @NonNull FillResponse response) {
synchronized (mLock) {
- mEventHistory = new FillEventHistory(serviceUid, sessionId, response.getClientState());
+ mEventHistory = new FillEventHistory(sessionId, response.getClientState());
}
}
@@ -688,18 +702,54 @@ final class AutofillManagerServiceImpl {
*/
FillEventHistory getFillEventHistory(int callingUid) {
synchronized (mLock) {
- if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) {
+ if (mEventHistory != null
+ && isCalledByServiceLocked("getFillEventHistory", callingUid)) {
return mEventHistory;
}
}
+ return null;
+ }
+ // Called by Session - does not need to check uid
+ UserData getUserData() {
+ synchronized (mLock) {
+ return mUserData;
+ }
+ }
+
+ // Called by AutofillManager
+ UserData getUserData(int callingUid) {
+ synchronized (mLock) {
+ if (isCalledByServiceLocked("getUserData", callingUid)) {
+ return mUserData;
+ }
+ }
return null;
}
+ // Called by AutofillManager
+ void setUserData(int callingUid, UserData userData) {
+ synchronized (mLock) {
+ if (isCalledByServiceLocked("setUserData", callingUid)) {
+ mUserData = userData;
+ }
+ }
+ }
+
+ private boolean isCalledByServiceLocked(String methodName, int callingUid) {
+ if (getServiceUidLocked() != callingUid) {
+ Slog.w(TAG, methodName + "() called by UID " + callingUid
+ + ", but service UID is " + getServiceUidLocked());
+ return false;
+ }
+ return true;
+ }
+
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+ pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
pw.print(prefix); pw.print("Component: "); pw.println(mInfo != null
? mInfo.getServiceInfo().getComponentName() : null);
pw.print(prefix); pw.print("Component from settings: ");
@@ -762,8 +812,13 @@ final class AutofillManagerServiceImpl {
}
}
- pw.print(prefix); pw.println("Clients");
- mClients.dump(pw, prefix2);
+ pw.print(prefix); pw.print("Clients: ");
+ if (mClients == null) {
+ pw.println("N/A");
+ } else {
+ pw.println();
+ mClients.dump(pw, prefix2);
+ }
if (mEventHistory == null || mEventHistory.getEvents() == null
|| mEventHistory.getEvents().size() == 0) {
@@ -779,6 +834,14 @@ final class AutofillManagerServiceImpl {
+ event.getDatasetId());
}
}
+
+ pw.print(prefix); pw.print("User data: ");
+ if (mUserData == null) {
+ pw.println("N/A");
+ } else {
+ pw.println();
+ mUserData.dump(prefix2, pw);
+ }
}
void destroySessionsLocked() {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 831c488e95b2..aea9ad0e33da 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -97,7 +97,7 @@ final class RemoteFillService implements DeathRecipient {
private PendingRequest mPendingRequest;
public interface FillServiceCallbacks {
- void onFillRequestSuccess(int requestFlags, @Nullable FillResponse response, int serviceUid,
+ void onFillRequestSuccess(int requestFlags, @Nullable FillResponse response,
@NonNull String servicePackageName);
void onFillRequestFailure(@Nullable CharSequence message,
@NonNull String servicePackageName);
@@ -281,11 +281,11 @@ final class RemoteFillService implements DeathRecipient {
mContext.unbindService(mServiceConnection);
}
- private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
- int callingUid, int requestFlags, FillResponse response) {
+ private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest, int requestFlags,
+ FillResponse response) {
mHandler.getHandler().post(() -> {
if (handleResponseCallbackCommon(pendingRequest)) {
- mCallbacks.onFillRequestSuccess(requestFlags, response, callingUid,
+ mCallbacks.onFillRequestSuccess(requestFlags, response,
mComponentName.getPackageName());
}
});
@@ -546,7 +546,7 @@ final class RemoteFillService implements DeathRecipient {
final RemoteFillService remoteService = getService();
if (remoteService != null) {
remoteService.dispatchOnFillRequestSuccess(PendingFillRequest.this,
- getCallingUid(), request.getFlags(), response);
+ request.getFlags(), response);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 99b92b9c9cd4..4a054f734738 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -55,7 +55,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
-import android.service.autofill.FieldsDetection;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
@@ -63,6 +62,7 @@ import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
+import android.service.autofill.UserData;
import android.service.autofill.ValueFinder;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -480,7 +480,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// FillServiceCallbacks
@Override
public void onFillRequestSuccess(int requestFlags, @Nullable FillResponse response,
- int serviceUid, @NonNull String servicePackageName) {
+ @NonNull String servicePackageName) {
synchronized (mLock) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
@@ -494,13 +494,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// TODO(b/67867469): remove once feature is finished
- if (response.getFieldsDetection() != null && !mService.isFieldDetectionEnabled()) {
+ if (response.getFieldClassificationIds() != null && !mService.isFieldDetectionEnabled()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestFlags);
return;
}
- mService.setLastResponse(serviceUid, id, response);
+ mService.setLastResponse(id, response);
int sessionFinishedState = 0;
final long disableDuration = response.getDisableDuration();
@@ -903,7 +903,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final FillResponse response = mResponses.valueAt(i);
final List<Dataset> datasets = response.getDatasets();
if (datasets == null || datasets.isEmpty()) {
- if (sVerbose) Slog.v(TAG, "logContextCommitted() no datasets at " + i);
+ if (sVerbose) Slog.v(TAG, "logContextCommitted() no datasets at " + i);
} else {
for (int j = 0; j < datasets.size(); j++) {
final Dataset dataset = datasets.get(j);
@@ -926,25 +926,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
}
- final FieldsDetection fieldsDetection = lastResponse.getFieldsDetection();
+ final AutofillId[] fieldClassificationIds = lastResponse.getFieldClassificationIds();
- if (!hasAtLeastOneDataset && fieldsDetection == null) {
+ if (!hasAtLeastOneDataset && fieldClassificationIds == null) {
if (sVerbose) {
Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets nor fields "
- + "detection)");
+ + "classification ids)");
}
return;
}
+ final UserData userData = mService.getUserData();
final AutofillId detectableFieldId;
final String detectableRemoteId;
String detectedRemoteId = null;
- if (fieldsDetection == null) {
+ if (userData == null) {
detectableFieldId = null;
detectableRemoteId = null;
} else {
- detectableFieldId = fieldsDetection.getFieldId();
- detectableRemoteId = fieldsDetection.getRemoteId();
+ // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
+ detectableFieldId = fieldClassificationIds[0];
+ detectableRemoteId = userData.getRemoteIds()[0];
}
int detectedFieldScore = -1;
@@ -1057,7 +1059,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
&& currentValue.isText() && currentValue.getTextValue() != null) {
final String actualValue = currentValue.getTextValue().toString();
- final String expectedValue = fieldsDetection.getValue();
+ // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
+ final String expectedValue = userData.getValues()[0];
if (actualValue.equalsIgnoreCase(expectedValue)) {
detectedRemoteId = detectableRemoteId;
detectedFieldScore = 0;