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