diff --git a/api/test-current.txt b/api/test-current.txt index 4bab0d1281380..707df9a89eec0 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -563,11 +563,13 @@ package android.content { ctor public AutofillOptions(int, boolean); method public int describeContents(); method public static android.content.AutofillOptions forWhitelistingItself(); + method public boolean isAugmentedAutofillEnabled(@NonNull android.content.Context); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; - field public boolean augmentedEnabled; + field public boolean augmentedAutofillEnabled; field public final boolean compatModeEnabled; field public final int loggingLevel; + field @Nullable public android.util.ArraySet whitelistedActivitiesForAugmentedAutofill; } public final class ContentCaptureOptions implements android.os.Parcelable { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d1d4bd56f1c77..56bf8fa8ffcea 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -127,6 +127,7 @@ import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -717,7 +718,7 @@ public class Activity extends ContextThemeWrapper Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback, - AutofillManager.AutofillClient { + AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -1119,6 +1120,12 @@ public class Activity extends ContextThemeWrapper return this; } + /** @hide */ + @Override + public final ContentCaptureClient getContentCaptureClient() { + return this; + } + /** * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives * lifecycle callbacks for only this Activity. @@ -6464,6 +6471,12 @@ public class Activity extends ContextThemeWrapper return getComponentName(); } + /** @hide */ + @Override + public final ComponentName contentCaptureClientGetComponentName() { + return getComponentName(); + } + /** * Retrieve a {@link SharedPreferences} object for accessing preferences * that are private to this activity. This simply calls the underlying diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java index 0d25f4d5fe241..f59bc9891c86a 100644 --- a/core/java/android/content/AutofillOptions.java +++ b/core/java/android/content/AutofillOptions.java @@ -16,12 +16,15 @@ package android.content; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.app.ActivityThread; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArraySet; import android.util.Log; import android.view.autofill.AutofillManager; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import java.io.PrintWriter; @@ -51,14 +54,33 @@ public final class AutofillOptions implements Parcelable { /** * Whether package is whitelisted for augmented autofill. */ - public boolean augmentedEnabled; - // TODO(b/123100824): add (optional) list of activities + public boolean augmentedAutofillEnabled; + + /** + * List of whitelisted activities. + */ + @Nullable + public ArraySet whitelistedActivitiesForAugmentedAutofill; public AutofillOptions(int loggingLevel, boolean compatModeEnabled) { this.loggingLevel = loggingLevel; this.compatModeEnabled = compatModeEnabled; } + /** + * Returns whether activity is whitelisted for augmented autofill. + */ + public boolean isAugmentedAutofillEnabled(@NonNull Context context) { + if (!augmentedAutofillEnabled) return false; + + final ContentCaptureClient contentCaptureClient = context.getContentCaptureClient(); + if (contentCaptureClient == null) return false; + + final ComponentName component = contentCaptureClient.contentCaptureClientGetComponentName(); + return whitelistedActivitiesForAugmentedAutofill == null + || whitelistedActivitiesForAugmentedAutofill.contains(component); + } + /** * @hide */ @@ -78,7 +100,7 @@ public final class AutofillOptions implements Parcelable { final AutofillOptions options = new AutofillOptions( AutofillManager.FLAG_ADD_CLIENT_VERBOSE, /* compatModeAllowed= */ true); - options.augmentedEnabled = true; + options.augmentedAutofillEnabled = true; // Always log, as it's used by test only Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); @@ -87,15 +109,19 @@ public final class AutofillOptions implements Parcelable { @Override public String toString() { - return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" - + compatModeEnabled + ", augmentedEnabled=" + augmentedEnabled + "]"; + return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled + + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled + "]"; } /** @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); + pw.print(", augmented="); pw.print(augmentedAutofillEnabled); + if (whitelistedActivitiesForAugmentedAutofill != null) { + pw.print(", whitelistedActivitiesForAugmentedAutofill="); + pw.print(whitelistedActivitiesForAugmentedAutofill); + } } @Override @@ -107,7 +133,8 @@ public final class AutofillOptions implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(loggingLevel); parcel.writeBoolean(compatModeEnabled); - parcel.writeBoolean(augmentedEnabled); + parcel.writeBoolean(augmentedAutofillEnabled); + parcel.writeArraySet(whitelistedActivitiesForAugmentedAutofill); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = @@ -118,7 +145,9 @@ public final class AutofillOptions implements Parcelable { final int loggingLevel = parcel.readInt(); final boolean compatMode = parcel.readBoolean(); final AutofillOptions options = new AutofillOptions(loggingLevel, compatMode); - options.augmentedEnabled = parcel.readBoolean(); + options.augmentedAutofillEnabled = parcel.readBoolean(); + options.whitelistedActivitiesForAugmentedAutofill = + (ArraySet) parcel.readArraySet(null); return options; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index beb1fb68d2188..4199528893e6e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -69,6 +69,7 @@ import android.view.View; import android.view.ViewDebug; import android.view.WindowManager; import android.view.autofill.AutofillManager.AutofillClient; +import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.view.textclassifier.TextClassificationManager; import java.io.File; @@ -5340,6 +5341,14 @@ public abstract class Context { public void setAutofillClient(@SuppressWarnings("unused") AutofillClient client) { } + /** + * @hide + */ + @Nullable + public ContentCaptureClient getContentCaptureClient() { + return null; + } + /** * @hide */ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 874be814ed9ae..287365f703447 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9530,8 +9530,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // View is not important for "regular" autofill, so we must check if Augmented Autofill // is enabled for the activity final AutofillOptions options = mContext.getAutofillOptions(); - if (options == null || !options.augmentedEnabled) { - // TODO(b/123100824): should also check if activity is whitelisted + if (options == null || !options.isAugmentedAutofillEnabled(mContext)) { return false; } final AutofillManager afm = getAutofillManager(); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 885bd2a395804..a3e65496bcd01 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -192,6 +192,15 @@ public final class ContentCaptureManager { @GuardedBy("mLock") private MainContentCaptureSession mMainSession; + /** @hide */ + public interface ContentCaptureClient { + /** + * Gets the component name of the client. + */ + @NonNull + ComponentName contentCaptureClientGetComponentName(); + } + /** @hide */ public ContentCaptureManager(@NonNull Context context, @NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) { diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 6bf1eba30f554..1f0971e7d6c56 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -121,11 +121,11 @@ public abstract class ContentCaptureSession implements AutoCloseable { public static final int STATE_INTERNAL_ERROR = 0x100; /** - * Session is disabled because service didn't whitelist package. + * Session is disabled because service didn't whitelist package or activity. * * @hide */ - public static final int STATE_PACKAGE_NOT_WHITELISTED = 0x200; + public static final int STATE_NOT_WHITELISTED = 0x200; private static final int INITIAL_CHILDREN_CAPACITY = 5; diff --git a/core/java/com/android/internal/infra/WhitelistHelper.java b/core/java/com/android/internal/infra/WhitelistHelper.java new file mode 100644 index 0000000000000..eec82bc2403e1 --- /dev/null +++ b/core/java/com/android/internal/infra/WhitelistHelper.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 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 com.android.internal.infra; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.util.Preconditions; + +import java.io.PrintWriter; + +/** + * Helper class for keeping track of whitelisted packages/activities. + * + * @hide + */ +public final class WhitelistHelper { + + private static final String TAG = "WhitelistHelper"; + + /** + * Map of whitelisted packages/activities. The whole package is whitelisted if its + * corresponding value is {@code null}. + */ + @Nullable + private ArrayMap> mWhitelistedPackages; + + /** + * Sets the whitelist with the given packages and activities. The list is cleared if both + * packageNames and components are {@code null}. + * + * @param packageNames packages to be whitelisted. + * @param components activities to be whitelisted. + * + * @throws IllegalArgumentException if packages or components are empty. + */ + public void setWhitelist(@Nullable ArraySet packageNames, + @Nullable ArraySet components) { + mWhitelistedPackages = null; + if (packageNames == null && components == null) return; + + if ((packageNames != null && packageNames.isEmpty()) + || (components != null && components.isEmpty())) { + throw new IllegalArgumentException("Packages or Components cannot be empty."); + } + + mWhitelistedPackages = new ArrayMap<>(); + + if (packageNames != null) { + for (int i = 0; i < packageNames.size(); i++) { + mWhitelistedPackages.put(packageNames.valueAt(i), null); + } + } + + if (components != null) { + for (int i = 0; i < components.size(); i++) { + final ComponentName component = components.valueAt(i); + if (component == null) { + Log.w(TAG, "setWhitelist(): component is null"); + continue; + } + + final String packageName = component.getPackageName(); + ArraySet set = mWhitelistedPackages.get(packageName); + if (set == null) { + set = new ArraySet<>(); + mWhitelistedPackages.put(packageName, set); + } + set.add(component); + } + } + } + + /** + * Returns {@code true} if the entire package is whitelisted. + */ + public boolean isWhitelisted(@NonNull String packageName) { + Preconditions.checkNotNull(packageName); + + if (mWhitelistedPackages == null) return false; + + return mWhitelistedPackages.containsKey(packageName) + && mWhitelistedPackages.get(packageName) == null; + } + + /** + * Returns {@code true} if the specified activity is whitelisted. + */ + public boolean isWhitelisted(@NonNull ComponentName componentName) { + Preconditions.checkNotNull(componentName); + + final String packageName = componentName.getPackageName(); + final ArraySet whitelistedComponents = getWhitelistedComponents(packageName); + if (whitelistedComponents != null) { + return whitelistedComponents.contains(componentName); + } + + return isWhitelisted(packageName); + } + + /** + * Returns a set of whitelisted components with the given package, or null if nothing is + * whitelisted. + */ + @Nullable + public ArraySet getWhitelistedComponents(@NonNull String packageName) { + Preconditions.checkNotNull(packageName); + + return mWhitelistedPackages == null ? null : mWhitelistedPackages.get(packageName); + } + + @Override + public String toString() { + return "WhitelistHelper[" + mWhitelistedPackages + ']'; + } + + /** + * Dumps it! + */ + public void dump(@NonNull String prefix, @NonNull String message, @NonNull PrintWriter pw) { + if (mWhitelistedPackages == null || mWhitelistedPackages.size() == 0) { + pw.print(prefix); pw.print(message); pw.println(": (no whitelisted packages)"); + return; + } + + final int size = mWhitelistedPackages.size(); + pw.print(prefix); pw.print(message); pw.print(": "); pw.print(size); + pw.println(" packages"); + for (int i = 0; i < mWhitelistedPackages.size(); i++) { + final String packageName = mWhitelistedPackages.keyAt(i); + final ArraySet components = mWhitelistedPackages.valueAt(i); + pw.print(prefix); pw.print(i); pw.print("."); pw.print(packageName); pw.print(": "); + if (components == null) { + pw.println("(whole package)"); + continue; + } + + pw.print("["); pw.print(components.valueAt(0)); + for (int j = 1; j < components.size(); j++) { + pw.print(", "); pw.print(components.valueAt(i)); + } + pw.println("]"); + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/infra/WhitelistHelperTest.java b/core/tests/coretests/src/com/android/internal/infra/WhitelistHelperTest.java new file mode 100644 index 0000000000000..ab6830b106d02 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/infra/WhitelistHelperTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 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 com.android.internal.infra; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.content.ComponentName; +import android.util.ArraySet; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * Unit test for {@link WhitelistHelper}. + * + *

To run it: + * {@code atest FrameworksCoreTests:com.android.internal.infra.WhitelistHelperTest} + */ + +@RunWith(MockitoJUnitRunner.class) +public class WhitelistHelperTest { + private WhitelistHelper mWhitelistHelper = new WhitelistHelper(); + + private String mPackage1 = "com.example"; + private String mPackage2 = "com.example2"; + + private ComponentName mComponent1 = new ComponentName(mPackage1, "class1"); + private ComponentName mComponent2 = new ComponentName(mPackage1, "class2"); + private ComponentName mComponentDifferentPkg = new ComponentName(mPackage2, "class3"); + + @Test + public void testSetWhitelist_emptyArguments() { + assertThrows(IllegalArgumentException.class, + () -> mWhitelistHelper.setWhitelist(new ArraySet<>(), null)); + assertThrows(IllegalArgumentException.class, + () -> mWhitelistHelper.setWhitelist(null, new ArraySet<>())); + assertThrows(IllegalArgumentException.class, + () -> mWhitelistHelper.setWhitelist(new ArraySet<>(), new ArraySet<>())); + } + + @Test + public void testWhitelistHelper_nullArguments() { + assertThrows(NullPointerException.class, + () -> mWhitelistHelper.isWhitelisted((String) null)); + assertThrows(NullPointerException.class, + () -> mWhitelistHelper.isWhitelisted((ComponentName) null)); + assertThrows(NullPointerException.class, + () -> mWhitelistHelper.getWhitelistedComponents(null)); + } + + @Test + public void testSetWhitelist_nullPackage() { + final ArraySet packages = new ArraySet<>(); + packages.add(null); + mWhitelistHelper.setWhitelist(packages, null); + + assertThat(mWhitelistHelper.isWhitelisted(mPackage1)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mPackage2)).isFalse(); + + assertThat(mWhitelistHelper.isWhitelisted(mComponent1)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mComponent2)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mComponentDifferentPkg)).isFalse(); + } + + @Test + public void testSetWhitelist_nullActivity() { + final ArraySet components = new ArraySet<>(); + components.add(null); + mWhitelistHelper.setWhitelist(null, components); + + assertThat(mWhitelistHelper.isWhitelisted(mPackage1)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mPackage2)).isFalse(); + + assertThat(mWhitelistHelper.isWhitelisted(mComponent1)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mComponent2)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mComponentDifferentPkg)).isFalse(); + } + + @Test + public void testSetWhitelist_replaceWhitelist() { + final ArraySet components = new ArraySet<>(); + components.add(mComponent1); + mWhitelistHelper.setWhitelist(null, components); + assertThat(mWhitelistHelper.isWhitelisted(mComponent1)).isTrue(); + assertThat(mWhitelistHelper.isWhitelisted(mComponent2)).isFalse(); + + final ArraySet components2 = new ArraySet<>(); + components2.add(mComponent2); + mWhitelistHelper.setWhitelist(null, components2); + assertThat(mWhitelistHelper.isWhitelisted(mComponent1)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mComponent2)).isTrue(); + } + + @Test + public void testIsWhitelisted_packageWhitelisted() { + final ArraySet packages = new ArraySet<>(); + packages.add(mPackage1); + mWhitelistHelper.setWhitelist(packages, null); + + assertThat(mWhitelistHelper.isWhitelisted(mPackage1)).isTrue(); + assertThat(mWhitelistHelper.isWhitelisted(mPackage2)).isFalse(); + + assertThat(mWhitelistHelper.isWhitelisted(mComponent1)).isTrue(); + assertThat(mWhitelistHelper.isWhitelisted(mComponent2)).isTrue(); + assertThat(mWhitelistHelper.isWhitelisted(mComponentDifferentPkg)).isFalse(); + } + + @Test + public void testIsWhitelisted_activityWhitelisted() { + final ArraySet components = new ArraySet<>(); + components.add(mComponent1); + mWhitelistHelper.setWhitelist(null, components); + + assertThat(mWhitelistHelper.isWhitelisted(mPackage1)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mPackage2)).isFalse(); + + assertThat(mWhitelistHelper.isWhitelisted(mComponent1)).isTrue(); + assertThat(mWhitelistHelper.isWhitelisted(mComponent2)).isFalse(); + assertThat(mWhitelistHelper.isWhitelisted(mComponentDifferentPkg)).isFalse(); + } +} diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 3f33813ff4e75..62deaffd9ea43 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -72,6 +72,7 @@ import android.view.autofill.IAutoFillManagerClient; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.infra.WhitelistHelper; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.LocalServices; @@ -173,10 +174,10 @@ final class AutofillManagerServiceImpl private ServiceInfo mRemoteAugmentedAutofillServiceInfo; /** - * List of packages that are whitelisted to be trigger augmented autofill. + * List of packages/activities that are whitelisted to be trigger augmented autofill. */ @GuardedBy("mLock") - private final ArraySet mWhitelistedAugmentAutofillPackages = new ArraySet<>(); + private final WhitelistHelper mAugmentedWhitelistHelper = new WhitelistHelper(); AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui, @@ -905,13 +906,8 @@ final class AutofillManagerServiceImpl 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("augmented autofill whitelist: "); + mAugmentedWhitelistHelper.dump(prefix2, "Whitelist", pw); pw.print(prefix); pw.print("Field classification enabled: "); pw.println(isFieldClassificationEnabledLocked()); @@ -1129,9 +1125,8 @@ final class AutofillManagerServiceImpl Slog.v(TAG, "setAugmentedAutofillWhitelistLocked(packages=" + packages + ", activities=" + activities + ")"); } - whitelistForAugmentedAutofillPackages(packages); + whitelistForAugmentedAutofillPackages(packages, activities); - // TODO(b/123100824): whitelist activities as well // TODO(b/122858578): log metrics return true; } @@ -1171,28 +1166,30 @@ final class AutofillManagerServiceImpl @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); + return mAugmentedWhitelistHelper.isWhitelisted(componentName); } @GuardedBy("mLock") void setAugmentedAutofillWhitelistLocked(@NonNull AutofillOptions options, @NonNull String packageName) { - // TODO(b/122595322): need to setwhitelisted activities as well. - options.augmentedEnabled = mWhitelistedAugmentAutofillPackages.contains(packageName); + options.augmentedAutofillEnabled = mAugmentedWhitelistHelper.isWhitelisted(packageName); + options.whitelistedActivitiesForAugmentedAutofill = mAugmentedWhitelistHelper + .getWhitelistedComponents(packageName); } - private void whitelistForAugmentedAutofillPackages(@NonNull List packages) { + /** + * + * @throws IllegalArgumentException if packages or components are empty. + */ + private void whitelistForAugmentedAutofillPackages(@Nullable List packages, + @Nullable List components) { // 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); + if (mMaster.verbose) { + Slog.v(TAG, "whitelisting packages: " + packages + "and activities: " + components); } + mAugmentedWhitelistHelper.setWhitelist(new ArraySet<>(packages), + new ArraySet<>(components)); mRemoteAugmentedAutofillService = getRemoteAugmentedAutofillServiceLocked(); } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index b33259d369a0a..955d764797c87 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -20,8 +20,8 @@ import static android.service.contentcapture.ContentCaptureService.setClientStat import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR; +import static android.view.contentcapture.ContentCaptureSession.STATE_NOT_WHITELISTED; import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE; -import static android.view.contentcapture.ContentCaptureSession.STATE_PACKAGE_NOT_WHITELISTED; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; @@ -29,6 +29,7 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppGlobals; @@ -55,6 +56,7 @@ import android.util.Slog; import android.view.contentcapture.UserDataRemovalRequest; import com.android.internal.annotations.GuardedBy; +import com.android.internal.infra.WhitelistHelper; import com.android.internal.os.IResultReceiver; import com.android.server.LocalServices; import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks; @@ -94,7 +96,7 @@ final class ContentCapturePerUserService * List of packages that are whitelisted to be content captured. */ @GuardedBy("mLock") - private final ArraySet mWhitelistedPackages = new ArraySet<>(); + private final WhitelistHelper mWhitelistHelper = new WhitelistHelper(); // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's @@ -194,7 +196,7 @@ final class ContentCapturePerUserService final int taskId = activityPresentationInfo.taskId; final int displayId = activityPresentationInfo.displayId; final ComponentName componentName = activityPresentationInfo.componentName; - final boolean whitelisted = isWhitelistedLocked(componentName); + final boolean whiteListed = isWhitelistedLocked(componentName); final ComponentName serviceComponentName = getServiceComponentName(); final boolean enabled = isEnabledLocked(); if (mMaster.mRequestsHistory != null) { @@ -204,7 +206,7 @@ final class ContentCapturePerUserService + " t=" + taskId + " d=" + displayId + " s=" + ComponentName.flattenToShortString(serviceComponentName) + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)") - + " w=" + whitelisted; + + " w=" + whiteListed; mMaster.mRequestsHistory.log(historyItem); } @@ -225,12 +227,12 @@ final class ContentCapturePerUserService return; } - if (!whitelisted) { + if (!whiteListed) { if (mMaster.debug) { - Slog.d(TAG, "startSession(" + componentName + "): not whitelisted"); + Slog.d(TAG, "startSession(" + componentName + "): package or component " + + "not whitelisted"); } - // TODO(b/122595322): need to return STATE_ACTIVITY_NOT_WHITELISTED as well - setClientState(clientReceiver, STATE_DISABLED | STATE_PACKAGE_NOT_WHITELISTED, + setClientState(clientReceiver, STATE_DISABLED | STATE_NOT_WHITELISTED, /* binder= */ null); return; } @@ -270,21 +272,20 @@ final class ContentCapturePerUserService @GuardedBy("mLock") private boolean isWhitelistedLocked(@NonNull ComponentName componentName) { - // TODO(b/122595322): need to check whitelisted activities as well. - final String packageName = componentName.getPackageName(); - return mWhitelistedPackages.contains(packageName); + return mWhitelistHelper.isWhitelisted(componentName); } - private void whitelistPackages(@NonNull List packages) { + /** + * @throws IllegalArgumentException if packages or components are empty. + */ + private void setWhitelist(@Nullable List packages, + @Nullable List components) { // TODO(b/122595322): add CTS test for when it's null synchronized (mLock) { - if (packages == null) { - if (mMaster.verbose) Slog.v(TAG, "clearing all whitelisted packages"); - mWhitelistedPackages.clear(); - } else { - if (mMaster.verbose) Slog.v(TAG, "whitelisting packages: " + packages); - mWhitelistedPackages.addAll(packages); + if (mMaster.verbose) { + Slog.v(TAG, "whitelisting packages: " + packages + "and activities: " + components); } + mWhitelistHelper.setWhitelist(new ArraySet<>(packages), new ArraySet<>(components)); } } @@ -411,15 +412,15 @@ final class ContentCapturePerUserService @GuardedBy("mLock") ContentCaptureOptions getOptionsForPackageLocked(@NonNull String packageName) { - if (!mWhitelistedPackages.contains(packageName)) { + if (!mWhitelistHelper.isWhitelisted(packageName)) { if (mMaster.verbose) { Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted"); } return null; } - // TODO(b/122595322): need to check whitelisted activities as well. - final ArraySet whitelistedComponents = null; + final ArraySet whitelistedComponents = mWhitelistHelper + .getWhitelistedComponents(packageName); ContentCaptureOptions options = new ContentCaptureOptions(mMaster.mDevCfgLoggingLevel, mMaster.mDevCfgMaxBufferSize, mMaster.mDevCfgIdleFlushingFrequencyMs, mMaster.mDevCfgTextChangeFlushingFrequencyMs, mMaster.mDevCfgLogHistorySize, @@ -440,12 +441,7 @@ final class ContentCapturePerUserService mRemoteService.dump(prefix2, pw); } - final int whitelistSize = mWhitelistedPackages.size(); - pw.print(prefix); pw.print("Whitelisted packages: "); pw.println(whitelistSize); - for (int i = 0; i < whitelistSize; i++) { - final String whitelistedPkg = mWhitelistedPackages.valueAt(i); - pw.print(prefix2); pw.print(i + 1); pw.print(": "); pw.println(whitelistedPkg); - } + pw.print(prefix); pw.print("Whitelist: "); pw.println(mWhitelistHelper); if (mSessions.isEmpty()) { pw.print(prefix); pw.println("no sessions"); @@ -485,9 +481,8 @@ final class ContentCapturePerUserService Slog.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities=" + activities + ")"); } - whitelistPackages(packages); + setWhitelist(packages, activities); - // TODO(b/122595322): whitelist activities as well // TODO(b/119613670): log metrics }