• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.android.server.usage;
18 
19 import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
20 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
21 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
22 import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
23 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
24 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
25 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
26 import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
27 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
28 import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
29 import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
30 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
31 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
32 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
33 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
34 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
35 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
36 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
37 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
38 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
39 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
40 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
41 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
42 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
43 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED;
44 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
45 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
46 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
47 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
48 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
49 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
50 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
51 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
52 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
53 
54 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
55 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
56 
57 import android.annotation.NonNull;
58 import android.annotation.Nullable;
59 import android.annotation.UserIdInt;
60 import android.app.ActivityManager;
61 import android.app.usage.AppStandbyInfo;
62 import android.app.usage.UsageEvents;
63 import android.app.usage.UsageStatsManager.StandbyBuckets;
64 import android.app.usage.UsageStatsManager.SystemForcedReasons;
65 import android.app.usage.UsageStatsManagerInternal;
66 import android.appwidget.AppWidgetManager;
67 import android.content.BroadcastReceiver;
68 import android.content.ContentResolver;
69 import android.content.Context;
70 import android.content.Intent;
71 import android.content.IntentFilter;
72 import android.content.pm.ApplicationInfo;
73 import android.content.pm.CrossProfileAppsInternal;
74 import android.content.pm.PackageInfo;
75 import android.content.pm.PackageManager;
76 import android.content.pm.PackageManagerInternal;
77 import android.database.ContentObserver;
78 import android.hardware.display.DisplayManager;
79 import android.net.NetworkScoreManager;
80 import android.os.BatteryManager;
81 import android.os.BatteryStats;
82 import android.os.Build;
83 import android.os.Environment;
84 import android.os.Handler;
85 import android.os.IDeviceIdleController;
86 import android.os.Looper;
87 import android.os.Message;
88 import android.os.PowerManager;
89 import android.os.Process;
90 import android.os.RemoteException;
91 import android.os.ServiceManager;
92 import android.os.SystemClock;
93 import android.os.Trace;
94 import android.os.UserHandle;
95 import android.provider.DeviceConfig;
96 import android.provider.Settings.Global;
97 import android.telephony.TelephonyManager;
98 import android.util.ArraySet;
99 import android.util.IndentingPrintWriter;
100 import android.util.Slog;
101 import android.util.SparseArray;
102 import android.util.SparseBooleanArray;
103 import android.util.TimeUtils;
104 import android.view.Display;
105 import android.widget.Toast;
106 
107 import com.android.internal.R;
108 import com.android.internal.annotations.GuardedBy;
109 import com.android.internal.annotations.VisibleForTesting;
110 import com.android.internal.app.IBatteryStats;
111 import com.android.internal.util.ArrayUtils;
112 import com.android.internal.util.ConcurrentUtils;
113 import com.android.server.AlarmManagerInternal;
114 import com.android.server.JobSchedulerBackgroundThread;
115 import com.android.server.LocalServices;
116 import com.android.server.pm.parsing.pkg.AndroidPackage;
117 import com.android.server.usage.AppIdleHistory.AppUsageHistory;
118 
119 import libcore.util.EmptyArray;
120 
121 import java.io.File;
122 import java.io.PrintWriter;
123 import java.util.ArrayList;
124 import java.util.Arrays;
125 import java.util.Collections;
126 import java.util.List;
127 import java.util.Set;
128 import java.util.concurrent.CountDownLatch;
129 
130 /**
131  * Manages the standby state of an app, listening to various events.
132  *
133  * Unit test:
134  * atest com.android.server.usage.AppStandbyControllerTests
135  */
136 public class AppStandbyController
137         implements AppStandbyInternal, UsageStatsManagerInternal.UsageEventListener {
138 
139     private static final String TAG = "AppStandbyController";
140     // Do not submit with true.
141     static final boolean DEBUG = false;
142 
143     static final boolean COMPRESS_TIME = false;
144     private static final long ONE_MINUTE = 60 * 1000;
145     private static final long ONE_HOUR = ONE_MINUTE * 60;
146     private static final long ONE_DAY = ONE_HOUR * 24;
147 
148     /**
149      * The default minimum amount of time the screen must have been on before an app can time out
150      * from its current bucket to the next bucket.
151      */
152     @VisibleForTesting
153     static final long[] DEFAULT_SCREEN_TIME_THRESHOLDS = {
154             0,
155             0,
156             COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR,
157             COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR,
158             COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR
159     };
160 
161     /** The minimum allowed values for each index in {@link #DEFAULT_SCREEN_TIME_THRESHOLDS}. */
162     @VisibleForTesting
163     static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME
164             ? new long[DEFAULT_SCREEN_TIME_THRESHOLDS.length]
165             : new long[]{
166                     0,
167                     0,
168                     0,
169                     30 * ONE_MINUTE,
170                     ONE_HOUR
171             };
172 
173     /**
174      * The default minimum amount of elapsed time that must have passed before an app can time out
175      * from its current bucket to the next bucket.
176      */
177     @VisibleForTesting
178     static final long[] DEFAULT_ELAPSED_TIME_THRESHOLDS = {
179             0,
180             COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
181             COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
182             COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
183             COMPRESS_TIME ? 32 * ONE_MINUTE : 45 * ONE_DAY
184     };
185 
186     /** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
187     @VisibleForTesting
188     static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME
189             ? new long[DEFAULT_ELAPSED_TIME_THRESHOLDS.length]
190             : new long[]{
191                     0,
192                     ONE_HOUR,
193                     ONE_HOUR,
194                     2 * ONE_HOUR,
195                     4 * ONE_HOUR
196             };
197 
198     private static final int[] THRESHOLD_BUCKETS = {
199             STANDBY_BUCKET_ACTIVE,
200             STANDBY_BUCKET_WORKING_SET,
201             STANDBY_BUCKET_FREQUENT,
202             STANDBY_BUCKET_RARE,
203             STANDBY_BUCKET_RESTRICTED
204     };
205 
206     /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
207     private static final long DEFAULT_PREDICTION_TIMEOUT =
208             COMPRESS_TIME ? 10 * ONE_MINUTE : 12 * ONE_HOUR;
209 
210     /**
211      * Indicates the maximum wait time for admin data to be available;
212      */
213     private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
214 
215     private static final int HEADLESS_APP_CHECK_FLAGS =
216             PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
217                     | PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS;
218 
219     // To name the lock for stack traces
220     static class Lock {}
221 
222     /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
223     private final Object mAppIdleLock = new Lock();
224 
225     /** Keeps the history and state for each app. */
226     @GuardedBy("mAppIdleLock")
227     private AppIdleHistory mAppIdleHistory;
228 
229     @GuardedBy("mPackageAccessListeners")
230     private final ArrayList<AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>();
231 
232     /**
233      * Lock specifically for bookkeeping around the carrier-privileged app set.
234      * Do not acquire any other locks while holding this one.  Methods that
235      * require this lock to be held are named with a "CPL" suffix.
236      */
237     private final Object mCarrierPrivilegedLock = new Lock();
238 
239     /** Whether we've queried the list of carrier privileged apps. */
240     @GuardedBy("mCarrierPrivilegedLock")
241     private boolean mHaveCarrierPrivilegedApps;
242 
243     /** List of carrier-privileged apps that should be excluded from standby */
244     @GuardedBy("mCarrierPrivilegedLock")
245     private List<String> mCarrierPrivilegedApps;
246 
247     @GuardedBy("mActiveAdminApps")
248     private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
249 
250     /**
251      * Set of system apps that are headless (don't have any declared activities, enabled or
252      * disabled). Presence in this map indicates that the app is a headless system app.
253      */
254     @GuardedBy("mHeadlessSystemApps")
255     private final ArraySet<String> mHeadlessSystemApps = new ArraySet<>();
256 
257     private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
258 
259     // Cache the active network scorer queried from the network scorer service
260     private volatile String mCachedNetworkScorer = null;
261     // The last time the network scorer service was queried
262     private volatile long mCachedNetworkScorerAtMillis = 0L;
263     // How long before querying the network scorer again. During this time, subsequent queries will
264     // get the cached value
265     private static final long NETWORK_SCORER_CACHE_DURATION_MILLIS = 5000L;
266 
267     // Messages for the handler
268     static final int MSG_INFORM_LISTENERS = 3;
269     static final int MSG_FORCE_IDLE_STATE = 4;
270     static final int MSG_CHECK_IDLE_STATES = 5;
271     static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
272     static final int MSG_PAROLE_STATE_CHANGED = 9;
273     static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
274     /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
275     static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
276     static final int MSG_REPORT_SYNC_SCHEDULED = 12;
277     static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
278 
279     long mCheckIdleIntervalMillis = Math.min(DEFAULT_ELAPSED_TIME_THRESHOLDS[1] / 4,
280             ConstantsObserver.DEFAULT_CHECK_IDLE_INTERVAL_MS);
281     /**
282      * The minimum amount of time the screen must have been on before an app can time out from its
283      * current bucket to the next bucket.
284      */
285     long[] mAppStandbyScreenThresholds = DEFAULT_SCREEN_TIME_THRESHOLDS;
286     /**
287      * The minimum amount of elapsed time that must have passed before an app can time out from its
288      * current bucket to the next bucket.
289      */
290     long[] mAppStandbyElapsedThresholds = DEFAULT_ELAPSED_TIME_THRESHOLDS;
291     /** Minimum time a strong usage event should keep the bucket elevated. */
292     long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_STRONG_USAGE_TIMEOUT;
293     /** Minimum time a notification seen event should keep the bucket elevated. */
294     long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT;
295     /** Minimum time a system update event should keep the buckets elevated. */
296     long mSystemUpdateUsageTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_UPDATE_TIMEOUT;
297     /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
298     long mPredictionTimeoutMillis = DEFAULT_PREDICTION_TIMEOUT;
299     /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
300     long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_SYNC_ADAPTER_TIMEOUT;
301     /**
302      * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
303      * non-doze
304      */
305     long mExemptedSyncScheduledNonDozeTimeoutMillis =
306             ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT;
307     /**
308      * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
309      * doze
310      */
311     long mExemptedSyncScheduledDozeTimeoutMillis =
312             ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT;
313     /**
314      * Maximum time an exempted sync should keep the buckets elevated, when sync is started.
315      */
316     long mExemptedSyncStartTimeoutMillis = ConstantsObserver.DEFAULT_EXEMPTED_SYNC_START_TIMEOUT;
317     /**
318      * Maximum time an unexempted sync should keep the buckets elevated, when sync is scheduled
319      */
320     long mUnexemptedSyncScheduledTimeoutMillis =
321             ConstantsObserver.DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT;
322     /** Maximum time a system interaction should keep the buckets elevated. */
323     long mSystemInteractionTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_INTERACTION_TIMEOUT;
324     /**
325      * Maximum time a foreground service start should keep the buckets elevated if the service
326      * start is the first usage of the app
327      */
328     long mInitialForegroundServiceStartTimeoutMillis =
329             ConstantsObserver.DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT;
330     /**
331      * User usage that would elevate an app's standby bucket will also elevate the standby bucket of
332      * cross profile connected apps. Explicit standby bucket setting via
333      * {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated.
334      */
335     boolean mLinkCrossProfileApps =
336             ConstantsObserver.DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS;
337     /**
338      * Whether we should allow apps into the
339      * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
340      * If false, any attempts to put an app into the bucket will put the app into the
341      * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE} bucket instead.
342      */
343     private boolean mAllowRestrictedBucket;
344 
345     private volatile boolean mAppIdleEnabled;
346     private boolean mIsCharging;
347     private boolean mSystemServicesReady = false;
348     // There was a system update, defaults need to be initialized after services are ready
349     private boolean mPendingInitializeDefaults;
350 
351     private volatile boolean mPendingOneTimeCheckIdleStates;
352 
353     private final AppStandbyHandler mHandler;
354     private final Context mContext;
355 
356     private AppWidgetManager mAppWidgetManager;
357     private PackageManager mPackageManager;
358     Injector mInjector;
359 
360     private static class Pool<T> {
361         private final T[] mArray;
362         private int mSize = 0;
363 
Pool(T[] array)364         Pool(T[] array) {
365             mArray = array;
366         }
367 
368         @Nullable
obtain()369         synchronized T obtain() {
370             return mSize > 0 ? mArray[--mSize] : null;
371         }
372 
recycle(T instance)373         synchronized void recycle(T instance) {
374             if (mSize < mArray.length) {
375                 mArray[mSize++] = instance;
376             }
377         }
378     }
379 
380     private static class StandbyUpdateRecord {
381         private static final Pool<StandbyUpdateRecord> sPool =
382                 new Pool<>(new StandbyUpdateRecord[10]);
383 
384         // Identity of the app whose standby state has changed
385         String packageName;
386         int userId;
387 
388         // What the standby bucket the app is now in
389         int bucket;
390 
391         // Whether the bucket change is because the user has started interacting with the app
392         boolean isUserInteraction;
393 
394         // Reason for bucket change
395         int reason;
396 
obtain(String pkgName, int userId, int bucket, int reason, boolean isInteraction)397         public static StandbyUpdateRecord obtain(String pkgName, int userId,
398                 int bucket, int reason, boolean isInteraction) {
399             StandbyUpdateRecord r = sPool.obtain();
400             if (r == null) {
401                 r = new StandbyUpdateRecord();
402             }
403             r.packageName = pkgName;
404             r.userId = userId;
405             r.bucket = bucket;
406             r.reason = reason;
407             r.isUserInteraction = isInteraction;
408             return r;
409 
410         }
411 
recycle()412         public void recycle() {
413             sPool.recycle(this);
414         }
415     }
416 
417     private static class ContentProviderUsageRecord {
418         private static final Pool<ContentProviderUsageRecord> sPool =
419                 new Pool<>(new ContentProviderUsageRecord[10]);
420 
421         public String name;
422         public String packageName;
423         public int userId;
424 
obtain(String name, String packageName, int userId)425         public static ContentProviderUsageRecord obtain(String name, String packageName,
426                 int userId) {
427             ContentProviderUsageRecord r = sPool.obtain();
428             if (r == null) {
429                 r = new ContentProviderUsageRecord();
430             }
431             r.name = name;
432             r.packageName = packageName;
433             r.userId = userId;
434             return r;
435         }
436 
recycle()437         public void recycle() {
438             sPool.recycle(this);
439         }
440     }
441 
AppStandbyController(Context context)442     public AppStandbyController(Context context) {
443         this(new Injector(context, JobSchedulerBackgroundThread.get().getLooper()));
444     }
445 
AppStandbyController(Injector injector)446     AppStandbyController(Injector injector) {
447         mInjector = injector;
448         mContext = mInjector.getContext();
449         mHandler = new AppStandbyHandler(mInjector.getLooper());
450         mPackageManager = mContext.getPackageManager();
451 
452         DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver();
453         IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
454         deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
455         deviceStates.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
456         mContext.registerReceiver(deviceStateReceiver, deviceStates);
457 
458         synchronized (mAppIdleLock) {
459             mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
460                     mInjector.elapsedRealtime());
461         }
462 
463         IntentFilter packageFilter = new IntentFilter();
464         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
465         packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
466         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
467         packageFilter.addDataScheme("package");
468 
469         mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
470                 null, mHandler);
471     }
472 
473     @VisibleForTesting
setAppIdleEnabled(boolean enabled)474     void setAppIdleEnabled(boolean enabled) {
475         // Don't call out to USM with the lock held. Also, register the listener before we
476         // change our internal state so no events fall through the cracks.
477         final UsageStatsManagerInternal usmi =
478                 LocalServices.getService(UsageStatsManagerInternal.class);
479         if (enabled) {
480             usmi.registerListener(this);
481         } else {
482             usmi.unregisterListener(this);
483         }
484 
485         synchronized (mAppIdleLock) {
486             if (mAppIdleEnabled != enabled) {
487                 final boolean oldParoleState = isInParole();
488                 mAppIdleEnabled = enabled;
489 
490                 if (isInParole() != oldParoleState) {
491                     postParoleStateChanged();
492                 }
493             }
494         }
495     }
496 
497     @Override
isAppIdleEnabled()498     public boolean isAppIdleEnabled() {
499         return mAppIdleEnabled;
500     }
501 
502     @Override
onBootPhase(int phase)503     public void onBootPhase(int phase) {
504         mInjector.onBootPhase(phase);
505         if (phase == PHASE_SYSTEM_SERVICES_READY) {
506             Slog.d(TAG, "Setting app idle enabled state");
507 
508             if (mAppIdleEnabled) {
509                 LocalServices.getService(UsageStatsManagerInternal.class).registerListener(this);
510             }
511 
512             // Observe changes to the threshold
513             ConstantsObserver settingsObserver = new ConstantsObserver(mHandler);
514             settingsObserver.start();
515 
516             mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
517 
518             mInjector.registerDisplayListener(mDisplayListener, mHandler);
519             synchronized (mAppIdleLock) {
520                 mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
521             }
522 
523             mSystemServicesReady = true;
524 
525             boolean userFileExists;
526             synchronized (mAppIdleLock) {
527                 userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
528             }
529 
530             if (mPendingInitializeDefaults || !userFileExists) {
531                 initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
532             }
533 
534             if (mPendingOneTimeCheckIdleStates) {
535                 postOneTimeCheckIdleStates();
536             }
537         } else if (phase == PHASE_BOOT_COMPLETED) {
538             setChargingState(mInjector.isCharging());
539 
540             // Offload to handler thread after boot completed to avoid boot time impact. This means
541             // that app standby buckets may be slightly out of date and headless system apps may be
542             // put in a lower bucket until boot has completed.
543             mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
544             mHandler.post(this::loadHeadlessSystemAppCache);
545         }
546     }
547 
reportContentProviderUsage(String authority, String providerPkgName, int userId)548     private void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
549         if (!mAppIdleEnabled) return;
550 
551         // Get sync adapters for the authority
552         String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
553                 authority, userId);
554         final long elapsedRealtime = mInjector.elapsedRealtime();
555         for (String packageName: packages) {
556             // Only force the sync adapters to active if the provider is not in the same package and
557             // the sync adapter is a system package.
558             try {
559                 PackageInfo pi = mPackageManager.getPackageInfoAsUser(
560                         packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
561                 if (pi == null || pi.applicationInfo == null) {
562                     continue;
563                 }
564                 if (!packageName.equals(providerPkgName)) {
565                     final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName,
566                             userId);
567                     synchronized (mAppIdleLock) {
568                         reportNoninteractiveUsageCrossUserLocked(packageName, userId,
569                                 STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
570                                 elapsedRealtime, mSyncAdapterTimeoutMillis, linkedProfiles);
571                     }
572                 }
573             } catch (PackageManager.NameNotFoundException e) {
574                 // Shouldn't happen
575             }
576         }
577     }
578 
reportExemptedSyncScheduled(String packageName, int userId)579     private void reportExemptedSyncScheduled(String packageName, int userId) {
580         if (!mAppIdleEnabled) return;
581 
582         final int bucketToPromote;
583         final int usageReason;
584         final long durationMillis;
585 
586         if (!mInjector.isDeviceIdleMode()) {
587             // Not dozing.
588             bucketToPromote = STANDBY_BUCKET_ACTIVE;
589             usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
590             durationMillis = mExemptedSyncScheduledNonDozeTimeoutMillis;
591         } else {
592             // Dozing.
593             bucketToPromote = STANDBY_BUCKET_WORKING_SET;
594             usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
595             durationMillis = mExemptedSyncScheduledDozeTimeoutMillis;
596         }
597 
598         final long elapsedRealtime = mInjector.elapsedRealtime();
599         final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
600         synchronized (mAppIdleLock) {
601             reportNoninteractiveUsageCrossUserLocked(packageName, userId, bucketToPromote,
602                     usageReason, elapsedRealtime, durationMillis, linkedProfiles);
603         }
604     }
605 
reportUnexemptedSyncScheduled(String packageName, int userId)606     private void reportUnexemptedSyncScheduled(String packageName, int userId) {
607         if (!mAppIdleEnabled) return;
608 
609         final long elapsedRealtime = mInjector.elapsedRealtime();
610         synchronized (mAppIdleLock) {
611             final int currentBucket =
612                     mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
613             if (currentBucket == STANDBY_BUCKET_NEVER) {
614                 final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
615                 // Bring the app out of the never bucket
616                 reportNoninteractiveUsageCrossUserLocked(packageName, userId,
617                         STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED,
618                         elapsedRealtime, mUnexemptedSyncScheduledTimeoutMillis, linkedProfiles);
619             }
620         }
621     }
622 
reportExemptedSyncStart(String packageName, int userId)623     private void reportExemptedSyncStart(String packageName, int userId) {
624         if (!mAppIdleEnabled) return;
625 
626         final long elapsedRealtime = mInjector.elapsedRealtime();
627         final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
628         synchronized (mAppIdleLock) {
629             reportNoninteractiveUsageCrossUserLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
630                     REASON_SUB_USAGE_EXEMPTED_SYNC_START, elapsedRealtime,
631                     mExemptedSyncStartTimeoutMillis, linkedProfiles);
632         }
633     }
634 
635     /**
636      * Helper method to report indirect user usage of an app and handle reporting the usage
637      * against cross profile connected apps. <br>
638      * Use {@link #reportNoninteractiveUsageLocked(String, int, int, int, long, long)} if
639      * cross profile connected apps do not need to be handled.
640      */
reportNoninteractiveUsageCrossUserLocked(String packageName, int userId, int bucket, int subReason, long elapsedRealtime, long nextCheckDelay, List<UserHandle> otherProfiles)641     private void reportNoninteractiveUsageCrossUserLocked(String packageName, int userId,
642             int bucket, int subReason, long elapsedRealtime, long nextCheckDelay,
643             List<UserHandle> otherProfiles) {
644         reportNoninteractiveUsageLocked(packageName, userId, bucket, subReason, elapsedRealtime,
645                 nextCheckDelay);
646         final int size = otherProfiles.size();
647         for (int profileIndex = 0; profileIndex < size; profileIndex++) {
648             final int otherUserId = otherProfiles.get(profileIndex).getIdentifier();
649             reportNoninteractiveUsageLocked(packageName, otherUserId, bucket, subReason,
650                     elapsedRealtime, nextCheckDelay);
651         }
652     }
653 
654     /**
655      * Helper method to report indirect user usage of an app. <br>
656      * Use
657      * {@link #reportNoninteractiveUsageCrossUserLocked(String, int, int, int, long, long, List)}
658      * if cross profile connected apps need to be handled.
659      */
reportNoninteractiveUsageLocked(String packageName, int userId, int bucket, int subReason, long elapsedRealtime, long nextCheckDelay)660     private void reportNoninteractiveUsageLocked(String packageName, int userId, int bucket,
661             int subReason, long elapsedRealtime, long nextCheckDelay) {
662         final AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, bucket,
663                 subReason, 0, elapsedRealtime + nextCheckDelay);
664         mHandler.sendMessageDelayed(
665                 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
666                 nextCheckDelay);
667         maybeInformListeners(packageName, userId, elapsedRealtime, appUsage.currentBucket,
668                 appUsage.bucketingReason, false);
669     }
670 
671     @VisibleForTesting
setChargingState(boolean isCharging)672     void setChargingState(boolean isCharging) {
673         synchronized (mAppIdleLock) {
674             if (mIsCharging != isCharging) {
675                 if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging);
676                 mIsCharging = isCharging;
677                 postParoleStateChanged();
678             }
679         }
680     }
681 
682     @Override
isInParole()683     public boolean isInParole() {
684         return !mAppIdleEnabled || mIsCharging;
685     }
686 
postParoleStateChanged()687     private void postParoleStateChanged() {
688         if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
689         mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
690         mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
691     }
692 
693     @Override
postCheckIdleStates(int userId)694     public void postCheckIdleStates(int userId) {
695         mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
696     }
697 
698     @Override
postOneTimeCheckIdleStates()699     public void postOneTimeCheckIdleStates() {
700         if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
701             // Not booted yet; wait for it!
702             mPendingOneTimeCheckIdleStates = true;
703         } else {
704             mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
705             mPendingOneTimeCheckIdleStates = false;
706         }
707     }
708 
709     @VisibleForTesting
checkIdleStates(int checkUserId)710     boolean checkIdleStates(int checkUserId) {
711         if (!mAppIdleEnabled) {
712             return false;
713         }
714 
715         final int[] runningUserIds;
716         try {
717             runningUserIds = mInjector.getRunningUserIds();
718             if (checkUserId != UserHandle.USER_ALL
719                     && !ArrayUtils.contains(runningUserIds, checkUserId)) {
720                 return false;
721             }
722         } catch (RemoteException re) {
723             throw re.rethrowFromSystemServer();
724         }
725 
726         final long elapsedRealtime = mInjector.elapsedRealtime();
727         for (int i = 0; i < runningUserIds.length; i++) {
728             final int userId = runningUserIds[i];
729             if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
730                 continue;
731             }
732             if (DEBUG) {
733                 Slog.d(TAG, "Checking idle state for user " + userId);
734             }
735             List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
736                     PackageManager.MATCH_DISABLED_COMPONENTS,
737                     userId);
738             final int packageCount = packages.size();
739             for (int p = 0; p < packageCount; p++) {
740                 final PackageInfo pi = packages.get(p);
741                 final String packageName = pi.packageName;
742                 checkAndUpdateStandbyState(packageName, userId, pi.applicationInfo.uid,
743                         elapsedRealtime);
744             }
745         }
746         if (DEBUG) {
747             Slog.d(TAG, "checkIdleStates took "
748                     + (mInjector.elapsedRealtime() - elapsedRealtime));
749         }
750         return true;
751     }
752 
753     /** Check if we need to update the standby state of a specific app. */
checkAndUpdateStandbyState(String packageName, @UserIdInt int userId, int uid, long elapsedRealtime)754     private void checkAndUpdateStandbyState(String packageName, @UserIdInt int userId,
755             int uid, long elapsedRealtime) {
756         if (uid <= 0) {
757             try {
758                 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
759             } catch (PackageManager.NameNotFoundException e) {
760                 // Not a valid package for this user, nothing to do
761                 // TODO: Remove any history of removed packages
762                 return;
763             }
764         }
765         final int minBucket = getAppMinBucket(packageName,
766                 UserHandle.getAppId(uid),
767                 userId);
768         if (DEBUG) {
769             Slog.d(TAG, "   Checking idle state for " + packageName
770                     + " minBucket=" + minBucket);
771         }
772         if (minBucket <= STANDBY_BUCKET_ACTIVE) {
773             // No extra processing needed for ACTIVE or higher since apps can't drop into lower
774             // buckets.
775             synchronized (mAppIdleLock) {
776                 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
777                         minBucket, REASON_MAIN_DEFAULT);
778             }
779             maybeInformListeners(packageName, userId, elapsedRealtime,
780                     minBucket, REASON_MAIN_DEFAULT, false);
781         } else {
782             synchronized (mAppIdleLock) {
783                 final AppIdleHistory.AppUsageHistory app =
784                         mAppIdleHistory.getAppUsageHistory(packageName,
785                         userId, elapsedRealtime);
786                 int reason = app.bucketingReason;
787                 final int oldMainReason = reason & REASON_MAIN_MASK;
788 
789                 // If the bucket was forced by the user/developer, leave it alone.
790                 // A usage event will be the only way to bring it out of this forced state
791                 if (oldMainReason == REASON_MAIN_FORCED_BY_USER) {
792                     return;
793                 }
794                 final int oldBucket = app.currentBucket;
795                 if (oldBucket == STANDBY_BUCKET_NEVER) {
796                     // None of this should bring an app out of the NEVER bucket.
797                     return;
798                 }
799                 int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
800                 boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
801                 // Compute age-based bucket
802                 if (oldMainReason == REASON_MAIN_DEFAULT
803                         || oldMainReason == REASON_MAIN_USAGE
804                         || oldMainReason == REASON_MAIN_TIMEOUT
805                         || predictionLate) {
806 
807                     if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
808                             && app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
809                         newBucket = app.lastPredictedBucket;
810                         reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
811                         if (DEBUG) {
812                             Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
813                         }
814                     } else {
815                         newBucket = getBucketForLocked(packageName, userId,
816                                 elapsedRealtime);
817                         if (DEBUG) {
818                             Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
819                         }
820                         reason = REASON_MAIN_TIMEOUT;
821                     }
822                 }
823 
824                 // Check if the app is within one of the timeouts for forced bucket elevation
825                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
826                 if (newBucket >= STANDBY_BUCKET_ACTIVE
827                         && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
828                     newBucket = STANDBY_BUCKET_ACTIVE;
829                     reason = app.bucketingReason;
830                     if (DEBUG) {
831                         Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
832                     }
833                 } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
834                         && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
835                     newBucket = STANDBY_BUCKET_WORKING_SET;
836                     // If it was already there, keep the reason, else assume timeout to WS
837                     reason = (newBucket == oldBucket)
838                             ? app.bucketingReason
839                             : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
840                     if (DEBUG) {
841                         Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
842                     }
843                 }
844 
845                 if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
846                         && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
847                         >= mInjector.getAutoRestrictedBucketDelayMs()) {
848                     newBucket = STANDBY_BUCKET_RESTRICTED;
849                     reason = app.lastRestrictReason;
850                     if (DEBUG) {
851                         Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
852                     }
853                 }
854                 if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
855                     newBucket = STANDBY_BUCKET_RARE;
856                     // Leave the reason alone.
857                     if (DEBUG) {
858                         Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch");
859                     }
860                 }
861                 if (newBucket > minBucket) {
862                     newBucket = minBucket;
863                     // Leave the reason alone.
864                     if (DEBUG) {
865                         Slog.d(TAG, "Bringing up from " + newBucket + " to " + minBucket
866                                 + " due to min bucketing");
867                     }
868                 }
869                 if (DEBUG) {
870                     Slog.d(TAG, "     Old bucket=" + oldBucket
871                             + ", newBucket=" + newBucket);
872                 }
873                 if (oldBucket != newBucket || predictionLate) {
874                     mAppIdleHistory.setAppStandbyBucket(packageName, userId,
875                             elapsedRealtime, newBucket, reason);
876                     maybeInformListeners(packageName, userId, elapsedRealtime,
877                             newBucket, reason, false);
878                 }
879             }
880         }
881     }
882 
883     /** Returns true if there hasn't been a prediction for the app in a while. */
predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime)884     private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
885         return app.lastPredictedTime > 0
886                 && mAppIdleHistory.getElapsedTime(elapsedRealtime)
887                     - app.lastPredictedTime > mPredictionTimeoutMillis;
888     }
889 
890     /** Inform listeners if the bucket has changed since it was last reported to listeners */
maybeInformListeners(String packageName, int userId, long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting)891     private void maybeInformListeners(String packageName, int userId,
892             long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
893         synchronized (mAppIdleLock) {
894             if (mAppIdleHistory.shouldInformListeners(packageName, userId,
895                     elapsedRealtime, bucket)) {
896                 final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
897                         bucket, reason, userStartedInteracting);
898                 if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
899                 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
900             }
901         }
902     }
903 
904     /**
905      * Evaluates next bucket based on time since last used and the bucketing thresholds.
906      * @param packageName the app
907      * @param userId the user
908      * @param elapsedRealtime as the name suggests, current elapsed time
909      * @return the bucket for the app, based on time since last used
910      */
911     @GuardedBy("mAppIdleLock")
912     @StandbyBuckets
getBucketForLocked(String packageName, int userId, long elapsedRealtime)913     private int getBucketForLocked(String packageName, int userId,
914             long elapsedRealtime) {
915         int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
916                 elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
917         return THRESHOLD_BUCKETS[bucketIndex];
918     }
919 
notifyBatteryStats(String packageName, int userId, boolean idle)920     private void notifyBatteryStats(String packageName, int userId, boolean idle) {
921         try {
922             final int uid = mPackageManager.getPackageUidAsUser(packageName,
923                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
924             if (idle) {
925                 mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
926                         packageName, uid);
927             } else {
928                 mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
929                         packageName, uid);
930             }
931         } catch (PackageManager.NameNotFoundException | RemoteException e) {
932         }
933     }
934 
935     /**
936      * Callback to inform listeners of a new event.
937      */
onUsageEvent(int userId, @NonNull UsageEvents.Event event)938     public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
939         if (!mAppIdleEnabled) return;
940         final int eventType = event.getEventType();
941         if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
942                 || eventType == UsageEvents.Event.ACTIVITY_PAUSED
943                 || eventType == UsageEvents.Event.SYSTEM_INTERACTION
944                 || eventType == UsageEvents.Event.USER_INTERACTION
945                 || eventType == UsageEvents.Event.NOTIFICATION_SEEN
946                 || eventType == UsageEvents.Event.SLICE_PINNED
947                 || eventType == UsageEvents.Event.SLICE_PINNED_PRIV
948                 || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
949             final String pkg = event.getPackageName();
950             final List<UserHandle> linkedProfiles = getCrossProfileTargets(pkg, userId);
951             synchronized (mAppIdleLock) {
952                 final long elapsedRealtime = mInjector.elapsedRealtime();
953                 reportEventLocked(pkg, eventType, elapsedRealtime, userId);
954 
955                 final int size = linkedProfiles.size();
956                 for (int profileIndex = 0; profileIndex < size; profileIndex++) {
957                     final int linkedUserId = linkedProfiles.get(profileIndex).getIdentifier();
958                     reportEventLocked(pkg, eventType, elapsedRealtime, linkedUserId);
959                 }
960             }
961         }
962     }
963 
reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId)964     private void reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId) {
965         // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
966         // about apps that are on some kind of whitelist anyway.
967         final boolean previouslyIdle = mAppIdleHistory.isIdle(
968                 pkg, userId, elapsedRealtime);
969 
970         final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
971                 pkg, userId, elapsedRealtime);
972         final int prevBucket = appHistory.currentBucket;
973         final int prevBucketReason = appHistory.bucketingReason;
974         final long nextCheckDelay;
975         final int subReason = usageEventToSubReason(eventType);
976         final int reason = REASON_MAIN_USAGE | subReason;
977         if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
978                 || eventType == UsageEvents.Event.SLICE_PINNED) {
979             // Mild usage elevates to WORKING_SET but doesn't change usage time.
980             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
981                     STANDBY_BUCKET_WORKING_SET, subReason,
982                     0, elapsedRealtime + mNotificationSeenTimeoutMillis);
983             nextCheckDelay = mNotificationSeenTimeoutMillis;
984         } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
985             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
986                     STANDBY_BUCKET_ACTIVE, subReason,
987                     0, elapsedRealtime + mSystemInteractionTimeoutMillis);
988             nextCheckDelay = mSystemInteractionTimeoutMillis;
989         } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
990             // Only elevate bucket if this is the first usage of the app
991             if (prevBucket != STANDBY_BUCKET_NEVER) return;
992             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
993                     STANDBY_BUCKET_ACTIVE, subReason,
994                     0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
995             nextCheckDelay = mInitialForegroundServiceStartTimeoutMillis;
996         } else {
997             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
998                     STANDBY_BUCKET_ACTIVE, subReason,
999                     elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
1000             nextCheckDelay = mStrongUsageTimeoutMillis;
1001         }
1002         if (appHistory.currentBucket != prevBucket) {
1003             mHandler.sendMessageDelayed(
1004                     mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
1005                     nextCheckDelay);
1006             final boolean userStartedInteracting =
1007                     appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
1008                             && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
1009             maybeInformListeners(pkg, userId, elapsedRealtime,
1010                     appHistory.currentBucket, reason, userStartedInteracting);
1011         }
1012 
1013         if (previouslyIdle) {
1014             notifyBatteryStats(pkg, userId, false);
1015         }
1016     }
1017 
1018     /**
1019      * Note: don't call this with the lock held since it makes calls to other system services.
1020      */
getCrossProfileTargets(String pkg, int userId)1021     private @NonNull List<UserHandle> getCrossProfileTargets(String pkg, int userId) {
1022         synchronized (mAppIdleLock) {
1023             if (!mLinkCrossProfileApps) return Collections.emptyList();
1024         }
1025         return mInjector.getValidCrossProfileTargets(pkg, userId);
1026     }
1027 
usageEventToSubReason(int eventType)1028     private int usageEventToSubReason(int eventType) {
1029         switch (eventType) {
1030             case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
1031             case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
1032             case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
1033             case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
1034             case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
1035             case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
1036             case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
1037             case UsageEvents.Event.FOREGROUND_SERVICE_START:
1038                 return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
1039             default: return 0;
1040         }
1041     }
1042 
1043     @VisibleForTesting
forceIdleState(String packageName, int userId, boolean idle)1044     void forceIdleState(String packageName, int userId, boolean idle) {
1045         if (!mAppIdleEnabled) return;
1046 
1047         final int appId = getAppId(packageName);
1048         if (appId < 0) return;
1049         final long elapsedRealtime = mInjector.elapsedRealtime();
1050 
1051         final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
1052                 userId, elapsedRealtime);
1053         final int standbyBucket;
1054         synchronized (mAppIdleLock) {
1055             standbyBucket = mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
1056         }
1057         final boolean stillIdle = isAppIdleFiltered(packageName, appId,
1058                 userId, elapsedRealtime);
1059         // Inform listeners if necessary
1060         if (previouslyIdle != stillIdle) {
1061             maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
1062                     REASON_MAIN_FORCED_BY_USER, false);
1063             if (!stillIdle) {
1064                 notifyBatteryStats(packageName, userId, idle);
1065             }
1066         }
1067     }
1068 
1069     @Override
setLastJobRunTime(String packageName, int userId, long elapsedRealtime)1070     public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
1071         synchronized (mAppIdleLock) {
1072             mAppIdleHistory.setLastJobRunTime(packageName, userId, elapsedRealtime);
1073         }
1074     }
1075 
1076     @Override
getTimeSinceLastJobRun(String packageName, int userId)1077     public long getTimeSinceLastJobRun(String packageName, int userId) {
1078         final long elapsedRealtime = mInjector.elapsedRealtime();
1079         synchronized (mAppIdleLock) {
1080             return mAppIdleHistory.getTimeSinceLastJobRun(packageName, userId, elapsedRealtime);
1081         }
1082     }
1083 
1084     @Override
onUserRemoved(int userId)1085     public void onUserRemoved(int userId) {
1086         synchronized (mAppIdleLock) {
1087             mAppIdleHistory.onUserRemoved(userId);
1088             synchronized (mActiveAdminApps) {
1089                 mActiveAdminApps.remove(userId);
1090             }
1091         }
1092     }
1093 
isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime)1094     private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
1095         synchronized (mAppIdleLock) {
1096             return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
1097         }
1098     }
1099 
1100     @Override
addListener(AppIdleStateChangeListener listener)1101     public void addListener(AppIdleStateChangeListener listener) {
1102         synchronized (mPackageAccessListeners) {
1103             if (!mPackageAccessListeners.contains(listener)) {
1104                 mPackageAccessListeners.add(listener);
1105             }
1106         }
1107     }
1108 
1109     @Override
removeListener(AppIdleStateChangeListener listener)1110     public void removeListener(AppIdleStateChangeListener listener) {
1111         synchronized (mPackageAccessListeners) {
1112             mPackageAccessListeners.remove(listener);
1113         }
1114     }
1115 
1116     @Override
getAppId(String packageName)1117     public int getAppId(String packageName) {
1118         try {
1119             ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
1120                     PackageManager.MATCH_ANY_USER
1121                             | PackageManager.MATCH_DISABLED_COMPONENTS);
1122             return ai.uid;
1123         } catch (PackageManager.NameNotFoundException re) {
1124             return -1;
1125         }
1126     }
1127 
1128     @Override
isAppIdleFiltered(String packageName, int userId, long elapsedRealtime, boolean shouldObfuscateInstantApps)1129     public boolean isAppIdleFiltered(String packageName, int userId, long elapsedRealtime,
1130             boolean shouldObfuscateInstantApps) {
1131         if (shouldObfuscateInstantApps &&
1132                 mInjector.isPackageEphemeral(userId, packageName)) {
1133             return false;
1134         }
1135         return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
1136     }
1137 
1138     @StandbyBuckets
getAppMinBucket(String packageName, int userId)1139     private int getAppMinBucket(String packageName, int userId) {
1140         try {
1141             final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
1142             return getAppMinBucket(packageName, UserHandle.getAppId(uid), userId);
1143         } catch (PackageManager.NameNotFoundException e) {
1144             // Not a valid package for this user, nothing to do
1145             return STANDBY_BUCKET_NEVER;
1146         }
1147     }
1148 
1149     /**
1150      * Return the lowest bucket this app should ever enter.
1151      */
1152     @StandbyBuckets
getAppMinBucket(String packageName, int appId, int userId)1153     private int getAppMinBucket(String packageName, int appId, int userId) {
1154         if (packageName == null) return STANDBY_BUCKET_NEVER;
1155         // If not enabled at all, of course nobody is ever idle.
1156         if (!mAppIdleEnabled) {
1157             return STANDBY_BUCKET_EXEMPTED;
1158         }
1159         if (appId < Process.FIRST_APPLICATION_UID) {
1160             // System uids never go idle.
1161             return STANDBY_BUCKET_EXEMPTED;
1162         }
1163         if (packageName.equals("android")) {
1164             // Nor does the framework (which should be redundant with the above, but for MR1 we will
1165             // retain this for safety).
1166             return STANDBY_BUCKET_EXEMPTED;
1167         }
1168         if (mSystemServicesReady) {
1169             // We allow all whitelisted apps, including those that don't want to be whitelisted
1170             // for idle mode, because app idle (aka app standby) is really not as big an issue
1171             // for controlling who participates vs. doze mode.
1172             if (mInjector.isNonIdleWhitelisted(packageName)) {
1173                 return STANDBY_BUCKET_EXEMPTED;
1174             }
1175 
1176             if (isActiveDeviceAdmin(packageName, userId)) {
1177                 return STANDBY_BUCKET_EXEMPTED;
1178             }
1179 
1180             if (isActiveNetworkScorer(packageName)) {
1181                 return STANDBY_BUCKET_EXEMPTED;
1182             }
1183 
1184             if (mAppWidgetManager != null
1185                     && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
1186                 return STANDBY_BUCKET_ACTIVE;
1187             }
1188 
1189             if (isDeviceProvisioningPackage(packageName)) {
1190                 return STANDBY_BUCKET_EXEMPTED;
1191             }
1192 
1193             if (mInjector.isWellbeingPackage(packageName)) {
1194                 return STANDBY_BUCKET_WORKING_SET;
1195             }
1196 
1197             if (mInjector.hasScheduleExactAlarm(packageName, UserHandle.getUid(userId, appId))) {
1198                 return STANDBY_BUCKET_WORKING_SET;
1199             }
1200         }
1201 
1202         // Check this last, as it can be the most expensive check
1203         if (isCarrierApp(packageName)) {
1204             return STANDBY_BUCKET_EXEMPTED;
1205         }
1206 
1207         if (isHeadlessSystemApp(packageName)) {
1208             return STANDBY_BUCKET_ACTIVE;
1209         }
1210 
1211         if (mPackageManager.checkPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
1212                 packageName) == PERMISSION_GRANTED) {
1213             return STANDBY_BUCKET_FREQUENT;
1214         }
1215 
1216         return STANDBY_BUCKET_NEVER;
1217     }
1218 
isHeadlessSystemApp(String packageName)1219     private boolean isHeadlessSystemApp(String packageName) {
1220         synchronized (mHeadlessSystemApps) {
1221             return mHeadlessSystemApps.contains(packageName);
1222         }
1223     }
1224 
1225     @Override
isAppIdleFiltered(String packageName, int appId, int userId, long elapsedRealtime)1226     public boolean isAppIdleFiltered(String packageName, int appId, int userId,
1227             long elapsedRealtime) {
1228         if (getAppMinBucket(packageName, appId, userId) < AppIdleHistory.IDLE_BUCKET_CUTOFF) {
1229             return false;
1230         } else {
1231             synchronized (mAppIdleLock) {
1232                 if (!mAppIdleEnabled || mIsCharging) {
1233                     return false;
1234                 }
1235             }
1236             return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
1237         }
1238     }
1239 
isUserUsage(int reason)1240     static boolean isUserUsage(int reason) {
1241         if ((reason & REASON_MAIN_MASK) == REASON_MAIN_USAGE) {
1242             final int subReason = reason & REASON_SUB_MASK;
1243             return subReason == REASON_SUB_USAGE_USER_INTERACTION
1244                     || subReason == REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
1245         }
1246         return false;
1247     }
1248 
1249     @Override
getIdleUidsForUser(int userId)1250     public int[] getIdleUidsForUser(int userId) {
1251         if (!mAppIdleEnabled) {
1252             return EmptyArray.INT;
1253         }
1254 
1255         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getIdleUidsForUser");
1256 
1257         final long elapsedRealtime = mInjector.elapsedRealtime();
1258 
1259         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
1260         final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, Process.myUid());
1261         if (apps == null) {
1262             return EmptyArray.INT;
1263         }
1264 
1265         // State of each uid: Key is the uid, value is whether all the apps in that uid are idle.
1266         final SparseBooleanArray uidIdleStates = new SparseBooleanArray();
1267         int notIdleCount = 0;
1268         for (int i = apps.size() - 1; i >= 0; i--) {
1269             final ApplicationInfo ai = apps.get(i);
1270             final int index = uidIdleStates.indexOfKey(ai.uid);
1271 
1272             final boolean currentIdle = (index < 0) ? true : uidIdleStates.valueAt(index);
1273 
1274             final boolean newIdle = currentIdle && isAppIdleFiltered(ai.packageName,
1275                     UserHandle.getAppId(ai.uid), userId, elapsedRealtime);
1276 
1277             if (currentIdle && !newIdle) {
1278                 // This transition from true to false can happen at most once per uid in this loop.
1279                 notIdleCount++;
1280             }
1281             if (index < 0) {
1282                 uidIdleStates.put(ai.uid, newIdle);
1283             } else {
1284                 uidIdleStates.setValueAt(index, newIdle);
1285             }
1286         }
1287 
1288         int numIdleUids = uidIdleStates.size() - notIdleCount;
1289         final int[] idleUids = new int[numIdleUids];
1290         for (int i = uidIdleStates.size() - 1; i >= 0; i--) {
1291             if (uidIdleStates.valueAt(i)) {
1292                 idleUids[--numIdleUids] = uidIdleStates.keyAt(i);
1293             }
1294         }
1295         if (DEBUG) {
1296             Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
1297         }
1298         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1299 
1300         return idleUids;
1301     }
1302 
1303     @Override
setAppIdleAsync(String packageName, boolean idle, int userId)1304     public void setAppIdleAsync(String packageName, boolean idle, int userId) {
1305         if (packageName == null || !mAppIdleEnabled) return;
1306 
1307         mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
1308                 .sendToTarget();
1309     }
1310 
1311     @Override
getAppStandbyBucket(String packageName, int userId, long elapsedRealtime, boolean shouldObfuscateInstantApps)1312     @StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
1313             long elapsedRealtime, boolean shouldObfuscateInstantApps) {
1314         if (!mAppIdleEnabled || (shouldObfuscateInstantApps
1315                 && mInjector.isPackageEphemeral(userId, packageName))) {
1316             return STANDBY_BUCKET_ACTIVE;
1317         }
1318 
1319         synchronized (mAppIdleLock) {
1320             return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
1321         }
1322     }
1323 
1324     @VisibleForTesting
getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime)1325     int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) {
1326         synchronized (mAppIdleLock) {
1327             return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime);
1328         }
1329     }
1330 
1331     @Override
getAppStandbyBuckets(int userId)1332     public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
1333         synchronized (mAppIdleLock) {
1334             return mAppIdleHistory.getAppStandbyBuckets(userId, mAppIdleEnabled);
1335         }
1336     }
1337 
1338     @Override
restrictApp(@onNull String packageName, int userId, @SystemForcedReasons int restrictReason)1339     public void restrictApp(@NonNull String packageName, int userId,
1340             @SystemForcedReasons int restrictReason) {
1341         restrictApp(packageName, userId, REASON_MAIN_FORCED_BY_SYSTEM, restrictReason);
1342     }
1343 
1344     @Override
restrictApp(@onNull String packageName, int userId, int mainReason, @SystemForcedReasons int restrictReason)1345     public void restrictApp(@NonNull String packageName, int userId, int mainReason,
1346             @SystemForcedReasons int restrictReason) {
1347         if (mainReason != REASON_MAIN_FORCED_BY_SYSTEM
1348                 && mainReason != REASON_MAIN_FORCED_BY_USER) {
1349             Slog.e(TAG, "Tried to restrict app " + packageName + " for an unsupported reason");
1350             return;
1351         }
1352         // If the package is not installed, don't allow the bucket to be set.
1353         if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
1354             Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
1355             return;
1356         }
1357 
1358         final int reason = (REASON_MAIN_MASK & mainReason) | (REASON_SUB_MASK & restrictReason);
1359         final long nowElapsed = mInjector.elapsedRealtime();
1360         final int bucket = mAllowRestrictedBucket ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE;
1361         setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false);
1362     }
1363 
1364     @Override
setAppStandbyBucket(@onNull String packageName, int bucket, int userId, int callingUid, int callingPid)1365     public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
1366             int callingUid, int callingPid) {
1367         setAppStandbyBuckets(
1368                 Collections.singletonList(new AppStandbyInfo(packageName, bucket)),
1369                 userId, callingUid, callingPid);
1370     }
1371 
1372     @Override
setAppStandbyBuckets(@onNull List<AppStandbyInfo> appBuckets, int userId, int callingUid, int callingPid)1373     public void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId,
1374             int callingUid, int callingPid) {
1375         userId = ActivityManager.handleIncomingUser(
1376                 callingPid, callingUid, userId, false, true, "setAppStandbyBucket", null);
1377         final boolean shellCaller = callingUid == Process.ROOT_UID
1378                 || callingUid == Process.SHELL_UID;
1379         final int reason;
1380         // The Settings app runs in the system UID but in a separate process. Assume
1381         // things coming from other processes are due to the user.
1382         if ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) && callingPid != Process.myPid())
1383                 || shellCaller) {
1384             reason = REASON_MAIN_FORCED_BY_USER;
1385         } else if (UserHandle.isCore(callingUid)) {
1386             reason = REASON_MAIN_FORCED_BY_SYSTEM;
1387         } else {
1388             reason = REASON_MAIN_PREDICTED;
1389         }
1390         final int packageFlags = PackageManager.MATCH_ANY_USER
1391                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
1392                 | PackageManager.MATCH_DIRECT_BOOT_AWARE;
1393         final int numApps = appBuckets.size();
1394         final long elapsedRealtime = mInjector.elapsedRealtime();
1395         for (int i = 0; i < numApps; ++i) {
1396             final AppStandbyInfo bucketInfo = appBuckets.get(i);
1397             final String packageName = bucketInfo.mPackageName;
1398             final int bucket = bucketInfo.mStandbyBucket;
1399             if (bucket < STANDBY_BUCKET_ACTIVE || bucket > STANDBY_BUCKET_NEVER) {
1400                 throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
1401             }
1402             final int packageUid = mInjector.getPackageManagerInternal()
1403                     .getPackageUid(packageName, packageFlags, userId);
1404             // Caller cannot set their own standby state
1405             if (packageUid == callingUid) {
1406                 throw new IllegalArgumentException("Cannot set your own standby bucket");
1407             }
1408             if (packageUid < 0) {
1409                 throw new IllegalArgumentException(
1410                         "Cannot set standby bucket for non existent package (" + packageName + ")");
1411             }
1412             setAppStandbyBucket(packageName, userId, bucket, reason, elapsedRealtime, shellCaller);
1413         }
1414     }
1415 
1416     @VisibleForTesting
setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, int reason)1417     void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
1418             int reason) {
1419         setAppStandbyBucket(
1420                 packageName, userId, newBucket, reason, mInjector.elapsedRealtime(), false);
1421     }
1422 
setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, int reason, long elapsedRealtime, boolean resetTimeout)1423     private void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
1424             int reason, long elapsedRealtime, boolean resetTimeout) {
1425         if (!mAppIdleEnabled) return;
1426 
1427         synchronized (mAppIdleLock) {
1428             // If the package is not installed, don't allow the bucket to be set.
1429             if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
1430                 Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
1431                 return;
1432             }
1433             if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
1434                 newBucket = STANDBY_BUCKET_RARE;
1435             }
1436             AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
1437                     userId, elapsedRealtime);
1438             boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
1439 
1440             // Don't allow changing bucket if higher than ACTIVE
1441             if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
1442 
1443             // Don't allow prediction to change from/to NEVER.
1444             if ((app.currentBucket == STANDBY_BUCKET_NEVER || newBucket == STANDBY_BUCKET_NEVER)
1445                     && predicted) {
1446                 return;
1447             }
1448 
1449             final boolean wasForcedBySystem =
1450                     (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
1451 
1452             // If the bucket was forced, don't allow prediction to override
1453             if (predicted
1454                     && ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER
1455                     || wasForcedBySystem)) {
1456                 return;
1457             }
1458 
1459             final boolean isForcedBySystem =
1460                     (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
1461 
1462             if (app.currentBucket == newBucket && wasForcedBySystem && isForcedBySystem) {
1463                 mAppIdleHistory
1464                         .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
1465                 // Keep track of all restricting reasons
1466                 reason = REASON_MAIN_FORCED_BY_SYSTEM
1467                         | (app.bucketingReason & REASON_SUB_MASK)
1468                         | (reason & REASON_SUB_MASK);
1469                 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
1470                         newBucket, reason, resetTimeout);
1471                 return;
1472             }
1473 
1474             final boolean isForcedByUser =
1475                     (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
1476 
1477             if (app.currentBucket == STANDBY_BUCKET_RESTRICTED) {
1478                 if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_TIMEOUT) {
1479                     if (predicted && newBucket >= STANDBY_BUCKET_RARE) {
1480                         // Predicting into RARE or below means we don't expect the user to use the
1481                         // app anytime soon, so don't elevate it from RESTRICTED.
1482                         return;
1483                     }
1484                 } else if (!isUserUsage(reason) && !isForcedByUser) {
1485                     // If the current bucket is RESTRICTED, only user force or usage should bring
1486                     // it out, unless the app was put into the bucket due to timing out.
1487                     return;
1488                 }
1489             }
1490 
1491             if (newBucket == STANDBY_BUCKET_RESTRICTED) {
1492                 mAppIdleHistory
1493                         .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
1494 
1495                 if (isForcedByUser) {
1496                     // Only user force can bypass the delay restriction. If the user forced the
1497                     // app into the RESTRICTED bucket, then a toast confirming the action
1498                     // shouldn't be surprising.
1499                     if (Build.IS_DEBUGGABLE) {
1500                         Toast.makeText(mContext,
1501                                 // Since AppStandbyController sits low in the lock hierarchy,
1502                                 // make sure not to call out with the lock held.
1503                                 mHandler.getLooper(),
1504                                 mContext.getResources().getString(
1505                                         R.string.as_app_forced_to_restricted_bucket, packageName),
1506                                 Toast.LENGTH_SHORT)
1507                                 .show();
1508                     } else {
1509                         Slog.i(TAG, packageName + " restricted by user");
1510                     }
1511                 } else {
1512                     final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
1513                             + mInjector.getAutoRestrictedBucketDelayMs() - elapsedRealtime;
1514                     if (timeUntilRestrictPossibleMs > 0) {
1515                         Slog.w(TAG, "Tried to restrict recently used app: " + packageName
1516                                 + " due to " + reason);
1517                         mHandler.sendMessageDelayed(
1518                                 mHandler.obtainMessage(
1519                                         MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
1520                                 timeUntilRestrictPossibleMs);
1521                         return;
1522                     }
1523                 }
1524             }
1525 
1526             // If the bucket is required to stay in a higher state for a specified duration, don't
1527             // override unless the duration has passed
1528             if (predicted) {
1529                 // Check if the app is within one of the timeouts for forced bucket elevation
1530                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
1531                 // In case of not using the prediction, just keep track of it for applying after
1532                 // ACTIVE or WORKING_SET timeout.
1533                 mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
1534 
1535                 if (newBucket > STANDBY_BUCKET_ACTIVE
1536                         && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
1537                     newBucket = STANDBY_BUCKET_ACTIVE;
1538                     reason = app.bucketingReason;
1539                     if (DEBUG) {
1540                         Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
1541                     }
1542                 } else if (newBucket > STANDBY_BUCKET_WORKING_SET
1543                         && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
1544                     newBucket = STANDBY_BUCKET_WORKING_SET;
1545                     if (app.currentBucket != newBucket) {
1546                         reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
1547                     } else {
1548                         reason = app.bucketingReason;
1549                     }
1550                     if (DEBUG) {
1551                         Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
1552                     }
1553                 } else if (newBucket == STANDBY_BUCKET_RARE
1554                         && mAllowRestrictedBucket
1555                         && getBucketForLocked(packageName, userId, elapsedRealtime)
1556                         == STANDBY_BUCKET_RESTRICTED) {
1557                     // Prediction doesn't think the app will be used anytime soon and
1558                     // it's been long enough that it could just time out into restricted,
1559                     // so time it out there instead. Using TIMEOUT will allow prediction
1560                     // to raise the bucket when it needs to.
1561                     newBucket = STANDBY_BUCKET_RESTRICTED;
1562                     reason = REASON_MAIN_TIMEOUT;
1563                     if (DEBUG) {
1564                         Slog.d(TAG,
1565                                 "Prediction to RARE overridden by timeout into RESTRICTED");
1566                     }
1567                 }
1568             }
1569 
1570             // Make sure we don't put the app in a lower bucket than it's supposed to be in.
1571             newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
1572             mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
1573                     reason, resetTimeout);
1574         }
1575         maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
1576     }
1577 
1578     @VisibleForTesting
isActiveDeviceAdmin(String packageName, int userId)1579     boolean isActiveDeviceAdmin(String packageName, int userId) {
1580         synchronized (mActiveAdminApps) {
1581             final Set<String> adminPkgs = mActiveAdminApps.get(userId);
1582             return adminPkgs != null && adminPkgs.contains(packageName);
1583         }
1584     }
1585 
1586     @Override
addActiveDeviceAdmin(String adminPkg, int userId)1587     public void addActiveDeviceAdmin(String adminPkg, int userId) {
1588         synchronized (mActiveAdminApps) {
1589             Set<String> adminPkgs = mActiveAdminApps.get(userId);
1590             if (adminPkgs == null) {
1591                 adminPkgs = new ArraySet<>();
1592                 mActiveAdminApps.put(userId, adminPkgs);
1593             }
1594             adminPkgs.add(adminPkg);
1595         }
1596     }
1597 
1598     @Override
setActiveAdminApps(Set<String> adminPkgs, int userId)1599     public void setActiveAdminApps(Set<String> adminPkgs, int userId) {
1600         synchronized (mActiveAdminApps) {
1601             if (adminPkgs == null) {
1602                 mActiveAdminApps.remove(userId);
1603             } else {
1604                 mActiveAdminApps.put(userId, adminPkgs);
1605             }
1606         }
1607     }
1608 
1609     @Override
onAdminDataAvailable()1610     public void onAdminDataAvailable() {
1611         mAdminDataAvailableLatch.countDown();
1612     }
1613 
1614     /**
1615      * This will only ever be called once - during device boot.
1616      */
waitForAdminData()1617     private void waitForAdminData() {
1618         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
1619             ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
1620                     WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
1621         }
1622     }
1623 
1624     @VisibleForTesting
getActiveAdminAppsForTest(int userId)1625     Set<String> getActiveAdminAppsForTest(int userId) {
1626         synchronized (mActiveAdminApps) {
1627             return mActiveAdminApps.get(userId);
1628         }
1629     }
1630 
1631     /**
1632      * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
1633      * returns {@code false}.
1634      */
isDeviceProvisioningPackage(String packageName)1635     private boolean isDeviceProvisioningPackage(String packageName) {
1636         String deviceProvisioningPackage = mContext.getResources().getString(
1637                 com.android.internal.R.string.config_deviceProvisioningPackage);
1638         return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
1639     }
1640 
isCarrierApp(String packageName)1641     private boolean isCarrierApp(String packageName) {
1642         synchronized (mCarrierPrivilegedLock) {
1643             if (!mHaveCarrierPrivilegedApps) {
1644                 fetchCarrierPrivilegedAppsCPL();
1645             }
1646             if (mCarrierPrivilegedApps != null) {
1647                 return mCarrierPrivilegedApps.contains(packageName);
1648             }
1649             return false;
1650         }
1651     }
1652 
1653     @Override
clearCarrierPrivilegedApps()1654     public void clearCarrierPrivilegedApps() {
1655         if (DEBUG) {
1656             Slog.i(TAG, "Clearing carrier privileged apps list");
1657         }
1658         synchronized (mCarrierPrivilegedLock) {
1659             mHaveCarrierPrivilegedApps = false;
1660             mCarrierPrivilegedApps = null; // Need to be refetched.
1661         }
1662     }
1663 
1664     @GuardedBy("mCarrierPrivilegedLock")
fetchCarrierPrivilegedAppsCPL()1665     private void fetchCarrierPrivilegedAppsCPL() {
1666         TelephonyManager telephonyManager =
1667                 mContext.getSystemService(TelephonyManager.class);
1668         mCarrierPrivilegedApps =
1669                 telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions();
1670         mHaveCarrierPrivilegedApps = true;
1671         if (DEBUG) {
1672             Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
1673         }
1674     }
1675 
isActiveNetworkScorer(String packageName)1676     private boolean isActiveNetworkScorer(String packageName) {
1677         // Validity of network scorer cache is limited to a few seconds. Fetch it again
1678         // if longer since query.
1679         // This is a temporary optimization until there's a callback mechanism for changes to network scorer.
1680         final long now = SystemClock.elapsedRealtime();
1681         if (mCachedNetworkScorer == null
1682                 || mCachedNetworkScorerAtMillis < now - NETWORK_SCORER_CACHE_DURATION_MILLIS) {
1683             mCachedNetworkScorer = mInjector.getActiveNetworkScorer();
1684             mCachedNetworkScorerAtMillis = now;
1685         }
1686         return packageName != null && packageName.equals(mCachedNetworkScorer);
1687     }
1688 
informListeners(String packageName, int userId, int bucket, int reason, boolean userInteraction)1689     private void informListeners(String packageName, int userId, int bucket, int reason,
1690             boolean userInteraction) {
1691         final boolean idle = bucket >= STANDBY_BUCKET_RARE;
1692         synchronized (mPackageAccessListeners) {
1693             for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
1694                 listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
1695                 if (userInteraction) {
1696                     listener.onUserInteractionStarted(packageName, userId);
1697                 }
1698             }
1699         }
1700     }
1701 
informParoleStateChanged()1702     private void informParoleStateChanged() {
1703         final boolean paroled = isInParole();
1704         synchronized (mPackageAccessListeners) {
1705             for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
1706                 listener.onParoleStateChanged(paroled);
1707             }
1708         }
1709     }
1710 
1711 
1712     @Override
flushToDisk()1713     public void flushToDisk() {
1714         synchronized (mAppIdleLock) {
1715             mAppIdleHistory.writeAppIdleTimes();
1716             mAppIdleHistory.writeAppIdleDurations();
1717         }
1718     }
1719 
isDisplayOn()1720     private boolean isDisplayOn() {
1721         return mInjector.isDefaultDisplayOn();
1722     }
1723 
1724     @VisibleForTesting
clearAppIdleForPackage(String packageName, int userId)1725     void clearAppIdleForPackage(String packageName, int userId) {
1726         synchronized (mAppIdleLock) {
1727             mAppIdleHistory.clearUsage(packageName, userId);
1728         }
1729     }
1730 
1731     /**
1732      * Remove an app from the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
1733      * bucket if it was forced into the bucket by the system because it was buggy.
1734      */
1735     @VisibleForTesting
maybeUnrestrictBuggyApp(String packageName, int userId)1736     void maybeUnrestrictBuggyApp(String packageName, int userId) {
1737         synchronized (mAppIdleLock) {
1738             final long elapsedRealtime = mInjector.elapsedRealtime();
1739             final AppIdleHistory.AppUsageHistory app =
1740                     mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime);
1741             if (app.currentBucket != STANDBY_BUCKET_RESTRICTED
1742                     || (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_FORCED_BY_SYSTEM) {
1743                 return;
1744             }
1745 
1746             final int newBucket;
1747             final int newReason;
1748             if ((app.bucketingReason & REASON_SUB_MASK) == REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY) {
1749                 // If bugginess was the only reason the app should be restricted, then lift it out.
1750                 newBucket = STANDBY_BUCKET_RARE;
1751                 newReason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_UPDATE;
1752             } else {
1753                 // There's another reason the app was restricted. Remove the buggy bit and call
1754                 // it a day.
1755                 newBucket = STANDBY_BUCKET_RESTRICTED;
1756                 newReason = app.bucketingReason & ~REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
1757             }
1758             mAppIdleHistory.setAppStandbyBucket(
1759                     packageName, userId, elapsedRealtime, newBucket, newReason);
1760         }
1761     }
1762 
updatePowerWhitelistCache()1763     private void updatePowerWhitelistCache() {
1764         if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
1765             return;
1766         }
1767         mInjector.updatePowerWhitelistCache();
1768         postCheckIdleStates(UserHandle.USER_ALL);
1769     }
1770 
1771     private class PackageReceiver extends BroadcastReceiver {
1772         @Override
onReceive(Context context, Intent intent)1773         public void onReceive(Context context, Intent intent) {
1774             final String action = intent.getAction();
1775             final String pkgName = intent.getData().getSchemeSpecificPart();
1776             final int userId = getSendingUserId();
1777             if (Intent.ACTION_PACKAGE_ADDED.equals(action)
1778                     || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1779                 final String[] cmpList = intent.getStringArrayExtra(
1780                         Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
1781                 // If this is PACKAGE_ADDED (cmpList == null), or if it's a whole-package
1782                 // enable/disable event (cmpList is just the package name itself), drop
1783                 // our carrier privileged app & system-app caches and let them refresh
1784                 if (cmpList == null
1785                         || (cmpList.length == 1 && pkgName.equals(cmpList[0]))) {
1786                     clearCarrierPrivilegedApps();
1787                     evaluateSystemAppException(pkgName, userId);
1788                 }
1789                 // component-level enable/disable can affect bucketing, so we always
1790                 // reevaluate that for any PACKAGE_CHANGED
1791                 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkgName)
1792                     .sendToTarget();
1793             }
1794             if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
1795                     Intent.ACTION_PACKAGE_ADDED.equals(action))) {
1796                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1797                     maybeUnrestrictBuggyApp(pkgName, userId);
1798                 } else {
1799                     clearAppIdleForPackage(pkgName, userId);
1800                 }
1801             }
1802         }
1803     }
1804 
evaluateSystemAppException(String packageName, int userId)1805     private void evaluateSystemAppException(String packageName, int userId) {
1806         if (!mSystemServicesReady) {
1807             // The app will be evaluated in when services are ready.
1808             return;
1809         }
1810         try {
1811             PackageInfo pi = mPackageManager.getPackageInfoAsUser(
1812                     packageName, HEADLESS_APP_CHECK_FLAGS, userId);
1813             evaluateSystemAppException(pi);
1814         } catch (PackageManager.NameNotFoundException e) {
1815             synchronized (mHeadlessSystemApps) {
1816                 mHeadlessSystemApps.remove(packageName);
1817             }
1818         }
1819     }
1820 
1821     /** Returns true if the exception status changed. */
evaluateSystemAppException(@ullable PackageInfo pkgInfo)1822     private boolean evaluateSystemAppException(@Nullable PackageInfo pkgInfo) {
1823         if (pkgInfo == null || pkgInfo.applicationInfo == null
1824                 || (!pkgInfo.applicationInfo.isSystemApp()
1825                         && !pkgInfo.applicationInfo.isUpdatedSystemApp())) {
1826             return false;
1827         }
1828         synchronized (mHeadlessSystemApps) {
1829             if (pkgInfo.activities == null || pkgInfo.activities.length == 0) {
1830                 // Headless system app.
1831                 return mHeadlessSystemApps.add(pkgInfo.packageName);
1832             } else {
1833                 return mHeadlessSystemApps.remove(pkgInfo.packageName);
1834             }
1835         }
1836     }
1837 
1838     /** Call on a system version update to temporarily reset system app buckets. */
1839     @Override
initializeDefaultsForSystemApps(int userId)1840     public void initializeDefaultsForSystemApps(int userId) {
1841         if (!mSystemServicesReady) {
1842             // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
1843             mPendingInitializeDefaults = true;
1844             return;
1845         }
1846         Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", "
1847                 + "appIdleEnabled=" + mAppIdleEnabled);
1848         final long elapsedRealtime = mInjector.elapsedRealtime();
1849         List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
1850                 PackageManager.MATCH_DISABLED_COMPONENTS,
1851                 userId);
1852         final int packageCount = packages.size();
1853         synchronized (mAppIdleLock) {
1854             for (int i = 0; i < packageCount; i++) {
1855                 final PackageInfo pi = packages.get(i);
1856                 String packageName = pi.packageName;
1857                 if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
1858                     // Mark app as used for 2 hours. After that it can timeout to whatever the
1859                     // past usage pattern was.
1860                     mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
1861                             REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
1862                             elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
1863                 }
1864             }
1865             // Immediately persist defaults to disk
1866             mAppIdleHistory.writeAppIdleTimes(userId);
1867         }
1868     }
1869 
1870     /** Call on system boot to get the initial set of headless system apps. */
loadHeadlessSystemAppCache()1871     private void loadHeadlessSystemAppCache() {
1872         Slog.d(TAG, "Loading headless system app cache. appIdleEnabled=" + mAppIdleEnabled);
1873         final List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
1874                 HEADLESS_APP_CHECK_FLAGS, UserHandle.USER_SYSTEM);
1875         final int packageCount = packages.size();
1876         for (int i = 0; i < packageCount; i++) {
1877             PackageInfo pkgInfo = packages.get(i);
1878             if (pkgInfo != null && evaluateSystemAppException(pkgInfo)) {
1879                 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE,
1880                         UserHandle.USER_SYSTEM, -1, pkgInfo.packageName)
1881                     .sendToTarget();
1882             }
1883         }
1884     }
1885 
1886     @Override
postReportContentProviderUsage(String name, String packageName, int userId)1887     public void postReportContentProviderUsage(String name, String packageName, int userId) {
1888         ContentProviderUsageRecord record = ContentProviderUsageRecord.obtain(name, packageName,
1889                 userId);
1890         mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, record)
1891                 .sendToTarget();
1892     }
1893 
1894     @Override
postReportSyncScheduled(String packageName, int userId, boolean exempted)1895     public void postReportSyncScheduled(String packageName, int userId, boolean exempted) {
1896         mHandler.obtainMessage(MSG_REPORT_SYNC_SCHEDULED, userId, exempted ? 1 : 0, packageName)
1897                 .sendToTarget();
1898     }
1899 
1900     @Override
postReportExemptedSyncStart(String packageName, int userId)1901     public void postReportExemptedSyncStart(String packageName, int userId) {
1902         mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
1903                 .sendToTarget();
1904     }
1905 
1906     @Override
dumpUsers(IndentingPrintWriter idpw, int[] userIds, List<String> pkgs)1907     public void dumpUsers(IndentingPrintWriter idpw, int[] userIds, List<String> pkgs) {
1908         synchronized (mAppIdleLock) {
1909             mAppIdleHistory.dumpUsers(idpw, userIds, pkgs);
1910         }
1911     }
1912 
1913     @Override
dumpState(String[] args, PrintWriter pw)1914     public void dumpState(String[] args, PrintWriter pw) {
1915         synchronized (mCarrierPrivilegedLock) {
1916             pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
1917                     + "): " + mCarrierPrivilegedApps);
1918         }
1919 
1920         pw.println();
1921         pw.println("Settings:");
1922 
1923         pw.print("  mCheckIdleIntervalMillis=");
1924         TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
1925         pw.println();
1926 
1927         pw.print("  mStrongUsageTimeoutMillis=");
1928         TimeUtils.formatDuration(mStrongUsageTimeoutMillis, pw);
1929         pw.println();
1930         pw.print("  mNotificationSeenTimeoutMillis=");
1931         TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
1932         pw.println();
1933         pw.print("  mSyncAdapterTimeoutMillis=");
1934         TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
1935         pw.println();
1936         pw.print("  mSystemInteractionTimeoutMillis=");
1937         TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
1938         pw.println();
1939         pw.print("  mInitialForegroundServiceStartTimeoutMillis=");
1940         TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw);
1941         pw.println();
1942 
1943         pw.print("  mPredictionTimeoutMillis=");
1944         TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
1945         pw.println();
1946 
1947         pw.print("  mExemptedSyncScheduledNonDozeTimeoutMillis=");
1948         TimeUtils.formatDuration(mExemptedSyncScheduledNonDozeTimeoutMillis, pw);
1949         pw.println();
1950         pw.print("  mExemptedSyncScheduledDozeTimeoutMillis=");
1951         TimeUtils.formatDuration(mExemptedSyncScheduledDozeTimeoutMillis, pw);
1952         pw.println();
1953         pw.print("  mExemptedSyncStartTimeoutMillis=");
1954         TimeUtils.formatDuration(mExemptedSyncStartTimeoutMillis, pw);
1955         pw.println();
1956         pw.print("  mUnexemptedSyncScheduledTimeoutMillis=");
1957         TimeUtils.formatDuration(mUnexemptedSyncScheduledTimeoutMillis, pw);
1958         pw.println();
1959 
1960         pw.print("  mSystemUpdateUsageTimeoutMillis=");
1961         TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
1962         pw.println();
1963 
1964         pw.println();
1965         pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
1966         pw.print(" mAllowRestrictedBucket=");
1967         pw.print(mAllowRestrictedBucket);
1968         pw.print(" mIsCharging=");
1969         pw.print(mIsCharging);
1970         pw.println();
1971         pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
1972         pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
1973         pw.println();
1974 
1975         pw.println("mHeadlessSystemApps=[");
1976         synchronized (mHeadlessSystemApps) {
1977             for (int i = mHeadlessSystemApps.size() - 1; i >= 0; --i) {
1978                 pw.print("  ");
1979                 pw.print(mHeadlessSystemApps.valueAt(i));
1980                 pw.println(",");
1981             }
1982         }
1983         pw.println("]");
1984         pw.println();
1985 
1986         mInjector.dump(pw);
1987     }
1988 
1989     /**
1990      * Injector for interaction with external code. Override methods to provide a mock
1991      * implementation for tests.
1992      * onBootPhase() must be called with at least the PHASE_SYSTEM_SERVICES_READY
1993      */
1994     static class Injector {
1995 
1996         private final Context mContext;
1997         private final Looper mLooper;
1998         private IBatteryStats mBatteryStats;
1999         private BatteryManager mBatteryManager;
2000         private PackageManagerInternal mPackageManagerInternal;
2001         private DisplayManager mDisplayManager;
2002         private PowerManager mPowerManager;
2003         private IDeviceIdleController mDeviceIdleController;
2004         private CrossProfileAppsInternal mCrossProfileAppsInternal;
2005         private AlarmManagerInternal mAlarmManagerInternal;
2006         int mBootPhase;
2007         /**
2008          * The minimum amount of time required since the last user interaction before an app can be
2009          * automatically placed in the RESTRICTED bucket.
2010          */
2011         long mAutoRestrictedBucketDelayMs =
2012                 ConstantsObserver.DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS;
2013         /**
2014          * Cached set of apps that are power whitelisted, including those not whitelisted from idle.
2015          */
2016         @GuardedBy("mPowerWhitelistedApps")
2017         private final ArraySet<String> mPowerWhitelistedApps = new ArraySet<>();
2018         private String mWellbeingApp = null;
2019 
Injector(Context context, Looper looper)2020         Injector(Context context, Looper looper) {
2021             mContext = context;
2022             mLooper = looper;
2023         }
2024 
getContext()2025         Context getContext() {
2026             return mContext;
2027         }
2028 
getLooper()2029         Looper getLooper() {
2030             return mLooper;
2031         }
2032 
onBootPhase(int phase)2033         void onBootPhase(int phase) {
2034             if (phase == PHASE_SYSTEM_SERVICES_READY) {
2035                 mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
2036                         ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
2037                 mBatteryStats = IBatteryStats.Stub.asInterface(
2038                         ServiceManager.getService(BatteryStats.SERVICE_NAME));
2039                 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
2040                 mDisplayManager = (DisplayManager) mContext.getSystemService(
2041                         Context.DISPLAY_SERVICE);
2042                 mPowerManager = mContext.getSystemService(PowerManager.class);
2043                 mBatteryManager = mContext.getSystemService(BatteryManager.class);
2044                 mCrossProfileAppsInternal = LocalServices.getService(
2045                         CrossProfileAppsInternal.class);
2046                 mAlarmManagerInternal = LocalServices.getService(AlarmManagerInternal.class);
2047 
2048                 final ActivityManager activityManager =
2049                         (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
2050                 if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
2051                     mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
2052                 }
2053             } else if (phase == PHASE_BOOT_COMPLETED) {
2054                 // mWellbeingApp needs to be initialized lazily after boot to allow for roles to be
2055                 // parsed and the wellbeing role-holder to be assigned
2056                 final PackageManager packageManager = mContext.getPackageManager();
2057                 mWellbeingApp = packageManager.getWellbeingPackageName();
2058             }
2059             mBootPhase = phase;
2060         }
2061 
getBootPhase()2062         int getBootPhase() {
2063             return mBootPhase;
2064         }
2065 
2066         /**
2067          * Returns the elapsed realtime since the device started. Override this
2068          * to control the clock.
2069          * @return elapsed realtime
2070          */
elapsedRealtime()2071         long elapsedRealtime() {
2072             return SystemClock.elapsedRealtime();
2073         }
2074 
currentTimeMillis()2075         long currentTimeMillis() {
2076             return System.currentTimeMillis();
2077         }
2078 
isAppIdleEnabled()2079         boolean isAppIdleEnabled() {
2080             final boolean buildFlag = mContext.getResources().getBoolean(
2081                     com.android.internal.R.bool.config_enableAutoPowerModes);
2082             final boolean runtimeFlag = Global.getInt(mContext.getContentResolver(),
2083                     Global.APP_STANDBY_ENABLED, 1) == 1
2084                     && Global.getInt(mContext.getContentResolver(),
2085                     Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 1;
2086             return buildFlag && runtimeFlag;
2087         }
2088 
isCharging()2089         boolean isCharging() {
2090             return mBatteryManager.isCharging();
2091         }
2092 
isNonIdleWhitelisted(String packageName)2093         boolean isNonIdleWhitelisted(String packageName) {
2094             if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) {
2095                 return false;
2096             }
2097             synchronized (mPowerWhitelistedApps) {
2098                 return mPowerWhitelistedApps.contains(packageName);
2099             }
2100         }
2101 
2102         /**
2103          * Returns {@code true} if the supplied package is the wellbeing app. Otherwise,
2104          * returns {@code false}.
2105          */
isWellbeingPackage(String packageName)2106         boolean isWellbeingPackage(String packageName) {
2107             return mWellbeingApp != null && mWellbeingApp.equals(packageName);
2108         }
2109 
hasScheduleExactAlarm(String packageName, int uid)2110         boolean hasScheduleExactAlarm(String packageName, int uid) {
2111             return mAlarmManagerInternal.hasScheduleExactAlarm(packageName, uid);
2112         }
2113 
updatePowerWhitelistCache()2114         void updatePowerWhitelistCache() {
2115             try {
2116                 // Don't call out to DeviceIdleController with the lock held.
2117                 final String[] whitelistedPkgs =
2118                         mDeviceIdleController.getFullPowerWhitelistExceptIdle();
2119                 synchronized (mPowerWhitelistedApps) {
2120                     mPowerWhitelistedApps.clear();
2121                     final int len = whitelistedPkgs.length;
2122                     for (int i = 0; i < len; ++i) {
2123                         mPowerWhitelistedApps.add(whitelistedPkgs[i]);
2124                     }
2125                 }
2126             } catch (RemoteException e) {
2127                 // Should not happen.
2128                 Slog.wtf(TAG, "Failed to get power whitelist", e);
2129             }
2130         }
2131 
isRestrictedBucketEnabled()2132         boolean isRestrictedBucketEnabled() {
2133             return Global.getInt(mContext.getContentResolver(),
2134                     Global.ENABLE_RESTRICTED_BUCKET,
2135                     Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1;
2136         }
2137 
getDataSystemDirectory()2138         File getDataSystemDirectory() {
2139             return Environment.getDataSystemDirectory();
2140         }
2141 
2142         /**
2143          * Return the minimum amount of time that must have passed since the last user usage before
2144          * an app can be automatically put into the
2145          * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
2146          */
getAutoRestrictedBucketDelayMs()2147         long getAutoRestrictedBucketDelayMs() {
2148             return mAutoRestrictedBucketDelayMs;
2149         }
2150 
noteEvent(int event, String packageName, int uid)2151         void noteEvent(int event, String packageName, int uid) throws RemoteException {
2152             mBatteryStats.noteEvent(event, packageName, uid);
2153         }
2154 
getPackageManagerInternal()2155         PackageManagerInternal getPackageManagerInternal() {
2156             return mPackageManagerInternal;
2157         }
2158 
isPackageEphemeral(int userId, String packageName)2159         boolean isPackageEphemeral(int userId, String packageName) {
2160             return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
2161         }
2162 
isPackageInstalled(String packageName, int flags, int userId)2163         boolean isPackageInstalled(String packageName, int flags, int userId) {
2164             return mPackageManagerInternal.getPackageUid(packageName, flags, userId) >= 0;
2165         }
2166 
getRunningUserIds()2167         int[] getRunningUserIds() throws RemoteException {
2168             return ActivityManager.getService().getRunningUserIds();
2169         }
2170 
isDefaultDisplayOn()2171         boolean isDefaultDisplayOn() {
2172             return mDisplayManager
2173                     .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
2174         }
2175 
registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler)2176         void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
2177             mDisplayManager.registerDisplayListener(listener, handler);
2178         }
2179 
getActiveNetworkScorer()2180         String getActiveNetworkScorer() {
2181             NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
2182                     Context.NETWORK_SCORE_SERVICE);
2183             return nsm.getActiveScorerPackage();
2184         }
2185 
isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName, int userId)2186         public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
2187                 int userId) {
2188             return appWidgetManager.isBoundWidgetPackage(packageName, userId);
2189         }
2190 
2191         @NonNull
getDeviceConfigProperties(String... keys)2192         DeviceConfig.Properties getDeviceConfigProperties(String... keys) {
2193             return DeviceConfig.getProperties(DeviceConfig.NAMESPACE_APP_STANDBY, keys);
2194         }
2195 
2196         /** Whether the device is in doze or not. */
isDeviceIdleMode()2197         public boolean isDeviceIdleMode() {
2198             return mPowerManager.isDeviceIdleMode();
2199         }
2200 
getValidCrossProfileTargets(String pkg, int userId)2201         public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) {
2202             final int uid = mPackageManagerInternal.getPackageUid(pkg, /* flags= */ 0, userId);
2203             final AndroidPackage aPkg = mPackageManagerInternal.getPackage(uid);
2204             if (uid < 0
2205                     || aPkg == null
2206                     || !aPkg.isCrossProfile()
2207                     || !mCrossProfileAppsInternal
2208                             .verifyUidHasInteractAcrossProfilePermission(pkg, uid)) {
2209                 if (uid >= 0 && aPkg == null) {
2210                     Slog.wtf(TAG, "Null package retrieved for UID " + uid);
2211                 }
2212                 return Collections.emptyList();
2213             }
2214             return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId);
2215         }
2216 
registerDeviceConfigPropertiesChangedListener( @onNull DeviceConfig.OnPropertiesChangedListener listener)2217         void registerDeviceConfigPropertiesChangedListener(
2218                 @NonNull DeviceConfig.OnPropertiesChangedListener listener) {
2219             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_STANDBY,
2220                     JobSchedulerBackgroundThread.getExecutor(), listener);
2221         }
2222 
dump(PrintWriter pw)2223         void dump(PrintWriter pw) {
2224             pw.println("mPowerWhitelistedApps=[");
2225             synchronized (mPowerWhitelistedApps) {
2226                 for (int i = mPowerWhitelistedApps.size() - 1; i >= 0; --i) {
2227                     pw.print("  ");
2228                     pw.print(mPowerWhitelistedApps.valueAt(i));
2229                     pw.println(",");
2230                 }
2231             }
2232             pw.println("]");
2233             pw.println();
2234         }
2235     }
2236 
2237     class AppStandbyHandler extends Handler {
2238 
AppStandbyHandler(Looper looper)2239         AppStandbyHandler(Looper looper) {
2240             super(looper);
2241         }
2242 
2243         @Override
handleMessage(Message msg)2244         public void handleMessage(Message msg) {
2245             switch (msg.what) {
2246                 case MSG_INFORM_LISTENERS:
2247                     StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
2248                     informListeners(r.packageName, r.userId, r.bucket, r.reason,
2249                             r.isUserInteraction);
2250                     r.recycle();
2251                     break;
2252 
2253                 case MSG_FORCE_IDLE_STATE:
2254                     forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
2255                     break;
2256 
2257                 case MSG_CHECK_IDLE_STATES:
2258                     if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
2259                         mHandler.sendMessageDelayed(mHandler.obtainMessage(
2260                                 MSG_CHECK_IDLE_STATES, msg.arg1, 0),
2261                                 mCheckIdleIntervalMillis);
2262                     }
2263                     break;
2264 
2265                 case MSG_ONE_TIME_CHECK_IDLE_STATES:
2266                     mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
2267                     waitForAdminData();
2268                     checkIdleStates(UserHandle.USER_ALL);
2269                     break;
2270 
2271                 case MSG_REPORT_CONTENT_PROVIDER_USAGE:
2272                     ContentProviderUsageRecord record = (ContentProviderUsageRecord) msg.obj;
2273                     reportContentProviderUsage(record.name, record.packageName, record.userId);
2274                     record.recycle();
2275                     break;
2276 
2277                 case MSG_PAROLE_STATE_CHANGED:
2278                     if (DEBUG) Slog.d(TAG, "Parole state: " + isInParole());
2279                     informParoleStateChanged();
2280                     break;
2281 
2282                 case MSG_CHECK_PACKAGE_IDLE_STATE:
2283                     checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
2284                             mInjector.elapsedRealtime());
2285                     break;
2286 
2287                 case MSG_REPORT_SYNC_SCHEDULED:
2288                     final boolean exempted = msg.arg2 > 0 ? true : false;
2289                     if (exempted) {
2290                         reportExemptedSyncScheduled((String) msg.obj, msg.arg1);
2291                     } else {
2292                         reportUnexemptedSyncScheduled((String) msg.obj, msg.arg1);
2293                     }
2294                     break;
2295 
2296                 case MSG_REPORT_EXEMPTED_SYNC_START:
2297                     reportExemptedSyncStart((String) msg.obj, msg.arg1);
2298                     break;
2299 
2300                 default:
2301                     super.handleMessage(msg);
2302                     break;
2303 
2304             }
2305         }
2306     };
2307 
2308     private class DeviceStateReceiver extends BroadcastReceiver {
2309         @Override
onReceive(Context context, Intent intent)2310         public void onReceive(Context context, Intent intent) {
2311             switch (intent.getAction()) {
2312                 case BatteryManager.ACTION_CHARGING:
2313                     setChargingState(true);
2314                     break;
2315                 case BatteryManager.ACTION_DISCHARGING:
2316                     setChargingState(false);
2317                     break;
2318                 case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
2319                     if (mSystemServicesReady) {
2320                         mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
2321                     }
2322                     break;
2323             }
2324         }
2325     }
2326 
2327     private final DisplayManager.DisplayListener mDisplayListener
2328             = new DisplayManager.DisplayListener() {
2329 
2330         @Override public void onDisplayAdded(int displayId) {
2331         }
2332 
2333         @Override public void onDisplayRemoved(int displayId) {
2334         }
2335 
2336         @Override public void onDisplayChanged(int displayId) {
2337             if (displayId == Display.DEFAULT_DISPLAY) {
2338                 final boolean displayOn = isDisplayOn();
2339                 synchronized (mAppIdleLock) {
2340                     mAppIdleHistory.updateDisplay(displayOn, mInjector.elapsedRealtime());
2341                 }
2342             }
2343         }
2344     };
2345 
2346     /**
2347      * Observe changes for {@link DeviceConfig#NAMESPACE_APP_STANDBY} and other standby related
2348      * Settings constants.
2349      */
2350     private class ConstantsObserver extends ContentObserver implements
2351             DeviceConfig.OnPropertiesChangedListener {
2352         private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
2353         private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
2354                 "notification_seen_duration";
2355         private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
2356                 "system_update_usage_duration";
2357         private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
2358         private static final String KEY_SYNC_ADAPTER_HOLD_DURATION = "sync_adapter_duration";
2359         private static final String KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION =
2360                 "exempted_sync_scheduled_nd_duration";
2361         private static final String KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION =
2362                 "exempted_sync_scheduled_d_duration";
2363         private static final String KEY_EXEMPTED_SYNC_START_HOLD_DURATION =
2364                 "exempted_sync_start_duration";
2365         private static final String KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION =
2366                 "unexempted_sync_scheduled_duration";
2367         private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
2368                 "system_interaction_duration";
2369         private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
2370                 "initial_foreground_service_start_duration";
2371         private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS =
2372                 "auto_restricted_bucket_delay_ms";
2373         private static final String KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS =
2374                 "cross_profile_apps_share_standby_buckets";
2375         private static final String KEY_PREFIX_SCREEN_TIME_THRESHOLD = "screen_threshold_";
2376         private final String[] KEYS_SCREEN_TIME_THRESHOLDS = {
2377                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "active",
2378                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "working_set",
2379                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "frequent",
2380                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "rare",
2381                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "restricted"
2382         };
2383         private static final String KEY_PREFIX_ELAPSED_TIME_THRESHOLD = "elapsed_threshold_";
2384         private final String[] KEYS_ELAPSED_TIME_THRESHOLDS = {
2385                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "active",
2386                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "working_set",
2387                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "frequent",
2388                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "rare",
2389                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "restricted"
2390         };
2391         public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
2392                 COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
2393         public static final long DEFAULT_STRONG_USAGE_TIMEOUT =
2394                 COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR;
2395         public static final long DEFAULT_NOTIFICATION_TIMEOUT =
2396                 COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
2397         public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT =
2398                 COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR;
2399         public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT =
2400                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
2401         public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT =
2402                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
2403         public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT =
2404                 COMPRESS_TIME ? (ONE_MINUTE / 2) : 10 * ONE_MINUTE;
2405         public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT =
2406                 COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
2407         public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT =
2408                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
2409         public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT =
2410                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
2411         public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT =
2412                 COMPRESS_TIME ? ONE_MINUTE : 30 * ONE_MINUTE;
2413         public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS =
2414                 COMPRESS_TIME ? ONE_MINUTE : ONE_DAY;
2415         public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
2416 
ConstantsObserver(Handler handler)2417         ConstantsObserver(Handler handler) {
2418             super(handler);
2419         }
2420 
start()2421         public void start() {
2422             final ContentResolver cr = mContext.getContentResolver();
2423             // APP_STANDBY_ENABLED is a SystemApi that some apps may be watching, so best to
2424             // leave it in Settings.
2425             cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
2426             // Leave ENABLE_RESTRICTED_BUCKET as a user-controlled setting which will stay in
2427             // Settings.
2428             // TODO: make setting user-specific
2429             cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
2430                     false, this);
2431             // ADAPTIVE_BATTERY_MANAGEMENT_ENABLED is a user setting, so it has to stay in Settings.
2432             cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
2433                     false, this);
2434             mInjector.registerDeviceConfigPropertiesChangedListener(this);
2435             // Load all the constants.
2436             // postOneTimeCheckIdleStates() doesn't need to be called on boot.
2437             processProperties(mInjector.getDeviceConfigProperties());
2438             updateSettings();
2439         }
2440 
2441         @Override
onChange(boolean selfChange)2442         public void onChange(boolean selfChange) {
2443             updateSettings();
2444             postOneTimeCheckIdleStates();
2445         }
2446 
2447         @Override
onPropertiesChanged(DeviceConfig.Properties properties)2448         public void onPropertiesChanged(DeviceConfig.Properties properties) {
2449             processProperties(properties);
2450             postOneTimeCheckIdleStates();
2451         }
2452 
processProperties(DeviceConfig.Properties properties)2453         private void processProperties(DeviceConfig.Properties properties) {
2454             boolean timeThresholdsUpdated = false;
2455             synchronized (mAppIdleLock) {
2456                 for (String name : properties.getKeyset()) {
2457                     if (name == null) {
2458                         continue;
2459                     }
2460                     switch (name) {
2461                         case KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS:
2462                             mInjector.mAutoRestrictedBucketDelayMs = Math.max(
2463                                     COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
2464                                     properties.getLong(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
2465                                             DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
2466                             break;
2467                         case KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS:
2468                             mLinkCrossProfileApps = properties.getBoolean(
2469                                     KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS,
2470                                     DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS);
2471                             break;
2472                         case KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION:
2473                             mInitialForegroundServiceStartTimeoutMillis = properties.getLong(
2474                                     KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
2475                                     DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
2476                             break;
2477                         case KEY_NOTIFICATION_SEEN_HOLD_DURATION:
2478                             mNotificationSeenTimeoutMillis = properties.getLong(
2479                                     KEY_NOTIFICATION_SEEN_HOLD_DURATION,
2480                                     DEFAULT_NOTIFICATION_TIMEOUT);
2481                             break;
2482                         case KEY_STRONG_USAGE_HOLD_DURATION:
2483                             mStrongUsageTimeoutMillis = properties.getLong(
2484                                     KEY_STRONG_USAGE_HOLD_DURATION, DEFAULT_STRONG_USAGE_TIMEOUT);
2485                             break;
2486                         case KEY_PREDICTION_TIMEOUT:
2487                             mPredictionTimeoutMillis = properties.getLong(
2488                                     KEY_PREDICTION_TIMEOUT, DEFAULT_PREDICTION_TIMEOUT);
2489                             break;
2490                         case KEY_SYSTEM_INTERACTION_HOLD_DURATION:
2491                             mSystemInteractionTimeoutMillis = properties.getLong(
2492                                     KEY_SYSTEM_INTERACTION_HOLD_DURATION,
2493                                     DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
2494                             break;
2495                         case KEY_SYSTEM_UPDATE_HOLD_DURATION:
2496                             mSystemUpdateUsageTimeoutMillis = properties.getLong(
2497                                     KEY_SYSTEM_UPDATE_HOLD_DURATION, DEFAULT_SYSTEM_UPDATE_TIMEOUT);
2498                             break;
2499                         case KEY_SYNC_ADAPTER_HOLD_DURATION:
2500                             mSyncAdapterTimeoutMillis = properties.getLong(
2501                                     KEY_SYNC_ADAPTER_HOLD_DURATION, DEFAULT_SYNC_ADAPTER_TIMEOUT);
2502                             break;
2503                         case KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION:
2504                             mExemptedSyncScheduledDozeTimeoutMillis = properties.getLong(
2505                                     KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
2506                                     DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT);
2507                             break;
2508                         case KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION:
2509                             mExemptedSyncScheduledNonDozeTimeoutMillis = properties.getLong(
2510                                     KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION,
2511                                     DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT);
2512                             break;
2513                         case KEY_EXEMPTED_SYNC_START_HOLD_DURATION:
2514                             mExemptedSyncStartTimeoutMillis = properties.getLong(
2515                                     KEY_EXEMPTED_SYNC_START_HOLD_DURATION,
2516                                     DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
2517                             break;
2518                         case KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION:
2519                             mUnexemptedSyncScheduledTimeoutMillis = properties.getLong(
2520                                     KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
2521                                     DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
2522                             break;
2523                         default:
2524                             if (!timeThresholdsUpdated
2525                                     && (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
2526                                     || name.startsWith(KEY_PREFIX_ELAPSED_TIME_THRESHOLD))) {
2527                                 updateTimeThresholds();
2528                                 timeThresholdsUpdated = true;
2529                             }
2530                             break;
2531                     }
2532                 }
2533             }
2534         }
2535 
updateTimeThresholds()2536         private void updateTimeThresholds() {
2537             // Query the values as an atomic set.
2538             final DeviceConfig.Properties screenThresholdProperties =
2539                     mInjector.getDeviceConfigProperties(KEYS_SCREEN_TIME_THRESHOLDS);
2540             final DeviceConfig.Properties elapsedThresholdProperties =
2541                     mInjector.getDeviceConfigProperties(KEYS_ELAPSED_TIME_THRESHOLDS);
2542             mAppStandbyScreenThresholds = generateThresholdArray(
2543                     screenThresholdProperties, KEYS_SCREEN_TIME_THRESHOLDS,
2544                     DEFAULT_SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS);
2545             mAppStandbyElapsedThresholds = generateThresholdArray(
2546                     elapsedThresholdProperties, KEYS_ELAPSED_TIME_THRESHOLDS,
2547                     DEFAULT_ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS);
2548             mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
2549                     DEFAULT_CHECK_IDLE_INTERVAL_MS);
2550         }
2551 
updateSettings()2552         void updateSettings() {
2553             if (DEBUG) {
2554                 Slog.d(TAG,
2555                         "appidle=" + Global.getString(mContext.getContentResolver(),
2556                                 Global.APP_STANDBY_ENABLED));
2557                 Slog.d(TAG,
2558                         "adaptivebat=" + Global.getString(mContext.getContentResolver(),
2559                                 Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
2560             }
2561 
2562             synchronized (mAppIdleLock) {
2563                 mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled();
2564             }
2565 
2566             setAppIdleEnabled(mInjector.isAppIdleEnabled());
2567         }
2568 
generateThresholdArray(@onNull DeviceConfig.Properties properties, @NonNull String[] keys, long[] defaults, long[] minValues)2569         long[] generateThresholdArray(@NonNull DeviceConfig.Properties properties,
2570                 @NonNull String[] keys, long[] defaults, long[] minValues) {
2571             if (properties.getKeyset().isEmpty()) {
2572                 // Reset to defaults
2573                 return defaults;
2574             }
2575             if (keys.length != THRESHOLD_BUCKETS.length) {
2576                 // This should only happen in development.
2577                 throw new IllegalStateException(
2578                         "# keys (" + keys.length + ") != # buckets ("
2579                                 + THRESHOLD_BUCKETS.length + ")");
2580             }
2581             if (defaults.length != THRESHOLD_BUCKETS.length) {
2582                 // This should only happen in development.
2583                 throw new IllegalStateException(
2584                         "# defaults (" + defaults.length + ") != # buckets ("
2585                                 + THRESHOLD_BUCKETS.length + ")");
2586             }
2587             if (minValues.length != THRESHOLD_BUCKETS.length) {
2588                 Slog.wtf(TAG, "minValues array is the wrong size");
2589                 // Use zeroes as the minimums.
2590                 minValues = new long[THRESHOLD_BUCKETS.length];
2591             }
2592             long[] array = new long[THRESHOLD_BUCKETS.length];
2593             for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
2594                 array[i] = Math.max(minValues[i], properties.getLong(keys[i], defaults[i]));
2595             }
2596             return array;
2597         }
2598     }
2599 }
2600