Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1cb6f806 authored by Adam He's avatar Adam He
Browse files

Merge app-specific and generic user data for field classification.

Bug: 118617202
Test: atest CtsAutoFillServiceTestCases
Test: atest android.autofillservice.cts.FieldsClassificationTest android.autofillservice.cts.UserDataTest

Change-Id: Ibb958e0ca790d798c7c62bce5f7dbc108041fe07
parent 76e986a2
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -1153,6 +1153,20 @@ package android.service.autofill {
    method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
  }

  public final class CompositeUserData implements android.os.Parcelable {
    ctor public CompositeUserData(android.service.autofill.UserData, android.service.autofill.UserData);
    method public int describeContents();
    method public java.lang.String[] getCategoryIds();
    method public android.os.Bundle getDefaultFieldClassificationArgs();
    method public java.lang.String getFieldClassificationAlgorithm();
    method public java.lang.String getFieldClassificationAlgorithmForCategory(java.lang.String);
    method public android.util.ArrayMap<java.lang.String, java.lang.String> getFieldClassificationAlgorithms();
    method public android.util.ArrayMap<java.lang.String, android.os.Bundle> getFieldClassificationArgs();
    method public java.lang.String[] getValues();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.CompositeUserData> CREATOR;
  }

  public final class CustomDescription implements android.os.Parcelable {
    method public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions();
  }
+211 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.view.autofill.Helper.sDebug;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;

import com.android.internal.util.Preconditions;

import java.util.ArrayList;
import java.util.Collections;

/**
 * Holds both a generic and package-specific userData used for
 * <a href="AutofillService.html#FieldClassification">field classification</a>.
 *
 * @hide
 */
@TestApi
public final class CompositeUserData implements FieldClassificationUserData, Parcelable {

    private final UserData mGenericUserData;
    private final UserData mPackageUserData;

    private final String[] mCategories;
    private final String[] mValues;

    public CompositeUserData(@Nullable UserData genericUserData,
            @NonNull UserData packageUserData) {
        mGenericUserData = genericUserData;
        mPackageUserData = packageUserData;

        final String[] packageCategoryIds = mPackageUserData.getCategoryIds();
        final String[] packageValues = mPackageUserData.getValues();

        final ArrayList<String> categoryIds = new ArrayList<>(packageCategoryIds.length);
        final ArrayList<String> values = new ArrayList<>(packageValues.length);

        Collections.addAll(categoryIds, packageCategoryIds);
        Collections.addAll(values, packageValues);

        if (mGenericUserData != null) {
            final String[] genericCategoryIds = mGenericUserData.getCategoryIds();
            final String[] genericValues = mGenericUserData.getValues();
            final int size = mGenericUserData.getCategoryIds().length;
            for (int i = 0; i < size; i++) {
                if (!categoryIds.contains(genericCategoryIds[i])) {
                    categoryIds.add(genericCategoryIds[i]);
                    values.add(genericValues[i]);
                }
            }
        }

        mCategories = new String[categoryIds.size()];
        categoryIds.toArray(mCategories);
        mValues = new String[values.size()];
        values.toArray(mValues);
    }

    @Nullable
    @Override
    public String getFieldClassificationAlgorithm() {
        final String packageDefaultAlgo = mPackageUserData.getFieldClassificationAlgorithm();
        if (packageDefaultAlgo != null) {
            return packageDefaultAlgo;
        } else {
            return mGenericUserData == null ? null :
                    mGenericUserData.getFieldClassificationAlgorithm();
        }
    }

    @Override
    public Bundle getDefaultFieldClassificationArgs() {
        final Bundle packageDefaultArgs = mPackageUserData.getDefaultFieldClassificationArgs();
        if (packageDefaultArgs != null) {
            return packageDefaultArgs;
        } else {
            return mGenericUserData == null ? null :
                    mGenericUserData.getDefaultFieldClassificationArgs();
        }
    }

