• 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.pm;
18 
19 import static android.os.Process.SYSTEM_UID;
20 
21 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
22 import static com.android.server.pm.PackageManagerService.TAG;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.UserIdInt;
27 import android.app.AppOpsManager;
28 import android.content.Intent;
29 import android.content.pm.SuspendDialogInfo;
30 import android.content.pm.UserPackage;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.PersistableBundle;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.util.ArrayMap;
37 import android.util.ArraySet;
38 import android.util.IntArray;
39 import android.util.Slog;
40 
41 import com.android.internal.util.ArrayUtils;
42 import com.android.internal.util.CollectionUtils;
43 import com.android.server.pm.pkg.AndroidPackage;
44 import com.android.server.pm.pkg.PackageStateInternal;
45 import com.android.server.pm.pkg.PackageUserStateInternal;
46 import com.android.server.pm.pkg.SuspendParams;
47 import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
48 import com.android.server.utils.WatchedArrayMap;
49 
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.Objects;
53 import java.util.function.Predicate;
54 
55 public final class SuspendPackageHelper {
56 
57     private static final String SYSTEM_EXEMPT_FROM_SUSPENSION = "system_exempt_from_suspension";
58 
59     // TODO(b/198166813): remove PMS dependency
60     private final PackageManagerService mPm;
61     private final PackageManagerServiceInjector mInjector;
62 
63     private final BroadcastHelper mBroadcastHelper;
64     private final ProtectedPackages mProtectedPackages;
65 
66     /**
67      * Constructor for {@link PackageManagerService}.
68      */
SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector, BroadcastHelper broadcastHelper, ProtectedPackages protectedPackages)69     SuspendPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
70             BroadcastHelper broadcastHelper, ProtectedPackages protectedPackages) {
71         mPm = pm;
72         mInjector = injector;
73         mBroadcastHelper = broadcastHelper;
74         mProtectedPackages = protectedPackages;
75     }
76 
77     /**
78      * Updates the package to the suspended or unsuspended state.
79      *
80      * @param packageNames The names of the packages to set the suspended status.
81      * @param suspended {@code true} to suspend packages, or {@code false} to unsuspend packages.
82      * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
83      *                  which will be shared with the apps being suspended. Ignored if
84      *                  {@code suspended} is false.
85      * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
86      *                       provide which will be shared with the launcher. Ignored if
87      *                       {@code suspended} is false.
88      * @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
89      *                   should be shown to the user when they try to launch a suspended app.
90      *                   Ignored if {@code suspended} is false.
91      * @param suspendingPackage The caller's package name.
92      * @param targetUserId The user where packages reside.
93      * @param callingUid The caller's uid.
94      * @return The names of failed packages.
95      */
96     @Nullable
setPackagesSuspended(@onNull Computer snapshot, @Nullable String[] packageNames, boolean suspended, @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo, @NonNull UserPackage suspendingPackage, @UserIdInt int targetUserId, int callingUid, boolean quarantined)97     String[] setPackagesSuspended(@NonNull Computer snapshot, @Nullable String[] packageNames,
98             boolean suspended, @Nullable PersistableBundle appExtras,
99             @Nullable PersistableBundle launcherExtras, @Nullable SuspendDialogInfo dialogInfo,
100             @NonNull UserPackage suspendingPackage, @UserIdInt int targetUserId, int callingUid,
101             boolean quarantined) {
102         if (ArrayUtils.isEmpty(packageNames)) {
103             return packageNames;
104         }
105         if (suspended && !quarantined
106                 && !isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) {
107             Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId);
108             return packageNames;
109         }
110 
111         final SuspendParams newSuspendParams = suspended
112                 ? new SuspendParams(dialogInfo, appExtras, launcherExtras, quarantined) : null;
113 
114         final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length);
115 
116         final List<String> notifyPackagesList = new ArrayList<>(packageNames.length);
117         final IntArray notifyUids = new IntArray(packageNames.length);
118         final ArraySet<String> changedPackagesList = new ArraySet<>(packageNames.length);
119         final IntArray changedUids = new IntArray(packageNames.length);
120 
121         final boolean[] canSuspend = suspended
122                 ? canSuspendPackageForUser(snapshot, packageNames, targetUserId, callingUid)
123                 : null;
124         for (int i = 0; i < packageNames.length; i++) {
125             final String packageName = packageNames[i];
126             if (suspendingPackage.packageName.equals(packageName)
127                     && suspendingPackage.userId == targetUserId) {
128                 Slog.w(TAG, "Suspending package: " + suspendingPackage + " trying to "
129                         + (suspended ? "" : "un") + "suspend itself. Ignoring");
130                 unmodifiablePackages.add(packageName);
131                 continue;
132             }
133             final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
134             if (packageState == null
135                     || !packageState.getUserStateOrDefault(targetUserId).isInstalled()
136                     || snapshot.shouldFilterApplication(packageState, callingUid, targetUserId)) {
137                 Slog.w(TAG, "Could not find package setting for package: " + packageName
138                         + ". Skipping suspending/un-suspending.");
139                 unmodifiablePackages.add(packageName);
140                 continue;
141             }
142             if (canSuspend != null && !canSuspend[i]) {
143                 unmodifiablePackages.add(packageName);
144                 continue;
145             }
146 
147             final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
148                     packageState.getUserStateOrDefault(targetUserId).getSuspendParams();
149             final SuspendParams oldSuspendParams = suspendParamsMap == null
150                     ? null : suspendParamsMap.get(suspendingPackage);
151             boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams);
152 
153             if (suspended && !changed) {
154                 // Carried over API behavior, must notify change even if no change
155                 notifyPackagesList.add(packageName);
156                 notifyUids.add(
157                         UserHandle.getUid(targetUserId, packageState.getAppId()));
158                 continue;
159             }
160 
161             // If only the suspendingPackage is suspending this package,
162             // it will be unsuspended when this change is committed
163             boolean packageUnsuspended = !suspended
164                     && CollectionUtils.size(suspendParamsMap) == 1
165                     && suspendParamsMap.containsKey(suspendingPackage);
166             if (suspended || packageUnsuspended) {
167                 // Always notify of a suspend call + notify when fully unsuspended
168                 notifyPackagesList.add(packageName);
169                 notifyUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
170             }
171 
172             if (changed) {
173                 changedPackagesList.add(packageName);
174                 changedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
175             } else {
176                 Slog.w(TAG, "No change is needed for package: " + packageName
177                         + ". Skipping suspending/un-suspending.");
178             }
179         }
180 
181         mPm.commitPackageStateMutation(null, mutator -> {
182             final int size = changedPackagesList.size();
183             for (int index = 0; index < size; index++) {
184                 final String packageName  = changedPackagesList.valueAt(index);
185                 final PackageUserStateWrite userState = mutator.forPackage(packageName)
186                         .userState(targetUserId);
187                 if (suspended) {
188                     userState.putSuspendParams(suspendingPackage, newSuspendParams);
189                 } else {
190                     userState.removeSuspension(suspendingPackage);
191                 }
192             }
193         });
194 
195         if (!notifyPackagesList.isEmpty()) {
196             final String[] changedPackages =
197                     notifyPackagesList.toArray(new String[0]);
198             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
199                     suspended ? Intent.ACTION_PACKAGES_SUSPENDED
200                             : Intent.ACTION_PACKAGES_UNSUSPENDED,
201                     changedPackages, notifyUids.toArray(), quarantined, targetUserId);
202             mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(mPm::snapshotComputer,
203                     changedPackages, suspended, targetUserId);
204             mPm.scheduleWritePackageRestrictions(targetUserId);
205         }
206         // Send the suspension changed broadcast to ensure suspension state is not stale.
207         if (!changedPackagesList.isEmpty()) {
208             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
209                     Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
210                     changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
211                     targetUserId);
212         }
213         return unmodifiablePackages.toArray(new String[0]);
214     }
215 
216     /**
217      * Returns the names in the {@code packageNames} which can not be suspended by the caller.
218      *
219      * @param packageNames The names of packages to check.
220      * @param targetUserId The user where packages reside.
221      * @param callingUid The caller's uid.
222      * @return The names of packages which are Unsuspendable.
223      */
224     @NonNull
getUnsuspendablePackagesForUser(@onNull Computer snapshot, @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid)225     String[] getUnsuspendablePackagesForUser(@NonNull Computer snapshot,
226             @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid) {
227         if (!isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) {
228             Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId);
229             return packageNames;
230         }
231         final ArraySet<String> unactionablePackages = new ArraySet<>();
232         final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, targetUserId,
233                 callingUid);
234         for (int i = 0; i < packageNames.length; i++) {
235             if (!canSuspend[i]) {
236                 unactionablePackages.add(packageNames[i]);
237                 continue;
238             }
239             final PackageStateInternal packageState =
240                     snapshot.getPackageStateForInstalledAndFiltered(
241                             packageNames[i], callingUid, targetUserId);
242             if (packageState == null) {
243                 Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
244                 unactionablePackages.add(packageNames[i]);
245             }
246         }
247         return unactionablePackages.toArray(new String[unactionablePackages.size()]);
248     }
249 
250     /**
251      * Returns the app extras of the given suspended package.
252      *
253      * @param packageName The suspended package name.
254      * @param userId The user where the package resides.
255      * @param callingUid The caller's uid.
256      * @return The app extras of the suspended package.
257      */
258     @Nullable
getSuspendedPackageAppExtras(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)259     static Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot,
260                                                @NonNull String packageName,
261                                                int userId,
262                                                int callingUid) {
263         final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, callingUid);
264         if (ps == null) {
265             return null;
266         }
267         final PackageUserStateInternal pus = ps.getUserStateOrDefault(userId);
268         final Bundle allExtras = new Bundle();
269         if (pus.isSuspended()) {
270             for (int i = 0; i < pus.getSuspendParams().size(); i++) {
271                 final SuspendParams params = pus.getSuspendParams().valueAt(i);
272                 if (params != null && params.getAppExtras() != null) {
273                     allExtras.putAll(params.getAppExtras());
274                 }
275             }
276         }
277         return (allExtras.size() > 0) ? allExtras : null;
278     }
279 
280     /**
281      * Removes any suspensions on given packages that were added by packages that pass the given
282      * predicate.
283      *
284      * <p> Caller must flush package restrictions if it cares about immediate data consistency.
285      *
286      * @param packagesToChange The packages on which the suspension are to be removed.
287      * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
288      *                                   suspensions will be removed.
289      * @param targetUserId The user for which the changes are taking place.
290      */
removeSuspensionsBySuspendingPackage(@onNull Computer snapshot, @NonNull String[] packagesToChange, @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId)291     void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot,
292             @NonNull String[] packagesToChange,
293             @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId) {
294         final List<String> unsuspendedPackages = new ArrayList<>();
295         final IntArray unsuspendedUids = new IntArray();
296         final ArrayMap<String, ArraySet<UserPackage>> pkgToSuspendingPkgsToCommit =
297                 new ArrayMap<>();
298         for (String packageName : packagesToChange) {
299             final PackageStateInternal packageState =
300                     snapshot.getPackageStateInternal(packageName);
301             final PackageUserStateInternal packageUserState = packageState == null
302                     ? null : packageState.getUserStateOrDefault(targetUserId);
303             if (packageUserState == null || !packageUserState.isSuspended()) {
304                 continue;
305             }
306 
307             WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
308                     packageUserState.getSuspendParams();
309             int countRemoved = 0;
310             for (int index = 0; index < suspendParamsMap.size(); index++) {
311                 UserPackage suspendingPackage = suspendParamsMap.keyAt(index);
312                 if (suspendingPackagePredicate.test(suspendingPackage)) {
313                     ArraySet<UserPackage> suspendingPkgsToCommit =
314                             pkgToSuspendingPkgsToCommit.get(packageName);
315                     if (suspendingPkgsToCommit == null) {
316                         suspendingPkgsToCommit = new ArraySet<>();
317                         pkgToSuspendingPkgsToCommit.put(packageName, suspendingPkgsToCommit);
318                     }
319                     suspendingPkgsToCommit.add(suspendingPackage);
320                     countRemoved++;
321                 }
322             }
323 
324             // Everything would be removed and package unsuspended
325             if (countRemoved == suspendParamsMap.size()) {
326                 unsuspendedPackages.add(packageState.getPackageName());
327                 unsuspendedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
328             }
329         }
330 
331         mPm.commitPackageStateMutation(null, mutator -> {
332             for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
333                 String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex);
334                 ArraySet<UserPackage> packagesToRemove =
335                         pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
336                 PackageUserStateWrite userState =
337                         mutator.forPackage(packageName).userState(targetUserId);
338                 for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) {
339                     userState.removeSuspension(packagesToRemove.valueAt(setIndex));
340                 }
341             }
342         });
343 
344         mPm.scheduleWritePackageRestrictions(targetUserId);
345         if (!unsuspendedPackages.isEmpty()) {
346             final String[] packageArray = unsuspendedPackages.toArray(
347                     new String[unsuspendedPackages.size()]);
348             mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(mPm::snapshotComputer,
349                     packageArray, false, targetUserId);
350             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(mPm::snapshotComputer,
351                     Intent.ACTION_PACKAGES_UNSUSPENDED,
352                     packageArray, unsuspendedUids.toArray(), false, targetUserId);
353         }
354     }
355 
356     /**
357      * Returns the launcher extras for the given suspended package.
358      *
359      * @param packageName The name of the suspended package.
360      * @param userId The user where the package resides.
361      * @param callingUid The caller's uid.
362      * @return The launcher extras.
363      */
364     @Nullable
getSuspendedPackageLauncherExtras(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)365     Bundle getSuspendedPackageLauncherExtras(@NonNull Computer snapshot,
366             @NonNull String packageName, int userId, int callingUid) {
367         final PackageStateInternal packageState =
368                 snapshot.getPackageStateInternal(packageName, callingUid);
369         if (packageState == null) {
370             return null;
371         }
372         Bundle allExtras = new Bundle();
373         PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
374         if (userState.isSuspended()) {
375             for (int i = 0; i < userState.getSuspendParams().size(); i++) {
376                 final SuspendParams params = userState.getSuspendParams().valueAt(i);
377                 if (params != null && params.getLauncherExtras() != null) {
378                     allExtras.putAll(params.getLauncherExtras());
379                 }
380             }
381         }
382         return (allExtras.size() > 0) ? allExtras : null;
383     }
384 
385     /**
386      * Return {@code true}, if the given package is suspended.
387      *
388      * @param packageName The name of package to check.
389      * @param userId The user where the package resides.
390      * @param callingUid The caller's uid.
391      * @return {@code true}, if the given package is suspended.
392      */
isPackageSuspended(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)393     boolean isPackageSuspended(@NonNull Computer snapshot, @NonNull String packageName, int userId,
394             int callingUid) {
395         final PackageStateInternal packageState =
396                 snapshot.getPackageStateInternal(packageName, callingUid);
397         return packageState != null && packageState.getUserStateOrDefault(userId)
398                 .isSuspended();
399     }
400 
401     /**
402      * Given a suspended package, returns the name of package which invokes suspending to it.
403      *
404      * @param suspendedPackage The suspended package to check.
405      * @param userId The user where the package resides.
406      * @param callingUid The caller's uid.
407      * @return The name of suspending package.
408      */
409     @Nullable
getSuspendingPackage(@onNull Computer snapshot, @NonNull String suspendedPackage, int userId, int callingUid)410     UserPackage getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage,
411             int userId, int callingUid) {
412         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
413                 suspendedPackage, callingUid);
414         if (packageState == null) {
415             return  null;
416         }
417 
418         final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
419         if (!userState.isSuspended()) {
420             return null;
421         }
422 
423         UserPackage suspendingPackage = null;
424         UserPackage suspendedBySystem = null;
425         UserPackage qasPackage = null;
426         for (int i = 0; i < userState.getSuspendParams().size(); i++) {
427             suspendingPackage = userState.getSuspendParams().keyAt(i);
428             var suspendParams = userState.getSuspendParams().valueAt(i);
429             if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) {
430                 suspendedBySystem = suspendingPackage;
431             }
432             if (suspendParams.isQuarantined() && qasPackage == null) {
433                 qasPackage = suspendingPackage;
434             }
435         }
436         // Precedence: quarantined, then system, then suspending.
437         if (qasPackage != null) {
438             return qasPackage;
439         }
440         if (suspendedBySystem != null) {
441             return suspendedBySystem;
442         }
443         return suspendingPackage;
444     }
445 
446     /**
447      *  Returns the dialog info of the given suspended package.
448      *
449      * @param suspendedPackage The name of the suspended package.
450      * @param suspendingPackage The name of the suspending package.
451      * @param userId The user where the package resides.
452      * @param callingUid The caller's uid.
453      * @return The dialog info.
454      */
455     @Nullable
getSuspendedDialogInfo(@onNull Computer snapshot, @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId, int callingUid)456     SuspendDialogInfo getSuspendedDialogInfo(@NonNull Computer snapshot,
457             @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId,
458             int callingUid) {
459         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
460                 suspendedPackage, callingUid);
461         if (packageState == null) {
462             return  null;
463         }
464 
465         final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
466         if (!userState.isSuspended()) {
467             return null;
468         }
469 
470         final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
471                 userState.getSuspendParams();
472         if (suspendParamsMap == null) {
473             return null;
474         }
475 
476         final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
477         return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
478     }
479 
480     /**
481      * Return {@code true} if the user is allowed to suspend packages by the caller.
482      *
483      * @param userId The user id to check.
484      * @param callingUid The caller's uid.
485      * @return {@code true} if the user is allowed to suspend packages by the caller.
486      */
isSuspendAllowedForUser(@onNull Computer snapshot, int userId, int callingUid)487     boolean isSuspendAllowedForUser(@NonNull Computer snapshot, int userId, int callingUid) {
488         final UserManagerService userManager = mInjector.getUserManagerService();
489         return isCallerDeviceOrProfileOwner(snapshot, userId, callingUid)
490                 || (!userManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
491                 && !userManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
492     }
493 
494     /**
495      * Returns an array of booleans, such that the ith boolean denotes whether the ith package can
496      * be suspended or not.
497      *
498      * @param packageNames  The package names to check suspendability for.
499      * @param targetUserId The user to check in
500      * @param callingUid The caller's uid.
501      * @return An array containing results of the checks
502      */
503     @NonNull
canSuspendPackageForUser(@onNull Computer snapshot, @NonNull String[] packageNames, int targetUserId, int callingUid)504     boolean[] canSuspendPackageForUser(@NonNull Computer snapshot, @NonNull String[] packageNames,
505             int targetUserId, int callingUid) {
506         final boolean[] canSuspend = new boolean[packageNames.length];
507         final boolean isCallerOwner =
508                 isCallerDeviceOrProfileOwner(snapshot, targetUserId, callingUid);
509         final long token = Binder.clearCallingIdentity();
510         try {
511             final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider();
512             final String activeLauncherPackageName =
513                     defaultAppProvider.getDefaultHome(targetUserId);
514             final String dialerPackageName = defaultAppProvider.getDefaultDialer(targetUserId);
515             final String requiredInstallerPackage =
516                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, targetUserId);
517             final String requiredUninstallerPackage =
518                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, targetUserId);
519             final String requiredVerifierPackage =
520                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, targetUserId);
521             final String requiredPermissionControllerPackage =
522                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
523                             targetUserId);
524             for (int i = 0; i < packageNames.length; i++) {
525                 canSuspend[i] = false;
526                 final String packageName = packageNames[i];
527 
528                 if (mPm.isPackageDeviceAdmin(packageName, targetUserId)) {
529                     Slog.w(TAG, "Cannot suspend package \"" + packageName
530                             + "\": has an active device admin");
531                     continue;
532                 }
533                 if (packageName.equals(activeLauncherPackageName)) {
534                     Slog.w(TAG, "Cannot suspend package \"" + packageName
535                             + "\": contains the active launcher");
536                     continue;
537                 }
538                 if (packageName.equals(requiredInstallerPackage)) {
539                     Slog.w(TAG, "Cannot suspend package \"" + packageName
540                             + "\": required for package installation");
541                     continue;
542                 }
543                 if (packageName.equals(requiredUninstallerPackage)) {
544                     Slog.w(TAG, "Cannot suspend package \"" + packageName
545                             + "\": required for package uninstallation");
546                     continue;
547                 }
548                 if (packageName.equals(requiredVerifierPackage)) {
549                     Slog.w(TAG, "Cannot suspend package \"" + packageName
550                             + "\": required for package verification");
551                     continue;
552                 }
553                 if (packageName.equals(dialerPackageName)) {
554                     Slog.w(TAG, "Cannot suspend package \"" + packageName
555                             + "\": is the default dialer");
556                     continue;
557                 }
558                 if (packageName.equals(requiredPermissionControllerPackage)) {
559                     Slog.w(TAG, "Cannot suspend package \"" + packageName
560                             + "\": required for permissions management");
561                     continue;
562                 }
563                 if (mProtectedPackages.isPackageStateProtected(targetUserId, packageName)) {
564                     Slog.w(TAG, "Cannot suspend package \"" + packageName
565                             + "\": protected package");
566                     continue;
567                 }
568                 if (!isCallerOwner && snapshot.getBlockUninstall(targetUserId, packageName)) {
569                     Slog.w(TAG, "Cannot suspend package \"" + packageName
570                             + "\": blocked by admin");
571                     continue;
572                 }
573 
574                 // Cannot suspend static shared libs as they are considered
575                 // a part of the using app (emulating static linking). Also
576                 // static libs are installed always on internal storage.
577                 PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
578                 AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
579                 if (pkg != null) {
580                     final int uid = UserHandle.getUid(targetUserId, packageState.getAppId());
581                     // Cannot suspend SDK libs as they are controlled by SDK manager.
582                     if (pkg.isSdkLibrary()) {
583                         Slog.w(TAG, "Cannot suspend package: " + packageName
584                                 + " providing SDK library: "
585                                 + pkg.getSdkLibraryName());
586                         continue;
587                     }
588                     // Cannot suspend static shared libs as they are considered
589                     // a part of the using app (emulating static linking). Also
590                     // static libs are installed always on internal storage.
591                     if (pkg.isStaticSharedLibrary()) {
592                         Slog.w(TAG, "Cannot suspend package: " + packageName
593                                 + " providing static shared library: "
594                                 + pkg.getStaticSharedLibraryName());
595                         continue;
596                     }
597                     if (exemptFromSuspensionByAppOp(uid, packageName)) {
598                         Slog.w(TAG, "Cannot suspend package \"" + packageName
599                                 + "\": has OP_SYSTEM_EXEMPT_FROM_SUSPENSION set");
600                         continue;
601                     }
602                 }
603                 if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
604                     Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
605                     continue;
606                 }
607                 canSuspend[i] = true;
608             }
609         } finally {
610             Binder.restoreCallingIdentity(token);
611         }
612         return canSuspend;
613     }
614 
exemptFromSuspensionByAppOp(int uid, String packageName)615     private boolean exemptFromSuspensionByAppOp(int uid, String packageName) {
616         final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class);
617         return appOpsManager.checkOpNoThrow(
618                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName)
619                         == AppOpsManager.MODE_ALLOWED;
620     }
621 
getKnownPackageName(@onNull Computer snapshot, @KnownPackages.KnownPackage int knownPackage, int userId)622     private String getKnownPackageName(@NonNull Computer snapshot,
623             @KnownPackages.KnownPackage int knownPackage, int userId) {
624         final String[] knownPackages =
625                 mPm.getKnownPackageNamesInternal(snapshot, knownPackage, userId);
626         return knownPackages.length > 0 ? knownPackages[0] : null;
627     }
628 
isCallerDeviceOrProfileOwner(@onNull Computer snapshot, int targetUserId, int callingUid)629     private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int targetUserId,
630             int callingUid) {
631         if (callingUid == SYSTEM_UID) {
632             return true;
633         }
634         final String ownerPackage =
635                 mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId);
636         if (ownerPackage != null) {
637             return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, targetUserId,
638                     callingUid);
639         }
640         return false;
641     }
642 }
643