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