From 24d5893b25ce62b7bc9ed9f35fa72b9d47f23cdd Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Tue, 21 Mar 2017 14:13:58 -0700 Subject: [PATCH] Added a UserManager.DISALLOW_AUTOFILL restriction. bug: 35710740 Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases -t android.autofillservice.cts.LoginActivityTest#testUserRestriction Test: cts-tradefed run commandAndExit cts-dev -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.MixedDeviceOwnerTest#testDisallowAutofill_allowed Test: cts-tradefed run commandAndExit cts-dev -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.MixedProfileOwnerTest#testDisallowAutofill_allowed Test: cts-tradefed run commandAndExit cts-dev -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testDisallowAutofill_allowed Change-Id: I41b2bf9fe3bc8df627c6650960bd11346c430a7e --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + cmds/pm/src/com/android/commands/pm/Pm.java | 1 + core/java/android/os/UserManager.java | 14 ++++ .../service/autofill/AutofillService.java | 7 +- .../android/service/autofill/Dataset.java | 5 +- .../autofill/AutofillManagerService.java | 70 +++++++++++++++---- .../autofill/AutofillManagerServiceImpl.java | 43 +++++++----- .../com/android/server/autofill/Helper.java | 27 ++++--- .../server/pm/UserRestrictionsUtils.java | 1 + 11 files changed, 119 insertions(+), 52 deletions(-) diff --git a/api/current.txt b/api/current.txt index 24586f0669f8e..653570dc21770 100644 --- a/api/current.txt +++ b/api/current.txt @@ -31661,6 +31661,7 @@ package android.os { field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user"; field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume"; field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps"; + field public static final java.lang.String DISALLOW_AUTOFILL = "no_autofill"; field public static final java.lang.String DISALLOW_BLUETOOTH = "no_bluetooth"; field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts"; diff --git a/api/system-current.txt b/api/system-current.txt index 7e9267106f775..a5285d18ab327 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -34475,6 +34475,7 @@ package android.os { field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user"; field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume"; field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps"; + field public static final java.lang.String DISALLOW_AUTOFILL = "no_autofill"; field public static final java.lang.String DISALLOW_BLUETOOTH = "no_bluetooth"; field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts"; diff --git a/api/test-current.txt b/api/test-current.txt index d33c1fa574806..9585809571dad 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -31786,6 +31786,7 @@ package android.os { field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user"; field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume"; field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps"; + field public static final java.lang.String DISALLOW_AUTOFILL = "no_autofill"; field public static final java.lang.String DISALLOW_BLUETOOTH = "no_bluetooth"; field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts"; diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 827a77f24ee95..deb5b3114e8fa 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1584,6 +1584,7 @@ public final class Pm { System.err.println(" pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm default-state [--user USER_ID] PACKAGE_OR_COMPONENT"); + System.err.println(" pm set-user-restriction [--user USER_ID] RESTRICTION VALUE"); System.err.println(" pm hide [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm grant [--user USER_ID] PACKAGE PERMISSION"); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f6712f8405856..42575b69409b7 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -705,6 +705,20 @@ public class UserManager { public static final String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking"; + /** + * Specifies if a user is not allowed to use Autofill Services. + * + *

Device owner and profile owner can set this restriction. When it is set by device owner, + * only the target user will be affected. + * + *

The default value is false. + * + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_AUTOFILL = "no_autofill"; + /** * Application restriction key that is used to indicate the pending arrival * of real restrictions for the app. diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 709e5f9e7ef78..6f17d0e5ec77a 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -228,8 +228,11 @@ public abstract class AutofillService extends Service { */ //TODO(b/33197203): remove once clients are not using anymore @Deprecated - public abstract void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data, - @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback); + public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data, + @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) { + // Should never be called because it was abstract before. + throw new UnsupportedOperationException("deprecated"); + } /** * Called when user requests service to save the fields of an {@link Activity}. diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 47e7803d29d5f..484b875dae17b 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -208,7 +208,7 @@ public final class Dataset implements Parcelable { } /** - * Sets the value of a field, usin a custom presentation to visualize it. + * Sets the value of a field, using a custom presentation to visualize it. * * @param id id returned by {@link * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. @@ -257,8 +257,7 @@ public final class Dataset implements Parcelable { throwIfDestroyed(); mDestroyed = true; if (mFieldIds == null) { - throw new IllegalArgumentException( - "at least one value must be set"); + throw new IllegalArgumentException("at least one value must be set"); } return new Dataset(this); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index c7ba1ff60a1ed..a77e533783775 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -18,7 +18,10 @@ package com.android.server.autofill; import static android.Manifest.permission.MANAGE_AUTO_FILL; import static android.content.Context.AUTOFILL_MANAGER_SERVICE; + +import static com.android.server.autofill.Helper.DEBUG; import static com.android.server.autofill.Helper.VERBOSE; +import static com.android.server.autofill.Helper.bundleToString; import android.Manifest; import android.annotation.NonNull; @@ -28,8 +31,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Rect; import android.net.Uri; @@ -41,16 +44,19 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; +import android.os.UserManager; +import android.os.UserManagerInternal; import android.provider.Settings; import android.util.LocalLog; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; - import android.view.autofill.IAutoFillManager; import android.view.autofill.IAutoFillManagerClient; + import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; import com.android.internal.os.IResultReceiver; @@ -90,20 +96,20 @@ public final class AutofillManagerService extends SystemService { * It has to be mapped by user id because the same current user could have simultaneous sessions * associated to different user profiles (for example, in a multi-window environment or when * device has work profiles). - *

