Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 107c162d authored by Felipe Leme's avatar Felipe Leme
Browse files

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
parent c0044f8c
Loading
Loading
Loading
Loading
+24 −1
Original line number Diff line number Diff line
@@ -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.
     */
+15 −0
Original line number Diff line number Diff line
@@ -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;
    }
}
+67 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}