Merge "New device policy API: DeviceAdminReceiver.onUnsafeOperationStateChanged()" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-02-12 01:29:17 +00:00
committed by Android (Google) Code Review
11 changed files with 257 additions and 58 deletions

View File

@@ -6896,6 +6896,7 @@ package android.app.admin {
method public void onLockTaskModeEntering(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull String);
method public void onLockTaskModeExiting(@NonNull android.content.Context, @NonNull android.content.Intent);
method public void onNetworkLogsAvailable(@NonNull android.content.Context, @NonNull android.content.Intent, long, @IntRange(from=1) int);
method public void onOperationSafetyStateChanged(@NonNull android.content.Context, int, boolean);
method @Deprecated public void onPasswordChanged(@NonNull android.content.Context, @NonNull android.content.Intent);
method public void onPasswordChanged(@NonNull android.content.Context, @NonNull android.content.Intent, @NonNull android.os.UserHandle);
method @Deprecated public void onPasswordExpiring(@NonNull android.content.Context, @NonNull android.content.Intent);
@@ -7072,6 +7073,7 @@ package android.app.admin {
method public boolean isProfileOwnerApp(String);
method public boolean isProvisioningAllowed(@NonNull String);
method public boolean isResetPasswordTokenActive(android.content.ComponentName);
method public boolean isSafeOperation(int);
method public boolean isSecurityLoggingEnabled(@Nullable android.content.ComponentName);
method public boolean isUninstallBlocked(@Nullable android.content.ComponentName, String);
method public boolean isUniqueDeviceAttestationSupported();
@@ -7300,6 +7302,7 @@ package android.app.admin {
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
field public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1; // 0x1
field public static final int PASSWORD_COMPLEXITY_HIGH = 327680; // 0x50000
field public static final int PASSWORD_COMPLEXITY_LOW = 65536; // 0x10000
field public static final int PASSWORD_COMPLEXITY_MEDIUM = 196608; // 0x30000
@@ -7336,7 +7339,6 @@ package android.app.admin {
field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
field public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1; // 0x1
field public static final int WIPE_EUICC = 4; // 0x4
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -7490,7 +7492,7 @@ package android.app.admin {
public final class UnsafeStateException extends java.lang.IllegalStateException implements android.os.Parcelable {
method public int describeContents();
method public int getReason();
method @NonNull public java.util.List<java.lang.Integer> getReasons();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.UnsafeStateException> CREATOR;
}

View File

@@ -391,11 +391,11 @@ package android.app.admin {
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
method @NonNull public static String unsafeOperationReasonToString(int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
@@ -425,6 +425,7 @@ package android.app.admin {
field public static final int OPERATION_REMOVE_KEY_PAIR = 28; // 0x1c
field public static final int OPERATION_REMOVE_USER = 6; // 0x6
field public static final int OPERATION_REQUEST_BUGREPORT = 29; // 0x1d
field public static final int OPERATION_SAFETY_REASON_NONE = -1; // 0xffffffff
field public static final int OPERATION_SET_ALWAYS_ON_VPN_PACKAGE = 30; // 0x1e
field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf
field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10
@@ -460,7 +461,6 @@ package android.app.admin {
field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
field public static final int UNSAFE_OPERATION_REASON_NONE = -1; // 0xffffffff
}
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {

View File

@@ -16,6 +16,8 @@
package android.app.admin;
import static android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.accounts.AccountManager;
import android.annotation.BroadcastBehavior;
import android.annotation.IntDef;
@@ -35,6 +37,7 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.security.KeyChain;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -72,8 +75,8 @@ import java.lang.annotation.RetentionPolicy;
* </div>
*/
public class DeviceAdminReceiver extends BroadcastReceiver {
private static String TAG = "DevicePolicy";
private static boolean localLOGV = false;
private static final String TAG = "DevicePolicy";
private static final boolean LOCAL_LOGV = false;
/**
* This is the primary action that a device administrator must implement to be
@@ -509,6 +512,36 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
public static final String EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE =
"android.app.extra.TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE";
/**
* Broadcast action: notify the admin that the state of operations that can be unsafe because
* of a given reason (specified by the {@link #EXTRA_OPERATION_SAFETY_REASON} {@code int} extra)
* has changed (the new value is specified by the {@link #EXTRA_OPERATION_SAFETY_STATE}
* {@code boolean} extra).
*
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_OPERATION_SAFETY_STATE_CHANGED =
"android.app.action.OPERATION_SAFETY_STATE_CHANGED";
/**
* An {@code int} extra specifying an {@link OperationSafetyReason}.
*
* @hide
*/
public static final String EXTRA_OPERATION_SAFETY_REASON =
"android.app.extra.OPERATION_SAFETY_REASON";
/**
* An {@code boolean} extra specifying whether an operation will fail due to a
* {@link OperationSafetyReason}. {@code true} means operations that rely on that reason are
* safe, while {@code false} means they're unsafe.
*
* @hide
*/
public static final String EXTRA_OPERATION_SAFETY_STATE =
"android.app.extra.OPERATION_SAFETY_STATE";
private DevicePolicyManager mManager;
private ComponentName mWho;
@@ -1017,6 +1050,51 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
@NonNull UserHandle user) {
}
/**
* Called to notify the state of operations that can be unsafe to execute has changed.
*
* <p><b>Note:/b> notice that the operation safety state might change between the time this
* callback is received and the operation's method on {@link DevicePolicyManager} is called, so
* calls to the latter could still throw a {@link UnsafeStateException} even when this method
* is called with {@code isSafe} as {@code true}
*
* @param context the running context as per {@link #onReceive}
* @param reason the reason an operation could be unsafe.
* @param isSafe whether the operation is safe to be executed.
*/
public void onOperationSafetyStateChanged(@NonNull Context context,
@OperationSafetyReason int reason, boolean isSafe) {
if (LOCAL_LOGV) {
Log.v(TAG, String.format("onOperationSafetyStateChanged(): %s=%b",
DevicePolicyManager.operationSafetyReasonToString(reason), isSafe));
}
}
private void onOperationSafetyStateChanged(Context context, Intent intent) {
if (!hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_REASON)
|| !hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_STATE)) {
return;
}
int reason = intent.getIntExtra(EXTRA_OPERATION_SAFETY_REASON,
DevicePolicyManager.OPERATION_SAFETY_REASON_NONE);
if (!DevicePolicyManager.isValidOperationSafetyReason(reason)) {
Log.wtf(TAG, "Received invalid reason on " + intent.getAction() + ": " + reason);
return;
}
boolean isSafe = intent.getBooleanExtra(EXTRA_OPERATION_SAFETY_STATE,
/* defaultValue=*/ false);
onOperationSafetyStateChanged(context, reason, isSafe);
}
private boolean hasRequiredExtra(Intent intent, String extra) {
if (intent.hasExtra(extra)) return true;
Log.wtf(TAG, "Missing '" + extra + "' on intent " + intent);
return false;
}
/**
* Intercept standard device administrator broadcasts. Implementations
* should not override this method; it is better to implement the
@@ -1025,6 +1103,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
String action = intent.getAction();
if (LOCAL_LOGV) {
Log.v(TAG, "onReceive(): received " + action + " on user " + context.getUserId());
}
if (ACTION_PASSWORD_CHANGED.equals(action)) {
onPasswordChanged(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
@@ -1092,6 +1173,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
} else if (ACTION_AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
onTransferAffiliatedProfileOwnershipComplete(context,
intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_OPERATION_SAFETY_STATE_CHANGED.equals(action)) {
onOperationSafetyStateChanged(context, intent);
}
}
}

View File

@@ -2922,33 +2922,61 @@ public class DevicePolicyManager {
return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation);
}
private static final String PREFIX_UNSAFE_OPERATION_REASON = "UNSAFE_OPERATION_REASON_";
private static final String PREFIX_OPERATION_SAFETY_REASON = "OPERATION_SAFETY_REASON_";
/** @hide */
@IntDef(prefix = PREFIX_UNSAFE_OPERATION_REASON, value = {
UNSAFE_OPERATION_REASON_NONE,
UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION
@IntDef(prefix = PREFIX_OPERATION_SAFETY_REASON, value = {
OPERATION_SAFETY_REASON_NONE,
OPERATION_SAFETY_REASON_DRIVING_DISTRACTION
})
@Retention(RetentionPolicy.SOURCE)
public static @interface UnsafeOperationReason {
public static @interface OperationSafetyReason {
}
/** @hide */
@TestApi
public static final int UNSAFE_OPERATION_REASON_NONE = -1;
public static final int OPERATION_SAFETY_REASON_NONE = -1;
/**
* Indicates that a {@link UnsafeStateException} was thrown because the operation would distract
* the driver of the vehicle.
*/
public static final int UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION = 1;
public static final int OPERATION_SAFETY_REASON_DRIVING_DISTRACTION = 1;
/** @hide */
@NonNull
@TestApi
public static String unsafeOperationReasonToString(@UnsafeOperationReason int reason) {
public static String operationSafetyReasonToString(@OperationSafetyReason int reason) {
return DebugUtils.constantToString(DevicePolicyManager.class,
PREFIX_UNSAFE_OPERATION_REASON, reason);
PREFIX_OPERATION_SAFETY_REASON, reason);
}
/** @hide */
public static boolean isValidOperationSafetyReason(@OperationSafetyReason int reason) {
return reason == OPERATION_SAFETY_REASON_DRIVING_DISTRACTION;
}
/**
* Checks if it's safe to run operations that can be affected by the given {@code reason}.
*
* <p><b>Note:/b> notice that the operation safety state might change between the time this
* method returns and the operation's method is called, so calls to the latter could still throw
* a {@link UnsafeStateException} even when this method returns {@code true}.
*
* @param reason currently, only supported reason is
* {@link #OPERATION_SAFETY_REASON_DRIVING_DISTRACTION}.
*
* @return whether it's safe to run operations that can be affected by the given {@code reason}.
*/
// TODO(b/173541467): should it throw SecurityException if caller is not admin?
public boolean isSafeOperation(@OperationSafetyReason int reason) {
if (mService == null) return false;
try {
return mService.isSafeOperation(reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** @hide */
@@ -13157,7 +13185,7 @@ public class DevicePolicyManager {
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS)
public void setNextOperationSafety(@DevicePolicyOperation int operation,
@UnsafeOperationReason int reason) {
@OperationSafetyReason int reason) {
if (mService != null) {
try {
mService.setNextOperationSafety(operation, reason);

View File

@@ -18,6 +18,7 @@ package android.app.admin;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.content.ComponentName;
import android.content.Intent;
import android.os.UserHandle;
@@ -255,4 +256,14 @@ public abstract class DevicePolicyManagerInternal {
* {@link #supportsResetOp(int)} is true.
*/
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
/**
* Notifies the system that an unsafe operation reason has changed.
*
* @throws IllegalArgumentException if {@code checker} is not the same as set on
* {@code DevicePolicyManagerService}.
*/
public abstract void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
@OperationSafetyReason int reason, boolean isSafe);
}

View File

@@ -17,7 +17,7 @@ package android.app.admin;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import com.android.internal.os.IResultReceiver;
@@ -31,15 +31,20 @@ public interface DevicePolicySafetyChecker {
/**
* Returns whether the given {@code operation} can be safely executed at the moment.
*/
@UnsafeOperationReason
@OperationSafetyReason
int getUnsafeOperationReason(@DevicePolicyOperation int operation);
/**
* Return whether it's safe to run operations that can be affected by the given {@code reason}.
*/
boolean isSafeOperation(@OperationSafetyReason int reason);
/**
* Returns a new exception for when the given {@code operation} cannot be safely executed.
*/
@NonNull
default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation,
@UnsafeOperationReason int reason) {
@OperationSafetyReason int reason) {
return new UnsafeStateException(operation, reason);
}

View File

@@ -492,6 +492,7 @@ interface IDevicePolicyManager {
boolean canProfileOwnerResetPasswordWhenLocked(int userId);
void setNextOperationSafety(int operation, int reason);
boolean isSafeOperation(int reason);
String getEnrollmentSpecificId(String callerPackage);
void setOrganizationIdForUser(in String callerPackage, in String enterpriseId, int userId);

View File

@@ -15,18 +15,20 @@
*/
package android.app.admin;
import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION;
import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
import static android.app.admin.DevicePolicyManager.isValidOperationSafetyReason;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
import java.util.Arrays;
import java.util.List;
/**
* Exception thrown when a {@link android.app.admin.DevicePolicyManager} operation failed because it
* was not safe to be executed at that moment.
@@ -39,17 +41,15 @@ import com.android.internal.util.Preconditions;
public final class UnsafeStateException extends IllegalStateException implements Parcelable {
private final @DevicePolicyOperation int mOperation;
private final @UnsafeOperationReason int mReason;
private final @OperationSafetyReason int mReason;
/** @hide */
@TestApi
public UnsafeStateException(@DevicePolicyOperation int operation,
@UnsafeOperationReason int reason) {
@OperationSafetyReason int reason) {
super();
Preconditions.checkArgument(reason == UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
"invalid reason %d, must be %d (%s)", reason,
UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION,
unsafeOperationReasonToString(UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION));
Preconditions.checkArgument(isValidOperationSafetyReason(reason), "invalid reason %d",
reason);
mOperation = operation;
mReason = reason;
}
@@ -61,19 +61,20 @@ public final class UnsafeStateException extends IllegalStateException implements
}
/**
* Gets the reason the operation is unsafe.
* Gets the reasons the operation is unsafe.
*
* @return currently, only valid reason is
* {@link android.app.admin.DevicePolicyManager#UNSAFE_OPERATION_REASON_DRIVING_DISTRACTION}.
* {@link android.app.admin.DevicePolicyManager#OPERATION_SAFETY_REASON_DRIVING_DISTRACTION}.
*/
public @UnsafeOperationReason int getReason() {
return mReason;
@NonNull
public List<Integer> getReasons() {
return Arrays.asList(mReason);
}
/** @hide */
@Override
public String getMessage() {
return DevicePolicyManager.unsafeOperationReasonToString(mReason);
return DevicePolicyManager.operationSafetyReasonToString(mReason);
}
@Override

View File

@@ -62,6 +62,7 @@ import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
@@ -92,7 +93,6 @@ import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_REMOVE_N
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
@@ -157,9 +157,9 @@ import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
@@ -1101,7 +1101,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
int reason = getUnsafeOperationReason(operation);
if (reason == UNSAFE_OPERATION_REASON_NONE) return;
if (reason == OPERATION_SAFETY_REASON_NONE) return;
if (mSafetyChecker == null) {
// Happens on CTS after it's set just once (by OneTimeSafetyChecker)
@@ -1114,23 +1114,28 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
/**
* Returns whether it's safe to execute the given {@code operation}, and why.
*/
@UnsafeOperationReason
@OperationSafetyReason
int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
return mSafetyChecker == null ? UNSAFE_OPERATION_REASON_NONE
return mSafetyChecker == null ? OPERATION_SAFETY_REASON_NONE
: mSafetyChecker.getUnsafeOperationReason(operation);
}
@Override
public void setNextOperationSafety(@DevicePolicyOperation int operation,
@UnsafeOperationReason int reason) {
@OperationSafetyReason int reason) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %s)",
DevicePolicyManager.operationToString(operation),
DevicePolicyManager.unsafeOperationReasonToString(reason)));
DevicePolicyManager.operationSafetyReasonToString(reason)));
mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
}
@Override
public boolean isSafeOperation(@OperationSafetyReason int reason) {
return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason);
}
// Used by DevicePolicyManagerServiceShellCommand
List<OwnerDto> listAllOwners() {
Preconditions.checkCallAuthorization(
@@ -7522,19 +7527,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
sendActiveAdminCommand(action, extras, deviceOwnerUserId, receiverComponent);
}
private void sendProfileOwnerCommand(String action, Bundle extras, int userHandle) {
sendActiveAdminCommand(action, extras, userHandle,
mOwners.getProfileOwnerComponent(userHandle));
private void sendProfileOwnerCommand(String action, Bundle extras, @UserIdInt int userId) {
sendActiveAdminCommand(action, extras, userId,
mOwners.getProfileOwnerComponent(userId));
}
private void sendActiveAdminCommand(String action, Bundle extras,
int userHandle, ComponentName receiverComponent) {
@UserIdInt int userId, ComponentName receiverComponent) {
if (VERBOSE_LOG) {
Slog.v(LOG_TAG, "sending intent " + action + " to "
+ receiverComponent.flattenToShortString() + " on user " + userId);
}
final Intent intent = new Intent(action);
intent.setComponent(receiverComponent);
if (extras != null) {
intent.putExtras(extras);
}
mContext.sendBroadcastAsUser(intent, UserHandle.of(userHandle));
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
private void sendOwnerChangedBroadcast(String broadcast, int userId) {
@@ -12224,6 +12233,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
packageName, findInteractAcrossProfilesResetMode(packageName), userId);
}
@Override
public void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker, int reason,
boolean isSafe) {
// TODO(b/178494483): use EventLog instead
// TODO(b/178494483): log metrics?
if (VERBOSE_LOG) {
Slog.v(LOG_TAG, String.format("notifyUnsafeOperationStateChanged(): %s=%b",
DevicePolicyManager.operationSafetyReasonToString(reason), isSafe));
}
Preconditions.checkArgument(mSafetyChecker == checker,
"invalid checker: should be %s, was %s", mSafetyChecker, checker);
Bundle extras = new Bundle();
extras.putInt(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_REASON, reason);
extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe);
// TODO(b/178494483): add CTS test
sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
extras);
for (int profileOwnerId : mOwners.getProfileOwnerKeys()) {
sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
extras, profileOwnerId);
}
}
private @Mode int findInteractAcrossProfilesResetMode(String packageName) {
return getDefaultCrossProfilePackages().contains(packageName)
? AppOpsManager.MODE_ALLOWED

View File

@@ -27,6 +27,7 @@ import java.util.Objects;
final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason";
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
private static final String CMD_LIST_OWNERS = "list-owners";
@@ -53,6 +54,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
switch (cmd) {
case CMD_IS_SAFE_OPERATION:
return runIsSafeOperation(pw);
case CMD_IS_SAFE_OPERATION_BY_REASON:
return runIsSafeOperationByReason(pw);
case CMD_SET_SAFE_OPERATION:
return runSetSafeOperation(pw);
case CMD_LIST_OWNERS:
@@ -73,12 +76,13 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
return -1;
}
private void showHelp(PrintWriter pw) {
pw.printf(" help\n");
pw.printf(" Prints this help text.\n\n");
pw.printf(" %s <OPERATION_ID>\n", CMD_IS_SAFE_OPERATION);
pw.printf(" Checks if the give operation is safe \n\n");
pw.printf(" %s <REASON_ID>\n", CMD_IS_SAFE_OPERATION_BY_REASON);
pw.printf(" Checks if the operations are safe for the given reason\n\n");
pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
pw.printf(" Emulates the result of the next call to check if the given operation is safe"
+ " \n\n");
@@ -89,10 +93,19 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private int runIsSafeOperation(PrintWriter pw) {
int operation = Integer.parseInt(getNextArgRequired());
int reason = mService.getUnsafeOperationReason(operation);
boolean safe = reason == DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
boolean safe = reason == DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
pw.printf("Operation %s is %b. Reason: %s\n",
DevicePolicyManager.operationToString(operation), safe,
DevicePolicyManager.unsafeOperationReasonToString(reason));
DevicePolicyManager.operationSafetyReasonToString(reason));
return 0;
}
private int runIsSafeOperationByReason(PrintWriter pw) {
int reason = Integer.parseInt(getNextArgRequired());
boolean safe = mService.isSafeOperation(reason);
pw.printf("Operations affected by %s are %s\n",
DevicePolicyManager.operationSafetyReasonToString(reason),
(safe ? "SAFE" : "UNSAFE"));
return 0;
}
@@ -102,7 +115,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
mService.setNextOperationSafety(operation, reason);
pw.printf("Next call to check operation %s will return %s\n",
DevicePolicyManager.operationToString(operation),
DevicePolicyManager.unsafeOperationReasonToString(reason));
DevicePolicyManager.operationSafetyReasonToString(reason));
return 0;
}

View File

@@ -15,16 +15,18 @@
*/
package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.UNSAFE_OPERATION_REASON_NONE;
import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE;
import static android.app.admin.DevicePolicyManager.operationSafetyReasonToString;
import static android.app.admin.DevicePolicyManager.operationToString;
import static android.app.admin.DevicePolicyManager.unsafeOperationReasonToString;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.UnsafeOperationReason;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.util.Slog;
import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
import java.util.Objects;
@@ -43,10 +45,10 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker {
private final DevicePolicyManagerService mService;
private final DevicePolicySafetyChecker mRealSafetyChecker;
private final @DevicePolicyOperation int mOperation;
private final @UnsafeOperationReason int mReason;
private final @OperationSafetyReason int mReason;
OneTimeSafetyChecker(DevicePolicyManagerService service,
@DevicePolicyOperation int operation, @UnsafeOperationReason int reason) {
@DevicePolicyOperation int operation, @OperationSafetyReason int reason) {
mService = Objects.requireNonNull(service);
mOperation = operation;
mReason = reason;
@@ -55,23 +57,41 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker {
}
@Override
@UnsafeOperationReason
@OperationSafetyReason
public int getUnsafeOperationReason(@DevicePolicyOperation int operation) {
String name = operationToString(operation);
int reason = UNSAFE_OPERATION_REASON_NONE;
Slog.i(TAG, "getUnsafeOperationReason(" + name + ")");
int reason = OPERATION_SAFETY_REASON_NONE;
if (operation == mOperation) {
reason = mReason;
} else {
Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name
+ ", should be " + operationToString(mOperation));
}
Slog.i(TAG, "getDevicePolicyOperationSafety(" + name + "): returning "
+ unsafeOperationReasonToString(reason)
String reasonName = operationSafetyReasonToString(reason);
DevicePolicyManagerInternal dpmi = LocalServices
.getService(DevicePolicyManagerInternal.class);
Slog.i(TAG, "notifying " + reasonName + " is active");
dpmi.notifyUnsafeOperationStateChanged(this, reason, true);
Slog.i(TAG, "notifying " + reasonName + " is inactive");
dpmi.notifyUnsafeOperationStateChanged(this, reason, false);
Slog.i(TAG, "returning " + reasonName
+ " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
return reason;
}
@Override
public boolean isSafeOperation(@OperationSafetyReason int reason) {
boolean safe = mReason != reason;
Slog.i(TAG, "isSafeOperation(" + operationSafetyReasonToString(reason) + "): " + safe);
return safe;
}
@Override
public void onFactoryReset(IResultReceiver callback) {
throw new UnsupportedOperationException();