    @Nullable
    @Override
    public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
        Preconditions.checkNotNull(categoryId);
        final ArrayMap<String, String> categoryAlgorithms = getFieldClassificationAlgorithms();
        if (categoryAlgorithms == null || !categoryAlgorithms.containsKey(categoryId)) {
            return null;
        }
        return categoryAlgorithms.get(categoryId);
    }

    @Override
    public ArrayMap<String, String> getFieldClassificationAlgorithms() {
        final ArrayMap<String, String> packageAlgos = mPackageUserData
                .getFieldClassificationAlgorithms();
        final ArrayMap<String, String> genericAlgos = mGenericUserData == null ? null :
                mGenericUserData.getFieldClassificationAlgorithms();

        ArrayMap<String, String> categoryAlgorithms = null;
        if (packageAlgos != null || genericAlgos != null) {
            categoryAlgorithms = new ArrayMap<>();
            if (genericAlgos != null) {
                categoryAlgorithms.putAll(genericAlgos);
            }
            if (packageAlgos != null) {
                categoryAlgorithms.putAll(packageAlgos);
            }
        }

        return categoryAlgorithms;
    }

    @Override
    public ArrayMap<String, Bundle> getFieldClassificationArgs() {
        final ArrayMap<String, Bundle> packageArgs = mPackageUserData.getFieldClassificationArgs();
        final ArrayMap<String, Bundle> genericArgs = mGenericUserData == null ? null :
                mGenericUserData.getFieldClassificationArgs();

        ArrayMap<String, Bundle> categoryArgs = null;
        if (packageArgs != null || genericArgs != null) {
            categoryArgs = new ArrayMap<>();
            if (genericArgs != null) {
                categoryArgs.putAll(genericArgs);
            }
            if (packageArgs != null) {
                categoryArgs.putAll(packageArgs);
            }
        }

        return categoryArgs;
    }

    @Override
    public String[] getCategoryIds() {
        return mCategories;
    }

    @Override
    public String[] getValues() {
        return mValues;
    }

    /////////////////////////////////////
    // Object "contract" methods. //
    /////////////////////////////////////
    @Override
    public String toString() {
        if (!sDebug) return super.toString();

        // OK to print UserData because UserData.toString() is PII-aware
        final StringBuilder builder = new StringBuilder("genericUserData=")
                .append(mGenericUserData)
                .append(", packageUserData=").append(mPackageUserData);
        return builder.toString();
    }

    /////////////////////////////////////
    // Parcelable "contract" methods. //
    /////////////////////////////////////

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeParcelable(mGenericUserData, 0);
        parcel.writeParcelable(mPackageUserData, 0);
    }

    public static final Parcelable.Creator<CompositeUserData> CREATOR =
            new Parcelable.Creator<CompositeUserData>() {
                @Override
                public CompositeUserData 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 UserData genericUserData = parcel.readParcelable(null);
                    final UserData packageUserData = parcel.readParcelable(null);
                    return new CompositeUserData(genericUserData, packageUserData);
                }

                @Override
                public CompositeUserData[] newArray(int size) {
                    return new CompositeUserData[size];
                }
            };
}
+64 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.os.Bundle;
import android.util.ArrayMap;

/**
 * Class used to define a generic UserData for field classification
 *
 * @hide
 */
public interface FieldClassificationUserData {
    /**
     * Gets the name of the default algorithm that is used to calculate
     * {@link FieldClassification.Match#getScore()} match scores}.
     */
    String getFieldClassificationAlgorithm();

    /**
     * Gets the default field classification args.
     */
    Bundle getDefaultFieldClassificationArgs();

    /**
     * Gets the name of the field classification algorithm for a specific category.
     *
     * @param categoryId id of the specific category.
     */
    String getFieldClassificationAlgorithmForCategory(String categoryId);

    /**
     * Gets all field classification algorithms for specific categories.
     */
    ArrayMap<String, String> getFieldClassificationAlgorithms();

    /**
     * Gets all field classification args for specific categories.
     */
    ArrayMap<String, Bundle> getFieldClassificationArgs();

    /**
     * Gets all category ids
     */
    String[] getCategoryIds();

    /**
     * Gets all string values for field classification
     */
    String[] getValues();
}
+3 −0
Original line number Diff line number Diff line
@@ -516,6 +516,9 @@ public final class FillResponse implements Parcelable {
        /**
         * Sets a specific {@link UserData} for field classification for this request only.
         *
         * <p>Any fields in this UserData will override corresponding fields in the generic
         * UserData object
         *
         * @return this builder
         * @throws IllegalStateException if the FillResponse
         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+8 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ import java.util.ArrayList;
 * Defines the user data used for
 * <a href="AutofillService.html#FieldClassification">field classification</a>.
 */
public final class UserData implements Parcelable {
public final class UserData implements FieldClassificationUserData, Parcelable {

    private static final String TAG = "UserData";

@@ -86,11 +86,13 @@ public final class UserData implements Parcelable {
     * {@link Match#getScore()} match scores}.
     */
    @Nullable
    @Override
    public String getFieldClassificationAlgorithm() {
        return mDefaultAlgorithm;
    }

    /** @hide */
    @Override
    public Bundle getDefaultFieldClassificationArgs() {
        return mDefaultArgs;
    }
@@ -104,6 +106,7 @@ public final class UserData implements Parcelable {
     * @return String name of algorithm, null if none found.
     */
    @Nullable
    @Override
    public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
        Preconditions.checkNotNull(categoryId);
        if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
@@ -120,22 +123,26 @@ public final class UserData implements Parcelable {
    }

    /** @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;
    }
Loading