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