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.pm;
18 
19 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
20 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
21 import static android.os.Process.SYSTEM_UID;
22 
23 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
24 import static com.android.server.pm.PackageManagerService.TAG;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.UserIdInt;
29 import android.app.ActivityManager;
30 import android.app.AppOpsManager;
31 import android.app.BroadcastOptions;
32 import android.app.IActivityManager;
33 import android.app.admin.DevicePolicyManagerInternal;
34 import android.content.Intent;
35 import android.content.pm.PackageInfo;
36 import android.content.pm.SuspendDialogInfo;
37 import android.os.Binder;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.PersistableBundle;
41 import android.os.Process;
42 import android.os.UserHandle;
43 import android.os.UserManager;
44 import android.provider.DeviceConfig;
45 import android.util.ArrayMap;
46 import android.util.ArraySet;
47 import android.util.IntArray;
48 import android.util.Slog;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.util.ArrayUtils;
52 import com.android.internal.util.CollectionUtils;
53 import com.android.server.LocalServices;
54 import com.android.server.pm.pkg.AndroidPackage;
55 import com.android.server.pm.pkg.PackageStateInternal;
56 import com.android.server.pm.pkg.PackageUserStateInternal;
57 import com.android.server.pm.pkg.SuspendParams;
58 import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
59 import com.android.server.utils.WatchedArrayMap;
60 
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.List;
64 import java.util.Objects;
65 import java.util.Set;
66 import java.util.function.Predicate;
67 
68 public final class SuspendPackageHelper {
69 
70     private static final String SYSTEM_EXEMPT_FROM_SUSPENSION = "system_exempt_from_suspension";
71 
72     // TODO(b/198166813): remove PMS dependency
73     private final PackageManagerService mPm;
74     private final PackageManagerServiceInjector mInjector;
75 
76     private final UserManagerService mUserManager;
77     private final BroadcastHelper mBroadcastHelper;
78     private final ProtectedPackages mProtectedPackages;
79 
80     /**
81      * Constructor for {@link PackageManagerService}.
82      */
SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector, UserManagerService userManager, BroadcastHelper broadcastHelper, ProtectedPackages protectedPackages)83     SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
84             UserManagerService userManager, BroadcastHelper broadcastHelper,
85             ProtectedPackages protectedPackages) {
86         mPm = pm;
87         mUserManager = userManager;
88         mInjector = injector;
89         mBroadcastHelper = broadcastHelper;
90         mProtectedPackages = protectedPackages;
91     }
92 
93     /**
94      * Updates the package to the suspended or unsuspended state.
95      *
96      * @param packageNames The names of the packages to set the suspended status.
97      * @param suspended {@code true} to suspend packages, or {@code false} to unsuspend packages.
98      * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
99      *                  which will be shared with the apps being suspended. Ignored if
100      *                  {@code suspended} is false.
101      * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
102      *                       provide which will be shared with the launcher. Ignored if
103      *                       {@code suspended} is false.
104      * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
105      *                   should be shown to the user when they try to launch a suspended app.
106      *                   Ignored if {@code suspended} is false.
107      * @param callingPackage The caller's package name.
108      * @param userId The user where packages reside.
109      * @param callingUid The caller's uid.
110      * @param forQuietMode Whether suspension is for quiet mode, in which case no apps are exempt.
111      * @return The names of failed packages.
112      */
113     @Nullable
setPackagesSuspended(@onNull Computer snapshot, @Nullable String[] packageNames, boolean suspended, @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo, @NonNull String callingPackage, @UserIdInt int userId, int callingUid, boolean forQuietMode)114     String[] setPackagesSuspended(@NonNull Computer snapshot, @Nullable String[] packageNames,
115             boolean suspended, @Nullable PersistableBundle appExtras,
116             @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
117             @NonNull String callingPackage, @UserIdInt int userId, int callingUid,
118             boolean forQuietMode) {
119         if (ArrayUtils.isEmpty(packageNames)) {
120             return packageNames;
121         }
122         if (suspended && !forQuietMode && !isSuspendAllowedForUser(snapshot, userId, callingUid)) {
123             Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
124             return packageNames;
125         }
126 
127         final SuspendParams newSuspendParams =
128                 new SuspendParams(dialogInfo, appExtras, launcherExtras);
129 
130         final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length);
131 
132         final List<String> notifyPackagesList = new ArrayList<>(packageNames.length);
133         final IntArray notifyUids = new IntArray(packageNames.length);
134         final ArraySet<String> changedPackagesList = new ArraySet<>(packageNames.length);
135         final IntArray changedUids = new IntArray(packageNames.length);
136 
137         final boolean[] canSuspend = suspended && !forQuietMode
138                 ? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid)
139                 : null;
140         for (int i = 0; i < packageNames.length; i++) {
141             final String packageName = packageNames[i];
142             if (callingPackage.equals(packageName)) {
143                 Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
144                         + (suspended ? "" : "un") + "suspend itself. Ignoring");
145                 unmodifiablePackages.add(packageName);
146                 continue;
147             }
148             final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
149             if (packageState == null || !packageState.getUserStateOrDefault(userId).isInstalled()
150                     || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
151                 Slog.w(TAG, "Could not find package setting for package: " + packageName
152                         + ". Skipping suspending/un-suspending.");
153                 unmodifiablePackages.add(packageName);
154                 continue;
155             }
156             if (canSuspend != null && !canSuspend[i]) {
157                 unmodifiablePackages.add(packageName);
158                 continue;
159             }
160 
161             final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
162                     packageState.getUserStateOrDefault(userId).getSuspendParams();
163 
164             SuspendParams oldSuspendParams = suspendParamsMap == null
165                     ? null : suspendParamsMap.get(packageName);
166             boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams);
167 
168             if (suspended && !changed) {
169                 // Carried over API behavior, must notify change even if no change
170                 notifyPackagesList.add(packageName);
171                 notifyUids.add(
172                         UserHandle.getUid(userId, packageState.getAppId()));
173                 continue;
174             }
175 
176             // If only the callingPackage is suspending this package,
177             // it will be unsuspended when this change is committed
178             boolean packageUnsuspended = !suspended
179                     && CollectionUtils.size(suspendParamsMap) == 1
180                     && suspendParamsMap.containsKey(callingPackage);
181             if (suspended || packageUnsuspended) {
182                 // Always notify of a suspend call + notify when fully unsuspended
183                 notifyPackagesList.add(packageName);
184                 notifyUids.add(UserHandle.getUid(userId, packageState.getAppId()));
185             }
186 
187             if (changed) {
188                 changedPackagesList.add(packageName);
189                 changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
190             } else {
191                 Slog.w(TAG, "No change is needed for package: " + packageName
192                         + ". Skipping suspending/un-suspending.");
193             }
194         }
195 
196         mPm.commitPackageStateMutation(null, mutator -> {
197             final int size = changedPackagesList.size();
198             for (int index = 0; index < size; index++) {
199                 final String packageName  = changedPackagesList.valueAt(index);
200                 final PackageUserStateWrite userState = mutator.forPackage(packageName)
201                         .userState(userId);
202                 if (suspended) {
203                     userState.putSuspendParams(callingPackage, newSuspendParams);
204                 } else {
205                     userState.removeSuspension(callingPackage);
206                 }
207             }
208         });
209 
210         if (!notifyPackagesList.isEmpty()) {
211             final String[] changedPackages =
212                     notifyPackagesList.toArray(new String[0]);
213             sendPackagesSuspendedForUser(
214                     suspended ? Intent.ACTION_PACKAGES_SUSPENDED
215                             : Intent.ACTION_PACKAGES_UNSUSPENDED,
216                     changedPackages, notifyUids.toArray(), userId);
217             sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
218             mPm.scheduleWritePackageRestrictions(userId);
219         }
220         // Send the suspension changed broadcast to ensure suspension state is not stale.
221         if (!changedPackagesList.isEmpty()) {
222             sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
223                     changedPackagesList.toArray(new String[0]), changedUids.toArray(), userId);
224         }
225         return unmodifiablePackages.toArray(new String[0]);
226     }
227 
228     /**
229      * Returns the names in the {@code packageNames} which can not be suspended by the caller.
230      *
231      * @param packageNames The names of packages to check.
232      * @param userId The user where packages reside.
233      * @param callingUid The caller's uid.
234      * @return The names of packages which are Unsuspendable.
235      */
236     @NonNull
getUnsuspendablePackagesForUser(@onNull Computer snapshot, @NonNull String[] packageNames, @UserIdInt int userId, int callingUid)237     String[] getUnsuspendablePackagesForUser(@NonNull Computer snapshot,
238             @NonNull String[] packageNames, @UserIdInt int userId, int callingUid) {
239         if (!isSuspendAllowedForUser(snapshot, userId, callingUid)) {
240             Slog.w(TAG, "Cannot suspend due to restrictions on user " + userId);
241             return packageNames;
242         }
243         final ArraySet<String> unactionablePackages = new ArraySet<>();
244         final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, userId,
245                 callingUid);
246         for (int i = 0; i < packageNames.length; i++) {
247             if (!canSuspend[i]) {
248                 unactionablePackages.add(packageNames[i]);
249                 continue;
250             }
251             final PackageStateInternal packageState =
252                     snapshot.getPackageStateForInstalledAndFiltered(
253                             packageNames[i], callingUid, userId);
254             if (packageState == null) {
255                 Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
256                 unactionablePackages.add(packageNames[i]);
257             }
258         }
259         return unactionablePackages.toArray(new String[unactionablePackages.size()]);
260     }
261 
262     /**
263      * Returns the app extras of the given suspended package.
264      *
265      * @param packageName The suspended package name.
266      * @param userId The user where the package resides.
267      * @param callingUid The caller's uid.
268      * @return The app extras of the suspended package.
269      */
270     @Nullable
getSuspendedPackageAppExtras(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)271     Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot, @NonNull String packageName,
272             int userId, int callingUid) {
273         final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, callingUid);
274         if (ps == null) {
275             return null;
276         }
277         final PackageUserStateInternal pus = ps.getUserStateOrDefault(userId);
278         final Bundle allExtras = new Bundle();
279         if (pus.isSuspended()) {
280             for (int i = 0; i < pus.getSuspendParams().size(); i++) {
281                 final SuspendParams params = pus.getSuspendParams().valueAt(i);
282                 if (params != null && params.getAppExtras() != null) {
283                     allExtras.putAll(params.getAppExtras());
284                 }
285             }
286         }
287         return (allExtras.size() > 0) ? allExtras : null;
288     }
289 
290     /**
291      * Removes any suspensions on given packages that were added by packages that pass the given
292      * predicate.
293      *
294      * <p> Caller must flush package restrictions if it cares about immediate data consistency.
295      *
296      * @param packagesToChange The packages on which the suspension are to be removed.
297      * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
298      *                                   suspensions will be removed.
299      * @param userId The user for which the changes are taking place.
300      */
removeSuspensionsBySuspendingPackage(@onNull Computer computer, @NonNull String[] packagesToChange, @NonNull Predicate<String> suspendingPackagePredicate, int userId)301     void removeSuspensionsBySuspendingPackage(@NonNull Computer computer,
302             @NonNull String[] packagesToChange,
303             @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
304         final List<String> unsuspendedPackages = new ArrayList<>();
305         final IntArray unsuspendedUids = new IntArray();
306         final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
307         for (String packageName : packagesToChange) {
308             final PackageStateInternal packageState =
309                     computer.getPackageStateInternal(packageName);
310             final PackageUserStateInternal packageUserState = packageState == null
311                     ? null : packageState.getUserStateOrDefault(userId);
312             if (packageUserState == null || !packageUserState.isSuspended()) {
313                 continue;
314             }
315 
316             WatchedArrayMap<String, SuspendParams> suspendParamsMap =
317                     packageUserState.getSuspendParams();
318             int countRemoved = 0;
319             for (int index = 0; index < suspendParamsMap.size(); index++) {
320                 String suspendingPackage = suspendParamsMap.keyAt(index);
321                 if (suspendingPackagePredicate.test(suspendingPackage)) {
322                     ArraySet<String> suspendingPkgsToCommit =
323                             pkgToSuspendingPkgsToCommit.get(packageName);
324                     if (suspendingPkgsToCommit == null) {
325                         suspendingPkgsToCommit = new ArraySet<>();
326                         pkgToSuspendingPkgsToCommit.put(packageName, suspendingPkgsToCommit);
327                     }
328                     suspendingPkgsToCommit.add(suspendingPackage);
329                     countRemoved++;
330                 }
331             }
332 
333             // Everything would be removed and package unsuspended
334             if (countRemoved == suspendParamsMap.size()) {
335                 unsuspendedPackages.add(packageState.getPackageName());
336                 unsuspendedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
337             }
338         }
339 
340         mPm.commitPackageStateMutation(null, mutator -> {
341             for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
342                 String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex);
343                 ArraySet<String> packagesToRemove = pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
344                 PackageUserStateWrite userState = mutator.forPackage(packageName).userState(userId);
345                 for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) {
346                     userState.removeSuspension(packagesToRemove.valueAt(setIndex));
347                 }
348             }
349         });
350 
351         mPm.scheduleWritePackageRestrictions(userId);
352         if (!unsuspendedPackages.isEmpty()) {
353             final String[] packageArray = unsuspendedPackages.toArray(
354                     new String[unsuspendedPackages.size()]);
355             sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
356             sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
357                     packageArray, unsuspendedUids.toArray(), userId);
358         }
359     }
360 
361     /**
362      * Returns the launcher extras for the given suspended package.
363      *
364      * @param packageName The name of the suspended package.
365      * @param userId The user where the package resides.
366      * @param callingUid The caller's uid.
367      * @return The launcher extras.
368      */
369     @Nullable
getSuspendedPackageLauncherExtras(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)370     Bundle getSuspendedPackageLauncherExtras(@NonNull Computer snapshot,
371             @NonNull String packageName, int userId, int callingUid) {
372         final PackageStateInternal packageState =
373                 snapshot.getPackageStateInternal(packageName, callingUid);
374         if (packageState == null) {
375             return null;
376         }
377         Bundle allExtras = new Bundle();
378         PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
379         if (userState.isSuspended()) {
380             for (int i = 0; i < userState.getSuspendParams().size(); i++) {
381                 final SuspendParams params = userState.getSuspendParams().valueAt(i);
382                 if (params != null && params.getLauncherExtras() != null) {
383                     allExtras.putAll(params.getLauncherExtras());
384                 }
385             }
386         }
387         return (allExtras.size() > 0) ? allExtras : null;
388     }
389 
390     /**
391      * Return {@code true}, if the given package is suspended.
392      *
393      * @param packageName The name of package to check.
394      * @param userId The user where the package resides.
395      * @param callingUid The caller's uid.
396      * @return {@code true}, if the given package is suspended.
397      */
isPackageSuspended(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)398     boolean isPackageSuspended(@NonNull Computer snapshot, @NonNull String packageName, int userId,
399             int callingUid) {
400         final PackageStateInternal packageState =
401                 snapshot.getPackageStateInternal(packageName, callingUid);
402         return packageState != null && packageState.getUserStateOrDefault(userId)
403                 .isSuspended();
404     }
405 
406     /**
407      * Given a suspended package, returns the name of package which invokes suspending to it.
408      *
409      * @param suspendedPackage The suspended package to check.
410      * @param userId The user where the package resides.
411      * @param callingUid The caller's uid.
412      * @return The name of suspending package.
413      */
414     @Nullable
getSuspendingPackage(@onNull Computer snapshot, @NonNull String suspendedPackage, int userId, int callingUid)415     String getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage,
416             int userId, int callingUid) {
417         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
418                 suspendedPackage, callingUid);
419         if (packageState == null) {
420             return  null;
421         }
422 
423         final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
424         if (!userState.isSuspended()) {
425             return null;
426         }
427 
428         String suspendingPackage = null;
429         for (int i = 0; i < userState.getSuspendParams().size(); i++) {
430             suspendingPackage = userState.getSuspendParams().keyAt(i);
431             if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
432                 return suspendingPackage;
433             }
434         }
435         return suspendingPackage;
436     }
437 
438     /**
439      *  Returns the dialog info of the given suspended package.
440      *
441      * @param suspendedPackage The name of the suspended package.
442      * @param suspendingPackage The name of the suspending package.
443      * @param userId The user where the package resides.
444      * @param callingUid The caller's uid.
445      * @return The dialog info.
446      */
447     @Nullable
getSuspendedDialogInfo(@onNull Computer snapshot, @NonNull String suspendedPackage, @NonNull String suspendingPackage, int userId, int callingUid)448     SuspendDialogInfo getSuspendedDialogInfo(@NonNull Computer snapshot,
449             @NonNull String suspendedPackage, @NonNull String suspendingPackage, int userId,
450             int callingUid) {
451         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
452                 suspendedPackage, callingUid);
453         if (packageState == null) {
454             return  null;
455         }
456 
457         final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
458         if (!userState.isSuspended()) {
459             return null;
460         }
461 
462         final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
463                 userState.getSuspendParams();
464         if (suspendParamsMap == null) {
465             return null;
466         }
467 
468         final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
469         return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
470     }
471 
472     /**
473      * Return {@code true} if the user is allowed to suspend packages by the caller.
474      *
475      * @param userId The user id to check.
476      * @param callingUid The caller's uid.
477      * @return {@code true} if the user is allowed to suspend packages by the caller.
478      */
isSuspendAllowedForUser(@onNull Computer snapshot, int userId, int callingUid)479     boolean isSuspendAllowedForUser(@NonNull Computer snapshot, int userId, int callingUid) {
480         final UserManagerService userManager = mInjector.getUserManagerService();
481         return isCallerDeviceOrProfileOwner(snapshot, userId, callingUid)
482                 || (!userManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
483                 && !userManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
484     }
485 
486     /**
487      * Returns an array of booleans, such that the ith boolean denotes whether the ith package can
488      * be suspended or not.
489      *
490      * @param packageNames  The package names to check suspendability for.
491      * @param userId The user to check in
492      * @param callingUid The caller's uid.
493      * @return An array containing results of the checks
494      */
495     @NonNull
canSuspendPackageForUser(@onNull Computer snapshot, @NonNull String[] packageNames, int userId, int callingUid)496     boolean[] canSuspendPackageForUser(@NonNull Computer snapshot, @NonNull String[] packageNames,
497             int userId, int callingUid) {
498         final boolean[] canSuspend = new boolean[packageNames.length];
499         final boolean isCallerOwner = isCallerDeviceOrProfileOwner(snapshot, userId, callingUid);
500         final long token = Binder.clearCallingIdentity();
501         try {
502             final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider();
503             final String activeLauncherPackageName = defaultAppProvider.getDefaultHome(userId);
504             final String dialerPackageName = defaultAppProvider.getDefaultDialer(userId);
505             final String requiredInstallerPackage =
506                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, userId);
507             final String requiredUninstallerPackage =
508                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, userId);
509             final String requiredVerifierPackage =
510                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, userId);
511             final String requiredPermissionControllerPackage =
512                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
513                             userId);
514             final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class);
515             final boolean isSystemExemptFlagEnabled = DeviceConfig.getBoolean(
516                     DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
517                     SYSTEM_EXEMPT_FROM_SUSPENSION, /* defaultValue= */ true);
518             for (int i = 0; i < packageNames.length; i++) {
519                 canSuspend[i] = false;
520                 final String packageName = packageNames[i];
521 
522                 if (mPm.isPackageDeviceAdmin(packageName, userId)) {
523                     Slog.w(TAG, "Cannot suspend package \"" + packageName
524                             + "\": has an active device admin");
525                     continue;
526                 }
527                 if (packageName.equals(activeLauncherPackageName)) {
528                     Slog.w(TAG, "Cannot suspend package \"" + packageName
529                             + "\": contains the active launcher");
530                     continue;
531                 }
532                 if (packageName.equals(requiredInstallerPackage)) {
533                     Slog.w(TAG, "Cannot suspend package \"" + packageName
534                             + "\": required for package installation");
535                     continue;
536                 }
537                 if (packageName.equals(requiredUninstallerPackage)) {
538                     Slog.w(TAG, "Cannot suspend package \"" + packageName
539                             + "\": required for package uninstallation");
540                     continue;
541                 }
542                 if (packageName.equals(requiredVerifierPackage)) {
543                     Slog.w(TAG, "Cannot suspend package \"" + packageName
544                             + "\": required for package verification");
545                     continue;
546                 }
547                 if (packageName.equals(dialerPackageName)) {
548                     Slog.w(TAG, "Cannot suspend package \"" + packageName
549                             + "\": is the default dialer");
550                     continue;
551                 }
552                 if (packageName.equals(requiredPermissionControllerPackage)) {
553                     Slog.w(TAG, "Cannot suspend package \"" + packageName
554                             + "\": required for permissions management");
555                     continue;
556                 }
557                 if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
558                     Slog.w(TAG, "Cannot suspend package \"" + packageName
559                             + "\": protected package");
560                     continue;
561                 }
562                 if (!isCallerOwner && snapshot.getBlockUninstall(userId, packageName)) {
563                     Slog.w(TAG, "Cannot suspend package \"" + packageName
564                             + "\": blocked by admin");
565                     continue;
566                 }
567 
568                 // Cannot suspend static shared libs as they are considered
569                 // a part of the using app (emulating static linking). Also
570                 // static libs are installed always on internal storage.
571                 PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
572                 AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
573                 if (pkg != null) {
574                     final int uid = UserHandle.getUid(userId, packageState.getAppId());
575                     // Cannot suspend SDK libs as they are controlled by SDK manager.
576                     if (pkg.isSdkLibrary()) {
577                         Slog.w(TAG, "Cannot suspend package: " + packageName
578                                 + " providing SDK library: "
579                                 + pkg.getSdkLibraryName());
580                         continue;
581                     }
582                     // Cannot suspend static shared libs as they are considered
583                     // a part of the using app (emulating static linking). Also
584                     // static libs are installed always on internal storage.
585                     if (pkg.isStaticSharedLibrary()) {
586                         Slog.w(TAG, "Cannot suspend package: " + packageName
587                                 + " providing static shared library: "
588                                 + pkg.getStaticSharedLibraryName());
589                         continue;
590                     }
591                     if (isSystemExemptFlagEnabled && appOpsManager.checkOpNoThrow(
592                             AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName)
593                             == AppOpsManager.MODE_ALLOWED) {
594                         Slog.w(TAG, "Cannot suspend package \"" + packageName
595                                 + "\": has OP_SYSTEM_EXEMPT_FROM_SUSPENSION set");
596                         continue;
597                     }
598                 }
599                 if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
600                     Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
601                     continue;
602                 }
603                 canSuspend[i] = true;
604             }
605         } finally {
606             Binder.restoreCallingIdentity(token);
607         }
608         return canSuspend;
609     }
610 
611     /**
612      * Send broadcast intents for packages suspension changes.
613      *
614      * @param intent The action name of the suspension intent.
615      * @param pkgList The names of packages which have suspension changes.
616      * @param uidList The uids of packages which have suspension changes.
617      * @param userId The user where packages reside.
618      */
619     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
sendPackagesSuspendedForUser(@onNull String intent, @NonNull String[] pkgList, @NonNull int[] uidList, int userId)620     void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList,
621             @NonNull int[] uidList, int userId) {
622         final Handler handler = mInjector.getHandler();
623         final Bundle extras = new Bundle(3);
624         extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
625         extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
626         final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
627         final Bundle options = new BroadcastOptions()
628                 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
629                 .toBundle();
630         handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
631                 extras, flags, null /* targetPkg */, null /* finishedReceiver */,
632                 new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
633                 (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
634                         mPm.snapshotComputer(), callingUid, intentExtras),
635                 options));
636     }
637 
638     /**
639      * Suspends packages on behalf of an admin.
640      *
641      * @return array of packages that are unsuspendable, either because admin is not allowed to
642      * suspend them (e.g. current dialer) or there was other problem (e.g. package not found).
643      */
setPackagesSuspendedByAdmin( Computer snapshot, int userId, String[] packageNames, boolean suspend)644     public String[] setPackagesSuspendedByAdmin(
645             Computer snapshot, int userId, String[] packageNames, boolean suspend) {
646         final Set<String> toSuspend = new ArraySet<>(packageNames);
647         List<String> unsuspendable = new ArrayList<>();
648 
649         if (mUserManager.isQuietModeEnabled(userId)) {
650             // If the user is in quiet mode, most apps will already be suspended, we shouldn't
651             // re-suspend or unsuspend them.
652             final Set<String> quiet = packagesToSuspendInQuietMode(snapshot, userId);
653             quiet.retainAll(toSuspend);
654             if (!quiet.isEmpty()) {
655                 Slog.i(TAG, "Ignoring quiet packages: " + String.join(", ", quiet));
656                 toSuspend.removeAll(quiet);
657             }
658 
659             // Some of the already suspended packages might not be suspendable by the admin
660             // (e.g. current dialer package), we need to report it back as unsuspendable the same
661             // way as if quiet mode wasn't enabled. In that latter case they'd be returned by
662             // setPackagesSuspended below after unsuccessful attempt to suspend them.
663             if (suspend) {
664                 unsuspendable = getUnsuspendablePackages(snapshot, userId, quiet);
665             }
666         }
667         if (!toSuspend.isEmpty()) {
668             unsuspendable.addAll(Arrays.asList(
669                     setPackagesSuspended(
670                             snapshot, toSuspend.toArray(new String[0]), suspend,
671                             null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
672                             PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
673                             false /* forQuietMode */)));
674         }
675         return unsuspendable.toArray(String[]::new);
676     }
677 
getUnsuspendablePackages( Computer snapshot, int userId, Set<String> packages)678     private List<String> getUnsuspendablePackages(
679             Computer snapshot, int userId, Set<String> packages) {
680         final String[] toSuspendArray = packages.toArray(String[]::new);
681         final boolean[] mask =
682                 canSuspendPackageForUser(snapshot, toSuspendArray, userId, Process.SYSTEM_UID);
683         final List<String> result = new ArrayList<>();
684         for (int i = 0; i < mask.length; i++) {
685             if (!mask[i]) {
686                 result.add(toSuspendArray[i]);
687             }
688         }
689         return result;
690     }
691 
692     /**
693      * Suspends or unsuspends all packages in the given user when quiet mode is toggled to prevent
694      * usage while quiet mode is enabled.
695      */
setPackagesSuspendedForQuietMode( Computer snapshot, int userId, boolean suspend)696     public void setPackagesSuspendedForQuietMode(
697             Computer snapshot, int userId, boolean suspend) {
698         final Set<String> toSuspend = packagesToSuspendInQuietMode(snapshot, userId);
699         if (!suspend) {
700             final DevicePolicyManagerInternal dpm =
701                     LocalServices.getService(DevicePolicyManagerInternal.class);
702             if (dpm != null) {
703                 toSuspend.removeAll(dpm.getPackagesSuspendedByAdmin(userId));
704             } else {
705                 Slog.wtf(TAG,
706                         "DevicePolicyManager unavailable while suspending apps for quiet mode");
707             }
708         }
709 
710         if (toSuspend.isEmpty()) {
711             return;
712         }
713 
714         setPackagesSuspended(snapshot, toSuspend.toArray(new String[0]),
715                 suspend, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */,
716                 PackageManagerService.PLATFORM_PACKAGE_NAME, userId, Process.SYSTEM_UID,
717                 true /* forQuietMode */);
718     }
719 
packagesToSuspendInQuietMode(Computer snapshot, int userId)720     private Set<String> packagesToSuspendInQuietMode(Computer snapshot, int userId) {
721         final List<PackageInfo> pkgInfos = snapshot.getInstalledPackages(
722                 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId).getList();
723         final Set<String> result = new ArraySet<>();
724         for (PackageInfo info : pkgInfos) {
725             result.add(info.packageName);
726         }
727 
728         // Role holder may be null, but ArraySet handles it correctly.
729         result.remove(mPm.getDevicePolicyManagementRoleHolderPackageName(userId));
730 
731         return result;
732     }
733 
getKnownPackageName(@onNull Computer snapshot, @KnownPackages.KnownPackage int knownPackage, int userId)734     private String getKnownPackageName(@NonNull Computer snapshot,
735             @KnownPackages.KnownPackage int knownPackage, int userId) {
736         final String[] knownPackages =
737                 mPm.getKnownPackageNamesInternal(snapshot, knownPackage, userId);
738         return knownPackages.length > 0 ? knownPackages[0] : null;
739     }
740 
isCallerDeviceOrProfileOwner(@onNull Computer snapshot, int userId, int callingUid)741     private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int userId,
742             int callingUid) {
743         if (callingUid == SYSTEM_UID) {
744             return true;
745         }
746         final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(userId);
747         if (ownerPackage != null) {
748             return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, userId,
749                     callingUid);
750         }
751         return false;
752     }
753 
sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended, int userId)754     private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
755             int userId) {
756         final Handler handler = mInjector.getHandler();
757         final String action = suspended
758                 ? Intent.ACTION_MY_PACKAGE_SUSPENDED
759                 : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
760         handler.post(() -> {
761             final IActivityManager am = ActivityManager.getService();
762             if (am == null) {
763                 Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
764                         + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
765                 return;
766             }
767             final int[] targetUserIds = new int[] {userId};
768             final Computer snapshot = mPm.snapshotComputer();
769             for (String packageName : affectedPackages) {
770                 final Bundle appExtras = suspended
771                         ? getSuspendedPackageAppExtras(snapshot, packageName, userId, SYSTEM_UID)
772                         : null;
773                 final Bundle intentExtras;
774                 if (appExtras != null) {
775                     intentExtras = new Bundle(1);
776                     intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
777                 } else {
778                     intentExtras = null;
779                 }
780                 mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
781                         Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
782                         targetUserIds, false, null, null, null);
783             }
784         });
785     }
786 }
787