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 long 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             if (android.app.Flags.appRestrictionsApi()) {
465                 noteHibernationChange(packageName, info.uid, true);
466             }
467             mIActivityManager.forceStopPackage(packageName, userId);
468             mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
469                     null /* observer */);
470             synchronized (mLock) {
471                 state.savedByte = stats.getCacheBytes();
472             }
473         } catch (RemoteException e) {
474             throw new IllegalStateException(
475                     "Failed to hibernate due to manager not being available", e);
476         } catch (PackageManager.NameNotFoundException e) {
477             Slog.e(TAG, "Package name not found when querying storage stats", e);
478         } catch (IOException e) {
479             Slog.e(TAG, "Storage device not found", e);
480         } finally {
481             Binder.restoreCallingIdentity(caller);
482             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
483         }
484     }
485 
486     /**
487      * Remove a package from hibernation for a given user. Do not hold {@link #mLock} while calling
488      * this.
489      */
unhibernatePackageForUser(@onNull String packageName, int userId)490     private void unhibernatePackageForUser(@NonNull String packageName, int userId) {
491         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
492         final long caller = Binder.clearCallingIdentity();
493         // Deliver LOCKED_BOOT_COMPLETE AND BOOT_COMPLETE broadcast so app can re-register
494         // their alarms/jobs/etc.
495         try {
496             if (android.app.Flags.appRestrictionsApi()) {
497                 ApplicationInfo info = mIPackageManager.getApplicationInfo(
498                         packageName, PACKAGE_MATCH_FLAGS, userId);
499                 noteHibernationChange(packageName, info.uid, false);
500             }
501             Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
502                     .setPackage(packageName);
503             final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
504             mIActivityManager.broadcastIntentWithFeature(
505                     null /* caller */,
506                     null /* callingFeatureId */,
507                     lockedBcIntent,
508                     null /* resolvedType */,
509                     null /* resultTo */,
510                     Activity.RESULT_OK,
511                     null /* resultData */,
512                     null /* resultExtras */,
513                     requiredPermissions,
514                     null /* excludedPermissions */,
515                     null /* excludedPackages */,
516                     OP_NONE,
517                     null /* bOptions */,
518                     false /* serialized */,
519                     false /* sticky */,
520                     userId);
521 
522             Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
523             mIActivityManager.broadcastIntentWithFeature(
524                     null /* caller */,
525                     null /* callingFeatureId */,
526                     bcIntent,
527                     null /* resolvedType */,
528                     null /* resultTo */,
529                     Activity.RESULT_OK,
530                     null /* resultData */,
531                     null /* resultExtras */,
532                     requiredPermissions,
533                     null /* excludedPermissions */,
534                     null /* excludedPackages */,
535                     OP_NONE,
536                     null /* bOptions */,
537                     false /* serialized */,
538                     false /* sticky */,
539                     userId);
540         } catch (RemoteException e) {
541             throw e.rethrowFromSystemServer();
542         } finally {
543             Binder.restoreCallingIdentity(caller);
544             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
545         }
546     }
547 
548     /**
549      * Put a package into global hibernation, optimizing its storage at a package / APK level. Do
550      * not hold {@link #mLock} while calling this.
551      */
hibernatePackageGlobally(@onNull String packageName, GlobalLevelState state)552     private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
553         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
554         long savedBytes = 0;
555         if (mOatArtifactDeletionEnabled) {
556             savedBytes = Math.max(
557                     mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName),
558                     0);
559         }
560         synchronized (mLock) {
561             state.savedByte = savedBytes;
562         }
563         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
564     }
565 
566     /** Inform ActivityManager that the app being stopped or unstopped due to hibernation */
noteHibernationChange(String packageName, int uid, boolean hibernated)567     private void noteHibernationChange(String packageName, int uid, boolean hibernated) {
568         try {
569             if (hibernated) {
570                 // TODO: Switch to an ActivityManagerInternal API
571                 mIActivityManager.noteAppRestrictionEnabled(
572                         packageName, uid, ActivityManager.RESTRICTION_LEVEL_FORCE_STOPPED,
573                         true, ActivityManager.RESTRICTION_REASON_DORMANT, null,
574                         ActivityManager.RESTRICTION_SOURCE_SYSTEM,
575                         /* TODO: fetch actual timeout - 90 days */ 90 * 24 * 60 * 60_000L);
576             }
577             // No need to log the unhibernate case as an unstop is logged already in ActivityMS
578         } catch (RemoteException e) {
579             Slog.e(TAG, "Couldn't set restriction state change");
580         }
581     }
582 
583     /**
584      * Initializes in-memory store of user-level hibernation states for the given user
585      *
586      * @param userId user id to add installed packages for
587      * @param diskStates states pulled from disk, if available
588      */
589     @GuardedBy("mLock")
initializeUserHibernationStates(int userId, @Nullable List<UserLevelState> diskStates)590     private void initializeUserHibernationStates(int userId,
591             @Nullable List<UserLevelState> diskStates) {
592         List<PackageInfo> packages;
593         try {
594             packages = mIPackageManager.getInstalledPackages(PACKAGE_MATCH_FLAGS, userId).getList();
595         } catch (RemoteException e) {
596             throw new IllegalStateException("Package manager not available", e);
597         }
598 
599         Map<String, UserLevelState> userLevelStates = new ArrayMap<>();
600 
601         for (int i = 0, size = packages.size(); i < size; i++) {
602             String packageName = packages.get(i).packageName;
603             UserLevelState state = new UserLevelState();
604             state.packageName = packageName;
605             userLevelStates.put(packageName, state);
606         }
607 
608         if (diskStates != null) {
609             Map<String, PackageInfo> installedPackages = new ArrayMap<>();
610             for (int i = 0, size = packages.size(); i < size; i++) {
611                 installedPackages.put(packages.get(i).packageName, packages.get(i));
612             }
613             for (int i = 0, size = diskStates.size(); i < size; i++) {
614                 String packageName = diskStates.get(i).packageName;
615                 PackageInfo pkgInfo = installedPackages.get(packageName);
616                 UserLevelState currentState = diskStates.get(i);
617                 if (pkgInfo == null) {
618                     Slog.w(TAG, TextUtils.formatSimple(
619                             "No hibernation state associated with package %s user %d. Maybe"
620                                     + "the package was uninstalled? ", packageName, userId));
621                     continue;
622                 }
623                 if (pkgInfo.applicationInfo != null
624                         && (pkgInfo.applicationInfo.flags &= ApplicationInfo.FLAG_STOPPED) == 0
625                         && currentState.hibernated) {
626                     // App is not stopped but is hibernated. Disk state is stale, so unhibernate
627                     // the app.
628                     currentState.hibernated = false;
629                     currentState.lastUnhibernatedMs = System.currentTimeMillis();
630                 }
631                 userLevelStates.put(packageName, currentState);
632             }
633         }
634         mUserStates.put(userId, userLevelStates);
635     }
636 
637     /**
638      * Initialize in-memory store of global level hibernation states.
639      *
640      * @param diskStates global level hibernation states pulled from disk, if available
641      */
642     @GuardedBy("mLock")
initializeGlobalHibernationStates(@ullable List<GlobalLevelState> diskStates)643     private void initializeGlobalHibernationStates(@Nullable List<GlobalLevelState> diskStates) {
644         List<PackageInfo> packages;
645         try {
646             packages = mIPackageManager.getInstalledPackages(
647                     PACKAGE_MATCH_FLAGS | MATCH_ANY_USER, 0 /* userId */).getList();
648         } catch (RemoteException e) {
649             throw new IllegalStateException("Package manager not available", e);
650         }
651 
652         for (int i = 0, size = packages.size(); i < size; i++) {
653             String packageName = packages.get(i).packageName;
654             GlobalLevelState state = new GlobalLevelState();
655             state.packageName = packageName;
656             mGlobalHibernationStates.put(packageName, state);
657         }
658         if (diskStates != null) {
659             Set<String> installedPackages = new ArraySet<>();
660             for (int i = 0, size = packages.size(); i < size; i++) {
661                 installedPackages.add(packages.get(i).packageName);
662             }
663             for (int i = 0, size = diskStates.size(); i < size; i++) {
664                 GlobalLevelState state = diskStates.get(i);
665                 if (!installedPackages.contains(state.packageName)) {
666                     Slog.w(TAG, TextUtils.formatSimple(
667                             "No hibernation state associated with package %s. Maybe the "
668                                     + "package was uninstalled? ", state.packageName));
669                     continue;
670                 }
671                 mGlobalHibernationStates.put(state.packageName, state);
672             }
673         }
674     }
675 
676     @Override
onUserUnlocking(@onNull TargetUser user)677     public void onUserUnlocking(@NonNull TargetUser user) {
678         int userId = user.getUserIdentifier();
679         HibernationStateDiskStore<UserLevelState> diskStore =
680                 mInjector.getUserLevelDiskStore(userId);
681         mUserDiskStores.put(userId, diskStore);
682         mBackgroundExecutor.execute(() -> {
683             List<UserLevelState> storedStates = diskStore.readHibernationStates();
684             synchronized (mLock) {
685                 // Ensure user hasn't stopped in the time to execute.
686                 if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
687                     initializeUserHibernationStates(userId, storedStates);
688                     // Globally unhibernate a package if the unlocked user does not have it
689                     // hibernated.
690                     for (UserLevelState userState : mUserStates.get(userId).values()) {
691                         String pkgName = userState.packageName;
692                         GlobalLevelState globalState = mGlobalHibernationStates.get(pkgName);
693                         if (globalState.hibernated && !userState.hibernated) {
694                             setHibernatingGlobally(pkgName, false);
695                         }
696                     }
697                 }
698             }
699         });
700     }
701 
702     @Override
onUserStopping(@onNull TargetUser user)703     public void onUserStopping(@NonNull TargetUser user) {
704         int userId = user.getUserIdentifier();
705         // TODO: Flush any scheduled writes to disk immediately on user stopping / power off.
706         synchronized (mLock) {
707             mUserDiskStores.remove(userId);
708             mUserStates.remove(userId);
709         }
710     }
711 
onPackageAdded(@onNull String packageName, int userId)712     private void onPackageAdded(@NonNull String packageName, int userId) {
713         synchronized (mLock) {
714             if (!mUserStates.contains(userId)) {
715                 return;
716             }
717             UserLevelState userState = new UserLevelState();
718             userState.packageName = packageName;
719             mUserStates.get(userId).put(packageName, userState);
720             if (!mGlobalHibernationStates.containsKey(packageName)) {
721                 GlobalLevelState globalState = new GlobalLevelState();
722                 globalState.packageName = packageName;
723                 mGlobalHibernationStates.put(packageName, globalState);
724             }
725         }
726     }
727 
onPackageRemoved(@onNull String packageName, int userId)728     private void onPackageRemoved(@NonNull String packageName, int userId) {
729         synchronized (mLock) {
730             if (!mUserStates.contains(userId)) {
731                 return;
732             }
733             mUserStates.get(userId).remove(packageName);
734         }
735     }
736 
onPackageRemovedForAllUsers(@onNull String packageName)737     private void onPackageRemovedForAllUsers(@NonNull String packageName) {
738         synchronized (mLock) {
739             mGlobalHibernationStates.remove(packageName);
740         }
741     }
742 
onDeviceConfigChanged(Properties properties)743     private void onDeviceConfigChanged(Properties properties) {
744         for (String key : properties.getKeyset()) {
745             if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) {
746                 sIsServiceEnabled = isDeviceConfigAppHibernationEnabled();
747                 Slog.d(TAG, "App hibernation changed to enabled=" + sIsServiceEnabled);
748                 break;
749             }
750         }
751     }
752 
753     /**
754      * Private helper method to get the real user id and enforce permission checks.
755      *
756      * @param userId user id to handle
757      * @param name name to use for exceptions
758      * @return real user id
759      */
handleIncomingUser(int userId, @NonNull String name)760     private int handleIncomingUser(int userId, @NonNull String name) {
761         int callingUid = Binder.getCallingUid();
762         try {
763             return mIActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
764                     false /* allowAll */, true /* requireFull */, name, null);
765         } catch (RemoteException re) {
766             throw re.rethrowFromSystemServer();
767         }
768     }
769 
770     /**
771      * Check that user states exist.
772      *
773      * @param userId user to check
774      * @param methodName method name that is calling. Used for logging purposes.
775      * @param shouldLog whether we should log why the user state doesn't exist
776      * @return true if user states exist
777      */
778     @GuardedBy("mLock")
checkUserStatesExist(int userId, String methodName, boolean shouldLog)779     private boolean checkUserStatesExist(int userId, String methodName, boolean shouldLog) {
780         if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
781             if (shouldLog) {
782                 Slog.w(TAG, TextUtils.formatSimple(
783                         "Attempt to call %s on stopped or nonexistent user %d",
784                         methodName, userId));
785             }
786             return false;
787         }
788         if (!mUserStates.contains(userId)) {
789             if (shouldLog) {
790                 Slog.w(TAG, TextUtils.formatSimple(
791                         "Attempt to call %s before states have been read from disk", methodName));
792             }
793             return false;
794         }
795         return true;
796     }
797 
dump(PrintWriter pw)798     private void dump(PrintWriter pw) {
799         // Check usage stats permission since hibernation indirectly informs usage.
800         if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
801 
802         IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
803 
804         synchronized (mLock) {
805             final int userCount = mUserStates.size();
806             for (int i = 0; i < userCount; i++) {
807                 final int userId = mUserStates.keyAt(i);
808                 idpw.print("User Level Hibernation States, ");
809                 idpw.printPair("user", userId);
810                 idpw.println();
811                 Map<String, UserLevelState> stateMap = mUserStates.get(userId);
812                 idpw.increaseIndent();
813                 for (UserLevelState state : stateMap.values()) {
814                     idpw.print(state);
815                     idpw.println();
816                 }
817                 idpw.decreaseIndent();
818             }
819             idpw.println();
820             idpw.print("Global Level Hibernation States");
821             idpw.println();
822             for (GlobalLevelState state : mGlobalHibernationStates.values()) {
823                 idpw.print(state);
824                 idpw.println();
825             }
826         }
827     }
828 
829     private final AppHibernationManagerInternal mLocalService = new LocalService(this);
830 
831     private static final class LocalService extends AppHibernationManagerInternal {
832         private final AppHibernationService mService;
833 
LocalService(AppHibernationService service)834         LocalService(AppHibernationService service) {
835             mService = service;
836         }
837 
838         @Override
isHibernatingForUser(String packageName, int userId)839         public boolean isHibernatingForUser(String packageName, int userId) {
840             return mService.isHibernatingForUser(packageName, userId);
841         }
842 
843         @Override
setHibernatingForUser(String packageName, int userId, boolean isHibernating)844         public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
845             mService.setHibernatingForUser(packageName, userId, isHibernating);
846         }
847 
848         @Override
setHibernatingGlobally(String packageName, boolean isHibernating)849         public void setHibernatingGlobally(String packageName, boolean isHibernating) {
850             mService.setHibernatingGlobally(packageName, isHibernating);
851         }
852 
853         @Override
isHibernatingGlobally(String packageName)854         public boolean isHibernatingGlobally(String packageName) {
855             return mService.isHibernatingGlobally(packageName);
856         }
857 
858         @Override
isOatArtifactDeletionEnabled()859         public boolean isOatArtifactDeletionEnabled() {
860             return mService.isOatArtifactDeletionEnabled();
861         }
862     }
863 
864     private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
865 
866     static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
867         final AppHibernationService mService;
868 
AppHibernationServiceStub(AppHibernationService service)869         AppHibernationServiceStub(AppHibernationService service) {
870             mService = service;
871         }
872 
873         @Override
isHibernatingForUser(String packageName, int userId)874         public boolean isHibernatingForUser(String packageName, int userId) {
875             return mService.isHibernatingForUser(packageName, userId);
876         }
877 
878         @Override
setHibernatingForUser(String packageName, int userId, boolean isHibernating)879         public void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
880             mService.setHibernatingForUser(packageName, userId, isHibernating);
881         }
882 
883         @Override
setHibernatingGlobally(String packageName, boolean isHibernating)884         public void setHibernatingGlobally(String packageName, boolean isHibernating) {
885             mService.setHibernatingGlobally(packageName, isHibernating);
886         }
887 
888         @Override
isHibernatingGlobally(String packageName)889         public boolean isHibernatingGlobally(String packageName) {
890             return mService.isHibernatingGlobally(packageName);
891         }
892 
893         @Override
getHibernatingPackagesForUser(int userId)894         public List<String> getHibernatingPackagesForUser(int userId) {
895             return mService.getHibernatingPackagesForUser(userId);
896         }
897 
898         @Override
getHibernationStatsForUser( @ullable List<String> packageNames, int userId)899         public Map<String, HibernationStats> getHibernationStatsForUser(
900                 @Nullable List<String> packageNames, int userId) {
901             Set<String> pkgsSet = packageNames != null ? new ArraySet<>(packageNames) : null;
902             return mService.getHibernationStatsForUser(pkgsSet, userId);
903         }
904 
905         @Override
isOatArtifactDeletionEnabled()906         public boolean isOatArtifactDeletionEnabled() {
907             return mService.isOatArtifactDeletionEnabled();
908         }
909 
910         @Override
onShellCommand(@ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver)911         public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
912                 @Nullable FileDescriptor err, @NonNull String[] args,
913                 @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
914             new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback,
915                     resultReceiver);
916         }
917 
918         @Override
dump(@onNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args)919         protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
920                 @Nullable String[] args) {
921             mService.dump(fout);
922         }
923     }
924 
925     // Broadcast receiver for package add/removal events
926     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
927         @Override
928         public void onReceive(Context context, Intent intent) {
929             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
930             if (userId == UserHandle.USER_NULL) {
931                 return;
932             }
933 
934             final String action = intent.getAction();
935             if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
936                 final String packageName = intent.getData().getSchemeSpecificPart();
937                 if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
938                     // Package removal/add is part of an update, so no need to modify package state.
939                     return;
940                 }
941 
942                 if (ACTION_PACKAGE_ADDED.equals(action)) {
943                     onPackageAdded(packageName, userId);
944                 } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
945                     onPackageRemoved(packageName, userId);
946                     if (intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false)) {
947                         onPackageRemovedForAllUsers(packageName);
948                     }
949                 }
950             }
951         }
952     };
953 
954     private final UsageEventListener mUsageEventListener = (userId, event) -> {
955         if (!isAppHibernationEnabled()) {
956             return;
957         }
958         final int eventType = event.mEventType;
959         if (eventType == UsageEvents.Event.USER_INTERACTION
960                 || eventType == UsageEvents.Event.ACTIVITY_RESUMED
961                 || eventType == UsageEvents.Event.APP_COMPONENT_USED) {
962             final String pkgName = event.mPackage;
963             setHibernatingForUser(pkgName, userId, false);
964             setHibernatingGlobally(pkgName, false);
965         }
966     };
967 
968     /**
969      * Whether app hibernation is enabled on this device.
970      *
971      * @return true if enabled, false otherwise
972      */
isAppHibernationEnabled()973     public static boolean isAppHibernationEnabled() {
974         return sIsServiceEnabled;
975     }
976 
isDeviceConfigAppHibernationEnabled()977     private static boolean isDeviceConfigAppHibernationEnabled() {
978         return DeviceConfig.getBoolean(
979                 NAMESPACE_APP_HIBERNATION,
980                 KEY_APP_HIBERNATION_ENABLED,
981                 true /* defaultValue */);
982     }
983 
984     /**
985      * Dependency injector for {@link #AppHibernationService)}.
986      */
987     interface Injector {
getContext()988         Context getContext();
989 
getPackageManager()990         IPackageManager getPackageManager();
991 
getPackageManagerInternal()992         PackageManagerInternal getPackageManagerInternal();
993 
getActivityManager()994         IActivityManager getActivityManager();
995 
getUserManager()996         UserManager getUserManager();
997 
getStorageStatsManager()998         StorageStatsManager getStorageStatsManager();
999 
getBackgroundExecutor()1000         Executor getBackgroundExecutor();
1001 
getUsageStatsManagerInternal()1002         UsageStatsManagerInternal getUsageStatsManagerInternal();
1003 
getGlobalLevelDiskStore()1004         HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
1005 
getUserLevelDiskStore(int userId)1006         HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
1007 
isOatArtifactDeletionEnabled()1008         boolean isOatArtifactDeletionEnabled();
1009     }
1010 
1011     private static final class InjectorImpl implements Injector {
1012         private static final String HIBERNATION_DIR_NAME = "hibernation";
1013         private final Context mContext;
1014         private final ScheduledExecutorService mScheduledExecutorService;
1015         private final UserLevelHibernationProto mUserLevelHibernationProto;
1016 
InjectorImpl(Context context)1017         InjectorImpl(Context context) {
1018             mContext = context;
1019             mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
1020             mUserLevelHibernationProto = new UserLevelHibernationProto();
1021         }
1022 
1023         @Override
getContext()1024         public Context getContext() {
1025             return mContext;
1026         }
1027 
1028         @Override
getPackageManager()1029         public IPackageManager getPackageManager() {
1030             return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
1031         }
1032 
1033         @Override
getPackageManagerInternal()1034         public PackageManagerInternal getPackageManagerInternal() {
1035             return LocalServices.getService(PackageManagerInternal.class);
1036         }
1037 
1038         @Override
getActivityManager()1039         public IActivityManager getActivityManager() {
1040             return ActivityManager.getService();
1041         }
1042 
1043         @Override
getUserManager()1044         public UserManager getUserManager() {
1045             return mContext.getSystemService(UserManager.class);
1046         }
1047 
1048         @Override
getStorageStatsManager()1049         public StorageStatsManager getStorageStatsManager() {
1050             return mContext.getSystemService(StorageStatsManager.class);
1051         }
1052 
1053         @Override
getBackgroundExecutor()1054         public Executor getBackgroundExecutor() {
1055             return mScheduledExecutorService;
1056         }
1057 
1058         @Override
getUsageStatsManagerInternal()1059         public UsageStatsManagerInternal getUsageStatsManagerInternal() {
1060             return LocalServices.getService(UsageStatsManagerInternal.class);
1061         }
1062 
1063         @Override
getGlobalLevelDiskStore()1064         public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
1065             File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
1066             return new HibernationStateDiskStore<>(
1067                     dir, new GlobalLevelHibernationProto(), mScheduledExecutorService);
1068         }
1069 
1070         @Override
getUserLevelDiskStore(int userId)1071         public HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId) {
1072             File dir = new File(Environment.getDataSystemCeDirectory(userId), HIBERNATION_DIR_NAME);
1073             return new HibernationStateDiskStore<>(
1074                     dir, mUserLevelHibernationProto, mScheduledExecutorService);
1075         }
1076 
1077         @Override
isOatArtifactDeletionEnabled()1078         public boolean isOatArtifactDeletionEnabled() {
1079             return mContext.getResources().getBoolean(
1080                     com.android.internal.R.bool.config_hibernationDeletesOatArtifactsEnabled);
1081         }
1082     }
1083 
1084     private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
1085 
1086         private static final int MEGABYTE_IN_BYTES = 1000000;
1087 
1088         @Override
onPullAtom(int atomTag, @NonNull List<StatsEvent> data)1089         public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
1090             if (!isAppHibernationEnabled()
1091                     && (atomTag == FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS
1092                     || atomTag == FrameworkStatsLog.GLOBAL_HIBERNATED_APPS)) {
1093                 return StatsManager.PULL_SUCCESS;
1094             }
1095 
1096             switch (atomTag) {
1097                 case FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS:
1098                     List<UserInfo> userInfos = mUserManager.getAliveUsers();
1099                     final int numUsers = userInfos.size();
1100                     for (int i = 0; i < numUsers; ++i) {
1101                         final int userId = userInfos.get(i).id;
1102                         if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
1103                             data.add(
1104                                     FrameworkStatsLog.buildStatsEvent(
1105                                             atomTag,
1106                                             getHibernatingPackagesForUser(userId).size(),
1107                                             userId)
1108                             );
1109                         }
1110                     }
1111                     break;
1112                 case FrameworkStatsLog.GLOBAL_HIBERNATED_APPS:
1113                     int hibernatedAppCount = 0;
1114                     long storage_saved_byte = 0;
1115                     synchronized (mLock) {
1116                         for (GlobalLevelState state : mGlobalHibernationStates.values()) {
1117                             if (state.hibernated) {
1118                                 hibernatedAppCount++;
1119                                 storage_saved_byte += state.savedByte;
1120                             }
1121                         }
1122                     }
1123                     data.add(
1124                             FrameworkStatsLog.buildStatsEvent(
1125                                     atomTag,
1126                                     hibernatedAppCount,
1127                                     storage_saved_byte / MEGABYTE_IN_BYTES)
1128                     );
1129                     break;
1130                 default:
1131                     return StatsManager.PULL_SKIP;
1132             }
1133             return StatsManager.PULL_SUCCESS;
1134         }
1135     }
1136 }
1137