539 lines
21 KiB
Java
539 lines
21 KiB
Java
![]() |
/*
|
||
|
* 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_CATEGORY_COUNT;
|
||
|
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.Bundle;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
import android.provider.Settings;
|
||
|
import android.service.autofill.FieldClassification.Match;
|
||
|
import android.text.TextUtils;
|
||
|
import android.util.ArrayMap;
|
||
|
import android.util.ArraySet;
|
||
|
import android.util.Log;
|
||
|
import android.view.autofill.AutofillManager;
|
||
|
import android.view.autofill.Helper;
|
||
|
|
||
|
import com.android.internal.util.Preconditions;
|
||
|
|
||
|
import java.io.PrintWriter;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Objects;
|
||
|
|
||
|
/**
|
||
|
* Defines the user data used for
|
||
|
* <a href="AutofillService.html#FieldClassification">field classification</a>.
|
||
|
*/
|
||
|
public final class UserData implements FieldClassificationUserData, Parcelable {
|
||
|
|
||
|
private static final String TAG = "UserData";
|
||
|
|
||
|
private static final int DEFAULT_MAX_USER_DATA_SIZE = 50;
|
||
|
private static final int DEFAULT_MAX_CATEGORY_COUNT = 10;
|
||
|
private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10;
|
||
|
private static final int DEFAULT_MIN_VALUE_LENGTH = 3;
|
||
|
private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
|
||
|
|
||
|
private final String mId;
|
||
|
private final String[] mCategoryIds;
|
||
|
private final String[] mValues;
|
||
|
|
||
|
private final String mDefaultAlgorithm;
|
||
|
private final Bundle mDefaultArgs;
|
||
|
private final ArrayMap<String, String> mCategoryAlgorithms;
|
||
|
private final ArrayMap<String, Bundle> mCategoryArgs;
|
||
|
|
||
|
private UserData(Builder builder) {
|
||
|
mId = builder.mId;
|
||
|
mCategoryIds = new String[builder.mCategoryIds.size()];
|
||
|
builder.mCategoryIds.toArray(mCategoryIds);
|
||
|
mValues = new String[builder.mValues.size()];
|
||
|
builder.mValues.toArray(mValues);
|
||
|
builder.mValues.toArray(mValues);
|
||
|
|
||
|
mDefaultAlgorithm = builder.mDefaultAlgorithm;
|
||
|
mDefaultArgs = builder.mDefaultArgs;
|
||
|
mCategoryAlgorithms = builder.mCategoryAlgorithms;
|
||
|
mCategoryArgs = builder.mCategoryArgs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the name of the default algorithm that is used to calculate
|
||
|
* {@link Match#getScore()} match scores}.
|
||
|
*/
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public String getFieldClassificationAlgorithm() {
|
||
|
return mDefaultAlgorithm;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public Bundle getDefaultFieldClassificationArgs() {
|
||
|
return mDefaultArgs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the name of the algorithm corresponding to the specific autofill category
|
||
|
* that is used to calculate {@link Match#getScore() match scores}
|
||
|
*
|
||
|
* @param categoryId autofill field category
|
||
|
*
|
||
|
* @return String name of algorithm, null if none found.
|
||
|
*/
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
|
||
|
Objects.requireNonNull(categoryId);
|
||
|
if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
|
||
|
return null;
|
||
|
}
|
||
|
return mCategoryAlgorithms.get(categoryId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the id.
|
||
|
*/
|
||
|
public String getId() {
|
||
|
return mId;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public String[] getCategoryIds() {
|
||
|
return mCategoryIds;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public String[] getValues() {
|
||
|
return mValues;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@TestApi
|
||
|
@Override
|
||
|
public ArrayMap<String, String> getFieldClassificationAlgorithms() {
|
||
|
return mCategoryAlgorithms;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public ArrayMap<String, Bundle> getFieldClassificationArgs() {
|
||
|
return mCategoryArgs;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void dump(String prefix, PrintWriter pw) {
|
||
|
pw.print(prefix); pw.print("id: "); pw.print(mId);
|
||
|
pw.print(prefix); pw.print("Default Algorithm: "); pw.print(mDefaultAlgorithm);
|
||
|
pw.print(prefix); pw.print("Default Args"); pw.print(mDefaultArgs);
|
||
|
if (mCategoryAlgorithms != null && mCategoryAlgorithms.size() > 0) {
|
||
|
pw.print(prefix); pw.print("Algorithms per category: ");
|
||
|
for (int i = 0; i < mCategoryAlgorithms.size(); i++) {
|
||
|
pw.print(prefix); pw.print(prefix); pw.print(mCategoryAlgorithms.keyAt(i));
|
||
|
pw.print(": "); pw.println(Helper.getRedacted(mCategoryAlgorithms.valueAt(i)));
|
||
|
pw.print("args="); pw.print(mCategoryArgs.get(mCategoryAlgorithms.keyAt(i)));
|
||
|
}
|
||
|
}
|
||
|
// Cannot disclose field ids or values because they could contain PII
|
||
|
pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length);
|
||
|
for (int i = 0; i < mCategoryIds.length; i++) {
|
||
|
pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
|
||
|
pw.println(Helper.getRedacted(mCategoryIds[i]));
|
||
|
}
|
||
|
pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length);
|
||
|
for (int i = 0; i < mValues.length; i++) {
|
||
|
pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
|
||
|
pw.println(Helper.getRedacted(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("maxCategoryCount: "); pw.println(getMaxCategoryCount());
|
||
|
pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength());
|
||
|
pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A builder for {@link UserData} objects.
|
||
|
*/
|
||
|
public static final class Builder {
|
||
|
private final String mId;
|
||
|
private final ArrayList<String> mCategoryIds;
|
||
|
private final ArrayList<String> mValues;
|
||
|
private String mDefaultAlgorithm;
|
||
|
private Bundle mDefaultArgs;
|
||
|
|
||
|
// Map of autofill field categories to fleid classification algorithms and args
|
||
|
private ArrayMap<String, String> mCategoryAlgorithms;
|
||
|
private ArrayMap<String, Bundle> mCategoryArgs;
|
||
|
|
||
|
private boolean mDestroyed;
|
||
|
|
||
|
// Non-persistent array used to limit the number of unique ids.
|
||
|
private final ArraySet<String> mUniqueCategoryIds;
|
||
|
// Non-persistent array used to ignore duplaicated value/category pairs.
|
||
|
private final ArraySet<String> mUniqueValueCategoryPairs;
|
||
|
|
||
|
/**
|
||
|
* Creates a new builder for the user data used for <a href="#FieldClassification">field
|
||
|
* classification</a>.
|
||
|
*
|
||
|
* <p>The user data must contain at least one pair of {@code value} -> {@code categoryId},
|
||
|
* and more pairs can be added through the {@link #add(String, String)} method. For example:
|
||
|
*
|
||
|
* <pre class="prettyprint">
|
||
|
* new UserData.Builder("v1", "Bart Simpson", "name")
|
||
|
* .add("bart.simpson@example.com", "email")
|
||
|
* .add("el_barto@example.com", "email")
|
||
|
* .build();
|
||
|
* </pre>
|
||
|
*
|
||
|
* @param id id used to identify the whole {@link UserData} object. This id is also returned
|
||
|
* by {@link AutofillManager#getUserDataId()}, which can be used to check if the
|
||
|
* {@link UserData} is up-to-date without fetching the whole object (through
|
||
|
* {@link AutofillManager#getUserData()}).
|
||
|
*
|
||
|
* @param value value of the user data.
|
||
|
* @param categoryId autofill field category.
|
||
|
*
|
||
|
* @throws IllegalArgumentException if any of the following occurs:
|
||
|
* <ul>
|
||
|
* <li>{@code id} is empty</li>
|
||
|
* <li>{@code categoryId} is empty</li>
|
||
|
* <li>{@code value} is empty</li>
|
||
|
* <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
|
||
|
* <li>the length of {@code value} is higher than
|
||
|
* {@link UserData#getMaxValueLength()}</li>
|
||
|
* </ul>
|
||
|
*/
|
||
|
public Builder(@NonNull String id, @NonNull String value, @NonNull String categoryId) {
|
||
|
mId = checkNotEmpty("id", id);
|
||
|
checkNotEmpty("categoryId", categoryId);
|
||
|
checkValidValue(value);
|
||
|
final int maxUserDataSize = getMaxUserDataSize();
|
||
|
mCategoryIds = new ArrayList<>(maxUserDataSize);
|
||
|
mValues = new ArrayList<>(maxUserDataSize);
|
||
|
mUniqueValueCategoryPairs = new ArraySet<>(maxUserDataSize);
|
||
|
|
||
|
mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount());
|
||
|
|
||
|
addMapping(value, categoryId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the default algorithm used for
|
||
|
* <a href="#FieldClassification">field classification</a>.
|
||
|
*
|
||
|
* <p>The currently available algorithms can be retrieve through
|
||
|
* {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
|
||
|
*
|
||
|
* <p>If not set, the
|
||
|
* {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is
|
||
|
* used instead.
|
||
|
*
|
||
|
* @param name name of the algorithm or {@code null} to used default.
|
||
|
* @param args optional arguments to the algorithm.
|
||
|
*
|
||
|
* @return this builder
|
||
|
*/
|
||
|
@NonNull
|
||
|
public Builder setFieldClassificationAlgorithm(@Nullable String name,
|
||
|
@Nullable Bundle args) {
|
||
|
throwIfDestroyed();
|
||
|
mDefaultAlgorithm = name;
|
||
|
mDefaultArgs = args;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the algorithm used for <a href="#FieldClassification">field classification</a>
|
||
|
* for the specified category.
|
||
|
*
|
||
|
* <p>The currently available algorithms can be retrieved through
|
||
|
* {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
|
||
|
*
|
||
|
* <p>If not set, the
|
||
|
* {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is
|
||
|
* used instead.
|
||
|
*
|
||
|
* @param categoryId autofill field category.
|
||
|
* @param name name of the algorithm or {@code null} to used default.
|
||
|
* @param args optional arguments to the algorithm.
|
||
|
*
|
||
|
* @return this builder
|
||
|
*/
|
||
|
@NonNull
|
||
|
public Builder setFieldClassificationAlgorithmForCategory(@NonNull String categoryId,
|
||
|
@Nullable String name, @Nullable Bundle args) {
|
||
|
throwIfDestroyed();
|
||
|
Objects.requireNonNull(categoryId);
|
||
|
if (mCategoryAlgorithms == null) {
|
||
|
mCategoryAlgorithms = new ArrayMap<>(getMaxCategoryCount());
|
||
|
}
|
||
|
if (mCategoryArgs == null) {
|
||
|
mCategoryArgs = new ArrayMap<>(getMaxCategoryCount());
|
||
|
}
|
||
|
mCategoryAlgorithms.put(categoryId, name);
|
||
|
mCategoryArgs.put(categoryId, args);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds a new value for user data.
|
||
|
*
|
||
|
* @param value value of the user data.
|
||
|
* @param categoryId string used to identify the category the value is associated with.
|
||
|
*
|
||
|
* @throws IllegalStateException if:
|
||
|
* <ul>
|
||
|
* <li>{@link #build()} already called</li>
|
||
|
* <li>the {@code value} has already been added (<b>Note: </b> this restriction was
|
||
|
* lifted on Android {@link android.os.Build.VERSION_CODES#Q} and later)</li>
|
||
|
* <li>the number of unique {@code categoryId} values added so far is more than
|
||
|
* {@link UserData#getMaxCategoryCount()}</li>
|
||
|
* <li>the number of {@code values} added so far is is more than
|
||
|
* {@link UserData#getMaxUserDataSize()}</li>
|
||
|
* </ul>
|
||
|
*
|
||
|
* @throws IllegalArgumentException if any of the following occurs:
|
||
|
* <ul>
|
||
|
* <li>{@code id} is empty</li>
|
||
|
* <li>{@code categoryId} is empty</li>
|
||
|
* <li>{@code value} is empty</li>
|
||
|
* <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
|
||
|
* <li>the length of {@code value} is higher than
|
||
|
* {@link UserData#getMaxValueLength()}</li>
|
||
|
* </ul>
|
||
|
*/
|
||
|
@NonNull
|
||
|
public Builder add(@NonNull String value, @NonNull String categoryId) {
|
||
|
throwIfDestroyed();
|
||
|
checkNotEmpty("categoryId", categoryId);
|
||
|
checkValidValue(value);
|
||
|
|
||
|
if (!mUniqueCategoryIds.contains(categoryId)) {
|
||
|
// New category - check size
|
||
|
Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(),
|
||
|
"already added %d unique category ids", mUniqueCategoryIds.size());
|
||
|
}
|
||
|
|
||
|
Preconditions.checkState(mValues.size() < getMaxUserDataSize(),
|
||
|
"already added %d elements", mValues.size());
|
||
|
addMapping(value, categoryId);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
private void addMapping(@NonNull String value, @NonNull String categoryId) {
|
||
|
final String pair = value + ":" + categoryId;
|
||
|
if (mUniqueValueCategoryPairs.contains(pair)) {
|
||
|
// Don't include value on message because it could contain PII
|
||
|
Log.w(TAG, "Ignoring entry with same value / category");
|
||
|
return;
|
||
|
}
|
||
|
mCategoryIds.add(categoryId);
|
||
|
mValues.add(value);
|
||
|
mUniqueCategoryIds.add(categoryId);
|
||
|
mUniqueValueCategoryPairs.add(pair);
|
||
|
}
|
||
|
|
||
|
private String checkNotEmpty(@NonNull String name, @Nullable String value) {
|
||
|
Objects.requireNonNull(value);
|
||
|
Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
private void checkValidValue(@Nullable String value) {
|
||
|
Objects.requireNonNull(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.
|
||
|
*/
|
||
|
@NonNull
|
||
|
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();
|
||
|
|
||
|
final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId);
|
||
|
// Cannot disclose category ids or values because they could contain PII
|
||
|
builder.append(", categoryIds=");
|
||
|
Helper.appendRedacted(builder, mCategoryIds);
|
||
|
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.writeString(mId);
|
||
|
parcel.writeStringArray(mCategoryIds);
|
||
|
parcel.writeStringArray(mValues);
|
||
|
parcel.writeString(mDefaultAlgorithm);
|
||
|
parcel.writeBundle(mDefaultArgs);
|
||
|
parcel.writeMap(mCategoryAlgorithms);
|
||
|
parcel.writeMap(mCategoryArgs);
|
||
|
}
|
||
|
|
||
|
public static final @android.annotation.NonNull 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 id = parcel.readString();
|
||
|
final String[] categoryIds = parcel.readStringArray();
|
||
|
final String[] values = parcel.readStringArray();
|
||
|
final String defaultAlgorithm = parcel.readString();
|
||
|
final Bundle defaultArgs = parcel.readBundle();
|
||
|
final ArrayMap<String, String> categoryAlgorithms = new ArrayMap<>();
|
||
|
parcel.readMap(categoryAlgorithms, String.class.getClassLoader());
|
||
|
final ArrayMap<String, Bundle> categoryArgs = new ArrayMap<>();
|
||
|
parcel.readMap(categoryArgs, Bundle.class.getClassLoader());
|
||
|
|
||
|
final Builder builder = new Builder(id, values[0], categoryIds[0])
|
||
|
.setFieldClassificationAlgorithm(defaultAlgorithm, defaultArgs);
|
||
|
|
||
|
for (int i = 1; i < categoryIds.length; i++) {
|
||
|
String categoryId = categoryIds[i];
|
||
|
builder.add(values[i], categoryId);
|
||
|
}
|
||
|
|
||
|
final int size = categoryAlgorithms.size();
|
||
|
if (size > 0) {
|
||
|
for (int i = 0; i < size; i++) {
|
||
|
final String categoryId = categoryAlgorithms.keyAt(i);
|
||
|
builder.setFieldClassificationAlgorithmForCategory(categoryId,
|
||
|
categoryAlgorithms.valueAt(i), categoryArgs.get(categoryId));
|
||
|
}
|
||
|
}
|
||
|
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 maximum number of unique category ids that can be passed to
|
||
|
* the builder's constructor and {@link Builder#add(String, String)}.
|
||
|
*/
|
||
|
public static int getMaxCategoryCount() {
|
||
|
return getInt(AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, DEFAULT_MAX_CATEGORY_COUNT);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 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);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|