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