Move disable autofill from AutofillManagerService to AutofillOptions.
Bug: 125007037
Test: atest CtsAutoFillServiceTestCases
Change-Id: Ib139cffaced15f5a458cb07e8b6cc1502492c9a5
diff --git a/api/test-current.txt b/api/test-current.txt
index 6fe79c4..aeaebda 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -619,10 +619,13 @@
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 8fb9501..082663e 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -21,6 +21,8 @@
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 @@
@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 @@
}
/**
+ * 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 @@
@Override
public String toString() {
return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled
- + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled + "]";
+ + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled
+ + ", appDisabledExpiration=" + appDisabledExpiration + "]";
}
/** @hide */
@@ -122,6 +158,11 @@
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 @@
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 @@
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 1f7ae0e..3bbd321 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -43,6 +43,7 @@
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 @@
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 @@
mServiceClientCleaner.clean();
mServiceClientCleaner = null;
}
+ notifyReenableAutofill();
}
}
sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
@@ -2403,6 +2425,37 @@
}
}
+ 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 @@
}
@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 85def7f..84949c8 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -18,6 +18,7 @@
import java.util.List;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
@@ -111,4 +112,8 @@
*/
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 a64f4e4..6b7c3e6 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 @@
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 @@
}
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 1e1e07d..d7ed2e9 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 @@
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 @@
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 @@
}
}
+ // 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 b4ee0b1..d66e4b9 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -767,13 +767,19 @@
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;
@@ -2554,6 +2560,17 @@
}
}
+ 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