1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.devicepolicy; 17 18 import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE; 19 import static android.app.admin.DevicePolicyManager.operationSafetyReasonToString; 20 import static android.app.admin.DevicePolicyManager.operationToString; 21 22 import android.app.admin.DevicePolicyManager.DevicePolicyOperation; 23 import android.app.admin.DevicePolicyManager.OperationSafetyReason; 24 import android.app.admin.DevicePolicyManagerLiteInternal; 25 import android.app.admin.DevicePolicySafetyChecker; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.util.Slog; 29 30 import com.android.internal.os.IResultReceiver; 31 import com.android.server.LocalServices; 32 33 import java.util.Objects; 34 35 //TODO(b/172376923): add unit tests 36 37 /** 38 * {@code DevicePolicySafetyChecker} implementation that overrides the real checker for just 39 * one command. 40 * 41 * <p>Used only for debugging and CTS tests. 42 */ 43 final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { 44 45 private static final String TAG = OneTimeSafetyChecker.class.getSimpleName(); 46 47 private static final long SELF_DESTRUCT_TIMEOUT_MS = 10_000; 48 49 private final DevicePolicyManagerService mService; 50 private final DevicePolicySafetyChecker mRealSafetyChecker; 51 private final @DevicePolicyOperation int mOperation; 52 private final @OperationSafetyReason int mReason; 53 private final Handler mHandler = new Handler(Looper.getMainLooper()); 54 55 private boolean mDone; 56 OneTimeSafetyChecker(DevicePolicyManagerService service, @DevicePolicyOperation int operation, @OperationSafetyReason int reason)57 OneTimeSafetyChecker(DevicePolicyManagerService service, 58 @DevicePolicyOperation int operation, @OperationSafetyReason int reason) { 59 mService = Objects.requireNonNull(service); 60 mOperation = operation; 61 mReason = reason; 62 mRealSafetyChecker = service.getDevicePolicySafetyChecker(); 63 Slog.i(TAG, "OneTimeSafetyChecker constructor: operation=" + operationToString(operation) 64 + ", reason=" + operationSafetyReasonToString(reason) 65 + ", realChecker=" + mRealSafetyChecker 66 + ", maxDuration=" + SELF_DESTRUCT_TIMEOUT_MS + "ms"); 67 mHandler.postDelayed(() -> selfDestruct(), SELF_DESTRUCT_TIMEOUT_MS); 68 } 69 70 @Override 71 @OperationSafetyReason getUnsafeOperationReason(@evicePolicyOperation int operation)72 public int getUnsafeOperationReason(@DevicePolicyOperation int operation) { 73 String name = operationToString(operation); 74 Slog.i(TAG, "getUnsafeOperationReason(" + name + ")"); 75 int reason = OPERATION_SAFETY_REASON_NONE; 76 if (operation == mOperation) { 77 reason = mReason; 78 } else { 79 Slog.wtf(TAG, "invalid call to isDevicePolicyOperationSafe(): asked for " + name 80 + ", should be " + operationToString(mOperation)); 81 } 82 String reasonName = operationSafetyReasonToString(reason); 83 DevicePolicyManagerLiteInternal dpmi = LocalServices 84 .getService(DevicePolicyManagerLiteInternal.class); 85 86 Slog.i(TAG, "notifying " + reasonName + " is UNSAFE"); 87 dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ false); 88 89 Slog.i(TAG, "notifying " + reasonName + " is SAFE"); 90 dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ true); 91 92 Slog.i(TAG, "returning " + reasonName); 93 94 disableSelf(); 95 return reason; 96 } 97 98 @Override isSafeOperation(@perationSafetyReason int reason)99 public boolean isSafeOperation(@OperationSafetyReason int reason) { 100 boolean safe = mReason != reason; 101 Slog.i(TAG, "isSafeOperation(" + operationSafetyReasonToString(reason) + "): " + safe); 102 103 disableSelf(); 104 return safe; 105 } 106 107 @Override onFactoryReset(IResultReceiver callback)108 public void onFactoryReset(IResultReceiver callback) { 109 throw new UnsupportedOperationException(); 110 } 111 disableSelf()112 private void disableSelf() { 113 if (mDone) { 114 Slog.w(TAG, "disableSelf(): already disabled"); 115 return; 116 } 117 Slog.i(TAG, "restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); 118 mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker); 119 mDone = true; 120 } 121 selfDestruct()122 private void selfDestruct() { 123 if (mDone) return; 124 125 // Usually happens when a CTS failed before calling the DPM method that would clear it 126 Slog.e(TAG, "Self destructing " + this + ", as it was not automatically disabled"); 127 disableSelf(); 128 } 129 130 @Override toString()131 public String toString() { 132 return "OneTimeSafetyChecker[id=" + System.identityHashCode(this) 133 + ", reason=" + operationSafetyReasonToString(mReason) 134 + ", operation=" + operationToString(mOperation) + ']'; 135 } 136 } 137