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         final Computer newSnapshot = mPm.snapshotComputer();
196         if (!notifyPackagesList.isEmpty()) {
197             final String[] changedPackages =
198                     notifyPackagesList.toArray(new String[0]);
199             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
200                     suspended ? Intent.ACTION_PACKAGES_SUSPENDED
201                             : Intent.ACTION_PACKAGES_UNSUSPENDED,
202                     changedPackages, notifyUids.toArray(), quarantined, targetUserId);
203             mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
204                     suspended, targetUserId);
205             mPm.scheduleWritePackageRestrictions(targetUserId);
206         }
207         // Send the suspension changed broadcast to ensure suspension state is not stale.
208         if (!changedPackagesList.isEmpty()) {
209             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
210                     Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
211                     changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
212                     targetUserId);
213         }
214         return unmodifiablePackages.toArray(new String[0]);
215     }
216 
217     /**
218      * Returns the names in the {@code packageNames} which can not be suspended by the caller.
219      *
220      * @param packageNames The names of packages to check.
221      * @param targetUserId The user where packages reside.
222      * @param callingUid The caller's uid.
223      * @return The names of packages which are Unsuspendable.
224      */
225     @NonNull
getUnsuspendablePackagesForUser(@onNull Computer snapshot, @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid)226     String[] getUnsuspendablePackagesForUser(@NonNull Computer snapshot,
227             @NonNull String[] packageNames, @UserIdInt int targetUserId, int callingUid) {
228         if (!isSuspendAllowedForUser(snapshot, targetUserId, callingUid)) {
229             Slog.w(TAG, "Cannot suspend due to restrictions on user " + targetUserId);
230             return packageNames;
231         }
232         final ArraySet<String> unactionablePackages = new ArraySet<>();
233         final boolean[] canSuspend = canSuspendPackageForUser(snapshot, packageNames, targetUserId,
234                 callingUid);
235         for (int i = 0; i < packageNames.length; i++) {
236             if (!canSuspend[i]) {
237                 unactionablePackages.add(packageNames[i]);
238                 continue;
239             }
240             final PackageStateInternal packageState =
241                     snapshot.getPackageStateForInstalledAndFiltered(
242                             packageNames[i], callingUid, targetUserId);
243             if (packageState == null) {
244                 Slog.w(TAG, "Could not find package setting for package: " + packageNames[i]);
245                 unactionablePackages.add(packageNames[i]);
246             }
247         }
248         return unactionablePackages.toArray(new String[unactionablePackages.size()]);
249     }
250 
251     /**
252      * Returns the app extras of the given suspended package.
253      *
254      * @param packageName The suspended package name.
255      * @param userId The user where the package resides.
256      * @param callingUid The caller's uid.
257      * @return The app extras of the suspended package.
258      */
259     @Nullable
getSuspendedPackageAppExtras(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)260     static Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot,
261                                                @NonNull String packageName,
262                                                int userId,
263                                                int callingUid) {
264         final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, callingUid);
265         if (ps == null) {
266             return null;
267         }
268         final PackageUserStateInternal pus = ps.getUserStateOrDefault(userId);
269         final Bundle allExtras = new Bundle();
270         if (pus.isSuspended()) {
271             for (int i = 0; i < pus.getSuspendParams().size(); i++) {
272                 final SuspendParams params = pus.getSuspendParams().valueAt(i);
273                 if (params != null && params.getAppExtras() != null) {
274                     allExtras.putAll(params.getAppExtras());
275                 }
276             }
277         }
278         return (allExtras.size() > 0) ? allExtras : null;
279     }
280 
281     /**
282      * Removes any suspensions on given packages that were added by packages that pass the given
283      * predicate.
284      *
285      * <p> Caller must flush package restrictions if it cares about immediate data consistency.
286      *
287      * @param packagesToChange The packages on which the suspension are to be removed.
288      * @param suspendingPackagePredicate A predicate identifying the suspending packages whose
289      *                                   suspensions will be removed.
290      * @param targetUserId The user for which the changes are taking place.
291      */
removeSuspensionsBySuspendingPackage(@onNull Computer snapshot, @NonNull String[] packagesToChange, @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId)292     void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot,
293             @NonNull String[] packagesToChange,
294             @NonNull Predicate<UserPackage> suspendingPackagePredicate, int targetUserId) {
295         final List<String> unsuspendedPackages = new ArrayList<>();
296         final IntArray unsuspendedUids = new IntArray();
297         final ArrayMap<String, ArraySet<UserPackage>> pkgToSuspendingPkgsToCommit =
298                 new ArrayMap<>();
299         for (String packageName : packagesToChange) {
300             final PackageStateInternal packageState =
301                     snapshot.getPackageStateInternal(packageName);
302             final PackageUserStateInternal packageUserState = packageState == null
303                     ? null : packageState.getUserStateOrDefault(targetUserId);
304             if (packageUserState == null || !packageUserState.isSuspended()) {
305                 continue;
306             }
307 
308             WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
309                     packageUserState.getSuspendParams();
310             int countRemoved = 0;
311             for (int index = 0; index < suspendParamsMap.size(); index++) {
312                 UserPackage suspendingPackage = suspendParamsMap.keyAt(index);
313                 if (suspendingPackagePredicate.test(suspendingPackage)) {
314                     ArraySet<UserPackage> suspendingPkgsToCommit =
315                             pkgToSuspendingPkgsToCommit.get(packageName);
316                     if (suspendingPkgsToCommit == null) {
317                         suspendingPkgsToCommit = new ArraySet<>();
318                         pkgToSuspendingPkgsToCommit.put(packageName, suspendingPkgsToCommit);
319                     }
320                     suspendingPkgsToCommit.add(suspendingPackage);
321                     countRemoved++;
322                 }
323             }
324 
325             // Everything would be removed and package unsuspended
326             if (countRemoved == suspendParamsMap.size()) {
327                 unsuspendedPackages.add(packageState.getPackageName());
328                 unsuspendedUids.add(UserHandle.getUid(targetUserId, packageState.getAppId()));
329             }
330         }
331 
332         mPm.commitPackageStateMutation(null, mutator -> {
333             for (int mapIndex = 0; mapIndex < pkgToSuspendingPkgsToCommit.size(); mapIndex++) {
334                 String packageName = pkgToSuspendingPkgsToCommit.keyAt(mapIndex);
335                 ArraySet<UserPackage> packagesToRemove =
336                         pkgToSuspendingPkgsToCommit.valueAt(mapIndex);
337                 PackageUserStateWrite userState =
338                         mutator.forPackage(packageName).userState(targetUserId);
339                 for (int setIndex = 0; setIndex < packagesToRemove.size(); setIndex++) {
340                     userState.removeSuspension(packagesToRemove.valueAt(setIndex));
341                 }
342             }
343         });
344 
345         mPm.scheduleWritePackageRestrictions(targetUserId);
346         final Computer newSnapshot = mPm.snapshotComputer();
347         if (!unsuspendedPackages.isEmpty()) {
348             final String[] packageArray = unsuspendedPackages.toArray(
349                     new String[unsuspendedPackages.size()]);
350             mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
351                     false, targetUserId);
352             mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
353                     Intent.ACTION_PACKAGES_UNSUSPENDED,
354                     packageArray, unsuspendedUids.toArray(), false, targetUserId);
355         }
356     }
357 
358     /**
359      * Returns the launcher extras for the given suspended package.
360      *
361      * @param packageName The name of the suspended package.
362      * @param userId The user where the package resides.
363      * @param callingUid The caller's uid.
364      * @return The launcher extras.
365      */
366     @Nullable
getSuspendedPackageLauncherExtras(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)367     Bundle getSuspendedPackageLauncherExtras(@NonNull Computer snapshot,
368             @NonNull String packageName, int userId, int callingUid) {
369         final PackageStateInternal packageState =
370                 snapshot.getPackageStateInternal(packageName, callingUid);
371         if (packageState == null) {
372             return null;
373         }
374         Bundle allExtras = new Bundle();
375         PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
376         if (userState.isSuspended()) {
377             for (int i = 0; i < userState.getSuspendParams().size(); i++) {
378                 final SuspendParams params = userState.getSuspendParams().valueAt(i);
379                 if (params != null && params.getLauncherExtras() != null) {
380                     allExtras.putAll(params.getLauncherExtras());
381                 }
382             }
383         }
384         return (allExtras.size() > 0) ? allExtras : null;
385     }
386 
387     /**
388      * Return {@code true}, if the given package is suspended.
389      *
390      * @param packageName The name of package to check.
391      * @param userId The user where the package resides.
392      * @param callingUid The caller's uid.
393      * @return {@code true}, if the given package is suspended.
394      */
isPackageSuspended(@onNull Computer snapshot, @NonNull String packageName, int userId, int callingUid)395     boolean isPackageSuspended(@NonNull Computer snapshot, @NonNull String packageName, int userId,
396             int callingUid) {
397         final PackageStateInternal packageState =
398                 snapshot.getPackageStateInternal(packageName, callingUid);
399         return packageState != null && packageState.getUserStateOrDefault(userId)
400                 .isSuspended();
401     }
402 
403     /**
404      * Given a suspended package, returns the name of package which invokes suspending to it.
405      *
406      * @param suspendedPackage The suspended package to check.
407      * @param userId The user where the package resides.
408      * @param callingUid The caller's uid.
409      * @return The name of suspending package.
410      */
411     @Nullable
getSuspendingPackage(@onNull Computer snapshot, @NonNull String suspendedPackage, int userId, int callingUid)412     UserPackage getSuspendingPackage(@NonNull Computer snapshot, @NonNull String suspendedPackage,
413             int userId, int callingUid) {
414         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
415                 suspendedPackage, callingUid);
416         if (packageState == null) {
417             return  null;
418         }
419 
420         final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
421         if (!userState.isSuspended()) {
422             return null;
423         }
424 
425         UserPackage suspendingPackage = null;
426         UserPackage suspendedBySystem = null;
427         UserPackage qasPackage = null;
428         for (int i = 0; i < userState.getSuspendParams().size(); i++) {
429             suspendingPackage = userState.getSuspendParams().keyAt(i);
430             var suspendParams = userState.getSuspendParams().valueAt(i);
431             if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage.packageName)) {
432                 suspendedBySystem = suspendingPackage;
433             }
434             if (suspendParams.isQuarantined() && qasPackage == null) {
435                 qasPackage = suspendingPackage;
436             }
437         }
438         // Precedence: quarantined, then system, then suspending.
439         if (qasPackage != null) {
440             return qasPackage;
441         }
442         if (suspendedBySystem != null) {
443             return suspendedBySystem;
444         }
445         return suspendingPackage;
446     }
447 
448     /**
449      *  Returns the dialog info of the given suspended package.
450      *
451      * @param suspendedPackage The name of the suspended package.
452      * @param suspendingPackage The name of the suspending package.
453      * @param userId The user where the package resides.
454      * @param callingUid The caller's uid.
455      * @return The dialog info.
456      */
457     @Nullable
getSuspendedDialogInfo(@onNull Computer snapshot, @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId, int callingUid)458     SuspendDialogInfo getSuspendedDialogInfo(@NonNull Computer snapshot,
459             @NonNull String suspendedPackage, @NonNull UserPackage suspendingPackage, int userId,
460             int callingUid) {
461         final PackageStateInternal packageState = snapshot.getPackageStateInternal(
462                 suspendedPackage, callingUid);
463         if (packageState == null) {
464             return  null;
465         }
466 
467         final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
468         if (!userState.isSuspended()) {
469             return null;
470         }
471 
472         final WatchedArrayMap<UserPackage, SuspendParams> suspendParamsMap =
473                 userState.getSuspendParams();
474         if (suspendParamsMap == null) {
475             return null;
476         }
477 
478         final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
479         return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
480     }
481 
482     /**
483      * Return {@code true} if the user is allowed to suspend packages by the caller.
484      *
485      * @param userId The user id to check.
486      * @param callingUid The caller's uid.
487      * @return {@code true} if the user is allowed to suspend packages by the caller.
488      */
isSuspendAllowedForUser(@onNull Computer snapshot, int userId, int callingUid)489     boolean isSuspendAllowedForUser(@NonNull Computer snapshot, int userId, int callingUid) {
490         final UserManagerService userManager = mInjector.getUserManagerService();
491         return isCallerDeviceOrProfileOwner(snapshot, userId, callingUid)
492                 || (!userManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)
493                 && !userManager.hasUserRestriction(UserManager.DISALLOW_UNINSTALL_APPS, userId));
494     }
495 
496     /**
497      * Returns an array of booleans, such that the ith boolean denotes whether the ith package can
498      * be suspended or not.
499      *
500      * @param packageNames  The package names to check suspendability for.
501      * @param targetUserId The user to check in
502      * @param callingUid The caller's uid.
503      * @return An array containing results of the checks
504      */
505     @NonNull
canSuspendPackageForUser(@onNull Computer snapshot, @NonNull String[] packageNames, int targetUserId, int callingUid)506     boolean[] canSuspendPackageForUser(@NonNull Computer snapshot, @NonNull String[] packageNames,
507             int targetUserId, int callingUid) {
508         final boolean[] canSuspend = new boolean[packageNames.length];
509         final boolean isCallerOwner =
510                 isCallerDeviceOrProfileOwner(snapshot, targetUserId, callingUid);
511         final long token = Binder.clearCallingIdentity();
512         try {
513             final DefaultAppProvider defaultAppProvider = mInjector.getDefaultAppProvider();
514             final String activeLauncherPackageName =
515                     defaultAppProvider.getDefaultHome(targetUserId);
516             final String dialerPackageName = defaultAppProvider.getDefaultDialer(targetUserId);
517             final String requiredInstallerPackage =
518                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_INSTALLER, targetUserId);
519             final String requiredUninstallerPackage =
520                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_UNINSTALLER, targetUserId);
521             final String requiredVerifierPackage =
522                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_VERIFIER, targetUserId);
523             final String requiredPermissionControllerPackage =
524                     getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
525                             targetUserId);
526             for (int i = 0; i < packageNames.length; i++) {
527                 canSuspend[i] = false;
528                 final String packageName = packageNames[i];
529 
530                 if (mPm.isPackageDeviceAdmin(packageName, targetUserId)) {
531                     Slog.w(TAG, "Cannot suspend package \"" + packageName
532                             + "\": has an active device admin");
533                     continue;
534                 }
535                 if (packageName.equals(activeLauncherPackageName)) {
536                     Slog.w(TAG, "Cannot suspend package \"" + packageName
537                             + "\": contains the active launcher");
538                     continue;
539                 }
540                 if (packageName.equals(requiredInstallerPackage)) {
541                     Slog.w(TAG, "Cannot suspend package \"" + packageName
542                             + "\": required for package installation");
543                     continue;
544                 }
545                 if (packageName.equals(requiredUninstallerPackage)) {
546                     Slog.w(TAG, "Cannot suspend package \"" + packageName
547                             + "\": required for package uninstallation");
548                     continue;
549                 }
550                 if (packageName.equals(requiredVerifierPackage)) {
551                     Slog.w(TAG, "Cannot suspend package \"" + packageName
552                             + "\": required for package verification");
553                     continue;
554                 }
555                 if (packageName.equals(dialerPackageName)) {
556                     Slog.w(TAG, "Cannot suspend package \"" + packageName
557                             + "\": is the default dialer");
558                     continue;
559                 }
560                 if (packageName.equals(requiredPermissionControllerPackage)) {
561                     Slog.w(TAG, "Cannot suspend package \"" + packageName
562                             + "\": required for permissions management");
563                     continue;
564                 }
565                 if (mProtectedPackages.isPackageStateProtected(targetUserId, packageName)) {
566                     Slog.w(TAG, "Cannot suspend package \"" + packageName
567                             + "\": protected package");
568                     continue;
569                 }
570                 if (!isCallerOwner && snapshot.getBlockUninstall(targetUserId, packageName)) {
571                     Slog.w(TAG, "Cannot suspend package \"" + packageName
572                             + "\": blocked by admin");
573                     continue;
574                 }
575 
576                 // Cannot suspend static shared libs as they are considered
577                 // a part of the using app (emulating static linking). Also
578                 // static libs are installed always on internal storage.
579                 PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
580                 AndroidPackage pkg = packageState == null ? null : packageState.getPkg();
581                 if (pkg != null) {
582                     final int uid = UserHandle.getUid(targetUserId, packageState.getAppId());
583                     // Cannot suspend SDK libs as they are controlled by SDK manager.
584                     if (pkg.isSdkLibrary()) {
585                         Slog.w(TAG, "Cannot suspend package: " + packageName
586                                 + " providing SDK library: "
587                                 + pkg.getSdkLibraryName());
588                         continue;
589                     }
590                     // Cannot suspend static shared libs as they are considered
591                     // a part of the using app (emulating static linking). Also
592                     // static libs are installed always on internal storage.
593                     if (pkg.isStaticSharedLibrary()) {
594                         Slog.w(TAG, "Cannot suspend package: " + packageName
595                                 + " providing static shared library: "
596                                 + pkg.getStaticSharedLibraryName());
597                         continue;
598                     }
599                     if (exemptFromSuspensionByAppOp(uid, packageName)) {
600                         Slog.w(TAG, "Cannot suspend package \"" + packageName
601                                 + "\": has OP_SYSTEM_EXEMPT_FROM_SUSPENSION set");
602                         continue;
603                     }
604                 }
605                 if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
606                     Slog.w(TAG, "Cannot suspend the platform package: " + packageName);
607                     continue;
608                 }
609                 canSuspend[i] = true;
610             }
611         } finally {
612             Binder.restoreCallingIdentity(token);
613         }
614         return canSuspend;
615     }
616 
exemptFromSuspensionByAppOp(int uid, String packageName)617     private boolean exemptFromSuspensionByAppOp(int uid, String packageName) {
618         final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class);
619         return appOpsManager.checkOpNoThrow(
620                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName)
621                         == AppOpsManager.MODE_ALLOWED;
622     }
623 
getKnownPackageName(@onNull Computer snapshot, @KnownPackages.KnownPackage int knownPackage, int userId)624     private String getKnownPackageName(@NonNull Computer snapshot,
625             @KnownPackages.KnownPackage int knownPackage, int userId) {
626         final String[] knownPackages =
627                 mPm.getKnownPackageNamesInternal(snapshot, knownPackage, userId);
628         return knownPackages.length > 0 ? knownPackages[0] : null;
629     }
630 
isCallerDeviceOrProfileOwner(@onNull Computer snapshot, int targetUserId, int callingUid)631     private boolean isCallerDeviceOrProfileOwner(@NonNull Computer snapshot, int targetUserId,
632             int callingUid) {
633         if (callingUid == SYSTEM_UID) {
634             return true;
635         }
636         final String ownerPackage =
637                 mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId);
638         if (ownerPackage != null) {
639             return callingUid == snapshot.getPackageUidInternal(ownerPackage, 0, targetUserId,
640                     callingUid);
641         }
642         return false;
643     }
644 }
645