New cmd to temporarily set safety operations.

Test: adb shell cmd device_policy set-operation-safe 1 false
Test: adb shell cmd device_policy is-operation-safe 1

Bug: 172376923

Change-Id: I877cd948b51032d0581cd41512a27a9a3a98a333
This commit is contained in:
Felipe Leme
2020-12-01 19:32:58 -08:00
parent c0044f8cfd
commit 107c162d84
3 changed files with 106 additions and 1 deletions

View File

@@ -983,10 +983,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker.getClass());
Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker);
mSafetyChecker = safetyChecker;
}
/**
* Used by {@link OneTimeSafetyChecker} only.
*/
DevicePolicySafetyChecker getDevicePolicySafetyChecker() {
return mSafetyChecker;
}
/**
* Checks if it's safe to execute the given {@code operation}.
*
@@ -994,6 +1001,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) {
if (!canExecute(operation)) {
if (mSafetyChecker == null) {
// Happens on CTS after it's set just once (by OneTimeSafetyChecker)
throw new UnsafeStateException(operation);
}
// Let mSafetyChecker customize it (for example, by explaining how to retry)
throw mSafetyChecker.newUnsafeStateException(operation);
}
}
@@ -1005,6 +1017,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation);
}
/**
* Used by {@code cmd device_policy} to set the result of the next safety operation check.
*/
void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
Slog.i(LOG_TAG, "setNextOperationSafety(" + DevicePolicyManager.operationToString(operation)
+ ", " + safe + ")");
mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe);
}
/**
* Unit test will subclass it to inject mocks.
*/

View File

@@ -24,6 +24,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_SET_SAFE_OPERATION = "set-operation-safe";
private final DevicePolicyManagerService mService;
@@ -48,6 +49,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
switch (cmd) {
case CMD_IS_SAFE_OPERATION:
return runIsSafeOperation(pw);
case CMD_SET_SAFE_OPERATION:
return runSetSafeOperation(pw);
default:
return onInvalidCommand(pw, cmd);
}
@@ -70,6 +73,9 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
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 <OPERATION_ID> <true|false>\n", CMD_SET_SAFE_OPERATION);
pw.printf(" Emulates the result of the next call to check if the given operation is safe"
+ " \n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
@@ -79,4 +85,13 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
safe ? "SAFE" : "UNSAFE");
return 0;
}
private int runSetSafeOperation(PrintWriter pw) {
int operation = Integer.parseInt(getNextArgRequired());
boolean safe = getNextArg().equals("true");
mService.setNextOperationSafety(operation, safe);
pw.printf("Next call to check operation %s will return %s\n",
DevicePolicyManager.operationToString(operation), safe ? "SAFE" : "UNSAFE");
return 0;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2020 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.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.operationToString;
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicySafetyChecker;
import android.util.Slog;
import java.util.Objects;
//TODO(b/172376923): add unit tests
/**
* {@code DevicePolicySafetyChecker} implementation that overrides the real checker for just
* one command.
*
* <p>Used only for debugging and CTS tests.
*/
final class OneTimeSafetyChecker implements DevicePolicySafetyChecker {
private static final String TAG = OneTimeSafetyChecker.class.getSimpleName();
private final DevicePolicyManagerService mService;
private final DevicePolicySafetyChecker mRealSafetyChecker;
private final @DevicePolicyOperation int mOperation;
private final boolean mSafe;
OneTimeSafetyChecker(DevicePolicyManagerService service,
@DevicePolicyOperation int operation, boolean safe) {
mService = Objects.requireNonNull(service);
mOperation = operation;
mSafe = safe;
mRealSafetyChecker = service.getDevicePolicySafetyChecker();
Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker);
}
@Override
public boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) {
String name = operationToString(operation);
boolean safe = true;
if (operation == mOperation) {
safe = mSafe;
} else {
Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name
+ ", should be " + operationToString(mOperation));
}
Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe
+ " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
mService.setDevicePolicySafetyChecker(mRealSafetyChecker);
return safe;
}
}