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

Commit bc055b0e authored by Felipe Leme's avatar Felipe Leme
Browse files

Moved Field Classification score logic to ExtServices.

Bug: 70939974
Test: atest CtsAutoFillServiceTestCases:FieldsClassificationTest \
            CtsAutoFillServiceTestCases:UserDataTest
Test: atest CtsAutoFillServiceTestCases

Change-Id: I75fd59b5d7530fcd7095b26f6e592d7459c7d235
parent d67e50eb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -242,6 +242,7 @@ java_library {
        "core/java/android/security/IKeystoreService.aidl",
        "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
        "core/java/android/service/autofill/IAutoFillService.aidl",
        "core/java/android/service/autofill/IAutofillFieldClassificationService.aidl",
        "core/java/android/service/autofill/IFillCallback.aidl",
        "core/java/android/service/autofill/ISaveCallback.aidl",
        "core/java/android/service/carrier/ICarrierService.aidl",
+22 −0
Original line number Diff line number Diff line
@@ -3855,6 +3855,28 @@ package android.security.keystore {

}

package android.service.autofill {

  public abstract class AutofillFieldClassificationService extends android.app.Service {
    method public android.os.IBinder onBind(android.content.Intent);
    method public java.util.List<java.lang.String> onGetAvailableAlgorithms();
    method public java.lang.String onGetDefaultAlgorithm();
    method public android.service.autofill.AutofillFieldClassificationService.Scores onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
    field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
  }

  public static final class AutofillFieldClassificationService.Scores implements android.os.Parcelable {
    ctor public AutofillFieldClassificationService.Scores(java.lang.String, int, int);
    ctor public AutofillFieldClassificationService.Scores(android.os.Parcel);
    method public int describeContents();
    method public java.lang.String getAlgorithm();
    method public float[][] getScores();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.service.autofill.AutofillFieldClassificationService.Scores> CREATOR;
  }

}

package android.service.notification {

  public final class Adjustment implements android.os.Parcelable {
+0 −5
Original line number Diff line number Diff line
@@ -563,11 +563,6 @@ package android.service.autofill {
    method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
  }

  public final class EditDistanceScorer {
    method public static android.service.autofill.EditDistanceScorer getInstance();
    method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
  }

  public final class FillResponse implements android.os.Parcelable {
    method public int getFlags();
  }
+284 −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.AutofillManager.EXTRA_AVAILABLE_ALGORITHMS;
import static android.view.autofill.AutofillManager.EXTRA_DEFAULT_ALGORITHM;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.view.autofill.AutofillValue;

import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;

import java.util.Arrays;
import java.util.List;

/**
 * A service that calculates field classification scores.
 *
 * <p>A field classification score is a {@code float} representing how well an
 * {@link AutofillValue} filled matches a expected value predicted by an autofill service
 * &mdash;a full-match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
 *
 * <p>The exact score depends on the algorithm used to calculate it&mdash; the service must provide
 * at least one default algorithm (which is used when the algorithm is not specified or is invalid),
 * but it could provide more (in which case the algorithm name should be specifiied by the caller
 * when calculating the scores).
 *
 * {@hide}
 */
@SystemApi
public abstract class AutofillFieldClassificationService extends Service {

    private static final String TAG = "AutofillFieldClassificationService";

    private static final int MSG_GET_AVAILABLE_ALGORITHMS = 1;
    private static final int MSG_GET_DEFAULT_ALGORITHM = 2;
    private static final int MSG_GET_SCORES = 3;

    /**
     * The {@link Intent} action that must be declared as handled by a service
     * in its manifest for the system to recognize it as a quota providing service.
     */
    public static final String SERVICE_INTERFACE =
            "android.service.autofill.AutofillFieldClassificationService";

    /** {@hide} **/
    public static final String EXTRA_SCORES = "scores";

    private AutofillFieldClassificationServiceWrapper mWrapper;

    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
        final int action = msg.what;
        final Bundle data = new Bundle();
        final RemoteCallback callback;
        switch (action) {
            case MSG_GET_AVAILABLE_ALGORITHMS:
                callback = (RemoteCallback) msg.obj;
                final List<String> availableAlgorithms = onGetAvailableAlgorithms();
                String[] asArray = null;
                if (availableAlgorithms != null) {
                    asArray = new String[availableAlgorithms.size()];
                    availableAlgorithms.toArray(asArray);
                }
                data.putStringArray(EXTRA_AVAILABLE_ALGORITHMS, asArray);
                break;
            case MSG_GET_DEFAULT_ALGORITHM:
                callback = (RemoteCallback) msg.obj;
                final String defaultAlgorithm = onGetDefaultAlgorithm();
                data.putString(EXTRA_DEFAULT_ALGORITHM, defaultAlgorithm);
                break;
            case MSG_GET_SCORES:
                final SomeArgs args = (SomeArgs) msg.obj;
                callback = (RemoteCallback) args.arg1;
                final String algorithmName = (String) args.arg2;
                final Bundle algorithmArgs = (Bundle) args.arg3;
                @SuppressWarnings("unchecked")
                final List<AutofillValue> actualValues = ((List<AutofillValue>) args.arg4);
                @SuppressWarnings("unchecked")
                final String[] userDataValues = (String[]) args.arg5;
                final Scores scores = onGetScores(algorithmName, algorithmArgs, actualValues,
                        Arrays.asList(userDataValues));
                data.putParcelable(EXTRA_SCORES, scores);
                break;
            default:
                Log.w(TAG, "Handling unknown message: " + action);
                return;
        }
        callback.sendResult(data);
    };

    private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
            mHandlerCallback, true);

    /** @hide */
    public AutofillFieldClassificationService() {

    }

    @Override
    public void onCreate() {
        super.onCreate();
        mWrapper = new AutofillFieldClassificationServiceWrapper();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mWrapper;
    }

    /**
     * Gets the name of all available algorithms.
     *
     * @throws UnsupportedOperationException if not implemented by service.
     */
    // TODO(b/70939974): rename to onGetAvailableAlgorithms if not removed
    @NonNull
    public List<String> onGetAvailableAlgorithms() {
        throw new UnsupportedOperationException("Must be implemented by external service");
    }

    /**
     * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
     *
     * @throws UnsupportedOperationException if not implemented by service.
     */
    @NonNull
    public String onGetDefaultAlgorithm() {
        throw new UnsupportedOperationException("Must be implemented by external service");
    }

    /**
     * Calculates field classification scores in a batch.
     *
     * <p>See {@link AutofillFieldClassificationService} for more info about field classification
     * scores.
     *
     * @param algorithm name of the algorithm to be used to calculate the scores. If invalid, the
     * default algorithm will be used instead.
     * @param args optional arguments to be passed to the algorithm.
     * @param actualValues values entered by the user.
     * @param userDataValues values predicted from the user data.
     * @return the calculated scores and the algorithm used.
     *
     * {@hide}
     */
    @Nullable
    @SystemApi
    public Scores onGetScores(@Nullable String algorithm,
            @Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
            @NonNull List<String> userDataValues) {
        throw new UnsupportedOperationException("Must be implemented by external service");
    }

    private final class AutofillFieldClassificationServiceWrapper
            extends IAutofillFieldClassificationService.Stub {

        @Override
        public void getAvailableAlgorithms(RemoteCallback callback) throws RemoteException {
            mHandlerCaller.obtainMessageO(MSG_GET_AVAILABLE_ALGORITHMS, callback).sendToTarget();
        }

        @Override
        public void getDefaultAlgorithm(RemoteCallback callback) throws RemoteException {
            mHandlerCaller.obtainMessageO(MSG_GET_DEFAULT_ALGORITHM, callback).sendToTarget();
        }

        @Override
        public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
                List<AutofillValue> actualValues, String[] userDataValues)
                        throws RemoteException {
            // TODO(b/70939974): refactor to use PooledLambda
            mHandlerCaller.obtainMessageOOOOO(MSG_GET_SCORES, callback, algorithmName,
                    algorithmArgs, actualValues, userDataValues).sendToTarget();
        }
    }


    // TODO(b/70939974): it might be simpler to remove this class and return the float[][] directly,
    // ignoring the request if the algorithm name is invalid.
    /**
     * Represents field classification scores used in a batch calculation.
     *
     * {@hide}
     */
    @SystemApi
    public static final class Scores implements Parcelable {
        private final String mAlgorithmName;
        private final float[][] mScores;

        /* @hide */
        public Scores(String algorithmName, int size1, int size2) {
            mAlgorithmName = algorithmName;
            mScores = new float[size1][size2];
        }

        public Scores(Parcel parcel) {
            mAlgorithmName = parcel.readString();
            final int size1 = parcel.readInt();
            final int size2 = parcel.readInt();
            mScores = new float[size1][size2];
            for (int i = 0; i < size1; i++) {
                for (int j = 0; j < size2; j++) {
                    mScores[i][j] = parcel.readFloat();
                }
            }
        }

        /**
         * Gets the name of algorithm used to calculate the score.
         */
        @NonNull
        public String getAlgorithm() {
            return mAlgorithmName;
        }

        /**
         * Gets the resulting scores, with the 1st dimension representing actual values and the 2nd
         * dimension values from {@link UserData}.
         */
        @NonNull
        public float[][] getScores() {
            return mScores;
        }

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

        @Override
        public void writeToParcel(Parcel parcel, int flags) {
            parcel.writeString(mAlgorithmName);
            int size1 = mScores.length;
            int size2 = mScores[0].length;
            parcel.writeInt(size1);
            parcel.writeInt(size2);
            for (int i = 0; i < size1; i++) {
                for (int j = 0; j < size2; j++) {
                    parcel.writeFloat(mScores[i][j]);
                }
            }
        }

        public static final Creator<Scores> CREATOR = new Creator<Scores>() {

            @Override
            public Scores createFromParcel(Parcel parcel) {
                return new Scores(parcel);
            }

            @Override
            public Scores[] newArray(int size) {
                return new Scores[size];
            }

        };
    }
}
+34 −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.os.RemoteCallback;
import android.view.autofill.AutofillValue;
import java.util.List;

/**
 * Service used to calculate match scores for Autofill Field Classification.
 *
 * @hide
 */
oneway interface IAutofillFieldClassificationService {
    void getAvailableAlgorithms(in RemoteCallback callback);
    void getDefaultAlgorithm(in RemoteCallback callback);
    void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs,
                  in List<AutofillValue> actualValues, in String[] userDataValues);
}
Loading