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