1 /* 2 * Copyright (C) 2022 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 17 package com.android.devicelockcontroller.policy; 18 19 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.CLEARED; 20 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.KIOSK_SETUP; 21 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.LOCKED; 22 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.PSEUDO_LOCKED; 23 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.PSEUDO_UNLOCKED; 24 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.SETUP_FAILED; 25 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.SETUP_IN_PROGRESS; 26 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.SETUP_SUCCEEDED; 27 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.UNLOCKED; 28 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.UNPROVISIONED; 29 30 import android.app.admin.DevicePolicyManager; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.os.UserManager; 35 import android.util.ArraySet; 36 37 import androidx.annotation.MainThread; 38 39 import com.android.devicelockcontroller.policy.DeviceStateController.DeviceState; 40 import com.android.devicelockcontroller.storage.SetupParametersClient; 41 import com.android.devicelockcontroller.util.LogUtil; 42 43 import com.google.common.util.concurrent.Futures; 44 import com.google.common.util.concurrent.ListenableFuture; 45 46 import java.util.Collections; 47 import java.util.Locale; 48 49 /** 50 * Enforces UserRestriction policies. 51 */ 52 final class UserRestrictionsPolicyHandler implements PolicyHandler { 53 54 private static final String TAG = "UserRestrictionsPolicyHandler"; 55 56 private final ArraySet<String> mAlwaysOnRestrictions = new ArraySet<>(); 57 58 /** 59 * A list of restrictions that will be always active, it is optional, partners can config the 60 * list via provisioning configs. 61 */ 62 private ArraySet<String> mOptionalAlwaysOnRestrictions; 63 64 private ArraySet<String> mLockModeRestrictions; 65 66 private final DevicePolicyManager mDpm; 67 private final UserManager mUserManager; 68 private final boolean mIsDebug; 69 UserRestrictionsPolicyHandler(DevicePolicyManager dpm, UserManager userManager, boolean isDebug)70 UserRestrictionsPolicyHandler(DevicePolicyManager dpm, UserManager userManager, 71 boolean isDebug) { 72 mDpm = dpm; 73 mUserManager = userManager; 74 mIsDebug = isDebug; 75 76 LogUtil.i(TAG, String.format(Locale.US, "Build type DEBUG = %s", isDebug)); 77 78 Collections.addAll(mAlwaysOnRestrictions, UserManager.DISALLOW_SAFE_BOOT); 79 if (!isDebug) { 80 Collections.addAll(mAlwaysOnRestrictions, UserManager.DISALLOW_DEBUGGING_FEATURES); 81 } 82 } 83 84 @Override 85 @ResultType setPolicyForState(@eviceState int state)86 public ListenableFuture<@ResultType Integer> setPolicyForState(@DeviceState int state) { 87 final Handler mainHandler = new Handler(Looper.getMainLooper()); 88 LogUtil.v(TAG, String.format(Locale.US, "Setting restrictions for %d", state)); 89 switch (state) { 90 case SETUP_IN_PROGRESS: 91 case SETUP_SUCCEEDED: 92 case UNLOCKED: 93 case KIOSK_SETUP: 94 setupRestrictions(mAlwaysOnRestrictions, true); 95 return Futures.whenAllSucceed( 96 setupRestrictions(retrieveOptionalAlwaysOnRestrictions(), true), 97 setupRestrictions(retrieveLockModeRestrictions(), false)) 98 .call( 99 () -> SUCCESS, mainHandler::post); 100 case LOCKED: 101 setupRestrictions(mAlwaysOnRestrictions, true); 102 return Futures.whenAllSucceed( 103 setupRestrictions(retrieveOptionalAlwaysOnRestrictions(), true), 104 setupRestrictions(retrieveLockModeRestrictions(), true)) 105 .call( 106 () -> SUCCESS, mainHandler::post); 107 case UNPROVISIONED: 108 case SETUP_FAILED: 109 case CLEARED: 110 setupRestrictions(mAlwaysOnRestrictions, false); 111 return Futures.whenAllSucceed( 112 setupRestrictions(retrieveOptionalAlwaysOnRestrictions(), false), 113 setupRestrictions(retrieveLockModeRestrictions(), false)) 114 .call( 115 () -> SUCCESS, mainHandler::post); 116 case PSEUDO_LOCKED: 117 case PSEUDO_UNLOCKED: 118 return Futures.immediateFuture(SUCCESS); 119 default: 120 return Futures.immediateFailedFuture( 121 new IllegalStateException(String.valueOf(state))); 122 } 123 124 125 } 126 127 @MainThread retrieveLockModeRestrictions()128 public ListenableFuture<ArraySet<String>> retrieveLockModeRestrictions() { 129 if (mLockModeRestrictions != null) return Futures.immediateFuture(mLockModeRestrictions); 130 final SetupParametersClient parameters = SetupParametersClient.getInstance(); 131 final ListenableFuture<String> kioskPackageTask = parameters.getKioskPackage(); 132 final ListenableFuture<Boolean> outgoingCallsDisabledTask = 133 parameters.getOutgoingCallsDisabled(); 134 return Futures.whenAllSucceed(kioskPackageTask, 135 outgoingCallsDisabledTask) 136 .call(() -> { 137 if (Futures.getDone(kioskPackageTask) == null) { 138 throw new IllegalStateException("Setup parameters does not exist!"); 139 } 140 if (mLockModeRestrictions == null) { 141 mLockModeRestrictions = new ArraySet<>(1); 142 if (Futures.getDone(outgoingCallsDisabledTask)) { 143 mLockModeRestrictions.add(UserManager.DISALLOW_OUTGOING_CALLS); 144 } 145 } 146 return mLockModeRestrictions; 147 }, new Handler(Looper.getMainLooper())::post); 148 } 149 150 private ListenableFuture<ArraySet<String>> retrieveOptionalAlwaysOnRestrictions() { 151 if (mOptionalAlwaysOnRestrictions != null) { 152 return Futures.immediateFuture( 153 mOptionalAlwaysOnRestrictions); 154 } 155 final SetupParametersClient parameters = SetupParametersClient.getInstance(); 156 final ListenableFuture<String> kioskPackageTask = parameters.getKioskPackage(); 157 final ListenableFuture<Boolean> installingFromUnknownSourcesDisallowedTask = 158 parameters.isInstallingFromUnknownSourcesDisallowed(); 159 160 return Futures.whenAllSucceed(kioskPackageTask, 161 installingFromUnknownSourcesDisallowedTask) 162 .call(() -> { 163 if (Futures.getDone(kioskPackageTask) == null) { 164 throw new IllegalStateException("Setup parameters does not exist!"); 165 } 166 if (mOptionalAlwaysOnRestrictions == null) { 167 mOptionalAlwaysOnRestrictions = new ArraySet<>(1); 168 if (Futures.getDone(installingFromUnknownSourcesDisallowedTask)) { 169 mOptionalAlwaysOnRestrictions.add( 170 UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); 171 } 172 } 173 return mOptionalAlwaysOnRestrictions; 174 }, new Handler(Looper.getMainLooper())::post); 175 } 176 177 @ResultType 178 private int setupRestrictions(ArraySet<String> restrictions, boolean enable) { 179 Bundle userRestrictionBundle = mUserManager.getUserRestrictions(); 180 181 for (int i = 0, size = restrictions.size(); i < size; i++) { 182 String restriction = restrictions.valueAt(i); 183 if (userRestrictionBundle.getBoolean(restriction, false) != enable) { 184 if (enable) { 185 mDpm.addUserRestriction(null /* admin */, restriction); 186 LogUtil.v(TAG, String.format(Locale.US, "enable %s restriction", restriction)); 187 } else { 188 mDpm.clearUserRestriction(null /* admin */, restriction); 189 LogUtil.v(TAG, String.format(Locale.US, "clear %s restriction", restriction)); 190 } 191 } 192 } 193 // clear the adb access restriction if we added it before 194 if (!mIsDebug 195 && enable 196 && restrictions.contains(UserManager.DISALLOW_DEBUGGING_FEATURES)) { 197 mDpm.clearUserRestriction(null /* admin */, UserManager.DISALLOW_DEBUGGING_FEATURES); 198 LogUtil.v(TAG, String.format(Locale.US, "clear %s restriction", 199 UserManager.DISALLOW_DEBUGGING_FEATURES)); 200 } 201 return SUCCESS; 202 } 203 204 @ResultType 205 private ListenableFuture<@ResultType Integer> setupRestrictions( 206 ListenableFuture<ArraySet<String>> restrictionsFuture, boolean enable) { 207 return Futures.transform(restrictionsFuture, 208 restrictions -> setupRestrictions(restrictions, enable), 209 new Handler(Looper.getMainLooper())::post); 210 } 211 212 private boolean checkRestrictions(ArraySet<String> restrictions, boolean value) { 213 Bundle userRestrictionBundle = mUserManager.getUserRestrictions(); 214 215 for (int i = 0, size = restrictions.size(); i < size; i++) { 216 String restriction = restrictions.valueAt(i); 217 if (value != userRestrictionBundle.getBoolean(restriction, false)) { 218 LogUtil.i(TAG, String.format(Locale.US, "%s restriction is not %b", 219 restriction, value)); 220 return false; 221 } 222 } 223 224 return true; 225 } 226 227 private ListenableFuture<Boolean> checkRestrictions( 228 ListenableFuture<ArraySet<String>> restrictionsFuture, boolean value) { 229 return Futures.transform(restrictionsFuture, 230 restrictions -> checkRestrictions(restrictions, value), 231 new Handler(Looper.getMainLooper())::post); 232 } 233 } 234