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