1 /* 2 * Copyright (C) 2021 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.apphibernation; 18 19 import static android.app.AppOpsManager.OP_NONE; 20 import static android.content.Intent.ACTION_PACKAGE_ADDED; 21 import static android.content.Intent.ACTION_PACKAGE_REMOVED; 22 import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS; 23 import static android.content.Intent.EXTRA_REPLACING; 24 import static android.content.pm.PackageManager.MATCH_ANY_USER; 25 import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; 26 27 import static com.android.server.apphibernation.AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED; 28 29 import android.Manifest; 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.app.Activity; 33 import android.app.ActivityManager; 34 import android.app.ActivityThread; 35 import android.app.IActivityManager; 36 import android.app.StatsManager; 37 import android.app.StatsManager.StatsPullAtomCallback; 38 import android.app.usage.StorageStats; 39 import android.app.usage.StorageStatsManager; 40 import android.app.usage.UsageEvents; 41 import android.app.usage.UsageStatsManagerInternal; 42 import android.app.usage.UsageStatsManagerInternal.UsageEventListener; 43 import android.apphibernation.HibernationStats; 44 import android.apphibernation.IAppHibernationService; 45 import android.content.BroadcastReceiver; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.IntentFilter; 49 import android.content.pm.ApplicationInfo; 50 import android.content.pm.IPackageManager; 51 import android.content.pm.PackageInfo; 52 import android.content.pm.PackageManager; 53 import android.content.pm.PackageManagerInternal; 54 import android.content.pm.UserInfo; 55 import android.os.Binder; 56 import android.os.Environment; 57 import android.os.RemoteException; 58 import android.os.ResultReceiver; 59 import android.os.ServiceManager; 60 import android.os.ShellCallback; 61 import android.os.Trace; 62 import android.os.UserHandle; 63 import android.os.UserManager; 64 import android.provider.DeviceConfig; 65 import android.provider.DeviceConfig.Properties; 66 import android.text.TextUtils; 67 import android.util.ArrayMap; 68 import android.util.ArraySet; 69 import android.util.Slog; 70 import android.util.SparseArray; 71 import android.util.StatsEvent; 72 73 import com.android.internal.annotations.GuardedBy; 74 import com.android.internal.annotations.VisibleForTesting; 75 import com.android.internal.util.DumpUtils; 76 import com.android.internal.util.FrameworkStatsLog; 77 import com.android.internal.util.IndentingPrintWriter; 78 import com.android.server.LocalServices; 79 import com.android.server.SystemService; 80 81 import java.io.File; 82 import java.io.FileDescriptor; 83 import java.io.IOException; 84 import java.io.PrintWriter; 85 import java.util.ArrayList; 86 import java.util.List; 87 import java.util.Map; 88 import java.util.Set; 89 import java.util.concurrent.Executor; 90 import java.util.concurrent.Executors; 91 import java.util.concurrent.ScheduledExecutorService; 92 93 /** 94 * System service that manages app hibernation state, a state apps can enter that means they are 95 * not being actively used and can be optimized for storage. The actual policy for determining 96 * if an app should hibernate is managed by PermissionController code. 97 */ 98 public final class AppHibernationService extends SystemService { 99 private static final String TAG = "AppHibernationService"; 100 private static final int PACKAGE_MATCH_FLAGS = 101 PackageManager.MATCH_DIRECT_BOOT_AWARE 102 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 103 | PackageManager.MATCH_UNINSTALLED_PACKAGES 104 | PackageManager.MATCH_DISABLED_COMPONENTS 105 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS 106 | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS; 107 108 /** 109 * Lock for accessing any in-memory hibernation state 110 */ 111 private final Object mLock = new Object(); 112 private final Context mContext; 113 private final IPackageManager mIPackageManager; 114 private final PackageManagerInternal mPackageManagerInternal; 115 private final IActivityManager mIActivityManager; 116 private final UserManager mUserManager; 117 private final StorageStatsManager mStorageStatsManager; 118 119 @GuardedBy("mLock") 120 private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>(); 121 private final SparseArray<HibernationStateDiskStore<UserLevelState>> mUserDiskStores = 122 new SparseArray<>(); 123 @GuardedBy("mLock") 124 private final Map<String, GlobalLevelState> mGlobalHibernationStates = new ArrayMap<>(); 125 private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore; 126 private final Injector mInjector; 127 private final Executor mBackgroundExecutor; 128 private final boolean mOatArtifactDeletionEnabled; 129 130 @VisibleForTesting 131 public static boolean sIsServiceEnabled; 132 133 /** 134 * Initializes the system service. 135 * <p> 136 * Subclasses must define a single argument constructor that accepts the context 137 * and passes it to super. 138 * </p> 139 * 140 * @param context The system server context. 141 */ AppHibernationService(@onNull Context context)142 public AppHibernationService(@NonNull Context context) { 143 this(new InjectorImpl(context)); 144 } 145 146 @VisibleForTesting AppHibernationService(@onNull Injector injector)147 AppHibernationService(@NonNull Injector injector) { 148 super(injector.getContext()); 149 mContext = injector.getContext(); 150 mIPackageManager = injector.getPackageManager(); 151 mPackageManagerInternal = injector.getPackageManagerInternal(); 152 mIActivityManager = injector.getActivityManager(); 153 mUserManager = injector.getUserManager(); 154 mStorageStatsManager = injector.getStorageStatsManager(); 155 mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore(); 156 mBackgroundExecutor = injector.getBackgroundExecutor(); 157 mOatArtifactDeletionEnabled = injector.isOatArtifactDeletionEnabled(); 158 mInjector = injector; 159 160 final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */); 161 162 IntentFilter intentFilter = new IntentFilter(); 163 intentFilter.addAction(ACTION_PACKAGE_ADDED); 164 intentFilter.addAction(ACTION_PACKAGE_REMOVED); 165 intentFilter.addDataScheme("package"); 166 userAllContext.registerReceiver(mBroadcastReceiver, intentFilter); 167 LocalServices.addService(AppHibernationManagerInternal.class, mLocalService); 168 mInjector.getUsageStatsManagerInternal().registerListener(mUsageEventListener); 169 } 170 171 @Override onStart()172 public void onStart() { 173 publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub); 174 } 175 176 @Override onBootPhase(int phase)177 public void onBootPhase(int phase) { 178 if (phase == PHASE_BOOT_COMPLETED) { 179 mBackgroundExecutor.execute(() -> { 180 List<GlobalLevelState> states = 181 mGlobalLevelHibernationDiskStore.readHibernationStates(); 182 synchronized (mLock) { 183 initializeGlobalHibernationStates(states); 184 } 185 }); 186 } 187 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 188 sIsServiceEnabled = isDeviceConfigAppHibernationEnabled(); 189 DeviceConfig.addOnPropertiesChangedListener( 190 NAMESPACE_APP_HIBERNATION, 191 ActivityThread.currentApplication().getMainExecutor(), 192 this::onDeviceConfigChanged); 193 final StatsManager statsManager = getContext().getSystemService(StatsManager.class); 194 final StatsPullAtomCallbackImpl pullAtomCallback = new StatsPullAtomCallbackImpl(); 195 statsManager.setPullAtomCallback( 196 FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS, 197 /* metadata */ null, // use default PullAtomMetadata values 198 mBackgroundExecutor, 199 pullAtomCallback); 200 statsManager.setPullAtomCallback( 201 FrameworkStatsLog.GLOBAL_HIBERNATED_APPS, 202 /* metadata */ null, // use default PullAtomMetadata values 203 mBackgroundExecutor, 204 pullAtomCallback); 205 } 206 } 207 208 /** 209 * Whether global hibernation should delete ART ahead-of-time compilation artifacts and prevent 210 * package manager from re-optimizing the APK. 211 */ isOatArtifactDeletionEnabled()212 private boolean isOatArtifactDeletionEnabled() { 213 return mOatArtifactDeletionEnabled; 214 } 215 216 /** 217 * Whether a package is hibernating for a given user. 218 * 219 * @param packageName the package to check 220 * @param userId the user to check 221 * @return true if package is hibernating for the user 222 */ isHibernatingForUser(String packageName, int userId)223 boolean isHibernatingForUser(String packageName, int userId) { 224 String methodName = "isHibernatingForUser"; 225 if (!sIsServiceEnabled) { 226 return false; 227 } 228 getContext().enforceCallingOrSelfPermission( 229 android.Manifest.permission.MANAGE_APP_HIBERNATION, 230 "Caller did not have permission while calling " + methodName); 231 userId = handleIncomingUser(userId, methodName); 232 synchronized (mLock) { 233 // Don't log as this method can be called before user states exist as part of the 234 // force-stop check. 235 if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ false)) { 236 return false; 237 } 238 final Map<String, UserLevelState> packageStates = mUserStates.get(userId); 239 final UserLevelState pkgState = packageStates.get(packageName); 240 if (pkgState == null 241 || !mPackageManagerInternal.canQueryPackage( 242 Binder.getCallingUid(), packageName)) { 243 return false; 244 } 245 return pkgState.hibernated; 246 } 247 } 248 249 /** 250 * Whether a package is hibernated globally. This only occurs when a package is hibernating for 251 * all users and allows us to make optimizations at the package or APK level. 252 * 253 * @param packageName package to check 254 */ isHibernatingGlobally(String packageName)255 boolean isHibernatingGlobally(String packageName) { 256 if (!sIsServiceEnabled) { 257 return false; 258 } 259 getContext().enforceCallingOrSelfPermission( 260 android.Manifest.permission.MANAGE_APP_HIBERNATION, 261 "Caller does not have MANAGE_APP_HIBERNATION permission."); 262 synchronized (mLock) { 263 GlobalLevelState state = mGlobalHibernationStates.get(packageName); 264 if (state == null 265 || !mPackageManagerInternal.canQueryPackage( 266 Binder.getCallingUid(), packageName)) { 267 // This API can be legitimately called before installation finishes as part of 268 // dex optimization, so we just return false here. 269 return false; 270 } 271 return state.hibernated; 272 } 273 } 274 275 /** 276 * Set whether the package is hibernating for the given user. 277 * 278 * @param packageName package to modify state 279 * @param userId user 280 * @param isHibernating new hibernation state 281 */ setHibernatingForUser(String packageName, int userId, boolean isHibernating)282 void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { 283 String methodName = "setHibernatingForUser"; 284 if (!sIsServiceEnabled) { 285 return; 286 } 287 getContext().enforceCallingOrSelfPermission( 288 android.Manifest.permission.MANAGE_APP_HIBERNATION, 289 "Caller does not have MANAGE_APP_HIBERNATION permission."); 290 final int realUserId = handleIncomingUser(userId, methodName); 291 synchronized (mLock) { 292 if (!checkUserStatesExist(realUserId, methodName, /* shouldLog= */ true)) { 293 return; 294 } 295 final Map<String, UserLevelState> packageStates = mUserStates.get(realUserId); 296 final UserLevelState pkgState = packageStates.get(packageName); 297 if (pkgState == null 298 || !mPackageManagerInternal.canQueryPackage( 299 Binder.getCallingUid(), packageName)) { 300 Slog.e(TAG, TextUtils.formatSimple("Package %s is not installed for user %s", 301 packageName, realUserId)); 302 return; 303 } 304 305 if (pkgState.hibernated == isHibernating) { 306 return; 307 } 308 309 pkgState.hibernated = isHibernating; 310 if (isHibernating) { 311 mBackgroundExecutor.execute( 312 () -> hibernatePackageForUser(packageName, realUserId, pkgState)); 313 } else { 314 mBackgroundExecutor.execute( 315 () -> unhibernatePackageForUser(packageName, realUserId)); 316 pkgState.lastUnhibernatedMs = System.currentTimeMillis(); 317 } 318 319 final UserLevelState stateSnapshot = new UserLevelState(pkgState); 320 final int userIdSnapshot = realUserId; 321 mBackgroundExecutor.execute(() -> { 322 FrameworkStatsLog.write( 323 FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED, 324 stateSnapshot.packageName, 325 userIdSnapshot, 326 stateSnapshot.hibernated); 327 }); 328 List<UserLevelState> states = new ArrayList<>(mUserStates.get(realUserId).values()); 329 mUserDiskStores.get(realUserId).scheduleWriteHibernationStates(states); 330 } 331 } 332 333 /** 334 * Set whether the package should be hibernated globally at a package level, allowing the 335 * the system to make optimizations at the package or APK level. 336 * 337 * @param packageName package to hibernate globally 338 * @param isHibernating new hibernation state 339 */ setHibernatingGlobally(String packageName, boolean isHibernating)340 void setHibernatingGlobally(String packageName, boolean isHibernating) { 341 if (!sIsServiceEnabled) { 342 return; 343 } 344 getContext().enforceCallingOrSelfPermission( 345 android.Manifest.permission.MANAGE_APP_HIBERNATION, 346 "Caller does not have MANAGE_APP_HIBERNATION permission."); 347 synchronized (mLock) { 348 GlobalLevelState state = mGlobalHibernationStates.get(packageName); 349 if (state == null 350 || !mPackageManagerInternal.canQueryPackage( 351 Binder.getCallingUid(), packageName)) { 352 Slog.e(TAG, TextUtils.formatSimple( 353 "Package %s is not installed for any user", packageName)); 354 return; 355 } 356 if (state.hibernated != isHibernating) { 357 state.hibernated = isHibernating; 358 if (isHibernating) { 359 mBackgroundExecutor.execute(() -> hibernatePackageGlobally(packageName, state)); 360 } else { 361 state.savedByte = 0; 362 state.lastUnhibernatedMs = System.currentTimeMillis(); 363 } 364 List<GlobalLevelState> states = new ArrayList<>(mGlobalHibernationStates.values()); 365 mGlobalLevelHibernationDiskStore.scheduleWriteHibernationStates(states); 366 } 367 } 368 } 369 370 /** 371 * Get the hibernating packages for the given user. This is equivalent to the list of 372 * packages for the user that return true for {@link #isHibernatingForUser}. 373 */ getHibernatingPackagesForUser(int userId)374 @NonNull List<String> getHibernatingPackagesForUser(int userId) { 375 ArrayList<String> hibernatingPackages = new ArrayList<>(); 376 String methodName = "getHibernatingPackagesForUser"; 377 if (!sIsServiceEnabled) { 378 return hibernatingPackages; 379 } 380 getContext().enforceCallingOrSelfPermission( 381 android.Manifest.permission.MANAGE_APP_HIBERNATION, 382 "Caller does not have MANAGE_APP_HIBERNATION permission."); 383 userId = handleIncomingUser(userId, methodName); 384 synchronized (mLock) { 385 if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ true)) { 386 return hibernatingPackages; 387 } 388 Map<String, UserLevelState> userStates = mUserStates.get(userId); 389 for (UserLevelState state : userStates.values()) { 390 String packageName = state.packageName; 391 if (!mPackageManagerInternal.canQueryPackage( 392 Binder.getCallingUid(), packageName)) { 393 // Package is not visible to caller 394 continue; 395 } 396 if (state.hibernated) { 397 hibernatingPackages.add(state.packageName); 398 } 399 } 400 return hibernatingPackages; 401 } 402 } 403 404 /** 405 * Return the stats from app hibernation for each package provided. 406 * 407 * @param packageNames the set of packages to return stats for. Returns all if null 408 * @return map from package to stats for that package 409 */ getHibernationStatsForUser( @ullable Set<String> packageNames, int userId)410 public Map<String, HibernationStats> getHibernationStatsForUser( 411 @Nullable Set<String> packageNames, int userId) { 412 Map<String, HibernationStats> statsMap = new ArrayMap<>(); 413 String methodName = "getHibernationStatsForUser"; 414 if (!sIsServiceEnabled) { 415 return statsMap; 416 } 417 getContext().enforceCallingOrSelfPermission( 418 android.Manifest.permission.MANAGE_APP_HIBERNATION, 419 "Caller does not have MANAGE_APP_HIBERNATION permission."); 420 userId = handleIncomingUser(userId, methodName); 421 synchronized (mLock) { 422 if (!checkUserStatesExist(userId, methodName, /* shouldLog= */ true)) { 423 return statsMap; 424 } 425 final Map<String, UserLevelState> userPackageStates = mUserStates.get(userId); 426 Set<String> pkgs = packageNames != null ? packageNames : userPackageStates.keySet(); 427 for (String pkgName : pkgs) { 428 if (!mPackageManagerInternal.canQueryPackage(Binder.getCallingUid(), pkgName)) { 429 // Package not visible to caller 430 continue; 431 } 432 if (!mGlobalHibernationStates.containsKey(pkgName) 433 || !userPackageStates.containsKey(pkgName)) { 434 Slog.w(TAG, TextUtils.formatSimple( 435 "No hibernation state associated with package %s user %d. Maybe" 436 + "the package was uninstalled? ", pkgName, userId)); 437 continue; 438 } 439 long diskBytesSaved = mGlobalHibernationStates.get(pkgName).savedByte 440 + userPackageStates.get(pkgName).savedByte; 441 HibernationStats stats = new HibernationStats(diskBytesSaved); 442 statsMap.put(pkgName, stats); 443 } 444 } 445 return statsMap; 446 } 447 448 /** 449 * Put an app into hibernation for a given user, allowing user-level optimizations to occur. Do 450 * not hold {@link #mLock} while calling this to avoid deadlock scenarios. 451 */ hibernatePackageForUser(@onNull String packageName, int userId, UserLevelState state)452 private void hibernatePackageForUser(@NonNull String packageName, int userId, 453 UserLevelState state) { 454 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage"); 455 final long caller = Binder.clearCallingIdentity(); 456 try { 457 ApplicationInfo info = mIPackageManager.getApplicationInfo( 458 packageName, PACKAGE_MATCH_FLAGS, userId); 459 StorageStats stats = mStorageStatsManager.queryStatsForPackage( 460 info.storageUuid, packageName, new UserHandle(userId)); 461 mIActivityManager.forceStopPackage(packageName, userId); 462 mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId, 463 null /* observer */); 464 synchronized (mLock) { 465 state.savedByte = stats.getCacheBytes(); 466 } 467 } catch (RemoteException e) { 468 throw new IllegalStateException( 469 "Failed to hibernate due to manager not being available", e); 470 } catch (PackageManager.NameNotFoundException e) { 471 Slog.e(TAG, "Package name not found when querying storage stats", e); 472 } catch (IOException e) { 473 Slog.e(TAG, "Storage device not found", e); 474 } finally { 475 Binder.restoreCallingIdentity(caller); 476 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); 477 } 478 } 479 480 /** 481 * Remove a package from hibernation for a given user. Do not hold {@link #mLock} while calling 482 * this. 483 */ unhibernatePackageForUser(@onNull String packageName, int userId)484 private void unhibernatePackageForUser(@NonNull String packageName, int userId) { 485 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage"); 486 final long caller = Binder.clearCallingIdentity(); 487 // Deliver LOCKED_BOOT_COMPLETE AND BOOT_COMPLETE broadcast so app can re-register 488 // their alarms/jobs/etc. 489 try { 490 Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED) 491 .setPackage(packageName); 492 final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED}; 493 mIActivityManager.broadcastIntentWithFeature( 494 null /* caller */, 495 null /* callingFeatureId */, 496 lockedBcIntent, 497 null /* resolvedType */, 498 null /* resultTo */, 499 Activity.RESULT_OK, 500 null /* resultData */, 501 null /* resultExtras */, 502 requiredPermissions, 503 null /* excludedPermissions */, 504 null /* excludedPackages */, 505 OP_NONE, 506 null /* bOptions */, 507 false /* serialized */, 508 false /* sticky */, 509 userId); 510 511 Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName); 512 mIActivityManager.broadcastIntentWithFeature( 513 null /* caller */, 514 null /* callingFeatureId */, 515 bcIntent, 516 null /* resolvedType */, 517 null /* resultTo */, 518 Activity.RESULT_OK, 519 null /* resultData */, 520 null /* resultExtras */, 521 requiredPermissions, 522 null /* excludedPermissions */, 523 null /* excludedPackages */, 524 OP_NONE, 525 null /* bOptions */, 526 false /* serialized */, 527 false /* sticky */, 528 userId); 529 } catch (RemoteException e) { 530 throw e.rethrowFromSystemServer(); 531 } finally { 532 Binder.restoreCallingIdentity(caller); 533 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); 534 } 535 } 536 537 /** 538 * Put a package into global hibernation, optimizing its storage at a package / APK level. Do 539 * not hold {@link #mLock} while calling this. 540 */ hibernatePackageGlobally(@onNull String packageName, GlobalLevelState state)541 private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) { 542 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally"); 543 long savedBytes = 0; 544 if (mOatArtifactDeletionEnabled) { 545 savedBytes = Math.max( 546 mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName), 547 0); 548 } 549 synchronized (mLock) { 550 state.savedByte = savedBytes; 551 } 552 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); 553 } 554 555 /** 556 * Initializes in-memory store of user-level hibernation states for the given user 557 * 558 * @param userId user id to add installed packages for 559 * @param diskStates states pulled from disk, if available 560 */ 561 @GuardedBy("mLock") initializeUserHibernationStates(int userId, @Nullable List<UserLevelState> diskStates)562 private void initializeUserHibernationStates(int userId, 563 @Nullable List<UserLevelState> diskStates) { 564 List<PackageInfo> packages; 565 try { 566 packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList(); 567 } catch (RemoteException e) { 568 throw new IllegalStateException("Package manager not available", e); 569 } 570 571 Map<String, UserLevelState> userLevelStates = new ArrayMap<>(); 572 573 for (int i = 0, size = packages.size(); i < size; i++) { 574 String packageName = packages.get(i).packageName; 575 UserLevelState state = new UserLevelState(); 576 state.packageName = packageName; 577 userLevelStates.put(packageName, state); 578 } 579 580 if (diskStates != null) { 581 Map<String, PackageInfo> installedPackages = new ArrayMap<>(); 582 for (int i = 0, size = packages.size(); i < size; i++) { 583 installedPackages.put(packages.get(i).packageName, packages.get(i)); 584 } 585 for (int i = 0, size = diskStates.size(); i < size; i++) { 586 String packageName = diskStates.get(i).packageName; 587 PackageInfo pkgInfo = installedPackages.get(packageName); 588 UserLevelState currentState = diskStates.get(i); 589 if (pkgInfo == null) { 590 Slog.w(TAG, TextUtils.formatSimple( 591 "No hibernation state associated with package %s user %d. Maybe" 592 + "the package was uninstalled? ", packageName, userId)); 593 continue; 594 } 595 if (pkgInfo.applicationInfo != null 596 && (pkgInfo.applicationInfo.flags &= ApplicationInfo.FLAG_STOPPED) == 0 597 && currentState.hibernated) { 598 // App is not stopped but is hibernated. Disk state is stale, so unhibernate 599 // the app. 600 currentState.hibernated = false; 601 currentState.lastUnhibernatedMs = System.currentTimeMillis(); 602 } 603 userLevelStates.put(packageName, currentState); 604 } 605 } 606 mUserStates.put(userId, userLevelStates); 607 } 608 609 /** 610 * Initialize in-memory store of global level hibernation states. 611 * 612 * @param diskStates global level hibernation states pulled from disk, if available 613 */ 614 @GuardedBy("mLock") initializeGlobalHibernationStates(@ullable List<GlobalLevelState> diskStates)615 private void initializeGlobalHibernationStates(@Nullable List<GlobalLevelState> diskStates) { 616 List<PackageInfo> packages; 617 try { 618 packages = mIPackageManager.getInstalledPackages( 619 PACKAGE_MATCH_FLAGS | MATCH_ANY_USER, 0 /* userId */).getList(); 620 } catch (RemoteException e) { 621 throw new IllegalStateException("Package manager not available", e); 622 } 623 624 for (int i = 0, size = packages.size(); i < size; i++) { 625 String packageName = packages.get(i).packageName; 626 GlobalLevelState state = new GlobalLevelState(); 627 state.packageName = packageName; 628 mGlobalHibernationStates.put(packageName, state); 629 } 630 if (diskStates != null) { 631 Set<String> installedPackages = new ArraySet<>(); 632 for (int i = 0, size = packages.size(); i < size; i++) { 633 installedPackages.add(packages.get(i).packageName); 634 } 635 for (int i = 0, size = diskStates.size(); i < size; i++) { 636 GlobalLevelState state = diskStates.get(i); 637 if (!installedPackages.contains(state.packageName)) { 638 Slog.w(TAG, TextUtils.formatSimple( 639 "No hibernation state associated with package %s. Maybe the " 640 + "package was uninstalled? ", state.packageName)); 641 continue; 642 } 643 mGlobalHibernationStates.put(state.packageName, state); 644 } 645 } 646 } 647 648 @Override onUserUnlocking(@onNull TargetUser user)649 public void onUserUnlocking(@NonNull TargetUser user) { 650 int userId = user.getUserIdentifier(); 651 HibernationStateDiskStore<UserLevelState> diskStore = 652 mInjector.getUserLevelDiskStore(userId); 653 mUserDiskStores.put(userId, diskStore); 654 mBackgroundExecutor.execute(() -> { 655 List<UserLevelState> storedStates = diskStore.readHibernationStates(); 656 synchronized (mLock) { 657 // Ensure user hasn't stopped in the time to execute. 658 if (mUserManager.isUserUnlockingOrUnlocked(userId)) { 659 initializeUserHibernationStates(userId, storedStates); 660 // Globally unhibernate a package if the unlocked user does not have it 661 // hibernated. 662 for (UserLevelState userState : mUserStates.get(userId).values()) { 663 String pkgName = userState.packageName; 664 GlobalLevelState globalState = mGlobalHibernationStates.get(pkgName); 665 if (globalState.hibernated && !userState.hibernated) { 666 setHibernatingGlobally(pkgName, false); 667 } 668 } 669 } 670 } 671 }); 672 } 673 674 @Override onUserStopping(@onNull TargetUser user)675 public void onUserStopping(@NonNull TargetUser user) { 676 int userId = user.getUserIdentifier(); 677 // TODO: Flush any scheduled writes to disk immediately on user stopping / power off. 678 synchronized (mLock) { 679 mUserDiskStores.remove(userId); 680 mUserStates.remove(userId); 681 } 682 } 683 onPackageAdded(@onNull String packageName, int userId)684 private void onPackageAdded(@NonNull String packageName, int userId) { 685 synchronized (mLock) { 686 if (!mUserStates.contains(userId)) { 687 return; 688 } 689 UserLevelState userState = new UserLevelState(); 690 userState.packageName = packageName; 691 mUserStates.get(userId).put(packageName, userState); 692 if (!mGlobalHibernationStates.containsKey(packageName)) { 693 GlobalLevelState globalState = new GlobalLevelState(); 694 globalState.packageName = packageName; 695 mGlobalHibernationStates.put(packageName, globalState); 696 } 697 } 698 } 699 onPackageRemoved(@onNull String packageName, int userId)700 private void onPackageRemoved(@NonNull String packageName, int userId) { 701 synchronized (mLock) { 702 if (!mUserStates.contains(userId)) { 703 return; 704 } 705 mUserStates.get(userId).remove(packageName); 706 } 707 } 708 onPackageRemovedForAllUsers(@onNull String packageName)709 private void onPackageRemovedForAllUsers(@NonNull String packageName) { 710 synchronized (mLock) { 711 mGlobalHibernationStates.remove(packageName); 712 } 713 } 714 onDeviceConfigChanged(Properties properties)715 private void onDeviceConfigChanged(Properties properties) { 716 for (String key : properties.getKeyset()) { 717 if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) { 718 sIsServiceEnabled = isDeviceConfigAppHibernationEnabled(); 719 Slog.d(TAG, "App hibernation changed to enabled=" + sIsServiceEnabled); 720 break; 721 } 722 } 723 } 724 725 /** 726 * Private helper method to get the real user id and enforce permission checks. 727 * 728 * @param userId user id to handle 729 * @param name name to use for exceptions 730 * @return real user id 731 */ handleIncomingUser(int userId, @NonNull String name)732 private int handleIncomingUser(int userId, @NonNull String name) { 733 int callingUid = Binder.getCallingUid(); 734 try { 735 return mIActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, 736 false /* allowAll */, true /* requireFull */, name, null); 737 } catch (RemoteException re) { 738 throw re.rethrowFromSystemServer(); 739 } 740 } 741 742 /** 743 * Check that user states exist. 744 * 745 * @param userId user to check 746 * @param methodName method name that is calling. Used for logging purposes. 747 * @param shouldLog whether we should log why the user state doesn't exist 748 * @return true if user states exist 749 */ 750 @GuardedBy("mLock") checkUserStatesExist(int userId, String methodName, boolean shouldLog)751 private boolean checkUserStatesExist(int userId, String methodName, boolean shouldLog) { 752 if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { 753 if (shouldLog) { 754 Slog.w(TAG, TextUtils.formatSimple( 755 "Attempt to call %s on stopped or nonexistent user %d", 756 methodName, userId)); 757 } 758 return false; 759 } 760 if (!mUserStates.contains(userId)) { 761 if (shouldLog) { 762 Slog.w(TAG, TextUtils.formatSimple( 763 "Attempt to call %s before states have been read from disk", methodName)); 764 } 765 return false; 766 } 767 return true; 768 } 769 dump(PrintWriter pw)770 private void dump(PrintWriter pw) { 771 // Check usage stats permission since hibernation indirectly informs usage. 772 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; 773 774 IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); 775 776 synchronized (mLock) { 777 final int userCount = mUserStates.size(); 778 for (int i = 0; i < userCount; i++) { 779 final int userId = mUserStates.keyAt(i); 780 idpw.print("User Level Hibernation States, "); 781 idpw.printPair("user", userId); 782 idpw.println(); 783 Map<String, UserLevelState> stateMap = mUserStates.get(userId); 784 idpw.increaseIndent(); 785 for (UserLevelState state : stateMap.values()) { 786 idpw.print(state); 787 idpw.println(); 788 } 789 idpw.decreaseIndent(); 790 } 791 idpw.println(); 792 idpw.print("Global Level Hibernation States"); 793 idpw.println(); 794 for (GlobalLevelState state : mGlobalHibernationStates.values()) { 795 idpw.print(state); 796 idpw.println(); 797 } 798 } 799 } 800 801 private final AppHibernationManagerInternal mLocalService = new LocalService(this); 802 803 private static final class LocalService extends AppHibernationManagerInternal { 804 private final AppHibernationService mService; 805 LocalService(AppHibernationService service)806 LocalService(AppHibernationService service) { 807 mService = service; 808 } 809 810 @Override isHibernatingForUser(String packageName, int userId)811 public boolean isHibernatingForUser(String packageName, int userId) { 812 return mService.isHibernatingForUser(packageName, userId); 813 } 814 815 @Override setHibernatingForUser(String packageName, int userId, boolean isHibernating)816 public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { 817 mService.setHibernatingForUser(packageName, userId, isHibernating); 818 } 819 820 @Override setHibernatingGlobally(String packageName, boolean isHibernating)821 public void setHibernatingGlobally(String packageName, boolean isHibernating) { 822 mService.setHibernatingGlobally(packageName, isHibernating); 823 } 824 825 @Override isHibernatingGlobally(String packageName)826 public boolean isHibernatingGlobally(String packageName) { 827 return mService.isHibernatingGlobally(packageName); 828 } 829 830 @Override isOatArtifactDeletionEnabled()831 public boolean isOatArtifactDeletionEnabled() { 832 return mService.isOatArtifactDeletionEnabled(); 833 } 834 } 835 836 private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this); 837 838 static final class AppHibernationServiceStub extends IAppHibernationService.Stub { 839 final AppHibernationService mService; 840 AppHibernationServiceStub(AppHibernationService service)841 AppHibernationServiceStub(AppHibernationService service) { 842 mService = service; 843 } 844 845 @Override isHibernatingForUser(String packageName, int userId)846 public boolean isHibernatingForUser(String packageName, int userId) { 847 return mService.isHibernatingForUser(packageName, userId); 848 } 849 850 @Override setHibernatingForUser(String packageName, int userId, boolean isHibernating)851 public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { 852 mService.setHibernatingForUser(packageName, userId, isHibernating); 853 } 854 855 @Override setHibernatingGlobally(String packageName, boolean isHibernating)856 public void setHibernatingGlobally(String packageName, boolean isHibernating) { 857 mService.setHibernatingGlobally(packageName, isHibernating); 858 } 859 860 @Override isHibernatingGlobally(String packageName)861 public boolean isHibernatingGlobally(String packageName) { 862 return mService.isHibernatingGlobally(packageName); 863 } 864 865 @Override getHibernatingPackagesForUser(int userId)866 public List<String> getHibernatingPackagesForUser(int userId) { 867 return mService.getHibernatingPackagesForUser(userId); 868 } 869 870 @Override getHibernationStatsForUser( @ullable List<String> packageNames, int userId)871 public Map<String, HibernationStats> getHibernationStatsForUser( 872 @Nullable List<String> packageNames, int userId) { 873 Set<String> pkgsSet = packageNames != null ? new ArraySet<>(packageNames) : null; 874 return mService.getHibernationStatsForUser(pkgsSet, userId); 875 } 876 877 @Override onShellCommand(@ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver)878 public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, 879 @Nullable FileDescriptor err, @NonNull String[] args, 880 @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) { 881 new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback, 882 resultReceiver); 883 } 884 885 @Override dump(@onNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args)886 protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, 887 @Nullable String[] args) { 888 mService.dump(fout); 889 } 890 } 891 892 // Broadcast receiver for package add/removal events 893 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 894 @Override 895 public void onReceive(Context context, Intent intent) { 896 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 897 if (userId == UserHandle.USER_NULL) { 898 return; 899 } 900 901 final String action = intent.getAction(); 902 if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) { 903 final String packageName = intent.getData().getSchemeSpecificPart(); 904 if (intent.getBooleanExtra(EXTRA_REPLACING, false)) { 905 // Package removal/add is part of an update, so no need to modify package state. 906 return; 907 } 908 909 if (ACTION_PACKAGE_ADDED.equals(action)) { 910 onPackageAdded(packageName, userId); 911 } else if (ACTION_PACKAGE_REMOVED.equals(action)) { 912 onPackageRemoved(packageName, userId); 913 if (intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false)) { 914 onPackageRemovedForAllUsers(packageName); 915 } 916 } 917 } 918 } 919 }; 920 921 private final UsageEventListener mUsageEventListener = (userId, event) -> { 922 if (!isAppHibernationEnabled()) { 923 return; 924 } 925 final int eventType = event.mEventType; 926 if (eventType == UsageEvents.Event.USER_INTERACTION 927 || eventType == UsageEvents.Event.ACTIVITY_RESUMED 928 || eventType == UsageEvents.Event.APP_COMPONENT_USED) { 929 final String pkgName = event.mPackage; 930 setHibernatingForUser(pkgName, userId, false); 931 setHibernatingGlobally(pkgName, false); 932 } 933 }; 934 935 /** 936 * Whether app hibernation is enabled on this device. 937 * 938 * @return true if enabled, false otherwise 939 */ isAppHibernationEnabled()940 public static boolean isAppHibernationEnabled() { 941 return sIsServiceEnabled; 942 } 943 isDeviceConfigAppHibernationEnabled()944 private static boolean isDeviceConfigAppHibernationEnabled() { 945 return DeviceConfig.getBoolean( 946 NAMESPACE_APP_HIBERNATION, 947 KEY_APP_HIBERNATION_ENABLED, 948 true /* defaultValue */); 949 } 950 951 /** 952 * Dependency injector for {@link #AppHibernationService)}. 953 */ 954 interface Injector { getContext()955 Context getContext(); 956 getPackageManager()957 IPackageManager getPackageManager(); 958 getPackageManagerInternal()959 PackageManagerInternal getPackageManagerInternal(); 960 getActivityManager()961 IActivityManager getActivityManager(); 962 getUserManager()963 UserManager getUserManager(); 964 getStorageStatsManager()965 StorageStatsManager getStorageStatsManager(); 966 getBackgroundExecutor()967 Executor getBackgroundExecutor(); 968 getUsageStatsManagerInternal()969 UsageStatsManagerInternal getUsageStatsManagerInternal(); 970 getGlobalLevelDiskStore()971 HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore(); 972 getUserLevelDiskStore(int userId)973 HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId); 974 isOatArtifactDeletionEnabled()975 boolean isOatArtifactDeletionEnabled(); 976 } 977 978 private static final class InjectorImpl implements Injector { 979 private static final String HIBERNATION_DIR_NAME = "hibernation"; 980 private final Context mContext; 981 private final ScheduledExecutorService mScheduledExecutorService; 982 private final UserLevelHibernationProto mUserLevelHibernationProto; 983 InjectorImpl(Context context)984 InjectorImpl(Context context) { 985 mContext = context; 986 mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); 987 mUserLevelHibernationProto = new UserLevelHibernationProto(); 988 } 989 990 @Override getContext()991 public Context getContext() { 992 return mContext; 993 } 994 995 @Override getPackageManager()996 public IPackageManager getPackageManager() { 997 return IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 998 } 999 1000 @Override getPackageManagerInternal()1001 public PackageManagerInternal getPackageManagerInternal() { 1002 return LocalServices.getService(PackageManagerInternal.class); 1003 } 1004 1005 @Override getActivityManager()1006 public IActivityManager getActivityManager() { 1007 return ActivityManager.getService(); 1008 } 1009 1010 @Override getUserManager()1011 public UserManager getUserManager() { 1012 return mContext.getSystemService(UserManager.class); 1013 } 1014 1015 @Override getStorageStatsManager()1016 public StorageStatsManager getStorageStatsManager() { 1017 return mContext.getSystemService(StorageStatsManager.class); 1018 } 1019 1020 @Override getBackgroundExecutor()1021 public Executor getBackgroundExecutor() { 1022 return mScheduledExecutorService; 1023 } 1024 1025 @Override getUsageStatsManagerInternal()1026 public UsageStatsManagerInternal getUsageStatsManagerInternal() { 1027 return LocalServices.getService(UsageStatsManagerInternal.class); 1028 } 1029 1030 @Override getGlobalLevelDiskStore()1031 public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() { 1032 File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME); 1033 return new HibernationStateDiskStore<>( 1034 dir, new GlobalLevelHibernationProto(), mScheduledExecutorService); 1035 } 1036 1037 @Override getUserLevelDiskStore(int userId)1038 public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) { 1039 File dir = new File(Environment.getDataSystemCeDirectory(userId), HIBERNATION_DIR_NAME); 1040 return new HibernationStateDiskStore<>( 1041 dir, mUserLevelHibernationProto, mScheduledExecutorService); 1042 } 1043 1044 @Override isOatArtifactDeletionEnabled()1045 public boolean isOatArtifactDeletionEnabled() { 1046 return mContext.getResources().getBoolean( 1047 com.android.internal.R.bool.config_hibernationDeletesOatArtifactsEnabled); 1048 } 1049 } 1050 1051 private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback { 1052 1053 private static final int MEGABYTE_IN_BYTES = 1000000; 1054 1055 @Override onPullAtom(int atomTag, @NonNull List<StatsEvent> data)1056 public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) { 1057 if (!isAppHibernationEnabled() 1058 && (atomTag == FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS 1059 || atomTag == FrameworkStatsLog.GLOBAL_HIBERNATED_APPS)) { 1060 return StatsManager.PULL_SUCCESS; 1061 } 1062 1063 switch (atomTag) { 1064 case FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS: 1065 List<UserInfo> userInfos = mUserManager.getAliveUsers(); 1066 final int numUsers = userInfos.size(); 1067 for (int i = 0; i < numUsers; ++i) { 1068 final int userId = userInfos.get(i).id; 1069 if (mUserManager.isUserUnlockingOrUnlocked(userId)) { 1070 data.add( 1071 FrameworkStatsLog.buildStatsEvent( 1072 atomTag, 1073 getHibernatingPackagesForUser(userId).size(), 1074 userId) 1075 ); 1076 } 1077 } 1078 break; 1079 case FrameworkStatsLog.GLOBAL_HIBERNATED_APPS: 1080 int hibernatedAppCount = 0; 1081 long storage_saved_byte = 0; 1082 synchronized (mLock) { 1083 for (GlobalLevelState state : mGlobalHibernationStates.values()) { 1084 if (state.hibernated) { 1085 hibernatedAppCount++; 1086 storage_saved_byte += state.savedByte; 1087 } 1088 } 1089 } 1090 data.add( 1091 FrameworkStatsLog.buildStatsEvent( 1092 atomTag, 1093 hibernatedAppCount, 1094 storage_saved_byte / MEGABYTE_IN_BYTES) 1095 ); 1096 break; 1097 default: 1098 return StatsManager.PULL_SKIP; 1099 } 1100 return StatsManager.PULL_SUCCESS; 1101 } 1102 } 1103 } 1104