diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index a6b40ed55ac5c..e9b16836157fb 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -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 packages, @Nullable List activities) { - // TODO(b/123100824): implement + setAugmentedAutofillWhitelist(toSet(packages), toSet(activities)); + } + + private ArraySet toSet(@Nullable List set) { + return set == null ? null : new ArraySet(set); } /** @@ -1814,7 +1823,32 @@ public final class AutofillManager { @TestApi public void setAugmentedAutofillWhitelist(@Nullable Set packages, @Nullable Set 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 ArrayList toList(@Nullable Set set) { + return set == null ? null : new ArrayList(set); } private void requestShowFillUi(int sessionId, AutofillId id, int width, int height, diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 26aeba5cfdfa1..9e6a4afe54149 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -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 packages, in List activities, + in IResultReceiver result); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 4a3f12674eb79..e0fb33799ff7a 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -1025,6 +1025,29 @@ public final class AutofillManagerService send(receiver, algorithm); } + @Override + public void setAugmentedAutofillWhitelist(@Nullable List packages, + @Nullable List 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 { diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 1bce11ee95de0..1a6dabb499080 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -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 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( - serviceName, mUserId, mAugmentedAutofillResolver.isTemporary(mUserId)); - if (componentName == null) return null; + final Pair pair = RemoteAugmentedAutofillService + .getComponentName( + serviceName, mUserId, mAugmentedAutofillResolver.isTemporary(mUserId)); + if (pair == null) return null; + + mRemoteAugmentedAutofillServiceInfo = pair.first; + final ComponentName componentName = pair.second; if (sVerbose) { Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName); } @@ -1067,9 +1097,87 @@ final class AutofillManagerServiceImpl private void updateRemoteAugmentedAutofillService(@Nullable String serviceName) { if (serviceName == null) { if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!"); - if (mRemoteAugmentedAutofillService != null) { - mRemoteAugmentedAutofillService.destroy(); - mRemoteAugmentedAutofillService = null; + 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 packages, + List 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 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); } } } diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 61f63d3bbc3d9..9947aed8f0b48 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -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 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 diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 04fc2588134ee..848f249d9a395 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2613,13 +2613,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + " when server returned null for session " + this.id); } + final boolean isWhitelisted = mService + .isWhitelistedForAugmentedAutofillLocked(mComponentName); + final String historyItem = "aug:id=" + id + " u=" + uid + " m=" + mode + " a=" + ComponentName.flattenToShortString(mComponentName) + " f=" + mCurrentViewId - + " s=" + remoteService.getComponentName(); + + " s=" + remoteService.getComponentName() + + " w=" + isWhitelisted; mService.getMaster().logRequestLocked(historyItem); + if (!isWhitelisted) { + if (sVerbose) { + Slog.v(TAG, mComponentName.toShortString() + " is not whitelisted for " + + "augmented autofill"); + } + return null; + } + final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue(); // TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize