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.server.devicepolicy;
18 
19 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
20 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.UserIdInt;
25 import android.app.AppGlobals;
26 import android.app.AppOpsManager;
27 import android.app.admin.DevicePolicyCache;
28 import android.app.admin.DevicePolicyManager;
29 import android.app.admin.DevicePolicyManagerInternal;
30 import android.app.admin.IntentFilterPolicyKey;
31 import android.app.admin.LockTaskPolicy;
32 import android.app.admin.PackagePermissionPolicyKey;
33 import android.app.admin.PackagePolicyKey;
34 import android.app.admin.PolicyKey;
35 import android.app.admin.UserRestrictionPolicyKey;
36 import android.app.admin.flags.Flags;
37 import android.app.usage.UsageStatsManagerInternal;
38 import android.content.ComponentName;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.pm.IPackageManager;
43 import android.content.pm.PackageManager;
44 import android.content.pm.PackageManagerInternal;
45 import android.os.Binder;
46 import android.os.Bundle;
47 import android.os.Process;
48 import android.os.RemoteException;
49 import android.os.ServiceManager;
50 import android.os.UserHandle;
51 import android.permission.AdminPermissionControlParams;
52 import android.permission.PermissionControllerManager;
53 import android.provider.Settings;
54 import android.util.ArraySet;
55 import android.util.Slog;
56 import android.view.IWindowManager;
57 
58 import com.android.internal.os.BackgroundThread;
59 import com.android.internal.util.ArrayUtils;
60 import com.android.server.LocalServices;
61 import com.android.server.pm.UserManagerInternal;
62 import com.android.server.utils.Slogf;
63 
64 import java.util.Collections;
65 import java.util.List;
66 import java.util.Objects;
67 import java.util.Set;
68 import java.util.concurrent.CountDownLatch;
69 import java.util.concurrent.TimeUnit;
70 import java.util.concurrent.atomic.AtomicReference;
71 
72 final class PolicyEnforcerCallbacks {
73 
74     private static final String LOG_TAG = "PolicyEnforcerCallbacks";
75 
noOp(T value, Context context, Integer userId, PolicyKey policyKey)76     static <T> boolean noOp(T value, Context context, Integer userId, PolicyKey policyKey) {
77         return true;
78     }
79 
setAutoTimezoneEnabled(@ullable Boolean enabled, @NonNull Context context)80     static boolean setAutoTimezoneEnabled(@Nullable Boolean enabled, @NonNull Context context) {
81         if (!DevicePolicyManagerService.isUnicornFlagEnabled()) {
82             Slogf.w(LOG_TAG, "Trying to enforce setAutoTimezoneEnabled while flag is off.");
83             return true;
84         }
85         return Binder.withCleanCallingIdentity(() -> {
86             Objects.requireNonNull(context);
87 
88             int value = enabled != null && enabled ? 1 : 0;
89             return Settings.Global.putInt(
90                     context.getContentResolver(), Settings.Global.AUTO_TIME_ZONE,
91                     value);
92         });
93     }
94 
setPermissionGrantState( @ullable Integer grantState, @NonNull Context context, int userId, @NonNull PolicyKey policyKey)95     static boolean setPermissionGrantState(
96             @Nullable Integer grantState, @NonNull Context context, int userId,
97             @NonNull PolicyKey policyKey) {
98         if (!DevicePolicyManagerService.isUnicornFlagEnabled()) {
99             Slogf.w(LOG_TAG, "Trying to enforce setPermissionGrantState while flag is off.");
100             return true;
101         }
102         return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
103             if (!(policyKey instanceof PackagePermissionPolicyKey)) {
104                 throw new IllegalArgumentException("policyKey is not of type "
105                         + "PermissionGrantStatePolicyKey, passed in policyKey is: " + policyKey);
106             }
107             PackagePermissionPolicyKey parsedKey = (PackagePermissionPolicyKey) policyKey;
108             Objects.requireNonNull(parsedKey.getPermissionName());
109             Objects.requireNonNull(parsedKey.getPackageName());
110             Objects.requireNonNull(context);
111 
112             int value = grantState == null
113                     ? DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
114                     : grantState;
115 
116             // TODO(b/278710449): stop blocking in the main thread
117             BlockingCallback callback = new BlockingCallback();
118             // TODO: remove canAdminGrantSensorPermissions once we expose a new method in
119             //  permissionController that doesn't need it.
120             AdminPermissionControlParams permissionParams = new AdminPermissionControlParams(
121                     parsedKey.getPackageName(), parsedKey.getPermissionName(), value,
122                     /* canAdminGrantSensorPermissions= */ true);
123             getPermissionControllerManager(context, UserHandle.of(userId))
124                     // TODO: remove callingPackage param and stop passing context.getPackageName()
125                     .setRuntimePermissionGrantStateByDeviceAdmin(context.getPackageName(),
126                             permissionParams, context.getMainExecutor(), callback::trigger);
127             try {
128                 return callback.await(20_000, TimeUnit.MILLISECONDS);
129             } catch (Exception e) {
130                 // TODO: add logging
131                 return false;
132             }
133         }));
134     }
135 
136     @NonNull
137     private static PermissionControllerManager getPermissionControllerManager(
138             Context context, UserHandle user) {
139         if (user.equals(context.getUser())) {
140             return context.getSystemService(PermissionControllerManager.class);
141         } else {
142             try {
143                 return context.createPackageContextAsUser(context.getPackageName(), /* flags= */ 0,
144                         user).getSystemService(PermissionControllerManager.class);
145             } catch (PackageManager.NameNotFoundException notPossible) {
146                 // not possible
147                 throw new IllegalStateException(notPossible);
148             }
149         }
150     }
151 
152     static boolean enforceSecurityLogging(
153             @Nullable Boolean value, @NonNull Context context, int userId,
154             @NonNull PolicyKey policyKey) {
155         final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
156         dpmi.enforceSecurityLoggingPolicy(Boolean.TRUE.equals(value));
157         return true;
158     }
159 
160     static boolean enforceAuditLogging(
161             @Nullable Boolean value, @NonNull Context context, int userId,
162             @NonNull PolicyKey policyKey) {
163         final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
164         dpmi.enforceAuditLoggingPolicy(Boolean.TRUE.equals(value));
165         return true;
166     }
167 
168     static boolean setLockTask(
169             @Nullable LockTaskPolicy policy, @NonNull Context context, int userId) {
170         List<String> packages = Collections.emptyList();
171         int flags = LockTaskPolicy.DEFAULT_LOCK_TASK_FLAG;
172         if (policy != null) {
173             packages = List.copyOf(policy.getPackages());
174             flags = policy.getFlags();
175         }
176         DevicePolicyManagerService.updateLockTaskPackagesLocked(context, packages, userId);
177         DevicePolicyManagerService.updateLockTaskFeaturesLocked(flags, userId);
178         return true;
179     }
180 
181 
182     /**
183      * Application restrictions are stored and retrieved from DPMS, so no enforcing (aka pushing
184      * it to UMS) is required. Only need to send broadcast to the target user here as we rely on
185      * the inheritable policy propagation logic in PolicyEngine to apply this policy to multiple
186      * profiles. The broadcast should only be sent when an application restriction is set, so we
187      * rely on the POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED flag so DPE only invokes this callback
188      * when the policy is set, and not during system boot or other situations.
189      */
190     static boolean setApplicationRestrictions(Bundle bundle, Context context, Integer userId,
191             PolicyKey policyKey) {
192         Binder.withCleanCallingIdentity(() -> {
193             PackagePolicyKey key = (PackagePolicyKey) policyKey;
194             String packageName = key.getPackageName();
195             Objects.requireNonNull(packageName);
196             Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
197             changeIntent.setPackage(packageName);
198             changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
199             context.sendBroadcastAsUser(changeIntent, UserHandle.of(userId));
200         });
201         return true;
202     }
203 
204     private static class BlockingCallback {
205         private final CountDownLatch mLatch = new CountDownLatch(1);
206         private final AtomicReference<Boolean> mValue = new AtomicReference<>();
207         public void trigger(Boolean value) {
208             mValue.set(value);
209             mLatch.countDown();
210         }
211 
212         public Boolean await(long timeout, TimeUnit unit) throws InterruptedException {
213             if (!mLatch.await(timeout, unit)) {
214                 Slogf.e(LOG_TAG, "Callback was not received");
215             }
216             return mValue.get();
217         }
218     }
219 
220     // TODO: when a local policy exists for a user, this callback will be invoked for this user
221     // individually as well as for USER_ALL. This can be optimized by separating local and global
222     // enforcement in the policy engine.
223     static boolean setUserControlDisabledPackages(
224             @Nullable Set<String> packages, Context context, int userId, PolicyKey policyKey) {
225         Binder.withCleanCallingIdentity(() -> {
226             PackageManagerInternal pmi =
227                     LocalServices.getService(PackageManagerInternal.class);
228             AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
229 
230             pmi.setOwnerProtectedPackages(userId,
231                     packages == null ? null : packages.stream().toList());
232             LocalServices.getService(UsageStatsManagerInternal.class)
233                     .setAdminProtectedPackages(
234                             packages == null ? null : new ArraySet<>(packages), userId);
235 
236             if (packages == null || packages.isEmpty()) {
237                 return;
238             }
239 
240             for (int user : resolveUsers(userId)) {
241                 if (Flags.disallowUserControlBgUsageFix()) {
242                     setBgUsageAppOp(packages, pmi, user, appOpsManager);
243                 }
244                 if (Flags.disallowUserControlStoppedStateFix()) {
245                     for (String packageName : packages) {
246                         pmi.setPackageStoppedState(packageName, false, user);
247                     }
248                 }
249             }
250         });
251         return true;
252     }
253 
254     /** Handles USER_ALL expanding it into the list of all intact users. */
255     private static List<Integer> resolveUsers(int userId) {
256         if (userId == UserHandle.USER_ALL) {
257             UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
258             return userManager.getUsers(/* excludeDying= */ true)
259                     .stream().map(ui -> ui.id).toList();
260         } else {
261             return List.of(userId);
262         }
263     }
264 
265     private static void setBgUsageAppOp(Set<String> packages, PackageManagerInternal pmi,
266             int userId, AppOpsManager appOpsManager) {
267         for (var pkg : packages) {
268             int packageFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
269             final var appInfo = pmi.getApplicationInfo(pkg, packageFlags, Process.myUid(), userId);
270             if (appInfo != null) {
271                 DevicePolicyManagerService.setBgUsageAppOp(appOpsManager, appInfo);
272             }
273         }
274     }
275 
276     static boolean addPersistentPreferredActivity(
277             @Nullable ComponentName preferredActivity, @NonNull Context context, int userId,
278             @NonNull PolicyKey policyKey) {
279         Binder.withCleanCallingIdentity(() -> {
280             try {
281                 if (!(policyKey instanceof IntentFilterPolicyKey)) {
282                     throw new IllegalArgumentException("policyKey is not of type "
283                             + "IntentFilterPolicyKey, passed in policyKey is: " + policyKey);
284                 }
285                 IntentFilterPolicyKey parsedKey =
286                         (IntentFilterPolicyKey) policyKey;
287                 IntentFilter filter = Objects.requireNonNull(parsedKey.getIntentFilter());
288 
289                 IPackageManager packageManager = AppGlobals.getPackageManager();
290                 if (preferredActivity != null) {
291                     packageManager.addPersistentPreferredActivity(
292                             filter, preferredActivity, userId);
293                 } else {
294                     packageManager.clearPersistentPreferredActivity(filter, userId);
295                 }
296                 packageManager.flushPackageRestrictionsAsUser(userId);
297             } catch (RemoteException re) {
298                 // Shouldn't happen
299                 Slog.wtf(LOG_TAG, "Error adding/removing persistent preferred activity", re);
300             }
301         });
302         return true;
303     }
304 
305     static boolean setUninstallBlocked(
306             @Nullable Boolean uninstallBlocked, @NonNull Context context, int userId,
307             @NonNull PolicyKey policyKey) {
308         return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
309             if (!(policyKey instanceof PackagePolicyKey)) {
310                 throw new IllegalArgumentException("policyKey is not of type "
311                         + "PackagePolicyKey, passed in policyKey is: " + policyKey);
312             }
313             PackagePolicyKey parsedKey = (PackagePolicyKey) policyKey;
314             String packageName = Objects.requireNonNull(parsedKey.getPackageName());
315             DevicePolicyManagerService.setUninstallBlockedUnchecked(
316                     packageName,
317                     uninstallBlocked != null && uninstallBlocked,
318                     userId);
319             return true;
320         }));
321     }
322 
323     static boolean setUserRestriction(
324             @Nullable Boolean enabled, @NonNull Context context, int userId,
325             @NonNull PolicyKey policyKey) {
326         return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
327             if (!(policyKey instanceof UserRestrictionPolicyKey)) {
328                 throw new IllegalArgumentException("policyKey is not of type "
329                         + "UserRestrictionPolicyKey, passed in policyKey is: " + policyKey);
330             }
331             UserRestrictionPolicyKey parsedKey =
332                     (UserRestrictionPolicyKey) policyKey;
333             UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
334             userManager.setUserRestriction(
335                     userId, parsedKey.getRestriction(), enabled != null && enabled);
336             return true;
337         }));
338     }
339 
340     static boolean setApplicationHidden(
341             @Nullable Boolean hide, @NonNull Context context, int userId,
342             @NonNull PolicyKey policyKey) {
343         return Boolean.TRUE.equals(Binder.withCleanCallingIdentity(() -> {
344             if (!(policyKey instanceof PackagePolicyKey)) {
345                 throw new IllegalArgumentException("policyKey is not of type "
346                         + "PackagePolicyKey, passed in policyKey is: " + policyKey);
347             }
348             PackagePolicyKey parsedKey = (PackagePolicyKey) policyKey;
349             String packageName = Objects.requireNonNull(parsedKey.getPackageName());
350             IPackageManager packageManager = AppGlobals.getPackageManager();
351             return packageManager.setApplicationHiddenSettingAsUser(
352                     packageName, hide != null && hide, userId);
353         }));
354     }
355 
356     static boolean setScreenCaptureDisabled(
357             @Nullable Boolean disabled, @NonNull Context context, int userId,
358             @NonNull PolicyKey policyKey) {
359         Binder.withCleanCallingIdentity(() -> {
360             DevicePolicyCache cache = DevicePolicyCache.getInstance();
361             if (cache instanceof DevicePolicyCacheImpl) {
362                 DevicePolicyCacheImpl parsedCache = (DevicePolicyCacheImpl) cache;
363                 parsedCache.setScreenCaptureDisallowedUser(
364                         userId, disabled != null && disabled);
365                 updateScreenCaptureDisabled();
366             }
367         });
368         return true;
369     }
370 
371     static boolean setContentProtectionPolicy(
372             @Nullable Integer value,
373             @NonNull Context context,
374             @UserIdInt Integer userId,
375             @NonNull PolicyKey policyKey) {
376         Binder.withCleanCallingIdentity(
377                 () -> {
378                     DevicePolicyCache cache = DevicePolicyCache.getInstance();
379                     if (cache instanceof DevicePolicyCacheImpl cacheImpl) {
380                         cacheImpl.setContentProtectionPolicy(userId, value);
381                     }
382                 });
383         return true;
384     }
385 
386     private static void updateScreenCaptureDisabled() {
387         BackgroundThread.getHandler().post(() -> {
388             try {
389                 IWindowManager.Stub
390                         .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE))
391                         .refreshScreenCaptureDisabled();
392             } catch (RemoteException e) {
393                 Slogf.w(LOG_TAG, "Unable to notify WindowManager.", e);
394             }
395         });
396     }
397 
398     static boolean setPersonalAppsSuspended(
399             @Nullable Boolean suspended, @NonNull Context context, int userId,
400             @NonNull PolicyKey policyKey) {
401         Binder.withCleanCallingIdentity(() -> {
402             if (suspended != null && suspended) {
403                 suspendPersonalAppsInPackageManager(context, userId);
404             } else {
405                 LocalServices.getService(PackageManagerInternal.class)
406                         .unsuspendAdminSuspendedPackages(userId);
407             }
408         });
409         return true;
410     }
411 
412     private static void suspendPersonalAppsInPackageManager(Context context, int userId) {
413         final String[] appsToSuspend = PersonalAppsSuspensionHelper.forUser(context, userId)
414                 .getPersonalAppsForSuspension();
415         Slogf.i(LOG_TAG, "Suspending personal apps: %s", String.join(",", appsToSuspend));
416         final String[] failedApps = LocalServices.getService(PackageManagerInternal.class)
417                 .setPackagesSuspendedByAdmin(userId, appsToSuspend, true);
418         if (!ArrayUtils.isEmpty(failedApps)) {
419             Slogf.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
420         }
421     }
422 
423     static boolean setUsbDataSignalingEnabled(@Nullable Boolean value, @NonNull Context context) {
424         return Binder.withCleanCallingIdentity(() -> {
425             Objects.requireNonNull(context);
426 
427             boolean enabled = value == null || value;
428             DevicePolicyManagerService.updateUsbDataSignal(context, enabled);
429             return true;
430         });
431     }
432 }
433