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 android.app.admin.DevicePolicyManager; 20 import android.os.Bundle; 21 import android.os.UserManager; 22 import android.util.ArraySet; 23 24 import com.android.devicelockcontroller.storage.SetupParametersClient; 25 import com.android.devicelockcontroller.util.LogUtil; 26 27 import com.google.common.util.concurrent.Futures; 28 import com.google.common.util.concurrent.ListenableFuture; 29 import com.google.common.util.concurrent.MoreExecutors; 30 31 import java.util.Collections; 32 import java.util.Locale; 33 import java.util.concurrent.Executor; 34 35 /** 36 * Enforces UserRestriction policies. 37 */ 38 final class UserRestrictionsPolicyHandler implements PolicyHandler { 39 40 private static final String TAG = "UserRestrictionsPolicyHandler"; 41 42 private final ArraySet<String> mAlwaysOnRestrictions = new ArraySet<>(); 43 private final Executor mBgExecutor; 44 45 /** 46 * A list of restrictions that will be always active, it is optional, partners can config the 47 * list via provisioning configs. 48 */ 49 private ArraySet<String> mOptionalAlwaysOnRestrictions; 50 51 private ArraySet<String> mLockModeRestrictions; 52 53 private final DevicePolicyManager mDpm; 54 private final UserManager mUserManager; 55 private final boolean mIsDebug; 56 UserRestrictionsPolicyHandler(DevicePolicyManager dpm, UserManager userManager, boolean isDebug, Executor bgExecutor)57 UserRestrictionsPolicyHandler(DevicePolicyManager dpm, UserManager userManager, 58 boolean isDebug, Executor bgExecutor) { 59 mDpm = dpm; 60 mUserManager = userManager; 61 mIsDebug = isDebug; 62 mBgExecutor = bgExecutor; 63 64 LogUtil.i(TAG, String.format(Locale.US, "Build type DEBUG = %s", isDebug)); 65 66 Collections.addAll(mAlwaysOnRestrictions, UserManager.DISALLOW_SAFE_BOOT); 67 } 68 69 @Override onProvisionInProgress()70 public ListenableFuture<Boolean> onProvisionInProgress() { 71 setupRestrictions(mAlwaysOnRestrictions, true); 72 return Futures.whenAllSucceed( 73 setupRestrictions(retrieveOptionalAlwaysOnRestrictions(), true), 74 setupRestrictions(retrieveLockModeRestrictions(), false)) 75 .call(() -> true, MoreExecutors.directExecutor()); 76 } 77 78 @Override onLocked()79 public ListenableFuture<Boolean> onLocked() { 80 setupRestrictions(mAlwaysOnRestrictions, true); 81 return Futures.whenAllSucceed( 82 setupRestrictions(retrieveOptionalAlwaysOnRestrictions(), true), 83 setupRestrictions(retrieveLockModeRestrictions(), true)) 84 .call(() -> true, MoreExecutors.directExecutor()); 85 } 86 87 @Override onUnlocked()88 public ListenableFuture<Boolean> onUnlocked() { 89 setupRestrictions(mAlwaysOnRestrictions, true); 90 return Futures.whenAllSucceed( 91 setupRestrictions(retrieveOptionalAlwaysOnRestrictions(), true), 92 setupRestrictions(retrieveLockModeRestrictions(), false)) 93 .call(() -> true, MoreExecutors.directExecutor()); 94 } 95 96 @Override onCleared()97 public ListenableFuture<Boolean> onCleared() { 98 setupRestrictions(mAlwaysOnRestrictions, false); 99 return Futures.whenAllSucceed( 100 setupRestrictions(retrieveOptionalAlwaysOnRestrictions(), false), 101 setupRestrictions(retrieveLockModeRestrictions(), false)) 102 .call(() -> true, MoreExecutors.directExecutor()); 103 } 104 retrieveLockModeRestrictions()105 private ListenableFuture<ArraySet<String>> retrieveLockModeRestrictions() { 106 if (mLockModeRestrictions != null) return Futures.immediateFuture(mLockModeRestrictions); 107 final SetupParametersClient parameters = SetupParametersClient.getInstance(); 108 final ListenableFuture<String> kioskPackageTask = parameters.getKioskPackage(); 109 final ListenableFuture<Boolean> outgoingCallsDisabledTask = 110 parameters.getOutgoingCallsDisabled(); 111 return Futures.whenAllSucceed(kioskPackageTask, 112 outgoingCallsDisabledTask) 113 .call(() -> { 114 if (Futures.getDone(kioskPackageTask) == null) { 115 throw new IllegalStateException("Setup parameters does not exist!"); 116 } 117 if (mLockModeRestrictions == null) { 118 mLockModeRestrictions = new ArraySet<>(1); 119 if (Futures.getDone(outgoingCallsDisabledTask)) { 120 mLockModeRestrictions.add(UserManager.DISALLOW_OUTGOING_CALLS); 121 } 122 } 123 return mLockModeRestrictions; 124 }, mBgExecutor); 125 } 126 127 private ListenableFuture<ArraySet<String>> retrieveOptionalAlwaysOnRestrictions() { 128 if (mOptionalAlwaysOnRestrictions != null) { 129 return Futures.immediateFuture( 130 mOptionalAlwaysOnRestrictions); 131 } 132 final SetupParametersClient parameters = SetupParametersClient.getInstance(); 133 final ListenableFuture<String> kioskPackageTask = parameters.getKioskPackage(); 134 final ListenableFuture<Boolean> installingFromUnknownSourcesDisallowedTask = 135 parameters.isInstallingFromUnknownSourcesDisallowed(); 136 final ListenableFuture<Boolean> debuggingAllowedTask = mIsDebug 137 ? Futures.immediateFuture(true) : parameters.isDebuggingAllowed(); 138 139 return Futures.whenAllSucceed(kioskPackageTask, 140 installingFromUnknownSourcesDisallowedTask, 141 debuggingAllowedTask) 142 .call(() -> { 143 if (Futures.getDone(kioskPackageTask) == null) { 144 throw new IllegalStateException("Setup parameters does not exist!"); 145 } 146 if (mOptionalAlwaysOnRestrictions == null) { 147 mOptionalAlwaysOnRestrictions = new ArraySet<>(1); 148 if (Futures.getDone(installingFromUnknownSourcesDisallowedTask)) { 149 mOptionalAlwaysOnRestrictions.add( 150 UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); 151 } 152 if (!Futures.getDone(debuggingAllowedTask)) { 153 mOptionalAlwaysOnRestrictions.add( 154 UserManager.DISALLOW_DEBUGGING_FEATURES); 155 } 156 } 157 return mOptionalAlwaysOnRestrictions; 158 }, mBgExecutor); 159 } 160 161 private boolean setupRestrictions(ArraySet<String> restrictions, boolean enable) { 162 Bundle userRestrictionBundle = mUserManager.getUserRestrictions(); 163 164 for (int i = 0, size = restrictions.size(); i < size; i++) { 165 String restriction = restrictions.valueAt(i); 166 if (userRestrictionBundle.getBoolean(restriction, false) != enable) { 167 if (enable) { 168 mDpm.addUserRestriction(null /* admin */, restriction); 169 LogUtil.v(TAG, String.format(Locale.US, "enable %s restriction", restriction)); 170 } else { 171 mDpm.clearUserRestriction(null /* admin */, restriction); 172 LogUtil.v(TAG, String.format(Locale.US, "clear %s restriction", restriction)); 173 } 174 } 175 } 176 return true; 177 } 178 179 private ListenableFuture<Boolean> setupRestrictions( 180 ListenableFuture<ArraySet<String>> restrictionsFuture, boolean enable) { 181 return Futures.transform(restrictionsFuture, 182 restrictions -> setupRestrictions(restrictions, enable), 183 mBgExecutor); 184 } 185 } 186