• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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