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

Commit 80e7bf11 authored by Felipe Leme's avatar Felipe Leme
Browse files

Initial implementation of AutofillManager.setAugmentedAutofillWhitelist()

For now it only whitelists packages, not activities.

Test: atest CtsAutoFillServiceTestCases:android.autofillservice.cts.augmented.AugmentedLoginActivityTest

Bug: 123100824

Change-Id: I043f4a22684fcdac7409393c208c2c1e6d987c5e
parent d6ea41c2
Loading
Loading
Loading
Loading
+36 −2
Original line number Diff line number Diff line
@@ -369,6 +369,11 @@ public final class AutofillManager {
    public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES =
            "smart_suggestion_supported_modes";

    /** @hide */
    public static final int RESULT_OK = 0;
    /** @hide */
    public static final int RESULT_CODE_NOT_SERVICE = -1;

    /**
     * Makes an authentication id from a request id and a dataset id.
     *
@@ -1789,7 +1794,11 @@ public final class AutofillManager {
    @Deprecated
    public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
            @Nullable List<ComponentName> activities) {
        // TODO(b/123100824): implement
        setAugmentedAutofillWhitelist(toSet(packages), toSet(activities));
    }

    private <T> ArraySet<T> toSet(@Nullable List<T> set) {
        return set == null ? null : new ArraySet<T>(set);
    }

    /**
@@ -1814,7 +1823,32 @@ public final class AutofillManager {
    @TestApi
    public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
            @Nullable Set<ComponentName> activities) {
        // TODO(b/123100824): implement
        if (!hasAutofillFeature()) {
            return;
        }

        final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
        final int resultCode;
        try {
            mService.setAugmentedAutofillWhitelist(toList(packages), toList(activities),
                    resultReceiver);
            resultCode = resultReceiver.getIntResult();
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        switch (resultCode) {
            case RESULT_OK:
                return;
            case RESULT_CODE_NOT_SERVICE:
                throw new SecurityException("caller is not user's Augmented Autofill Service");
            default:
                Log.wtf(TAG, "setAugmentedAutofillWhitelist(): received invalid result: "
                        + resultCode);
        }
    }

    private <T> ArrayList<T> toList(@Nullable Set<T> set) {
        return set == null ? null : new ArrayList<T>(set);
    }

    private void requestShowFillUi(int sessionId, AutofillId id, int width, int height,
+2 −0
Original line number Diff line number Diff line
@@ -63,4 +63,6 @@ oneway interface IAutoFillManager {
    void getAutofillServiceComponentName(in IResultReceiver result);
    void getAvailableFieldClassificationAlgorithms(in IResultReceiver result);
    void getDefaultFieldClassificationAlgorithm(in IResultReceiver result);
    void setAugmentedAutofillWhitelist(in List<String> packages, in List<ComponentName> activities,
        in IResultReceiver result);
}
+23 −0
Original line number Diff line number Diff line
@@ -1025,6 +1025,29 @@ public final class AutofillManagerService
            send(receiver, algorithm);
        }

        @Override
        public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
                @Nullable List<ComponentName> activities, @NonNull IResultReceiver receiver)
                throws RemoteException {
            final int userId = UserHandle.getCallingUserId();

            boolean ok;
            synchronized (mLock) {
                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                if (service != null) {
                    ok = service.setAugmentedAutofillWhitelistLocked(packages, activities,
                            getCallingUid());
                } else {
                    if (sVerbose) {
                        Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + userId);
                    }
                    ok = false;
                }
            }
            send(receiver,
                    ok ? AutofillManager.RESULT_OK : AutofillManager.RESULT_CODE_NOT_SERVICE);
        }

        @Override
        public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver)
                throws RemoteException {
+117 −9
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -58,6 +59,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -165,6 +167,16 @@ final class AutofillManagerServiceImpl
    @Nullable
    private RemoteAugmentedAutofillService mRemoteAugmentedAutofillService;

    @GuardedBy("mLock")
    @Nullable
    private ServiceInfo mRemoteAugmentedAutofillServiceInfo;

    /**
     * List of packages that are whitelisted to be trigger augmented autofill.
     */
    @GuardedBy("mLock")
    private final ArraySet<String> mWhitelistedAugmentAutofillPackages = new ArraySet<>();

    AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
            LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
            AutofillCompatState autofillCompatState,
@@ -844,10 +856,11 @@ final class AutofillManagerServiceImpl
    }

    @GuardedBy("mLock")
    private boolean isCalledByServiceLocked(String methodName, int callingUid) {
        if (getServiceUidLocked() != callingUid) {
    private boolean isCalledByServiceLocked(@NonNull String methodName, int callingUid) {
        final int serviceUid = getServiceUidLocked();
        if (serviceUid != callingUid) {
            Slog.w(TAG, methodName + "() called by UID " + callingUid
                    + ", but service UID is " + getServiceUidLocked());
                    + ", but service UID is " + serviceUid);
            return false;
        }
        return true;
@@ -886,6 +899,19 @@ final class AutofillManagerServiceImpl
            pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
            mRemoteAugmentedAutofillService.dump(prefix2, pw);
        }
        if (mRemoteAugmentedAutofillServiceInfo != null) {
            pw.print(prefix); pw.print("RemoteAugmentedAutofillServiceInfo: ");
            pw.println(mRemoteAugmentedAutofillServiceInfo);
        }

        final int whitelistSize = mWhitelistedAugmentAutofillPackages.size();
        pw.print(prefix); pw.print("Packages whitelisted for augmented autofill: ");
        pw.println(whitelistSize);
        for (int i = 0; i < whitelistSize; i++) {
            final String whitelistedPkg = mWhitelistedAugmentAutofillPackages.valueAt(i);
            pw.print(prefix2); pw.print(i + 1); pw.print(": "); pw.println(whitelistedPkg);
        }

        pw.print(prefix); pw.print("Field classification enabled: ");
            pw.println(isFieldClassificationEnabledLocked());
        pw.print(prefix); pw.print("Compat pkgs: ");
@@ -1037,9 +1063,13 @@ final class AutofillManagerServiceImpl
                }
                return null;
            }
            final ComponentName componentName = RemoteAugmentedAutofillService.getComponentName(
            final Pair<ServiceInfo, ComponentName> pair = RemoteAugmentedAutofillService
                    .getComponentName(
                            serviceName, mUserId, mAugmentedAutofillResolver.isTemporary(mUserId));
            if (componentName == null) return null;
            if (pair == null) return null;

            mRemoteAugmentedAutofillServiceInfo = pair.first;
            final ComponentName componentName = pair.second;
            if (sVerbose) {
                Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
            }
@@ -1067,12 +1097,90 @@ final class AutofillManagerServiceImpl
    private void updateRemoteAugmentedAutofillService(@Nullable String serviceName) {
        if (serviceName == null) {
            if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!");
            synchronized (mLock) {
                if (mRemoteAugmentedAutofillService != null) {
                    mRemoteAugmentedAutofillService.destroy();
                    mRemoteAugmentedAutofillService = null;
                }
            }
        }
    }

    /**
     * Sets which packages and activities can trigger augmented autofill.
     *
     * @return whether caller UID is the augmented autofill service for the user
     */
    boolean setAugmentedAutofillWhitelistLocked(List<String> packages,
            List<ComponentName> activities, int callingUid) {

        if (!isCalledByAugmentedAutofillServiceLocked("setAugmentedAutofillWhitelistLocked",
                callingUid)) {
            return false;
        }
        if (mMaster.verbose) {
            Slog.v(TAG, "setAugmentedAutofillWhitelistLocked(packages=" + packages + ", activities="
                    + activities + ")");
        }
        whitelistForAugmentedAutofillPackages(packages);

        // TODO(b/123100824): whitelist activities as well
        // TODO(b/122858578): log metrics
        return true;
    }

    @GuardedBy("mLock")
    private boolean isCalledByAugmentedAutofillServiceLocked(@NonNull String methodName,
            int callingUid) {
        // Lazy load service first
        final RemoteAugmentedAutofillService service = getRemoteAugmentedAutofillServiceLocked();
        if (service == null) {
            Slog.w(TAG, methodName + "() called by UID " + callingUid
                    + ", but there is no augmented autofill service defined for user "
                    + getUserId());
            return false;
        }

        if (getAugmentedAutofillServiceUidLocked() != callingUid) {
            Slog.w(TAG, methodName + "() called by UID " + callingUid
                    + ", but service UID is " + getAugmentedAutofillServiceUidLocked()
                    + " for user " + getUserId());
            return false;
        }
        return true;
    }

    @GuardedBy("mLock")
    private int getAugmentedAutofillServiceUidLocked() {
        if (mRemoteAugmentedAutofillServiceInfo == null) {
            if (mMaster.verbose) {
                Slog.v(TAG, "getAugmentedAutofillServiceUid(): "
                        + "no mRemoteAugmentedAutofillServiceInfo");
            }
            return Process.INVALID_UID;
        }
        return mRemoteAugmentedAutofillServiceInfo.applicationInfo.uid;
    }

    @GuardedBy("mLock")
    boolean isWhitelistedForAugmentedAutofillLocked(@NonNull ComponentName componentName) {
        // TODO(b/122595322): need to check whitelisted activities as well.
        final String packageName = componentName.getPackageName();
        return mWhitelistedAugmentAutofillPackages.contains(packageName);
    }

    private void whitelistForAugmentedAutofillPackages(@NonNull List<String> packages) {
        // TODO(b/123100824): add CTS test for when it's null
        synchronized (mLock) {
            if (packages == null) {
                if (mMaster.verbose) Slog.v(TAG, "clearing all whitelisted augmented packages");
                mWhitelistedAugmentAutofillPackages.clear();
            } else {
                if (mMaster.verbose) Slog.v(TAG, "whitelisting augmented packages: " + packages);
                mWhitelistedAugmentAutofillPackages.addAll(packages);
            }
        }
    }

    private void sendStateToClients(boolean resetClient) {
        final RemoteCallbackList<IAutoFillManagerClient> clients;
+3 −2
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.service.autofill.augmented.AugmentedAutofillService;
import android.service.autofill.augmented.IAugmentedAutofillService;
import android.service.autofill.augmented.IFillCallback;
import android.text.format.DateUtils;
import android.util.Pair;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
@@ -57,7 +58,7 @@ final class RemoteAugmentedAutofillService
    }

    @Nullable
    public static ComponentName getComponentName(@NonNull String componentName,
    static Pair<ServiceInfo, ComponentName> getComponentName(@NonNull String componentName,
            @UserIdInt int userId, boolean isTemporary) {
        int flags = PackageManager.GET_META_DATA;
        if (!isTemporary) {
@@ -78,7 +79,7 @@ final class RemoteAugmentedAutofillService
            Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
            return null;
        }
        return serviceComponent;
        return new Pair<>(serviceInfo, serviceComponent);
    }

    @Override // from AbstractRemoteService
Loading