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