• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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