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
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>Device owner and profile owner can set this restriction. When it is set by device owner,
|
||||
* only the target user will be affected.
|
||||
*
|
||||
* <p>The default value is <code>false</code>.
|
||||
*
|
||||
* @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.
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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).
|
||||
* <p>
|
||||
* Entries on this cache are added on demand and removed when:
|
||||
* <ol>
|
||||
* <li>An autofill service app is removed.
|
||||
* <li>The {@link android.provider.Settings.Secure#AUTOFILL_SERVICE} for an user change.
|
||||
* </ol>
|
||||
*/
|
||||
// TODO(b/33197203): Update the above comment
|
||||
@GuardedBy("mLock")
|
||||
private SparseArray<AutofillManagerServiceImpl> 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<UserInfo> 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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<String> 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<String> 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) {
|
||||
|
||||
@@ -109,6 +109,7 @@ public class UserRestrictionsUtils {
|
||||
UserManager.DISALLOW_SET_WALLPAPER,
|
||||
UserManager.DISALLOW_OEM_UNLOCK,
|
||||
UserManager.DISALLOW_UNMUTE_DEVICE,
|
||||
UserManager.DISALLOW_AUTOFILL,
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user