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