- * Entries on this cache are added on demand and removed when: - *

    - *
  1. An autofill service app is removed. - *
  2. The {@link android.provider.Settings.Secure#AUTOFILL_SERVICE} for an user change. - *
*/ - // TODO(b/33197203): Update the above comment @GuardedBy("mLock") private SparseArray mServicesCache = new SparseArray<>(); + /** + * Users disabled due to {@link UserManager} restrictions. + */ + @GuardedBy("mLock") + private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray(); + // TODO(b/33197203): set a different max (or disable it) on low-memory devices. private final LocalLog mRequestsHistory = new LocalLog(20); + // TODO(b/33197203): is this still needed? private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -122,10 +128,38 @@ public final class AutofillManagerService extends SystemService { mContext = context; mUi = new AutoFillUI(mContext); - IntentFilter filter = new IntentFilter(); + final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mContext.registerReceiver(mBroadcastReceiver, filter, null, - FgThread.getHandler()); + mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler()); + + // Hookup with UserManager to disable service when necessary. + final UserManager um = context.getSystemService(UserManager.class); + final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class); + final List users = um.getUsers(); + for (int i = 0; i < users.size(); i++) { + final int userId = users.get(i).id; + final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL); + if (disabled) { + mDisabledUsers.put(userId, disabled); + } + } + umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> { + final boolean disabledNow = + newRestrictions.getBoolean(UserManager.DISALLOW_AUTOFILL, false); + synchronized (mLock) { + final boolean disabledBefore = mDisabledUsers.get(userId); + if (disabledBefore == disabledNow) { + // Nothing changed, do nothing. + if (DEBUG) { + Slog.d(TAG, "Restriction not changed for user " + userId + ": " + + bundleToString(newRestrictions)); + return; + } + } + mDisabledUsers.put(userId, disabledNow); + updateCachedServiceLocked(userId, disabledNow); + } + }); } @Override @@ -164,7 +198,7 @@ public final class AutofillManagerService extends SystemService { AutofillManagerServiceImpl service = mServicesCache.get(userId); if (service == null) { service = new AutofillManagerServiceImpl(mContext, mLock, - mRequestsHistory, userId, mUi); + mRequestsHistory, userId, mUi, mDisabledUsers.get(userId)); mServicesCache.put(userId, service); } return service; @@ -272,9 +306,16 @@ public final class AutofillManagerService extends SystemService { * Updates a cached service for a given user. */ private void updateCachedServiceLocked(int userId) { + updateCachedServiceLocked(userId, mDisabledUsers.get(userId)); + } + + /** + * Updates a cached service for a given user. + */ + private void updateCachedServiceLocked(int userId, boolean disabled) { AutofillManagerServiceImpl service = mServicesCache.get(userId); if (service != null) { - service.updateLocked(); + service.updateLocked(disabled); } } @@ -386,6 +427,7 @@ public final class AutofillManagerService extends SystemService { return; } synchronized (mLock) { + pw.print("Disabled users: "); pw.println(mDisabledUsers); final int size = mServicesCache.size(); pw.print("Cached services: "); if (size == 0) { diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index d13cf77599553..6fb9f7c22f023 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -53,6 +53,7 @@ import android.os.Looper; import android.os.Parcelable; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.UserManager; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; @@ -105,6 +106,10 @@ final class AutofillManagerServiceImpl { private AutofillServiceInfo mInfo; private final LocalLog mRequestsHistory; + /** + * Whether service was disabled for user due to {@link UserManager} restrictions. + */ + private boolean mDisabled; private final HandlerCaller.Callback mHandlerCallback = (msg) -> { switch (msg.what) { @@ -183,13 +188,13 @@ final class AutofillManagerServiceImpl { }; AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, - int userId, AutoFillUI ui) { + int userId, AutoFillUI ui, boolean disabled) { mContext = context; mLock = lock; mRequestsHistory = requestsHistory; mUserId = userId; mUi = ui; - updateLocked(); + updateLocked(disabled); } CharSequence getServiceName() { @@ -209,7 +214,9 @@ final class AutofillManagerServiceImpl { } } - void updateLocked() { + void updateLocked(boolean disabled) { + final boolean wasEnabled = isEnabled(); + mDisabled = disabled; ComponentName serviceComponent = null; ServiceInfo serviceInfo = null; final String componentName = Settings.Secure.getStringForUser( @@ -225,15 +232,14 @@ final class AutofillManagerServiceImpl { } } try { - final boolean hadService = hasService(); if (serviceInfo != null) { mInfo = new AutofillServiceInfo(mContext.getPackageManager(), serviceComponent, mUserId); } else { mInfo = null; } - if (hadService != hasService()) { - if (!hasService()) { + if (wasEnabled != isEnabled()) { + if (!isEnabled()) { final int sessionCount = mSessions.size(); for (int i = sessionCount - 1; i >= 0; i--) { final Session session = mSessions.valueAt(i); @@ -251,7 +257,7 @@ final class AutofillManagerServiceImpl { * Used by {@link AutofillManagerServiceShellCommand} to request save for the current top app. */ void requestSaveForUserLocked(IBinder activityToken) { - if (!hasService()) { + if (!isEnabled()) { return; } final Session session = mSessions.get(activityToken); @@ -268,11 +274,11 @@ final class AutofillManagerServiceImpl { mClients = new RemoteCallbackList<>(); } mClients.register(client); - return hasService(); + return isEnabled(); } void setAuthenticationResultLocked(Bundle data, IBinder activityToken) { - if (!hasService()) { + if (!isEnabled()) { return; } final Session session = mSessions.get(activityToken); @@ -282,7 +288,7 @@ final class AutofillManagerServiceImpl { } void setHasCallback(IBinder activityToken, boolean hasIt) { - if (!hasService()) { + if (!isEnabled()) { return; } final Session session = mSessions.get(activityToken); @@ -295,7 +301,7 @@ final class AutofillManagerServiceImpl { @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect bounds, @Nullable AutofillValue value, boolean hasCallback, int flags, @NonNull String packageName) { - if (!hasService()) { + if (!isEnabled()) { return; } @@ -318,7 +324,7 @@ final class AutofillManagerServiceImpl { } void finishSessionLocked(IBinder activityToken) { - if (!hasService()) { + if (!isEnabled()) { return; } @@ -338,7 +344,7 @@ final class AutofillManagerServiceImpl { } void cancelSessionLocked(IBinder activityToken) { - if (!hasService()) { + if (!isEnabled()) { return; } @@ -423,8 +429,9 @@ final class AutofillManagerServiceImpl { pw.print(prefix); pw.print("Component:"); pw.println(mInfo != null ? mInfo.getServiceInfo().getComponentName() : null); + pw.print(prefix); pw.print("Disabled:"); pw.println(mDisabled); - if (VERBOSE) { + if (VERBOSE && mInfo != null) { // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps) pw.print(prefix); pw.println("ServiceInfo:"); mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix); @@ -467,9 +474,9 @@ final class AutofillManagerServiceImpl { } try { for (int i = 0; i < userClientCount; i++) { - IAutoFillManagerClient client = clients.getBroadcastItem(i); + final IAutoFillManagerClient client = clients.getBroadcastItem(i); try { - client.setState(hasService()); + client.setState(isEnabled()); } catch (RemoteException re) { /* ignore */ } @@ -479,8 +486,8 @@ final class AutofillManagerServiceImpl { } } - private boolean hasService() { - return mInfo != null; + private boolean isEnabled() { + return mInfo != null && !mDisabled; } @Override diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 090cf919cca5f..353b4ac7ae26a 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -31,24 +31,21 @@ final class Helper { static final boolean DEBUG = true; // TODO(b/33197203): set to false when stable static final boolean VERBOSE = false; - static final String REDACTED = "[REDACTED]"; static void append(StringBuilder builder, Bundle bundle) { - if (bundle == null) { - builder.append("N/A"); - } else if (!VERBOSE) { - builder.append(REDACTED); - } else { - final Set keySet = bundle.keySet(); - builder.append("[Bundle with ").append(keySet.size()).append(" extras:"); - for (String key : keySet) { - final Object value = bundle.get(key); - builder.append(' ').append(key).append('='); - builder.append((value instanceof Object[]) - ? Arrays.toString((Objects[]) value) : value); - } - builder.append(']'); + if (bundle == null || !DEBUG) { + builder.append("null"); + return; } + final Set keySet = bundle.keySet(); + builder.append("[Bundle with ").append(keySet.size()).append(" extras:"); + for (String key : keySet) { + final Object value = bundle.get(key); + builder.append(' ').append(key).append('='); + builder.append((value instanceof Object[]) + ? Arrays.toString((Objects[]) value) : value); + } + builder.append(']'); } static String bundleToString(Bundle bundle) { diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index cb2ed6e0d00f0..84381fe20acca 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -109,6 +109,7 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_SET_WALLPAPER, UserManager.DISALLOW_OEM_UNLOCK, UserManager.DISALLOW_UNMUTE_DEVICE, + UserManager.DISALLOW_AUTOFILL, }); /**