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