diff options
7 files changed, 204 insertions, 14 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index e961511165cb..1b2c59d87069 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -619,10 +619,13 @@ package android.content { method public int describeContents(); method public static android.content.AutofillOptions forWhitelistingItself(); method public boolean isAugmentedAutofillEnabled(@NonNull android.content.Context); + method public boolean isAutofillDisabledLocked(@NonNull android.content.ComponentName); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.AutofillOptions> CREATOR; + field public long appDisabledExpiration; field public boolean augmentedAutofillEnabled; field public final boolean compatModeEnabled; + field @Nullable public android.util.ArrayMap<java.lang.String,java.lang.Long> disabledActivities; field public final int loggingLevel; field @Nullable public android.util.ArraySet<android.content.ComponentName> whitelistedActivitiesForAugmentedAutofill; } diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java index 8fb9501b3319..082663e11ea0 100644 --- a/core/java/android/content/AutofillOptions.java +++ b/core/java/android/content/AutofillOptions.java @@ -21,6 +21,8 @@ import android.annotation.TestApi; import android.app.ActivityThread; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.view.autofill.AutofillManager; @@ -62,6 +64,18 @@ public final class AutofillOptions implements Parcelable { @Nullable public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill; + /** + * The package disable expiration by autofill service. + */ + public long appDisabledExpiration; + + /** + * The disabled Activities of the package. key is component name string, value is when they + * will be enabled. + */ + @Nullable + public ArrayMap<String, Long> disabledActivities; + public AutofillOptions(int loggingLevel, boolean compatModeEnabled) { this.loggingLevel = loggingLevel; this.compatModeEnabled = compatModeEnabled; @@ -82,6 +96,27 @@ public final class AutofillOptions implements Parcelable { } /** + * Returns if autofill is disabled by service to the given activity. + */ + public boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) { + final long elapsedTime = SystemClock.elapsedRealtime(); + final String component = componentName.flattenToString(); + // Check app first. + if (appDisabledExpiration >= elapsedTime) return true; + + // Then check activities. + if (disabledActivities != null) { + final Long expiration = disabledActivities.get(component); + if (expiration != null) { + if (expiration >= elapsedTime) return true; + disabledActivities.remove(component); + } + } + appDisabledExpiration = 0; + return false; + } + + /** * @hide */ @TestApi @@ -110,7 +145,8 @@ public final class AutofillOptions implements Parcelable { @Override public String toString() { return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled - + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled + "]"; + + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled + + ", appDisabledExpiration=" + appDisabledExpiration + "]"; } /** @hide */ @@ -122,6 +158,11 @@ public final class AutofillOptions implements Parcelable { pw.print(", whitelistedActivitiesForAugmentedAutofill="); pw.print(whitelistedActivitiesForAugmentedAutofill); } + pw.print(", appDisabledExpiration="); pw.print(appDisabledExpiration); + if (disabledActivities != null) { + pw.print(", disabledActivities="); + pw.print(disabledActivities); + } } @Override @@ -135,6 +176,16 @@ public final class AutofillOptions implements Parcelable { parcel.writeBoolean(compatModeEnabled); parcel.writeBoolean(augmentedAutofillEnabled); parcel.writeArraySet(whitelistedActivitiesForAugmentedAutofill); + parcel.writeLong(appDisabledExpiration); + final int size = disabledActivities != null ? disabledActivities.size() : 0; + parcel.writeInt(size); + if (size > 0) { + for (int i = 0; i < size; i++) { + final String key = disabledActivities.keyAt(i); + parcel.writeString(key); + parcel.writeLong(disabledActivities.get(key)); + } + } } public static final @android.annotation.NonNull Parcelable.Creator<AutofillOptions> CREATOR = @@ -148,6 +199,14 @@ public final class AutofillOptions implements Parcelable { options.augmentedAutofillEnabled = parcel.readBoolean(); options.whitelistedActivitiesForAugmentedAutofill = (ArraySet<ComponentName>) parcel.readArraySet(null); + options.appDisabledExpiration = parcel.readLong(); + final int size = parcel.readInt(); + if (size > 0) { + options.disabledActivities = new ArrayMap<>(); + for (int i = 0; i < size; i++) { + options.disabledActivities.put(parcel.readString(), parcel.readLong()); + } + } return options; } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 1f7ae0e7e245..3bbd3213409a 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -43,6 +43,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.RemoteException; +import android.os.SystemClock; import android.service.autofill.AutofillService; import android.service.autofill.FillEventHistory; import android.service.autofill.UserData; @@ -1737,6 +1738,26 @@ public final class AutofillManager { final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); final ComponentName componentName = client.autofillClientGetComponentName(); + + if (!mEnabledForAugmentedAutofillOnly && mOptions != null + && mOptions.isAutofillDisabledLocked(componentName)) { + if (mOptions.isAugmentedAutofillEnabled(mContext)) { + if (sDebug) { + Log.d(TAG, "startSession(" + componentName + "): disabled by service but " + + "whitelisted for augmented autofill"); + flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY; + } + } else { + if (sDebug) { + Log.d(TAG, "startSession(" + componentName + "): ignored because " + + "disabled by service and not whitelisted for augmented autofill"); + } + setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null); + client.autofillClientResetableStateAvailable(); + return; + } + } + mService.startSession(client.autofillClientGetActivityToken(), mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(), mCallback != null, flags, componentName, @@ -2067,6 +2088,7 @@ public final class AutofillManager { mServiceClientCleaner.clean(); mServiceClientCleaner = null; } + notifyReenableAutofill(); } } sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0; @@ -2403,6 +2425,37 @@ public final class AutofillManager { } } + private void notifyDisableAutofill(long disableDuration, ComponentName componentName) { + synchronized (mLock) { + if (mOptions == null) { + return; + } + long expiration = SystemClock.elapsedRealtime() + disableDuration; + // Protect it against overflow + if (expiration < 0) { + expiration = Long.MAX_VALUE; + } + if (componentName != null) { + if (mOptions.disabledActivities == null) { + mOptions.disabledActivities = new ArrayMap<>(); + } + mOptions.disabledActivities.put(componentName.flattenToString(), expiration); + } else { + mOptions.appDisabledExpiration = expiration; + } + } + } + + void notifyReenableAutofill() { + synchronized (mLock) { + if (mOptions == null) { + return; + } + mOptions.appDisabledExpiration = 0; + mOptions.disabledActivities = null; + } + } + private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) { if (sVerbose) { Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id @@ -3181,6 +3234,15 @@ public final class AutofillManager { } @Override + public void notifyDisableAutofill(long disableDuration, ComponentName componentName) + throws RemoteException { + final AutofillManager afm = mAfm.get(); + if (afm != null) { + afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName)); + } + } + + @Override public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) { final AutofillManager afm = mAfm.get(); if (afm != null) { diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 85def7fa7510..84949c87377b 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -18,6 +18,7 @@ package android.view.autofill; import java.util.List; +import android.content.ComponentName; import android.content.Intent; import android.content.IntentSender; import android.graphics.Rect; @@ -111,4 +112,8 @@ oneway interface IAutoFillManagerClient { */ void getAugmentedAutofillClient(in IResultReceiver result); + /** + * Notifies disables autofill for the app or activity. + */ + void notifyDisableAutofill(long disableDuration, in ComponentName componentName); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index a64f4e475b7d..6b7c3e69e54e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -807,6 +807,7 @@ public final class AutofillManagerService packageName, versionCode, userId); final AutofillOptions options = new AutofillOptions(loggingLevel, compatModeEnabled); mAugmentedAutofillState.injectAugmentedAutofillInfo(options, userId, packageName); + injectDisableAppInfo(options, userId, packageName); return options; } @@ -820,6 +821,19 @@ public final class AutofillManagerService } return false; } + + private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId, + String packageName) { + synchronized (mLock) { + final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + if (service != null) { + options.appDisabledExpiration = service.getAppDisabledExpirationLocked( + packageName); + options.disabledActivities = service.getAppDisabledActivitiesLocked( + packageName); + } + } + } } /** diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 1e1e07d32588..d7ed2e9abde4 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -980,12 +980,12 @@ final class AutofillManagerServiceImpl for (int i = 0; i < size; i++) { final String packageName = mDisabledApps.keyAt(i); final long expiration = mDisabledApps.valueAt(i); - builder.append(prefix).append(prefix) - .append(i).append(". ").append(packageName).append(": "); - TimeUtils.formatDuration((expiration - now), builder); - builder.append('\n'); - } - pw.println(builder); + builder.append(prefix).append(prefix) + .append(i).append(". ").append(packageName).append(": "); + TimeUtils.formatDuration((expiration - now), builder); + builder.append('\n'); + } + pw.println(builder); } pw.print(prefix); pw.print("Disabled activities: "); @@ -1000,12 +1000,12 @@ final class AutofillManagerServiceImpl for (int i = 0; i < size; i++) { final ComponentName component = mDisabledActivities.keyAt(i); final long expiration = mDisabledActivities.valueAt(i); - builder.append(prefix).append(prefix) - .append(i).append(". ").append(component).append(": "); - TimeUtils.formatDuration((expiration - now), builder); - builder.append('\n'); - } - pw.println(builder); + builder.append(prefix).append(prefix) + .append(i).append(". ").append(component).append(": "); + TimeUtils.formatDuration((expiration - now), builder); + builder.append('\n'); + } + pw.println(builder); } final int size = mSessions.size(); @@ -1418,6 +1418,36 @@ final class AutofillManagerServiceImpl } } + // Called by AutofillManagerService + long getAppDisabledExpirationLocked(@NonNull String packageName) { + if (mDisabledApps == null) { + return 0; + } + final Long expiration = mDisabledApps.get(packageName); + return expiration != null ? expiration : 0; + } + + // Called by AutofillManagerService + @Nullable + ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) { + if (mDisabledActivities != null) { + final int size = mDisabledActivities.size(); + ArrayMap<String, Long> disabledList = null; + for (int i = 0; i < size; i++) { + final ComponentName component = mDisabledActivities.keyAt(i); + if (packageName.equals(component.getPackageName())) { + if (disabledList == null) { + disabledList = new ArrayMap<>(); + } + final long expiration = mDisabledActivities.valueAt(i); + disabledList.put(component.flattenToShortString(), expiration); + } + } + return disabledList; + } + return null; + } + /** * Checks if autofill is disabled by service to the given activity. */ diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4579a3d5cf9f..48f16acc864a 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -772,13 +772,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final long disableDuration = response.getDisableDuration(); if (disableDuration > 0) { final int flags = response.getFlags(); - if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) { + final boolean disableActivityOnly = + (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0; + notifyDisableAutofillToClient(disableDuration, + disableActivityOnly ? mComponentName : null); + + if (disableActivityOnly) { mService.disableAutofillForActivity(mComponentName, disableDuration, id, mCompatMode); } else { mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration, id, mCompatMode); } + // Although "standard" autofill is disabled, it might still trigger augmented autofill if (triggerAugmentedAutofillLocked() != null) { mForAugmentedAutofillOnly = true; @@ -2567,6 +2573,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + private void notifyDisableAutofillToClient(long disableDuration, ComponentName componentName) { + synchronized (mLock) { + if (mCurrentViewId == null) return; + try { + mClient.notifyDisableAutofill(disableDuration, componentName); + } catch (RemoteException e) { + Slog.e(TAG, "Error notifying client disable autofill: id=" + mCurrentViewId, e); + } + } + } + @GuardedBy("mLock") private void updateTrackedIdsLocked() { // Only track the views of the last response as only those are reported back to the |