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