diff --git a/api/test-current.txt b/api/test-current.txt index 2a45cd3b15a29..1981d6802bcf5 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -483,6 +483,17 @@ package android.bluetooth { package android.content { + public final class AutofillOptions implements android.os.Parcelable { + ctor public AutofillOptions(int, boolean); + method public int describeContents(); + method public static android.content.AutofillOptions forWhitelistingItself(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public boolean augmentedEnabled; + field public final boolean compatModeEnabled; + field public final int loggingLevel; + } + public final class ContentCaptureOptions implements android.os.Parcelable { ctor public ContentCaptureOptions(int, int, int, int, int, @Nullable android.util.ArraySet); method public int describeContents(); @@ -509,7 +520,7 @@ package android.content { method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public android.os.UserHandle getUser(); method public int getUserId(); - method public void setAutofillCompatibilityEnabled(boolean); + method public void setAutofillOptions(@Nullable android.content.AutofillOptions); method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 89e848b2820ef..a63350cb89b48 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7608,7 +7608,7 @@ public class Activity extends ContextThemeWrapper mWindow.setColorMode(info.colorMode); - setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled()); + setAutofillOptions(application.getAutofillOptions()); setContentCaptureOptions(application.getContentCaptureOptions()); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 001cd69067a18..92302c501cafd 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -43,6 +43,7 @@ import android.app.servertransaction.PendingTransactionActions; import android.app.servertransaction.PendingTransactionActions.StopInfo; import android.app.servertransaction.TransactionExecutor; import android.app.servertransaction.TransactionExecutorHelper; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; @@ -745,7 +746,7 @@ public final class ActivityThread extends ClientTransactionHandler { /** Initial values for {@link Profiler}. */ ProfilerInfo initProfilerInfo; - boolean autofillCompatibilityEnabled; + AutofillOptions autofillOptions; /** * Content capture options for the application - when null, it means ContentCapture is not @@ -975,9 +976,8 @@ public final class ActivityThread extends ClientTransactionHandler { boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, - String buildSerial, boolean autofillCompatibilityEnabled, + String buildSerial, AutofillOptions autofillOptions, ContentCaptureOptions contentCaptureOptions) { - if (services != null) { if (false) { // Test code to make sure the app could see the passed-in services. @@ -1023,7 +1023,7 @@ public final class ActivityThread extends ClientTransactionHandler { data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; data.buildSerial = buildSerial; - data.autofillCompatibilityEnabled = autofillCompatibilityEnabled; + data.autofillOptions = autofillOptions; data.contentCaptureOptions = contentCaptureOptions; sendMessage(H.BIND_APPLICATION, data); } @@ -6164,7 +6164,7 @@ public final class ActivityThread extends ClientTransactionHandler { app = data.info.makeApplication(data.restrictedBackupMode, null); // Propagate autofill compat state - app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled); + app.setAutofillOptions(data.autofillOptions); // Propagate Content Capture options app.setContentCaptureOptions(data.contentCaptureOptions); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 3a1e80dc1c3f0..b792ad2a745c2 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -19,8 +19,8 @@ package android.app; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentCaptureOptions; @@ -215,8 +215,8 @@ class ContextImpl extends Context { // The name of the split this Context is representing. May be null. private @Nullable String mSplitName = null; - private AutofillClient mAutofillClient = null; - private boolean mIsAutofillCompatEnabled; + private @Nullable AutofillClient mAutofillClient = null; + private @Nullable AutofillOptions mAutofillOptions; private ContentCaptureOptions mContentCaptureOptions = null; @@ -2376,15 +2376,14 @@ class ContextImpl extends Context { /** @hide */ @Override - public boolean isAutofillCompatibilityEnabled() { - return mIsAutofillCompatEnabled; + public AutofillOptions getAutofillOptions() { + return mAutofillOptions; } /** @hide */ - @TestApi @Override - public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { - mIsAutofillCompatEnabled = autofillCompatEnabled; + public void setAutofillOptions(AutofillOptions options) { + mAutofillOptions = options; } /** @hide */ diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index b73092a1276f0..b8af8989170e3 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -21,6 +21,7 @@ import android.app.IUiAutomationConnection; import android.app.ProfilerInfo; import android.app.ResultInfo; import android.app.servertransaction.ClientTransaction; +import android.content.AutofillOptions; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.IIntentReceiver; @@ -69,7 +70,7 @@ oneway interface IApplicationThread { int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode, boolean persistent, in Configuration config, in CompatibilityInfo compatInfo, in Map services, - in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled, + in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, in ContentCaptureOptions contentCaptureOptions); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); diff --git a/core/java/android/content/AutofillOptions.aidl b/core/java/android/content/AutofillOptions.aidl new file mode 100644 index 0000000000000..7e4fed20957f4 --- /dev/null +++ b/core/java/android/content/AutofillOptions.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2019, 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.content; + +parcelable AutofillOptions; diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java new file mode 100644 index 0000000000000..fd7e52aaebcd1 --- /dev/null +++ b/core/java/android/content/AutofillOptions.java @@ -0,0 +1,130 @@ +/* + * 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.content; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.app.ActivityThread; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.view.autofill.AutofillManager; + +import java.io.PrintWriter; + +/** + * Autofill options for a given package. + * + *

This object is created by the Autofill System Service and passed back to the app when the + * application is created. + * + * @hide + */ +@TestApi +public final class AutofillOptions implements Parcelable { + + private static final String TAG = AutofillOptions.class.getSimpleName(); + + /** + * Logging level for {@code logcat} statements. + */ + public final int loggingLevel; + + /** + * Whether compatibility mode is enabled for the package. + */ + public final boolean compatModeEnabled; + + /** + * Whether package is whitelisted for augmented autofill. + */ + public boolean augmentedEnabled; + // TODO(b/123100824): add (optional) list of activities + + public AutofillOptions(int loggingLevel, boolean compatModeEnabled) { + this.loggingLevel = loggingLevel; + this.compatModeEnabled = compatModeEnabled; + } + + /** + * @hide + */ + @TestApi + public static AutofillOptions forWhitelistingItself() { + final ActivityThread at = ActivityThread.currentActivityThread(); + if (at == null) { + throw new IllegalStateException("No ActivityThread"); + } + + final String packageName = at.getApplication().getPackageName(); + + if (!"android.autofillservice.cts".equals(packageName)) { + Log.e(TAG, "forWhitelistingItself(): called by " + packageName); + throw new SecurityException("Thou shall not pass!"); + } + + final AutofillOptions options = new AutofillOptions( + AutofillManager.FLAG_ADD_CLIENT_VERBOSE, /* compatModeAllowed= */ true); + options.augmentedEnabled = true; + // Always log, as it's used by test only + Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); + + return options; + } + + @Override + public String toString() { + return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + + compatModeEnabled + ", augmentedEnabled=" + augmentedEnabled + "]"; + } + + /** @hide */ + public void dumpShort(@NonNull PrintWriter pw) { + pw.print("logLvl="); pw.print(loggingLevel); + pw.print(", compatMode="); pw.print(compatModeEnabled); + pw.print(", augmented="); pw.print(augmentedEnabled); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(loggingLevel); + parcel.writeBoolean(compatModeEnabled); + parcel.writeBoolean(augmentedEnabled); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public AutofillOptions createFromParcel(Parcel parcel) { + final int loggingLevel = parcel.readInt(); + final boolean compatMode = parcel.readBoolean(); + final AutofillOptions options = new AutofillOptions(loggingLevel, compatMode); + options.augmentedEnabled = parcel.readBoolean(); + return options; + } + + @Override + public AutofillOptions[] newArray(int size) { + return new AutofillOptions[size]; + } + }; +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fdb0041d49edd..1f3a000cea4bf 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5340,16 +5340,24 @@ public abstract class Context { /** * @hide */ - public boolean isAutofillCompatibilityEnabled() { - return false; + public final boolean isAutofillCompatibilityEnabled() { + final AutofillOptions options = getAutofillOptions(); + return options != null && options.compatModeEnabled; + } + + /** + * @hide + */ + @Nullable + public AutofillOptions getAutofillOptions() { + return null; } /** * @hide */ @TestApi - public void setAutofillCompatibilityEnabled( - @SuppressWarnings("unused") boolean autofillCompatEnabled) { + public void setAutofillOptions(@SuppressWarnings("unused") @Nullable AutofillOptions options) { } /** diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 68b4320568c29..fac3266e9bf86 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1031,22 +1031,17 @@ public class ContextWrapper extends Context { mBase.setAutofillClient(client); } - /** - * @hide - */ + /** @hide */ @Override - public boolean isAutofillCompatibilityEnabled() { - return mBase != null && mBase.isAutofillCompatibilityEnabled(); + public AutofillOptions getAutofillOptions() { + return mBase == null ? null : mBase.getAutofillOptions(); } - /** - * @hide - */ - @TestApi + /** @hide */ @Override - public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { + public void setAutofillOptions(AutofillOptions options) { if (mBase != null) { - mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled); + mBase.setAutofillOptions(options); } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index e9b16836157fb..9dcbe05a06d1a 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -28,6 +28,7 @@ import android.annotation.RequiresFeature; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.content.AutofillOptions; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -482,6 +483,9 @@ public final class AutofillManager { @GuardedBy("mLock") private CompatibilityBridge mCompatibilityBridge; + @Nullable + private final AutofillOptions mOptions; + /** @hide */ public interface AutofillClient { /** @@ -618,6 +622,12 @@ public final class AutofillManager { public AutofillManager(Context context, IAutoFillManager service) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); mService = service; + mOptions = context.getAutofillOptions(); + + if (mOptions != null) { + sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0; + sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0; + } } /** @@ -2352,6 +2362,9 @@ public final class AutofillManager { pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds); pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId); pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish); + if (mOptions != null) { + pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println(); + } pw.print(pfx); pw.print("compat mode enabled: "); synchronized (mLock) { if (mCompatibilityBridge != null) { diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java index 155fe721311c3..d5862bd2f9428 100644 --- a/core/java/android/view/autofill/AutofillManagerInternal.java +++ b/core/java/android/view/autofill/AutofillManagerInternal.java @@ -16,7 +16,9 @@ package android.view.autofill; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.AutofillOptions; /** * Autofill Manager local system service interface. @@ -31,12 +33,13 @@ public abstract class AutofillManagerInternal { public abstract void onBackKeyPressed(); /** - * Gets whether compatibility mode is enabled for a package + * Gets autofill options for a package * * @param packageName The package for which to query. * @param versionCode The package version code. * @param userId The user id for which to query. */ - public abstract boolean isCompatibilityModeRequested(@NonNull String packageName, + @Nullable + public abstract AutofillOptions getAutofillOptions(@NonNull String packageName, long versionCode, @UserIdInt int userId); } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 9fabe44f2a8ac..d73c174212bd2 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -28,6 +28,7 @@ import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; import android.app.ProfilerInfo; +import android.content.AutofillOptions; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.IIntentReceiver; @@ -407,7 +408,7 @@ public class TransactionParcelTests { IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1, - boolean autofillCompatEnabled, ContentCaptureOptions o) throws RemoteException { + AutofillOptions ao, ContentCaptureOptions co) throws RemoteException { } @Override diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index e0fb33799ff7a..1cca813ece25f 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.ActivityThread; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -699,12 +700,31 @@ public final class AutofillManagerService } @Override - public boolean isCompatibilityModeRequested(@NonNull String packageName, + public AutofillOptions getAutofillOptions(@NonNull String packageName, long versionCode, @UserIdInt int userId) { - return mAutofillCompatState.isCompatibilityModeRequested( + final int loggingLevel; + if (verbose) { + loggingLevel = AutofillManager.FLAG_ADD_CLIENT_VERBOSE + | AutofillManager.FLAG_ADD_CLIENT_DEBUG; + } else if (debug) { + loggingLevel = AutofillManager.FLAG_ADD_CLIENT_DEBUG; + } else { + loggingLevel = AutofillManager.NO_LOGGING; + } + final boolean compatModeEnabled = mAutofillCompatState.isCompatibilityModeRequested( packageName, versionCode, userId); - } + final AutofillOptions options = new AutofillOptions(loggingLevel, compatModeEnabled); + synchronized (mLock) { + final AutofillManagerServiceImpl service = + getServiceForUserLocked(UserHandle.getCallingUserId()); + if (service != null) { + service.setAugmentedAutofillWhitelistLocked(options, packageName); + } + } + + return options; + } } /** diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 15dce4af405be..364e537c34d29 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -28,6 +28,7 @@ import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; +import android.content.AutofillOptions; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -1169,6 +1170,13 @@ final class AutofillManagerServiceImpl return mWhitelistedAugmentAutofillPackages.contains(packageName); } + @GuardedBy("mLock") + void setAugmentedAutofillWhitelistLocked(@NonNull AutofillOptions options, + @NonNull String packageName) { + // TODO(b/122595322): need to setwhitelisted activities as well. + options.augmentedEnabled = mWhitelistedAugmentAutofillPackages.contains(packageName); + } + private void whitelistForAugmentedAutofillPackages(@NonNull List packages) { // TODO(b/123100824): add CTS test for when it's null synchronized (mLock) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7c46f1d70cb6b..ef8cf527f8b39 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -187,6 +187,7 @@ import android.app.backup.IBackupManager; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; @@ -4741,12 +4742,12 @@ public class ActivityManagerService extends IActivityManager.Stub // Figure out whether the app needs to run in autofill compat mode. - boolean isAutofillCompatEnabled = false; + AutofillOptions autofillOptions = null; if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) { final AutofillManagerInternal afm = LocalServices.getService( AutofillManagerInternal.class); if (afm != null) { - isAutofillCompatEnabled = afm.isCompatibilityModeRequested( + autofillOptions = afm.getAutofillOptions( app.info.packageName, app.info.versionCode, app.userId); } } @@ -4779,7 +4780,7 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(app.getWindowProcessController().getConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, isAutofillCompatEnabled, contentCaptureOptions); + buildSerial, autofillOptions, contentCaptureOptions); } else { thread.bindApplication(processName, appInfo, providers, null, profilerInfo, null, null, null, testMode, @@ -4788,7 +4789,7 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(app.getWindowProcessController().getConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, isAutofillCompatEnabled, contentCaptureOptions); + buildSerial, autofillOptions, contentCaptureOptions); } if (profilerInfo != null) { profilerInfo.closeFd();