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