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