• 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_RESTORED;
27 import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
28 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
29 import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_USER_FLAG_INTERACTION;
30 import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
31 import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
32 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
33 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
34 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
35 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
36 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
37 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
38 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
39 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
40 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
41 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
42 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
43 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
44 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
45 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED;
46 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
47 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
48 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
49 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
50 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
51 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
52 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
53 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
54 import static android.app.usage.UsageStatsManager.standbyBucketToString;
55 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
56 
57 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
58 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
59 import static com.android.server.usage.AppIdleHistory.STANDBY_BUCKET_UNKNOWN;
60 
61 import android.annotation.CurrentTimeMillisLong;
62 import android.annotation.DurationMillisLong;
63 import android.annotation.NonNull;
64 import android.annotation.Nullable;
65 import android.annotation.UserIdInt;
66 import android.app.ActivityManager;
67 import android.app.AppOpsManager;
68 import android.app.usage.AppStandbyInfo;
69 import android.app.usage.UsageEvents;
70 import android.app.usage.UsageStatsManager.ForcedReasons;
71 import android.app.usage.UsageStatsManager.StandbyBuckets;
72 import android.app.usage.UsageStatsManagerInternal;
73 import android.appwidget.AppWidgetManager;
74 import android.content.BroadcastReceiver;
75 import android.content.ContentResolver;
76 import android.content.Context;
77 import android.content.Intent;
78 import android.content.IntentFilter;
79 import android.content.pm.ApplicationInfo;
80 import android.content.pm.CrossProfileAppsInternal;
81 import android.content.pm.PackageInfo;
82 import android.content.pm.PackageManager;
83 import android.content.pm.PackageManagerInternal;
84 import android.content.pm.ResolveInfo;
85 import android.database.ContentObserver;
86 import android.hardware.display.DisplayManager;
87 import android.net.NetworkScoreManager;
88 import android.os.BatteryManager;
89 import android.os.BatteryStats;
90 import android.os.Build;
91 import android.os.Environment;
92 import android.os.Handler;
93 import android.os.IDeviceIdleController;
94 import android.os.Looper;
95 import android.os.Message;
96 import android.os.PowerManager;
97 import android.os.Process;
98 import android.os.RemoteException;
99 import android.os.ServiceManager;
100 import android.os.SystemClock;
101 import android.os.Trace;
102 import android.os.UserHandle;
103 import android.provider.DeviceConfig;
104 import android.provider.Settings.Global;
105 import android.telephony.TelephonyManager;
106 import android.text.TextUtils;
107 import android.util.ArrayMap;
108 import android.util.ArraySet;
109 import android.util.IndentingPrintWriter;
110 import android.util.Slog;
111 import android.util.SparseArray;
112 import android.util.SparseBooleanArray;
113 import android.util.SparseIntArray;
114 import android.util.SparseLongArray;
115 import android.util.SparseSetArray;
116 import android.util.TimeUtils;
117 import android.view.Display;
118 import android.widget.Toast;
119 
120 import com.android.internal.R;
121 import com.android.internal.annotations.GuardedBy;
122 import com.android.internal.annotations.VisibleForTesting;
123 import com.android.internal.app.IAppOpsCallback;
124 import com.android.internal.app.IAppOpsService;
125 import com.android.internal.app.IBatteryStats;
126 import com.android.internal.util.ArrayUtils;
127 import com.android.internal.util.ConcurrentUtils;
128 import com.android.server.AlarmManagerInternal;
129 import com.android.server.AppSchedulingModuleThread;
130 import com.android.server.LocalServices;
131 import com.android.server.pm.pkg.AndroidPackage;
132 import com.android.server.usage.AppIdleHistory.AppUsageHistory;
133 import com.android.tools.r8.keepanno.annotations.KeepItemKind;
134 import com.android.tools.r8.keepanno.annotations.UsedByReflection;
135 
136 import libcore.util.EmptyArray;
137 
138 import java.io.File;
139 import java.io.PrintWriter;
140 import java.util.ArrayList;
141 import java.util.Arrays;
142 import java.util.Collections;
143 import java.util.List;
144 import java.util.Map;
145 import java.util.Set;
146 import java.util.concurrent.CountDownLatch;
147 
148 /**
149  * Manages the standby state of an app, listening to various events.
150  *
151  * Unit test:
152  * atest com.android.server.usage.AppStandbyControllerTests
153  */
154 public class AppStandbyController
155         implements AppStandbyInternal, UsageStatsManagerInternal.UsageEventListener {
156 
157     private static final String TAG = "AppStandbyController";
158     // Do not submit with true.
159     static final boolean DEBUG = false;
160 
161     static final boolean COMPRESS_TIME = false;
162     private static final long ONE_MINUTE = 60 * 1000;
163     private static final long ONE_HOUR = ONE_MINUTE * 60;
164     private static final long ONE_DAY = ONE_HOUR * 24;
165 
166     /**
167      * The default minimum amount of time the screen must have been on before an app can time out
168      * from its current bucket to the next bucket.
169      */
170     @VisibleForTesting
171     static final long[] DEFAULT_SCREEN_TIME_THRESHOLDS = {
172             0,
173             0,
174             COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR,
175             COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR,
176             COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR
177     };
178 
179     /** The minimum allowed values for each index in {@link #DEFAULT_SCREEN_TIME_THRESHOLDS}. */
180     @VisibleForTesting
181     static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME
182             ? new long[DEFAULT_SCREEN_TIME_THRESHOLDS.length]
183             : new long[]{
184                     0,
185                     0,
186                     0,
187                     30 * ONE_MINUTE,
188                     ONE_HOUR
189             };
190 
191     /**
192      * The default minimum amount of elapsed time that must have passed before an app can time out
193      * from its current bucket to the next bucket.
194      */
195     @VisibleForTesting
196     static final long[] DEFAULT_ELAPSED_TIME_THRESHOLDS = {
197             0,
198             COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
199             COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
200             COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
201             COMPRESS_TIME ? 32 * ONE_MINUTE : 8 * ONE_DAY
202     };
203 
204     /** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
205     @VisibleForTesting
206     static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME
207             ? new long[DEFAULT_ELAPSED_TIME_THRESHOLDS.length]
208             : new long[]{
209                     0,
210                     ONE_HOUR,
211                     ONE_HOUR,
212                     2 * ONE_HOUR,
213                     4 * ONE_HOUR
214             };
215 
216     private static final int[] THRESHOLD_BUCKETS = {
217             STANDBY_BUCKET_ACTIVE,
218             STANDBY_BUCKET_WORKING_SET,
219             STANDBY_BUCKET_FREQUENT,
220             STANDBY_BUCKET_RARE,
221             STANDBY_BUCKET_RESTRICTED
222     };
223 
224     /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
225     private static final long DEFAULT_PREDICTION_TIMEOUT =
226             COMPRESS_TIME ? 10 * ONE_MINUTE : 12 * ONE_HOUR;
227 
228     /**
229      * Indicates the maximum wait time for admin data to be available;
230      */
231     private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
232 
233     private static final int HEADLESS_APP_CHECK_FLAGS =
234             PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
235                     | PackageManager.MATCH_DISABLED_COMPONENTS
236                     | PackageManager.MATCH_SYSTEM_ONLY;
237 
238     private static final int NOTIFICATION_SEEN_PROMOTED_BUCKET_FOR_PRE_T_APPS =
239             STANDBY_BUCKET_WORKING_SET;
240     private static final long NOTIFICATION_SEEN_HOLD_DURATION_FOR_PRE_T_APPS =
241             COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
242 
243     // To name the lock for stack traces
244     static class Lock {}
245 
246     /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
247     private final Object mAppIdleLock = new Lock();
248 
249     /** Keeps the history and state for each app. */
250     @GuardedBy("mAppIdleLock")
251     private AppIdleHistory mAppIdleHistory;
252 
253     @GuardedBy("mPackageAccessListeners")
254     private final ArrayList<AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>();
255 
256     /**
257      * Lock specifically for bookkeeping around the carrier-privileged app set.
258      * Do not acquire any other locks while holding this one.  Methods that
259      * require this lock to be held are named with a "CPL" suffix.
260      */
261     private final Object mCarrierPrivilegedLock = new Lock();
262 
263     /** Whether we've queried the list of carrier privileged apps. */
264     @GuardedBy("mCarrierPrivilegedLock")
265     private boolean mHaveCarrierPrivilegedApps;
266 
267     private final boolean mHasFeatureTelephonySubscription;
268 
269     /** List of carrier-privileged apps that should be excluded from standby */
270     @GuardedBy("mCarrierPrivilegedLock")
271     private List<String> mCarrierPrivilegedApps;
272 
273     @GuardedBy("mActiveAdminApps")
274     private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
275 
276     /** List of admin protected packages. Can contain {@link android.os.UserHandle#USER_ALL}. */
277     @GuardedBy("mAdminProtectedPackages")
278     private final SparseArray<Set<String>> mAdminProtectedPackages = new SparseArray<>();
279 
280     /**
281      * Set of system apps that are headless (don't have any "front door" activities, enabled or
282      * disabled). Presence in this map indicates that the app is a headless system app.
283      */
284     @GuardedBy("mHeadlessSystemApps")
285     private final ArraySet<String> mHeadlessSystemApps = new ArraySet<>();
286 
287     private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
288 
289     /**
290      * Set of user IDs and the next time (in the elapsed realtime timebase) when we should check the
291      * apps' idle states.
292      */
293     @GuardedBy("mPendingIdleStateChecks")
294     private final SparseLongArray mPendingIdleStateChecks = new SparseLongArray();
295 
296     /**
297      * Map of uids to their current app-op mode for
298      * {@link AppOpsManager#OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS}.
299      */
300     @GuardedBy("mSystemExemptionAppOpMode")
301     private final SparseIntArray mSystemExemptionAppOpMode = new SparseIntArray();
302 
303     // Cache the active network scorer queried from the network scorer service
304     private volatile String mCachedNetworkScorer = null;
305     // The last time the network scorer service was queried
306     private volatile long mCachedNetworkScorerAtMillis = 0L;
307     // How long before querying the network scorer again. During this time, subsequent queries will
308     // get the cached value
309     private static final long NETWORK_SCORER_CACHE_DURATION_MILLIS = 5000L;
310 
311     // Cache the device provisioning package queried from resource config_deviceProvisioningPackage.
312     // Note that there is no synchronization on this variable which is okay since in the worst case
313     // scenario, they might be a few extra reads from resources.
314     private String mCachedDeviceProvisioningPackage = null;
315 
316     // Messages for the handler
317     static final int MSG_INFORM_LISTENERS = 3;
318     static final int MSG_FORCE_IDLE_STATE = 4;
319     static final int MSG_CHECK_IDLE_STATES = 5;
320     static final int MSG_TRIGGER_LISTENER_QUOTA_BUMP = 7;
321     static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
322     static final int MSG_PAROLE_STATE_CHANGED = 9;
323     static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
324     /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
325     static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
326     static final int MSG_REPORT_SYNC_SCHEDULED = 12;
327     static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
328 
329     long mCheckIdleIntervalMillis = Math.min(DEFAULT_ELAPSED_TIME_THRESHOLDS[1] / 4,
330             ConstantsObserver.DEFAULT_CHECK_IDLE_INTERVAL_MS);
331     /**
332      * The minimum amount of time the screen must have been on before an app can time out from its
333      * current bucket to the next bucket.
334      */
335     long[] mAppStandbyScreenThresholds = DEFAULT_SCREEN_TIME_THRESHOLDS;
336     /**
337      * The minimum amount of elapsed time that must have passed before an app can time out from its
338      * current bucket to the next bucket.
339      */
340     long[] mAppStandbyElapsedThresholds = DEFAULT_ELAPSED_TIME_THRESHOLDS;
341     /** Minimum time a strong usage event should keep the bucket elevated. */
342     long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT;
343     /** Minimum time a notification seen event should keep the bucket elevated. */
344     long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT;
345     /** Minimum time a slice pinned event should keep the bucket elevated. */
346     long mSlicePinnedTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT;
347     /** The standby bucket that an app will be promoted on a notification-seen event */
348     int mNotificationSeenPromotedBucket =
349             ConstantsObserver.DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET;
350     /**
351      * If {@code true}, tell each {@link AppIdleStateChangeListener} to give quota bump for each
352      * notification seen event.
353      */
354     private boolean mTriggerQuotaBumpOnNotificationSeen =
355             ConstantsObserver.DEFAULT_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN;
356     /**
357      * If {@code true}, we will retain the pre-T impact of notification signal on apps targeting
358      * pre-T sdk levels regardless of other flag changes.
359      */
360     boolean mRetainNotificationSeenImpactForPreTApps =
361             ConstantsObserver.DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS;
362     /** Minimum time a system update event should keep the buckets elevated. */
363     long mSystemUpdateUsageTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_UPDATE_TIMEOUT;
364     /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
365     long mPredictionTimeoutMillis = DEFAULT_PREDICTION_TIMEOUT;
366     /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
367     long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT;
368     /** The bucket that an app will be promoted on a sync adapter associated with a CP */
369     int mSyncAdapaterPromotedBucket = STANDBY_BUCKET_ACTIVE;
370     /**
371      * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
372      * non-doze
373      */
374     long mExemptedSyncScheduledNonDozeTimeoutMillis =
375             ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT;
376     /**
377      * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
378      * doze
379      */
380     long mExemptedSyncScheduledDozeTimeoutMillis =
381             ConstantsObserver.DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT;
382     /**
383      * Maximum time an exempted sync should keep the buckets elevated, when sync is started.
384      */
385     long mExemptedSyncStartTimeoutMillis = ConstantsObserver.DEFAULT_EXEMPTED_SYNC_START_TIMEOUT;
386     /**
387      * Maximum time an unexempted sync should keep the buckets elevated, when sync is scheduled
388      */
389     long mUnexemptedSyncScheduledTimeoutMillis =
390             ConstantsObserver.DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT;
391     /** Maximum time a system interaction should keep the buckets elevated. */
392     long mSystemInteractionTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_INTERACTION_TIMEOUT;
393     /**
394      * Maximum time a foreground service start should keep the buckets elevated if the service
395      * start is the first usage of the app
396      */
397     long mInitialForegroundServiceStartTimeoutMillis =
398             ConstantsObserver.DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT;
399     /**
400      * User usage that would elevate an app's standby bucket will also elevate the standby bucket of
401      * cross profile connected apps. Explicit standby bucket setting via
402      * {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated.
403      */
404     boolean mLinkCrossProfileApps =
405             ConstantsObserver.DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS;
406 
407     /**
408      * Duration (in millis) for the window where events occurring will be considered as
409      * broadcast response, starting from the point when an app receives a broadcast.
410      */
411     volatile long mBroadcastResponseWindowDurationMillis =
412             ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS;
413 
414     /**
415      * Process state threshold that is used for deciding whether or not an app is in the background
416      * in the context of recording broadcast response stats. Apps whose process state is higher
417      * than this threshold state will be considered to be in background.
418      */
419     volatile int mBroadcastResponseFgThresholdState =
420             ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE;
421 
422     /**
423      * Duration (in millis) for the window within which any broadcasts occurred will be
424      * treated as one broadcast session.
425      */
426     volatile long mBroadcastSessionsDurationMs =
427             ConstantsObserver.DEFAULT_BROADCAST_SESSIONS_DURATION_MS;
428 
429     /**
430      * Duration (in millis) for the window within which any broadcasts occurred ((with a
431      * corresponding response event) will be treated as one broadcast session. This similar to
432      * {@link #mBroadcastSessionsDurationMs}, except that this duration will be used to group only
433      * broadcasts that have a corresponding response event into sessions.
434      */
435     volatile long mBroadcastSessionsWithResponseDurationMs =
436             ConstantsObserver.DEFAULT_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS;
437 
438     /**
439      * Denotes whether the response event should be attributed to all broadcast sessions or not.
440      * If this is {@code true}, then the response event should be attributed to all the broadcast
441      * sessions that occurred within the broadcast response window. Otherwise, the
442      * response event should be attributed to only the earliest broadcast session within the
443      * broadcast response window.
444      */
445     volatile boolean mNoteResponseEventForAllBroadcastSessions =
446             ConstantsObserver.DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS;
447 
448     /**
449      * List of roles whose holders are exempted from the requirement of starting
450      * a response event after receiving a broadcast.
451      *
452      * The list of roles will be separated by '|' in the string.
453      */
454     volatile String mBroadcastResponseExemptedRoles =
455             ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_EXEMPTED_ROLES;
456     volatile List<String> mBroadcastResponseExemptedRolesList = Collections.EMPTY_LIST;
457 
458     /**
459      * List of permissions whose holders are exempted from the requirement of starting
460      * a response event after receiving a broadcast.
461      *
462      * The list of permissions will be separated by '|' in the string.
463      */
464     volatile String mBroadcastResponseExemptedPermissions =
465             ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS;
466     volatile List<String> mBroadcastResponseExemptedPermissionsList = Collections.EMPTY_LIST;
467 
468     /**
469      * Map of last known values of keys in {@link DeviceConfig#NAMESPACE_APP_STANDBY}.
470      *
471      * Note: We are intentionally not guarding this by any lock since this is only updated on
472      * a handler thread and when querying, if we do end up seeing slightly older values, it is fine
473      * since the values are only used in tests and doesn't need to be queried in any other cases.
474      */
475     private final Map<String, String> mAppStandbyProperties = new ArrayMap<>();
476 
477     /**
478      * Set of apps that were restored via backup & restore, per user, that need their
479      * standby buckets to be adjusted when installed.
480      */
481     private final SparseSetArray<String> mAppsToRestoreToRare = new SparseSetArray<>();
482 
483     /**
484      * List of app-ids of system packages, populated on boot, when system services are ready.
485      */
486     private final ArrayList<Integer> mSystemPackagesAppIds = new ArrayList<>();
487 
488     /**
489      * PackageManager flags to query for all system packages, including those that are disabled
490      * and hidden.
491      */
492     private static final int SYSTEM_PACKAGE_FLAGS = PackageManager.MATCH_UNINSTALLED_PACKAGES
493             | PackageManager.MATCH_SYSTEM_ONLY
494             | PackageManager.MATCH_ANY_USER
495             | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
496             | PackageManager.MATCH_DIRECT_BOOT_AWARE
497             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
498 
499     private volatile boolean mAppIdleEnabled;
500     private volatile boolean mIsCharging;
501     private boolean mSystemServicesReady = false;
502     // There was a system update, defaults need to be initialized after services are ready
503     private boolean mPendingInitializeDefaults;
504 
505     private volatile boolean mPendingOneTimeCheckIdleStates;
506 
507     private final AppStandbyHandler mHandler;
508     private final Context mContext;
509 
510     private AppWidgetManager mAppWidgetManager;
511     private PackageManager mPackageManager;
512     private AppOpsManager mAppOpsManager;
513     Injector mInjector;
514 
515     private static class Pool<T> {
516         private final T[] mArray;
517         private int mSize = 0;
518 
Pool(T[] array)519         Pool(T[] array) {
520             mArray = array;
521         }
522 
523         @Nullable
obtain()524         synchronized T obtain() {
525             return mSize > 0 ? mArray[--mSize] : null;
526         }
527 
recycle(T instance)528         synchronized void recycle(T instance) {
529             if (mSize < mArray.length) {
530                 mArray[mSize++] = instance;
531             }
532         }
533     }
534 
535     private static class StandbyUpdateRecord {
536         private static final Pool<StandbyUpdateRecord> sPool =
537                 new Pool<>(new StandbyUpdateRecord[10]);
538 
539         // Identity of the app whose standby state has changed
540         String packageName;
541         int userId;
542 
543         // What the standby bucket the app is now in
544         int bucket;
545 
546         // Whether the bucket change is because the user has started interacting with the app
547         boolean isUserInteraction;
548 
549         // Reason for bucket change
550         int reason;
551 
obtain(String pkgName, int userId, int bucket, int reason, boolean isInteraction)552         public static StandbyUpdateRecord obtain(String pkgName, int userId,
553                 int bucket, int reason, boolean isInteraction) {
554             StandbyUpdateRecord r = sPool.obtain();
555             if (r == null) {
556                 r = new StandbyUpdateRecord();
557             }
558             r.packageName = pkgName;
559             r.userId = userId;
560             r.bucket = bucket;
561             r.reason = reason;
562             r.isUserInteraction = isInteraction;
563             return r;
564 
565         }
566 
recycle()567         public void recycle() {
568             sPool.recycle(this);
569         }
570     }
571 
572     private static class ContentProviderUsageRecord {
573         private static final Pool<ContentProviderUsageRecord> sPool =
574                 new Pool<>(new ContentProviderUsageRecord[10]);
575 
576         public String name;
577         public String packageName;
578         public int userId;
579 
obtain(String name, String packageName, int userId)580         public static ContentProviderUsageRecord obtain(String name, String packageName,
581                 int userId) {
582             ContentProviderUsageRecord r = sPool.obtain();
583             if (r == null) {
584                 r = new ContentProviderUsageRecord();
585             }
586             r.name = name;
587             r.packageName = packageName;
588             r.userId = userId;
589             return r;
590         }
591 
recycle()592         public void recycle() {
593             sPool.recycle(this);
594         }
595     }
596 
597     // This constructor is reflectively invoked from framework code in AppStandbyInternal.
598     @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
AppStandbyController(Context context)599     public AppStandbyController(Context context) {
600         this(new Injector(context, AppSchedulingModuleThread.get().getLooper()));
601     }
602 
AppStandbyController(Injector injector)603     AppStandbyController(Injector injector) {
604         mInjector = injector;
605         mContext = mInjector.getContext();
606         mHandler = new AppStandbyHandler(mInjector.getLooper());
607         mPackageManager = mContext.getPackageManager();
608         mHasFeatureTelephonySubscription = mPackageManager.hasSystemFeature(
609                 PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION);
610 
611         DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver();
612         IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
613         deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
614         deviceStates.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
615         mContext.registerReceiver(deviceStateReceiver, deviceStates);
616 
617         synchronized (mAppIdleLock) {
618             mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
619                     mInjector.elapsedRealtime());
620         }
621 
622         IntentFilter packageFilter = new IntentFilter();
623         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
624         packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
625         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
626         packageFilter.addDataScheme("package");
627 
628         mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
629                 null, mHandler);
630     }
631 
632     @VisibleForTesting
setAppIdleEnabled(boolean enabled)633     void setAppIdleEnabled(boolean enabled) {
634         // Don't call out to USM with the lock held. Also, register the listener before we
635         // change our internal state so no events fall through the cracks.
636         final UsageStatsManagerInternal usmi =
637                 LocalServices.getService(UsageStatsManagerInternal.class);
638         if (enabled) {
639             usmi.registerListener(this);
640         } else {
641             usmi.unregisterListener(this);
642         }
643 
644         synchronized (mAppIdleLock) {
645             if (mAppIdleEnabled != enabled) {
646                 final boolean oldParoleState = isInParole();
647                 mAppIdleEnabled = enabled;
648 
649                 if (isInParole() != oldParoleState) {
650                     postParoleStateChanged();
651                 }
652             }
653         }
654     }
655 
656     @Override
isAppIdleEnabled()657     public boolean isAppIdleEnabled() {
658         return mAppIdleEnabled;
659     }
660 
661     @Override
onBootPhase(int phase)662     public void onBootPhase(int phase) {
663         mInjector.onBootPhase(phase);
664         if (phase == PHASE_SYSTEM_SERVICES_READY) {
665             Slog.d(TAG, "Setting app idle enabled state");
666 
667             if (mAppIdleEnabled) {
668                 LocalServices.getService(UsageStatsManagerInternal.class).registerListener(this);
669             }
670 
671             // Observe changes to the threshold
672             ConstantsObserver settingsObserver = new ConstantsObserver(mHandler);
673             settingsObserver.start();
674 
675             mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
676             mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
677             IAppOpsService iAppOpsService = mInjector.getAppOpsService();
678             try {
679                 iAppOpsService.startWatchingMode(
680                         AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS,
681                         /*packageName=*/ null,
682                         new IAppOpsCallback.Stub() {
683                             @Override
684                             public void opChanged(int op, int uid, String packageName,
685                                     String persistentDeviceId) {
686                                 final int userId = UserHandle.getUserId(uid);
687                                 synchronized (mSystemExemptionAppOpMode) {
688                                     mSystemExemptionAppOpMode.delete(uid);
689                                 }
690                                 mHandler.obtainMessage(
691                                         MSG_CHECK_PACKAGE_IDLE_STATE, userId, uid, packageName)
692                                         .sendToTarget();
693                             }
694                         });
695             } catch (RemoteException e) {
696                 // Should not happen.
697                 Slog.wtf(TAG, "Failed start watching for app op", e);
698             }
699 
700             mInjector.registerDisplayListener(mDisplayListener, mHandler);
701             synchronized (mAppIdleLock) {
702                 mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
703             }
704 
705             mSystemServicesReady = true;
706 
707             boolean userFileExists;
708             synchronized (mAppIdleLock) {
709                 userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
710             }
711 
712             if (mPendingInitializeDefaults || !userFileExists) {
713                 initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
714             }
715 
716             if (!Flags.avoidIdleCheck() && mPendingOneTimeCheckIdleStates) {
717                 postOneTimeCheckIdleStates();
718             }
719 
720             // Populate list of system packages and their app-ids.
721             final List<ApplicationInfo> systemApps = mPackageManager.getInstalledApplications(
722                     SYSTEM_PACKAGE_FLAGS);
723             for (int i = 0, size = systemApps.size(); i < size; i++) {
724                 final ApplicationInfo appInfo = systemApps.get(i);
725                 mSystemPackagesAppIds.add(UserHandle.getAppId(appInfo.uid));
726             }
727         } else if (phase == PHASE_BOOT_COMPLETED) {
728             setChargingState(mInjector.isCharging());
729 
730             // Offload to handler thread after boot completed to avoid boot time impact. This means
731             // that app standby buckets may be slightly out of date and headless system apps may be
732             // put in a lower bucket until boot has completed.
733             mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
734             mHandler.post(this::loadHeadlessSystemAppCache);
735         }
736     }
737 
reportContentProviderUsage(String authority, String providerPkgName, int userId)738     private void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
739         if (!mAppIdleEnabled) return;
740 
741         // Get sync adapters for the authority
742         String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
743                 authority, userId);
744         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
745         final long elapsedRealtime = mInjector.elapsedRealtime();
746         for (String packageName : packages) {
747             // Don't force the sync adapter to active if the provider is in the same APK.
748             if (packageName.equals(providerPkgName)) {
749                 continue;
750             }
751 
752             final int appId = UserHandle.getAppId(pmi.getPackageUid(packageName, 0, userId));
753             // Elevate the sync adapter to active if it's a system app or
754             // is a non-system app and shares its app id with a system app.
755             if (mSystemPackagesAppIds.contains(appId)) {
756                 final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName,
757                         userId);
758                 synchronized (mAppIdleLock) {
759                     reportNoninteractiveUsageCrossUserLocked(packageName, userId,
760                             mSyncAdapaterPromotedBucket, REASON_SUB_USAGE_SYNC_ADAPTER,
761                             elapsedRealtime, mSyncAdapterTimeoutMillis, linkedProfiles);
762                 }
763             }
764         }
765     }
766 
reportExemptedSyncScheduled(String packageName, int userId)767     private void reportExemptedSyncScheduled(String packageName, int userId) {
768         if (!mAppIdleEnabled) return;
769 
770         final int bucketToPromote;
771         final int usageReason;
772         final long durationMillis;
773 
774         if (!mInjector.isDeviceIdleMode()) {
775             // Not dozing.
776             bucketToPromote = STANDBY_BUCKET_ACTIVE;
777             usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
778             durationMillis = mExemptedSyncScheduledNonDozeTimeoutMillis;
779         } else {
780             // Dozing.
781             bucketToPromote = STANDBY_BUCKET_WORKING_SET;
782             usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
783             durationMillis = mExemptedSyncScheduledDozeTimeoutMillis;
784         }
785 
786         final long elapsedRealtime = mInjector.elapsedRealtime();
787         final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
788         synchronized (mAppIdleLock) {
789             reportNoninteractiveUsageCrossUserLocked(packageName, userId, bucketToPromote,
790                     usageReason, elapsedRealtime, durationMillis, linkedProfiles);
791         }
792     }
793 
reportUnexemptedSyncScheduled(String packageName, int userId)794     private void reportUnexemptedSyncScheduled(String packageName, int userId) {
795         if (!mAppIdleEnabled) return;
796 
797         final long elapsedRealtime = mInjector.elapsedRealtime();
798         synchronized (mAppIdleLock) {
799             final int currentBucket =
800                     mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
801             if (currentBucket == STANDBY_BUCKET_NEVER) {
802                 final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
803                 // Bring the app out of the never bucket
804                 reportNoninteractiveUsageCrossUserLocked(packageName, userId,
805                         STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED,
806                         elapsedRealtime, mUnexemptedSyncScheduledTimeoutMillis, linkedProfiles);
807             }
808         }
809     }
810 
reportExemptedSyncStart(String packageName, int userId)811     private void reportExemptedSyncStart(String packageName, int userId) {
812         if (!mAppIdleEnabled) return;
813 
814         final long elapsedRealtime = mInjector.elapsedRealtime();
815         final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
816         synchronized (mAppIdleLock) {
817             reportNoninteractiveUsageCrossUserLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
818                     REASON_SUB_USAGE_EXEMPTED_SYNC_START, elapsedRealtime,
819                     mExemptedSyncStartTimeoutMillis, linkedProfiles);
820         }
821     }
822 
823     /**
824      * Helper method to report indirect user usage of an app and handle reporting the usage
825      * against cross profile connected apps. <br>
826      * Use {@link #reportNoninteractiveUsageLocked(String, int, int, int, long, long)} if
827      * cross profile connected apps do not need to be handled.
828      */
reportNoninteractiveUsageCrossUserLocked(String packageName, int userId, int bucket, int subReason, long elapsedRealtime, long nextCheckDelay, List<UserHandle> otherProfiles)829     private void reportNoninteractiveUsageCrossUserLocked(String packageName, int userId,
830             int bucket, int subReason, long elapsedRealtime, long nextCheckDelay,
831             List<UserHandle> otherProfiles) {
832         reportNoninteractiveUsageLocked(packageName, userId, bucket, subReason, elapsedRealtime,
833                 nextCheckDelay);
834         final int size = otherProfiles.size();
835         for (int profileIndex = 0; profileIndex < size; profileIndex++) {
836             final int otherUserId = otherProfiles.get(profileIndex).getIdentifier();
837             reportNoninteractiveUsageLocked(packageName, otherUserId, bucket, subReason,
838                     elapsedRealtime, nextCheckDelay);
839         }
840     }
841 
842     /**
843      * Helper method to report indirect user usage of an app. <br>
844      * Use
845      * {@link #reportNoninteractiveUsageCrossUserLocked(String, int, int, int, long, long, List)}
846      * if cross profile connected apps need to be handled.
847      */
reportNoninteractiveUsageLocked(String packageName, int userId, int bucket, int subReason, long elapsedRealtime, long nextCheckDelay)848     private void reportNoninteractiveUsageLocked(String packageName, int userId, int bucket,
849             int subReason, long elapsedRealtime, long nextCheckDelay) {
850         final AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, bucket,
851                 subReason, 0, elapsedRealtime + nextCheckDelay);
852         mHandler.sendMessageDelayed(
853                 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
854                 nextCheckDelay);
855         maybeInformListeners(packageName, userId, elapsedRealtime, appUsage.currentBucket,
856                 appUsage.bucketingReason, false);
857     }
858 
859     /** Trigger a quota bump in the listeners. */
triggerListenerQuotaBump(String packageName, int userId)860     private void triggerListenerQuotaBump(String packageName, int userId) {
861         if (!mAppIdleEnabled) return;
862 
863         synchronized (mPackageAccessListeners) {
864             for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
865                 listener.triggerTemporaryQuotaBump(packageName, userId);
866             }
867         }
868     }
869 
870     @VisibleForTesting
setChargingState(boolean isCharging)871     void setChargingState(boolean isCharging) {
872         if (mIsCharging != isCharging) {
873             if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging);
874             mIsCharging = isCharging;
875             postParoleStateChanged();
876         }
877     }
878 
879     @Override
isInParole()880     public boolean isInParole() {
881         return !mAppIdleEnabled || mIsCharging;
882     }
883 
postParoleStateChanged()884     private void postParoleStateChanged() {
885         if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
886         mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
887         mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
888     }
889 
890     @Override
postCheckIdleStates(int userId)891     public void postCheckIdleStates(int userId) {
892         if (userId == UserHandle.USER_ALL) {
893             postOneTimeCheckIdleStates();
894         } else {
895             synchronized (mPendingIdleStateChecks) {
896                 mPendingIdleStateChecks.put(userId, mInjector.elapsedRealtime());
897             }
898             mHandler.obtainMessage(MSG_CHECK_IDLE_STATES).sendToTarget();
899         }
900     }
901 
902     @Override
postOneTimeCheckIdleStates()903     public void postOneTimeCheckIdleStates() {
904         if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
905             // Not booted yet; wait for it!
906             mPendingOneTimeCheckIdleStates = true;
907         } else {
908             mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
909             mPendingOneTimeCheckIdleStates = false;
910         }
911     }
912 
913     @VisibleForTesting
checkIdleStates(int checkUserId)914     boolean checkIdleStates(int checkUserId) {
915         if (!mAppIdleEnabled) {
916             return false;
917         }
918 
919         final int[] runningUserIds;
920         try {
921             runningUserIds = mInjector.getRunningUserIds();
922             if (checkUserId != UserHandle.USER_ALL
923                     && !ArrayUtils.contains(runningUserIds, checkUserId)) {
924                 return false;
925             }
926         } catch (RemoteException re) {
927             throw re.rethrowFromSystemServer();
928         }
929 
930         final long elapsedRealtime = mInjector.elapsedRealtime();
931         for (int i = 0; i < runningUserIds.length; i++) {
932             final int userId = runningUserIds[i];
933             if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
934                 continue;
935             }
936             if (DEBUG) {
937                 Slog.d(TAG, "Checking idle state for user " + userId);
938             }
939             List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
940                     PackageManager.MATCH_DISABLED_COMPONENTS,
941                     userId);
942             final int packageCount = packages.size();
943             for (int p = 0; p < packageCount; p++) {
944                 final PackageInfo pi = packages.get(p);
945                 final String packageName = pi.packageName;
946                 checkAndUpdateStandbyState(packageName, userId, pi.applicationInfo.uid,
947                         elapsedRealtime);
948             }
949         }
950         if (DEBUG) {
951             Slog.d(TAG, "checkIdleStates took "
952                     + (mInjector.elapsedRealtime() - elapsedRealtime));
953         }
954         return true;
955     }
956 
957     /** Check if we need to update the standby state of a specific app. */
checkAndUpdateStandbyState(String packageName, @UserIdInt int userId, int uid, long elapsedRealtime)958     private void checkAndUpdateStandbyState(String packageName, @UserIdInt int userId,
959             int uid, long elapsedRealtime) {
960         if (uid <= 0) {
961             try {
962                 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
963             } catch (PackageManager.NameNotFoundException e) {
964                 // Not a valid package for this user, nothing to do
965                 // TODO: Remove any history of removed packages
966                 return;
967             }
968         }
969         final int minBucket = getAppMinBucket(packageName,
970                 UserHandle.getAppId(uid),
971                 userId);
972         if (DEBUG) {
973             Slog.d(TAG, "   Checking idle state for " + packageName
974                     + " minBucket=" + standbyBucketToString(minBucket));
975         }
976         final boolean previouslyIdle, stillIdle;
977         if (minBucket <= STANDBY_BUCKET_ACTIVE) {
978             // No extra processing needed for ACTIVE or higher since apps can't drop into lower
979             // buckets.
980             synchronized (mAppIdleLock) {
981                 previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
982                 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
983                         minBucket, REASON_MAIN_DEFAULT);
984                 stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
985             }
986             maybeInformListeners(packageName, userId, elapsedRealtime,
987                     minBucket, REASON_MAIN_DEFAULT, false);
988         } else {
989             synchronized (mAppIdleLock) {
990                 previouslyIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
991                 final AppIdleHistory.AppUsageHistory app =
992                         mAppIdleHistory.getAppUsageHistory(packageName,
993                         userId, elapsedRealtime);
994                 int reason = app.bucketingReason;
995                 final int oldMainReason = reason & REASON_MAIN_MASK;
996 
997                 // If the bucket was forced by the user/developer, leave it alone.
998                 // A usage event will be the only way to bring it out of this forced state
999                 if (oldMainReason == REASON_MAIN_FORCED_BY_USER) {
1000                     return;
1001                 }
1002                 final int oldBucket = app.currentBucket;
1003                 if (oldBucket == STANDBY_BUCKET_NEVER) {
1004                     // None of this should bring an app out of the NEVER bucket.
1005                     return;
1006                 }
1007                 int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
1008                 boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
1009                 // Compute age-based bucket
1010                 if (oldMainReason == REASON_MAIN_DEFAULT
1011                         || oldMainReason == REASON_MAIN_USAGE
1012                         || oldMainReason == REASON_MAIN_TIMEOUT
1013                         || predictionLate) {
1014 
1015                     if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
1016                             && app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
1017                         newBucket = app.lastPredictedBucket;
1018                         reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
1019                         if (DEBUG) {
1020                             Slog.d(TAG, "Restored predicted newBucket = "
1021                                     + standbyBucketToString(newBucket));
1022                         }
1023                     } else {
1024                         // Don't update the standby state for apps that were restored
1025                         if (!(oldMainReason == REASON_MAIN_DEFAULT
1026                                 && (app.bucketingReason & REASON_SUB_MASK)
1027                                         == REASON_SUB_DEFAULT_APP_RESTORED)) {
1028                             newBucket = getBucketForLocked(packageName, userId, elapsedRealtime);
1029                             if (DEBUG) {
1030                                 Slog.d(TAG, "Evaluated " + packageName + " newBucket = "
1031                                         + standbyBucketToString(newBucket));
1032                             }
1033                             reason = REASON_MAIN_TIMEOUT;
1034                         }
1035                     }
1036                 }
1037 
1038                 // Check if the app is within one of the expiry times for forced bucket elevation
1039                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
1040                 final int bucketWithValidExpiryTime = getMinBucketWithValidExpiryTime(app,
1041                         newBucket, elapsedTimeAdjusted);
1042                 if (bucketWithValidExpiryTime != STANDBY_BUCKET_UNKNOWN) {
1043                     newBucket = bucketWithValidExpiryTime;
1044                     if (newBucket == STANDBY_BUCKET_ACTIVE || app.currentBucket == newBucket) {
1045                         reason = app.bucketingReason;
1046                     } else {
1047                         reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
1048                     }
1049                     if (DEBUG) {
1050                         Slog.d(TAG, "    Keeping at " + standbyBucketToString(newBucket)
1051                                 + " due to min timeout");
1052                     }
1053                 }
1054 
1055                 if (app.lastUsedByUserElapsedTime >= 0
1056                         && app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
1057                         && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
1058                         >= mInjector.getAutoRestrictedBucketDelayMs()) {
1059                     newBucket = STANDBY_BUCKET_RESTRICTED;
1060                     reason = app.lastRestrictReason;
1061                     if (DEBUG) {
1062                         Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
1063                     }
1064                 }
1065                 if (newBucket > minBucket) {
1066                     newBucket = minBucket;
1067                     // Leave the reason alone.
1068                     if (DEBUG) {
1069                         Slog.d(TAG, "Bringing up from " + standbyBucketToString(newBucket)
1070                                 + " to " + standbyBucketToString(minBucket)
1071                                 + " due to min bucketing");
1072                     }
1073                 }
1074                 if (DEBUG) {
1075                     Slog.d(TAG, "     Old bucket=" + standbyBucketToString(oldBucket)
1076                             + ", newBucket=" + standbyBucketToString(newBucket));
1077                 }
1078                 if (oldBucket != newBucket || predictionLate) {
1079                     mAppIdleHistory.setAppStandbyBucket(packageName, userId,
1080                             elapsedRealtime, newBucket, reason);
1081                     stillIdle = mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
1082                     maybeInformListeners(packageName, userId, elapsedRealtime,
1083                             newBucket, reason, false);
1084                 } else {
1085                     stillIdle = previouslyIdle;
1086                 }
1087             }
1088         }
1089         if (previouslyIdle != stillIdle) {
1090             notifyBatteryStats(packageName, userId, stillIdle);
1091         }
1092     }
1093 
1094     /** Returns true if there hasn't been a prediction for the app in a while. */
predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime)1095     private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
1096         return app.lastPredictedTime > 0
1097                 && mAppIdleHistory.getElapsedTime(elapsedRealtime)
1098                     - app.lastPredictedTime > mPredictionTimeoutMillis;
1099     }
1100 
1101     /** 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)1102     private void maybeInformListeners(String packageName, int userId,
1103             long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
1104         synchronized (mAppIdleLock) {
1105             if (mAppIdleHistory.shouldInformListeners(packageName, userId,
1106                     elapsedRealtime, bucket)) {
1107                 final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
1108                         bucket, reason, userStartedInteracting);
1109                 if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
1110                 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
1111             }
1112         }
1113     }
1114 
1115     /**
1116      * Evaluates next bucket based on time since last used and the bucketing thresholds.
1117      * @param packageName the app
1118      * @param userId the user
1119      * @param elapsedRealtime as the name suggests, current elapsed time
1120      * @return the bucket for the app, based on time since last used
1121      */
1122     @GuardedBy("mAppIdleLock")
1123     @StandbyBuckets
getBucketForLocked(String packageName, int userId, long elapsedRealtime)1124     private int getBucketForLocked(String packageName, int userId,
1125             long elapsedRealtime) {
1126         int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
1127                 elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
1128         return bucketIndex >= 0 ? THRESHOLD_BUCKETS[bucketIndex] : STANDBY_BUCKET_NEVER;
1129     }
1130 
notifyBatteryStats(String packageName, int userId, boolean idle)1131     private void notifyBatteryStats(String packageName, int userId, boolean idle) {
1132         try {
1133             final int uid = mPackageManager.getPackageUidAsUser(packageName,
1134                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1135             if (idle) {
1136                 mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
1137                         packageName, uid);
1138             } else {
1139                 mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
1140                         packageName, uid);
1141             }
1142         } catch (PackageManager.NameNotFoundException | RemoteException e) {
1143         }
1144     }
1145 
1146     /**
1147      * Callback to inform listeners of a new event.
1148      */
onUsageEvent(int userId, @NonNull UsageEvents.Event event)1149     public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
1150         if (!mAppIdleEnabled) return;
1151         final int eventType = event.getEventType();
1152         if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
1153                 || eventType == UsageEvents.Event.ACTIVITY_PAUSED
1154                 || eventType == UsageEvents.Event.SYSTEM_INTERACTION
1155                 || eventType == UsageEvents.Event.USER_INTERACTION
1156                 || eventType == UsageEvents.Event.NOTIFICATION_SEEN
1157                 || eventType == UsageEvents.Event.SLICE_PINNED
1158                 || eventType == UsageEvents.Event.SLICE_PINNED_PRIV
1159                 || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
1160             final String pkg = event.getPackageName();
1161             final List<UserHandle> linkedProfiles = getCrossProfileTargets(pkg, userId);
1162             synchronized (mAppIdleLock) {
1163                 final long elapsedRealtime = mInjector.elapsedRealtime();
1164                 reportEventLocked(pkg, eventType, elapsedRealtime, userId);
1165 
1166                 final int size = linkedProfiles.size();
1167                 for (int profileIndex = 0; profileIndex < size; profileIndex++) {
1168                     final int linkedUserId = linkedProfiles.get(profileIndex).getIdentifier();
1169                     reportEventLocked(pkg, eventType, elapsedRealtime, linkedUserId);
1170                 }
1171             }
1172         }
1173     }
1174 
1175     @GuardedBy("mAppIdleLock")
reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId)1176     private void reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId) {
1177         // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
1178         // about apps that are on some kind of whitelist anyway.
1179         final boolean previouslyIdle = mAppIdleHistory.isIdle(
1180                 pkg, userId, elapsedRealtime);
1181 
1182         final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
1183                 pkg, userId, elapsedRealtime);
1184         final int prevBucket = appHistory.currentBucket;
1185         final int prevBucketReason = appHistory.bucketingReason;
1186         final long nextCheckDelay;
1187         final int subReason = usageEventToSubReason(eventType);
1188         final int reason = REASON_MAIN_USAGE | subReason;
1189         if (eventType == UsageEvents.Event.NOTIFICATION_SEEN) {
1190             final int notificationSeenPromotedBucket;
1191             final long notificationSeenTimeoutMillis;
1192             if (mRetainNotificationSeenImpactForPreTApps
1193                     && getTargetSdkVersion(pkg) < Build.VERSION_CODES.TIRAMISU) {
1194                 notificationSeenPromotedBucket =
1195                         NOTIFICATION_SEEN_PROMOTED_BUCKET_FOR_PRE_T_APPS;
1196                 notificationSeenTimeoutMillis =
1197                         NOTIFICATION_SEEN_HOLD_DURATION_FOR_PRE_T_APPS;
1198             } else {
1199                 if (mTriggerQuotaBumpOnNotificationSeen) {
1200                     mHandler.obtainMessage(MSG_TRIGGER_LISTENER_QUOTA_BUMP, userId, -1, pkg)
1201                             .sendToTarget();
1202                 }
1203                 notificationSeenPromotedBucket = mNotificationSeenPromotedBucket;
1204                 notificationSeenTimeoutMillis = mNotificationSeenTimeoutMillis;
1205             }
1206             // Notification-seen elevates to a higher bucket (depending on
1207             // {@link ConstantsObserver#KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET}) but doesn't
1208             // change usage time.
1209             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
1210                     notificationSeenPromotedBucket, subReason,
1211                     0, elapsedRealtime + notificationSeenTimeoutMillis);
1212             nextCheckDelay = notificationSeenTimeoutMillis;
1213         } else if (eventType == UsageEvents.Event.SLICE_PINNED) {
1214             // Mild usage elevates to WORKING_SET but doesn't change usage time.
1215             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
1216                     STANDBY_BUCKET_WORKING_SET, subReason,
1217                     0, elapsedRealtime + mSlicePinnedTimeoutMillis);
1218             nextCheckDelay = mSlicePinnedTimeoutMillis;
1219         } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
1220             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
1221                     STANDBY_BUCKET_ACTIVE, subReason,
1222                     0, elapsedRealtime + mSystemInteractionTimeoutMillis);
1223             nextCheckDelay = mSystemInteractionTimeoutMillis;
1224         } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
1225             // Only elevate bucket if this is the first usage of the app
1226             if (prevBucket != STANDBY_BUCKET_NEVER) return;
1227             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
1228                     STANDBY_BUCKET_ACTIVE, subReason,
1229                     0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
1230             nextCheckDelay = mInitialForegroundServiceStartTimeoutMillis;
1231         } else {
1232             mAppIdleHistory.reportUsage(appHistory, pkg, userId,
1233                     STANDBY_BUCKET_ACTIVE, subReason,
1234                     elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
1235             nextCheckDelay = mStrongUsageTimeoutMillis;
1236         }
1237         if (appHistory.currentBucket != prevBucket) {
1238             mHandler.sendMessageDelayed(
1239                     mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
1240                     nextCheckDelay);
1241             final boolean userStartedInteracting =
1242                     appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
1243                             && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
1244             maybeInformListeners(pkg, userId, elapsedRealtime,
1245                     appHistory.currentBucket, reason, userStartedInteracting);
1246         }
1247 
1248         final boolean stillIdle = appHistory.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
1249         if (previouslyIdle != stillIdle) {
1250             notifyBatteryStats(pkg, userId, stillIdle);
1251         }
1252     }
1253 
getTargetSdkVersion(String packageName)1254     private int getTargetSdkVersion(String packageName) {
1255         return mInjector.getPackageManagerInternal().getPackageTargetSdkVersion(packageName);
1256     }
1257 
1258     /**
1259      * Returns the lowest standby bucket that is better than {@code targetBucket} and has an
1260      * valid expiry time (i.e. the expiry time is not yet elapsed).
1261      */
getMinBucketWithValidExpiryTime(AppUsageHistory usageHistory, int targetBucket, long elapsedTimeMs)1262     private int getMinBucketWithValidExpiryTime(AppUsageHistory usageHistory,
1263             int targetBucket, long elapsedTimeMs) {
1264         if (usageHistory.bucketExpiryTimesMs == null) {
1265             return STANDBY_BUCKET_UNKNOWN;
1266         }
1267         final int size = usageHistory.bucketExpiryTimesMs.size();
1268         for (int i = 0; i < size; ++i) {
1269             final int bucket = usageHistory.bucketExpiryTimesMs.keyAt(i);
1270             if (targetBucket <= bucket) {
1271                 break;
1272             }
1273             final long expiryTimeMs = usageHistory.bucketExpiryTimesMs.valueAt(i);
1274             if (expiryTimeMs > elapsedTimeMs) {
1275                 return bucket;
1276             }
1277         }
1278         return STANDBY_BUCKET_UNKNOWN;
1279     }
1280 
1281     /**
1282      * Note: don't call this with the lock held since it makes calls to other system services.
1283      */
getCrossProfileTargets(String pkg, int userId)1284     private @NonNull List<UserHandle> getCrossProfileTargets(String pkg, int userId) {
1285         synchronized (mAppIdleLock) {
1286             if (!mLinkCrossProfileApps) return Collections.emptyList();
1287         }
1288         return mInjector.getValidCrossProfileTargets(pkg, userId);
1289     }
1290 
usageEventToSubReason(int eventType)1291     private int usageEventToSubReason(int eventType) {
1292         switch (eventType) {
1293             case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
1294             case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
1295             case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
1296             case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
1297             case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
1298             case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
1299             case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
1300             case UsageEvents.Event.FOREGROUND_SERVICE_START:
1301                 return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
1302             default: return 0;
1303         }
1304     }
1305 
1306     @VisibleForTesting
forceIdleState(String packageName, int userId, boolean idle)1307     void forceIdleState(String packageName, int userId, boolean idle) {
1308         if (!mAppIdleEnabled) return;
1309 
1310         final int appId = getAppId(packageName);
1311         if (appId < 0) return;
1312         final int minBucket = getAppMinBucket(packageName, appId, userId);
1313         if (idle && minBucket < AppIdleHistory.IDLE_BUCKET_CUTOFF) {
1314             Slog.e(TAG, "Tried to force an app to be idle when its min bucket is "
1315                     + standbyBucketToString(minBucket));
1316             return;
1317         }
1318         final long elapsedRealtime = mInjector.elapsedRealtime();
1319 
1320         final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
1321                 userId, elapsedRealtime);
1322         final int standbyBucket;
1323         synchronized (mAppIdleLock) {
1324             standbyBucket = mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
1325         }
1326         final boolean stillIdle = isAppIdleFiltered(packageName, appId,
1327                 userId, elapsedRealtime);
1328         // Inform listeners if necessary
1329         maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
1330                 REASON_MAIN_FORCED_BY_USER, false);
1331         if (previouslyIdle != stillIdle) {
1332             notifyBatteryStats(packageName, userId, stillIdle);
1333         }
1334     }
1335 
1336     @Override
setLastJobRunTime(String packageName, int userId, long elapsedRealtime)1337     public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
1338         synchronized (mAppIdleLock) {
1339             mAppIdleHistory.setLastJobRunTime(packageName, userId, elapsedRealtime);
1340         }
1341     }
1342 
1343     @Override
getTimeSinceLastJobRun(String packageName, int userId)1344     public long getTimeSinceLastJobRun(String packageName, int userId) {
1345         final long elapsedRealtime = mInjector.elapsedRealtime();
1346         synchronized (mAppIdleLock) {
1347             return mAppIdleHistory.getTimeSinceLastJobRun(packageName, userId, elapsedRealtime);
1348         }
1349     }
1350 
1351     @Override
setEstimatedLaunchTime(String packageName, int userId, @CurrentTimeMillisLong long launchTime)1352     public void setEstimatedLaunchTime(String packageName, int userId,
1353             @CurrentTimeMillisLong long launchTime) {
1354         final long nowElapsed = mInjector.elapsedRealtime();
1355         synchronized (mAppIdleLock) {
1356             mAppIdleHistory.setEstimatedLaunchTime(packageName, userId, nowElapsed, launchTime);
1357         }
1358     }
1359 
1360     @Override
1361     @CurrentTimeMillisLong
getEstimatedLaunchTime(String packageName, int userId)1362     public long getEstimatedLaunchTime(String packageName, int userId) {
1363         final long elapsedRealtime = mInjector.elapsedRealtime();
1364         synchronized (mAppIdleLock) {
1365             return mAppIdleHistory.getEstimatedLaunchTime(packageName, userId, elapsedRealtime);
1366         }
1367     }
1368 
1369     @Override
getTimeSinceLastUsedByUser(String packageName, int userId)1370     public long getTimeSinceLastUsedByUser(String packageName, int userId) {
1371         final long elapsedRealtime = mInjector.elapsedRealtime();
1372         synchronized (mAppIdleLock) {
1373             return mAppIdleHistory.getTimeSinceLastUsedByUser(packageName, userId, elapsedRealtime);
1374         }
1375     }
1376 
1377     @Override
onUserRemoved(int userId)1378     public void onUserRemoved(int userId) {
1379         synchronized (mAppIdleLock) {
1380             mAppIdleHistory.onUserRemoved(userId);
1381             synchronized (mActiveAdminApps) {
1382                 mActiveAdminApps.remove(userId);
1383             }
1384             synchronized (mAdminProtectedPackages) {
1385                 mAdminProtectedPackages.remove(userId);
1386             }
1387         }
1388     }
1389 
isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime)1390     private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
1391         synchronized (mAppIdleLock) {
1392             return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
1393         }
1394     }
1395 
1396     @Override
addListener(AppIdleStateChangeListener listener)1397     public void addListener(AppIdleStateChangeListener listener) {
1398         synchronized (mPackageAccessListeners) {
1399             if (!mPackageAccessListeners.contains(listener)) {
1400                 mPackageAccessListeners.add(listener);
1401             }
1402         }
1403     }
1404 
1405     @Override
removeListener(AppIdleStateChangeListener listener)1406     public void removeListener(AppIdleStateChangeListener listener) {
1407         synchronized (mPackageAccessListeners) {
1408             mPackageAccessListeners.remove(listener);
1409         }
1410     }
1411 
1412     @Override
getAppId(String packageName)1413     public int getAppId(String packageName) {
1414         try {
1415             ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
1416                     PackageManager.MATCH_ANY_USER
1417                             | PackageManager.MATCH_DISABLED_COMPONENTS);
1418             return ai.uid;
1419         } catch (PackageManager.NameNotFoundException re) {
1420             return -1;
1421         }
1422     }
1423 
1424     @Override
isAppIdleFiltered(String packageName, int userId, long elapsedRealtime, boolean shouldObfuscateInstantApps)1425     public boolean isAppIdleFiltered(String packageName, int userId, long elapsedRealtime,
1426             boolean shouldObfuscateInstantApps) {
1427         if (shouldObfuscateInstantApps &&
1428                 mInjector.isPackageEphemeral(userId, packageName)) {
1429             return false;
1430         }
1431         return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
1432     }
1433 
1434     @StandbyBuckets
getAppMinBucket(String packageName, int userId)1435     private int getAppMinBucket(String packageName, int userId) {
1436         try {
1437             final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
1438             return getAppMinBucket(packageName, UserHandle.getAppId(uid), userId);
1439         } catch (PackageManager.NameNotFoundException e) {
1440             // Not a valid package for this user, nothing to do
1441             return STANDBY_BUCKET_NEVER;
1442         }
1443     }
1444 
1445     /**
1446      * Return the lowest bucket this app should ever enter.
1447      */
1448     @StandbyBuckets
getAppMinBucket(String packageName, int appId, int userId)1449     private int getAppMinBucket(String packageName, int appId, int userId) {
1450         if (packageName == null) return STANDBY_BUCKET_NEVER;
1451         // If not enabled at all, of course nobody is ever idle.
1452         if (!mAppIdleEnabled) {
1453             return STANDBY_BUCKET_EXEMPTED;
1454         }
1455         if (appId < Process.FIRST_APPLICATION_UID) {
1456             // System uids never go idle.
1457             return STANDBY_BUCKET_EXEMPTED;
1458         }
1459         if (packageName.equals("android")) {
1460             // Nor does the framework (which should be redundant with the above, but for MR1 we will
1461             // retain this for safety).
1462             return STANDBY_BUCKET_EXEMPTED;
1463         }
1464         if (mSystemServicesReady) {
1465             // We allow all whitelisted apps, including those that don't want to be whitelisted
1466             // for idle mode, because app idle (aka app standby) is really not as big an issue
1467             // for controlling who participates vs. doze mode.
1468             if (mInjector.isNonIdleWhitelisted(packageName)) {
1469                 return STANDBY_BUCKET_EXEMPTED;
1470             }
1471 
1472             if (isActiveDeviceAdmin(packageName, userId)) {
1473                 return STANDBY_BUCKET_EXEMPTED;
1474             }
1475 
1476             if (isAdminProtectedPackages(packageName, userId)) {
1477                 return STANDBY_BUCKET_EXEMPTED;
1478             }
1479 
1480             if (isActiveNetworkScorer(packageName)) {
1481                 return STANDBY_BUCKET_EXEMPTED;
1482             }
1483 
1484             final int uid = UserHandle.getUid(userId, appId);
1485             synchronized (mSystemExemptionAppOpMode) {
1486                 if (mSystemExemptionAppOpMode.indexOfKey(uid) >= 0) {
1487                     if (mSystemExemptionAppOpMode.get(uid)
1488                             == AppOpsManager.MODE_ALLOWED) {
1489                         return STANDBY_BUCKET_EXEMPTED;
1490                     }
1491                 } else {
1492                     int mode = mAppOpsManager.checkOpNoThrow(
1493                             AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, uid,
1494                             packageName);
1495                     mSystemExemptionAppOpMode.put(uid, mode);
1496                     if (mode == AppOpsManager.MODE_ALLOWED) {
1497                         return STANDBY_BUCKET_EXEMPTED;
1498                     }
1499                 }
1500             }
1501 
1502             if (mAppWidgetManager != null
1503                     && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
1504                 return STANDBY_BUCKET_ACTIVE;
1505             }
1506 
1507             if (isDeviceProvisioningPackage(packageName)) {
1508                 return STANDBY_BUCKET_EXEMPTED;
1509             }
1510 
1511             if (mInjector.isWellbeingPackage(packageName)) {
1512                 return STANDBY_BUCKET_WORKING_SET;
1513             }
1514 
1515             if (mInjector.shouldGetExactAlarmBucketElevation(packageName,
1516                     UserHandle.getUid(userId, appId))) {
1517                 return STANDBY_BUCKET_WORKING_SET;
1518             }
1519         }
1520 
1521         // Check this last, as it can be the most expensive check
1522         if (mHasFeatureTelephonySubscription && isCarrierApp(packageName)) {
1523             return STANDBY_BUCKET_EXEMPTED;
1524         }
1525 
1526         if (isHeadlessSystemApp(packageName)) {
1527             return STANDBY_BUCKET_ACTIVE;
1528         }
1529 
1530         if (mPackageManager.checkPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
1531                 packageName) == PERMISSION_GRANTED) {
1532             return STANDBY_BUCKET_FREQUENT;
1533         }
1534 
1535         return STANDBY_BUCKET_NEVER;
1536     }
1537 
isHeadlessSystemApp(String packageName)1538     private boolean isHeadlessSystemApp(String packageName) {
1539         synchronized (mHeadlessSystemApps) {
1540             return mHeadlessSystemApps.contains(packageName);
1541         }
1542     }
1543 
1544     @Override
isAppIdleFiltered(String packageName, int appId, int userId, long elapsedRealtime)1545     public boolean isAppIdleFiltered(String packageName, int appId, int userId,
1546             long elapsedRealtime) {
1547         if (!mAppIdleEnabled || mIsCharging) {
1548             return false;
1549         }
1550 
1551         return isAppIdleUnfiltered(packageName, userId, elapsedRealtime)
1552                 && getAppMinBucket(packageName, appId, userId) >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
1553     }
1554 
isUserUsage(int reason)1555     static boolean isUserUsage(int reason) {
1556         if ((reason & REASON_MAIN_MASK) == REASON_MAIN_USAGE) {
1557             final int subReason = reason & REASON_SUB_MASK;
1558             return subReason == REASON_SUB_USAGE_USER_INTERACTION
1559                     || subReason == REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
1560         }
1561         return false;
1562     }
1563 
1564     @Override
getIdleUidsForUser(int userId)1565     public int[] getIdleUidsForUser(int userId) {
1566         if (!mAppIdleEnabled) {
1567             return EmptyArray.INT;
1568         }
1569 
1570         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getIdleUidsForUser");
1571 
1572         final long elapsedRealtime = mInjector.elapsedRealtime();
1573 
1574         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
1575         final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, Process.myUid());
1576         if (apps == null) {
1577             return EmptyArray.INT;
1578         }
1579 
1580         // State of each uid: Key is the uid, value is whether all the apps in that uid are idle.
1581         final SparseBooleanArray uidIdleStates = new SparseBooleanArray();
1582         int notIdleCount = 0;
1583         for (int i = apps.size() - 1; i >= 0; i--) {
1584             final ApplicationInfo ai = apps.get(i);
1585             final int index = uidIdleStates.indexOfKey(ai.uid);
1586 
1587             final boolean currentIdle = (index < 0) ? true : uidIdleStates.valueAt(index);
1588 
1589             final boolean newIdle = currentIdle && isAppIdleFiltered(ai.packageName,
1590                     UserHandle.getAppId(ai.uid), userId, elapsedRealtime);
1591 
1592             if (currentIdle && !newIdle) {
1593                 // This transition from true to false can happen at most once per uid in this loop.
1594                 notIdleCount++;
1595             }
1596             if (index < 0) {
1597                 uidIdleStates.put(ai.uid, newIdle);
1598             } else {
1599                 uidIdleStates.setValueAt(index, newIdle);
1600             }
1601         }
1602 
1603         int numIdleUids = uidIdleStates.size() - notIdleCount;
1604         final int[] idleUids = new int[numIdleUids];
1605         for (int i = uidIdleStates.size() - 1; i >= 0; i--) {
1606             if (uidIdleStates.valueAt(i)) {
1607                 idleUids[--numIdleUids] = uidIdleStates.keyAt(i);
1608             }
1609         }
1610         if (DEBUG) {
1611             Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
1612         }
1613         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1614 
1615         return idleUids;
1616     }
1617 
1618     @Override
setAppIdleAsync(String packageName, boolean idle, int userId)1619     public void setAppIdleAsync(String packageName, boolean idle, int userId) {
1620         if (packageName == null || !mAppIdleEnabled) return;
1621 
1622         mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
1623                 .sendToTarget();
1624     }
1625 
1626     @Override
getAppStandbyBucket(String packageName, int userId, long elapsedRealtime, boolean shouldObfuscateInstantApps)1627     @StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
1628             long elapsedRealtime, boolean shouldObfuscateInstantApps) {
1629         if (!mAppIdleEnabled) {
1630             return STANDBY_BUCKET_EXEMPTED;
1631         }
1632         if (shouldObfuscateInstantApps && mInjector.isPackageEphemeral(userId, packageName)) {
1633             return STANDBY_BUCKET_ACTIVE;
1634         }
1635 
1636         synchronized (mAppIdleLock) {
1637             return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
1638         }
1639     }
1640 
1641     @Override
getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime)1642     public int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) {
1643         synchronized (mAppIdleLock) {
1644             return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime);
1645         }
1646     }
1647 
1648     @Override
getAppStandbyBuckets(int userId)1649     public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
1650         synchronized (mAppIdleLock) {
1651             return mAppIdleHistory.getAppStandbyBuckets(userId, mAppIdleEnabled);
1652         }
1653     }
1654 
1655     @Override
1656     @StandbyBuckets
getAppMinStandbyBucket(String packageName, int appId, int userId, boolean shouldObfuscateInstantApps)1657     public int getAppMinStandbyBucket(String packageName, int appId, int userId,
1658             boolean shouldObfuscateInstantApps) {
1659         if (shouldObfuscateInstantApps && mInjector.isPackageEphemeral(userId, packageName)) {
1660             return STANDBY_BUCKET_NEVER;
1661         }
1662         synchronized (mAppIdleLock) {
1663             return getAppMinBucket(packageName, appId, userId);
1664         }
1665     }
1666 
1667     @Override
restrictApp(@onNull String packageName, int userId, @ForcedReasons int restrictReason)1668     public void restrictApp(@NonNull String packageName, int userId,
1669             @ForcedReasons int restrictReason) {
1670         restrictApp(packageName, userId, REASON_MAIN_FORCED_BY_SYSTEM, restrictReason);
1671     }
1672 
1673     @Override
restrictApp(@onNull String packageName, int userId, int mainReason, @ForcedReasons int restrictReason)1674     public void restrictApp(@NonNull String packageName, int userId, int mainReason,
1675             @ForcedReasons int restrictReason) {
1676         if (mainReason != REASON_MAIN_FORCED_BY_SYSTEM
1677                 && mainReason != REASON_MAIN_FORCED_BY_USER) {
1678             Slog.e(TAG, "Tried to restrict app " + packageName + " for an unsupported reason");
1679             return;
1680         }
1681         // If the package is not installed, don't allow the bucket to be set.
1682         if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
1683             Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
1684             return;
1685         }
1686 
1687         final int reason = (REASON_MAIN_MASK & mainReason) | (REASON_SUB_MASK & restrictReason);
1688         final long nowElapsed = mInjector.elapsedRealtime();
1689         final int bucket = STANDBY_BUCKET_RESTRICTED;
1690         setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false);
1691     }
1692 
1693     @Override
restoreAppsToRare(Set<String> restoredApps, int userId)1694     public void restoreAppsToRare(Set<String> restoredApps, int userId) {
1695         final int reason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_RESTORED;
1696         final long nowElapsed = mInjector.elapsedRealtime();
1697         for (String packageName : restoredApps) {
1698             // If the package is not installed, don't allow the bucket to be set. Instead, add it
1699             // to a list of all packages whose buckets need to be adjusted when installed.
1700             if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
1701                 Slog.i(TAG, "Tried to restore bucket for uninstalled app: " + packageName);
1702                 mAppsToRestoreToRare.add(userId, packageName);
1703                 continue;
1704             }
1705 
1706             restoreAppToRare(packageName, userId, nowElapsed, reason);
1707         }
1708         // Clear out the list of restored apps that need to have their standby buckets adjusted
1709         // if they still haven't been installed two days after initial restore.
1710         final long delayMillis = Flags.persistRestoreToRareAppsList()
1711                 ? AppIdleHistory.RESTORE_TO_RARE_APPS_LIST_EXPIRY : 8 * ONE_HOUR;
1712         mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId), delayMillis);
1713 
1714         // Persist the file in case the device reboots within 2 days after the initial restore.
1715         if (Flags.persistRestoreToRareAppsList()) {
1716             synchronized (mAppIdleLock) {
1717                 mAppIdleHistory.writeRestoreToRareAppsList(
1718                         userId, mAppsToRestoreToRare.get(userId));
1719             }
1720         }
1721     }
1722 
1723     /** Adjust the standby bucket of the given package for the user to RARE. */
restoreAppToRare(String pkgName, int userId, long nowElapsed, int reason)1724     private void restoreAppToRare(String pkgName, int userId, long nowElapsed, int reason) {
1725         final int standbyBucket = getAppStandbyBucket(pkgName, userId, nowElapsed, false);
1726         // Only update the standby bucket to RARE if the app is still in the NEVER bucket.
1727         if (standbyBucket == STANDBY_BUCKET_NEVER) {
1728             setAppStandbyBucket(pkgName, userId, STANDBY_BUCKET_RARE, reason, nowElapsed, false);
1729         }
1730     }
1731 
1732     @Override
setAppStandbyBucket(@onNull String packageName, int bucket, int userId, int callingUid, int callingPid)1733     public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
1734             int callingUid, int callingPid) {
1735         setAppStandbyBuckets(
1736                 Collections.singletonList(new AppStandbyInfo(packageName, bucket)),
1737                 userId, callingUid, callingPid);
1738     }
1739 
1740     @Override
setAppStandbyBuckets(@onNull List<AppStandbyInfo> appBuckets, int userId, int callingUid, int callingPid)1741     public void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId,
1742             int callingUid, int callingPid) {
1743         userId = ActivityManager.handleIncomingUser(
1744                 callingPid, callingUid, userId, false, true, "setAppStandbyBucket", null);
1745         final boolean shellCaller = callingUid == Process.ROOT_UID
1746                 || callingUid == Process.SHELL_UID;
1747         final int reason;
1748         // The Settings app runs in the system UID but in a separate process. Assume
1749         // things coming from other processes are due to the user.
1750         if ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) && callingPid != Process.myPid())
1751                 || shellCaller) {
1752             reason = REASON_MAIN_FORCED_BY_USER;
1753         } else if (UserHandle.isCore(callingUid)) {
1754             reason = REASON_MAIN_FORCED_BY_SYSTEM;
1755         } else {
1756             reason = REASON_MAIN_PREDICTED;
1757         }
1758         final int packageFlags = PackageManager.MATCH_ANY_USER
1759                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
1760                 | PackageManager.MATCH_DIRECT_BOOT_AWARE;
1761         final int numApps = appBuckets.size();
1762         final long elapsedRealtime = mInjector.elapsedRealtime();
1763         for (int i = 0; i < numApps; ++i) {
1764             final AppStandbyInfo bucketInfo = appBuckets.get(i);
1765             final String packageName = bucketInfo.mPackageName;
1766             final int bucket = bucketInfo.mStandbyBucket;
1767             if (bucket < STANDBY_BUCKET_ACTIVE || bucket > STANDBY_BUCKET_NEVER) {
1768                 throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
1769             }
1770             final int packageUid = mInjector.getPackageManagerInternal()
1771                     .getPackageUid(packageName, packageFlags, userId);
1772             // Caller cannot set their own standby state
1773             if (packageUid == callingUid) {
1774                 throw new IllegalArgumentException("Cannot set your own standby bucket");
1775             }
1776             if (packageUid < 0) {
1777                 throw new IllegalArgumentException(
1778                         "Cannot set standby bucket for non existent package (" + packageName + ")");
1779             }
1780             setAppStandbyBucket(packageName, userId, bucket, reason, elapsedRealtime, shellCaller);
1781         }
1782     }
1783 
1784     @VisibleForTesting
setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, int reason)1785     void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
1786             int reason) {
1787         setAppStandbyBucket(
1788                 packageName, userId, newBucket, reason, mInjector.elapsedRealtime(), false);
1789     }
1790 
setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket, int reason, long elapsedRealtime, boolean resetTimeout)1791     private void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
1792             int reason, long elapsedRealtime, boolean resetTimeout) {
1793         if (!mAppIdleEnabled) return;
1794 
1795         synchronized (mAppIdleLock) {
1796             // If the package is not installed, don't allow the bucket to be set.
1797             if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
1798                 Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
1799                 return;
1800             }
1801             AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
1802                     userId, elapsedRealtime);
1803             boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
1804 
1805             // Don't allow changing bucket if higher than ACTIVE
1806             if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
1807 
1808             // Don't allow prediction to change from/to NEVER.
1809             if ((app.currentBucket == STANDBY_BUCKET_NEVER || newBucket == STANDBY_BUCKET_NEVER)
1810                     && predicted) {
1811                 return;
1812             }
1813 
1814             final boolean wasForcedBySystem =
1815                     (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
1816 
1817             // If the bucket was forced, don't allow prediction to override
1818             if (predicted
1819                     && ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER
1820                     || wasForcedBySystem)) {
1821                 return;
1822             }
1823 
1824             final boolean isForcedBySystem =
1825                     (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
1826 
1827             if (app.currentBucket == newBucket && wasForcedBySystem && isForcedBySystem) {
1828                 if (newBucket == STANDBY_BUCKET_RESTRICTED) {
1829                     mAppIdleHistory
1830                             .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
1831                 }
1832                 // Keep track of all restricting reasons
1833                 reason = REASON_MAIN_FORCED_BY_SYSTEM
1834                         | (app.bucketingReason & REASON_SUB_MASK)
1835                         | (reason & REASON_SUB_MASK);
1836                 final boolean previouslyIdle =
1837                         app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
1838                 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
1839                         newBucket, reason, resetTimeout);
1840                 final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
1841                 if (previouslyIdle != stillIdle) {
1842                     notifyBatteryStats(packageName, userId, stillIdle);
1843                 }
1844                 return;
1845             }
1846 
1847             final boolean isForcedByUser =
1848                     (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
1849 
1850             if (app.currentBucket == STANDBY_BUCKET_RESTRICTED) {
1851                 if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_TIMEOUT) {
1852                     if (predicted && newBucket >= STANDBY_BUCKET_RARE) {
1853                         // Predicting into RARE or below means we don't expect the user to use the
1854                         // app anytime soon, so don't elevate it from RESTRICTED.
1855                         return;
1856                     }
1857                 } else if (!isUserUsage(reason) && !isForcedByUser) {
1858                     // If the current bucket is RESTRICTED, only user force or usage should bring
1859                     // it out, unless the app was put into the bucket due to timing out.
1860                     return;
1861                 }
1862             }
1863 
1864             if (newBucket == STANDBY_BUCKET_RESTRICTED) {
1865                 mAppIdleHistory
1866                         .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
1867 
1868                 if (isForcedByUser) {
1869                     // Only user force can bypass the delay restriction. If the user forced the
1870                     // app into the RESTRICTED bucket, then a toast confirming the action
1871                     // shouldn't be surprising.
1872                     // Exclude REASON_SUB_FORCED_USER_FLAG_INTERACTION since the RESTRICTED bucket
1873                     // isn't directly visible in that flow.
1874                     if (Build.IS_DEBUGGABLE
1875                             && (reason & REASON_SUB_MASK)
1876                             != REASON_SUB_FORCED_USER_FLAG_INTERACTION) {
1877                         Toast.makeText(mContext,
1878                                 // Since AppStandbyController sits low in the lock hierarchy,
1879                                 // make sure not to call out with the lock held.
1880                                 mHandler.getLooper(),
1881                                 mContext.getResources().getString(
1882                                         R.string.as_app_forced_to_restricted_bucket, packageName),
1883                                 Toast.LENGTH_SHORT)
1884                                 .show();
1885                     } else {
1886                         Slog.i(TAG, packageName + " restricted by user");
1887                     }
1888                 } else {
1889                     final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
1890                             + mInjector.getAutoRestrictedBucketDelayMs() - elapsedRealtime;
1891                     if (timeUntilRestrictPossibleMs > 0) {
1892                         Slog.w(TAG, "Tried to restrict recently used app: " + packageName
1893                                 + " due to " + reason);
1894                         mHandler.sendMessageDelayed(
1895                                 mHandler.obtainMessage(
1896                                         MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
1897                                 timeUntilRestrictPossibleMs);
1898                         return;
1899                     }
1900                 }
1901             }
1902 
1903             // If the bucket is required to stay in a higher state for a specified duration, don't
1904             // override unless the duration has passed
1905             if (predicted) {
1906                 // Check if the app is within one of the timeouts for forced bucket elevation
1907                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
1908                 // In case of not using the prediction, just keep track of it for applying after
1909                 // ACTIVE or WORKING_SET timeout.
1910                 mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
1911 
1912                 final int bucketWithValidExpiryTime = getMinBucketWithValidExpiryTime(app,
1913                         newBucket, elapsedTimeAdjusted);
1914                 if (bucketWithValidExpiryTime != STANDBY_BUCKET_UNKNOWN) {
1915                     newBucket = bucketWithValidExpiryTime;
1916                     if (newBucket == STANDBY_BUCKET_ACTIVE || app.currentBucket == newBucket) {
1917                         reason = app.bucketingReason;
1918                     } else {
1919                         reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
1920                     }
1921                     if (DEBUG) {
1922                         Slog.d(TAG, "    Keeping at " + standbyBucketToString(newBucket)
1923                                 + " due to min timeout");
1924                     }
1925                 } else if (newBucket == STANDBY_BUCKET_RARE
1926                         && getBucketForLocked(packageName, userId, elapsedRealtime)
1927                         == STANDBY_BUCKET_RESTRICTED) {
1928                     // Prediction doesn't think the app will be used anytime soon and
1929                     // it's been long enough that it could just time out into restricted,
1930                     // so time it out there instead. Using TIMEOUT will allow prediction
1931                     // to raise the bucket when it needs to.
1932                     newBucket = STANDBY_BUCKET_RESTRICTED;
1933                     reason = REASON_MAIN_TIMEOUT;
1934                     if (DEBUG) {
1935                         Slog.d(TAG,
1936                                 "Prediction to RARE overridden by timeout into RESTRICTED");
1937                     }
1938                 }
1939             }
1940 
1941             // Make sure we don't put the app in a lower bucket than it's supposed to be in.
1942             newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
1943             final boolean previouslyIdle = app.currentBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
1944             mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
1945                     reason, resetTimeout);
1946             final boolean stillIdle = newBucket >= AppIdleHistory.IDLE_BUCKET_CUTOFF;
1947             if (previouslyIdle != stillIdle) {
1948                 notifyBatteryStats(packageName, userId, stillIdle);
1949             }
1950         }
1951         maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
1952     }
1953 
1954     @VisibleForTesting
1955     @Override
isActiveDeviceAdmin(String packageName, int userId)1956     public boolean isActiveDeviceAdmin(String packageName, int userId) {
1957         synchronized (mActiveAdminApps) {
1958             final Set<String> adminPkgs = mActiveAdminApps.get(userId);
1959             return adminPkgs != null && adminPkgs.contains(packageName);
1960         }
1961     }
1962 
isAdminProtectedPackages(String packageName, int userId)1963     private boolean isAdminProtectedPackages(String packageName, int userId) {
1964         synchronized (mAdminProtectedPackages) {
1965             if (mAdminProtectedPackages.contains(UserHandle.USER_ALL)
1966                     && mAdminProtectedPackages.get(UserHandle.USER_ALL).contains(packageName)) {
1967                 return true;
1968             }
1969             return mAdminProtectedPackages.contains(userId)
1970                     && mAdminProtectedPackages.get(userId).contains(packageName);
1971         }
1972     }
1973 
1974     @Override
addActiveDeviceAdmin(String adminPkg, int userId)1975     public void addActiveDeviceAdmin(String adminPkg, int userId) {
1976         synchronized (mActiveAdminApps) {
1977             Set<String> adminPkgs = mActiveAdminApps.get(userId);
1978             if (adminPkgs == null) {
1979                 adminPkgs = new ArraySet<>();
1980                 mActiveAdminApps.put(userId, adminPkgs);
1981             }
1982             adminPkgs.add(adminPkg);
1983         }
1984     }
1985 
1986     @Override
setActiveAdminApps(Set<String> adminPkgs, int userId)1987     public void setActiveAdminApps(Set<String> adminPkgs, int userId) {
1988         synchronized (mActiveAdminApps) {
1989             if (adminPkgs == null) {
1990                 mActiveAdminApps.remove(userId);
1991             } else {
1992                 mActiveAdminApps.put(userId, adminPkgs);
1993             }
1994         }
1995     }
1996 
1997     @Override
setAdminProtectedPackages(Set<String> packageNames, int userId)1998     public void setAdminProtectedPackages(Set<String> packageNames, int userId) {
1999         synchronized (mAdminProtectedPackages) {
2000             if (packageNames == null || packageNames.isEmpty()) {
2001                 mAdminProtectedPackages.remove(userId);
2002             } else {
2003                 mAdminProtectedPackages.put(userId, packageNames);
2004             }
2005         }
2006         if (!Flags.avoidIdleCheck() || mInjector.getBootPhase() >= PHASE_BOOT_COMPLETED) {
2007             postCheckIdleStates(userId);
2008         }
2009     }
2010 
2011     @Override
onAdminDataAvailable()2012     public void onAdminDataAvailable() {
2013         mAdminDataAvailableLatch.countDown();
2014     }
2015 
2016     /**
2017      * This will only ever be called once - during device boot.
2018      */
waitForAdminData()2019     private void waitForAdminData() {
2020         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
2021             ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
2022                     WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
2023         }
2024     }
2025 
2026     @VisibleForTesting
getActiveAdminAppsForTest(int userId)2027     Set<String> getActiveAdminAppsForTest(int userId) {
2028         synchronized (mActiveAdminApps) {
2029             return mActiveAdminApps.get(userId);
2030         }
2031     }
2032 
2033     @VisibleForTesting
getAdminProtectedPackagesForTest(int userId)2034     Set<String> getAdminProtectedPackagesForTest(int userId) {
2035         synchronized (mAdminProtectedPackages) {
2036             return mAdminProtectedPackages.get(userId);
2037         }
2038     }
2039 
2040     /**
2041      * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
2042      * returns {@code false}.
2043      */
isDeviceProvisioningPackage(String packageName)2044     private boolean isDeviceProvisioningPackage(String packageName) {
2045         if (mCachedDeviceProvisioningPackage == null) {
2046             mCachedDeviceProvisioningPackage = mContext.getResources().getString(
2047                     com.android.internal.R.string.config_deviceProvisioningPackage);
2048         }
2049         return mCachedDeviceProvisioningPackage.equals(packageName);
2050     }
2051 
isCarrierApp(String packageName)2052     private boolean isCarrierApp(String packageName) {
2053         synchronized (mCarrierPrivilegedLock) {
2054             if (!mHaveCarrierPrivilegedApps) {
2055                 fetchCarrierPrivilegedAppsCPL();
2056             }
2057             if (mCarrierPrivilegedApps != null) {
2058                 return mCarrierPrivilegedApps.contains(packageName);
2059             }
2060             return false;
2061         }
2062     }
2063 
2064     @Override
clearCarrierPrivilegedApps()2065     public void clearCarrierPrivilegedApps() {
2066         if (DEBUG) {
2067             Slog.i(TAG, "Clearing carrier privileged apps list");
2068         }
2069         synchronized (mCarrierPrivilegedLock) {
2070             mHaveCarrierPrivilegedApps = false;
2071             mCarrierPrivilegedApps = null; // Need to be refetched.
2072         }
2073     }
2074 
2075     @GuardedBy("mCarrierPrivilegedLock")
fetchCarrierPrivilegedAppsCPL()2076     private void fetchCarrierPrivilegedAppsCPL() {
2077         TelephonyManager telephonyManager =
2078                 mContext.getSystemService(TelephonyManager.class);
2079         mCarrierPrivilegedApps =
2080                 telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions();
2081         mHaveCarrierPrivilegedApps = true;
2082         if (DEBUG) {
2083             Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
2084         }
2085     }
2086 
isActiveNetworkScorer(@onNull String packageName)2087     private boolean isActiveNetworkScorer(@NonNull String packageName) {
2088         // Validity of network scorer cache is limited to a few seconds. Fetch it again
2089         // if longer since query.
2090         // This is a temporary optimization until there's a callback mechanism for changes to network scorer.
2091         final long now = SystemClock.elapsedRealtime();
2092         if (mCachedNetworkScorer == null
2093                 || mCachedNetworkScorerAtMillis < now - NETWORK_SCORER_CACHE_DURATION_MILLIS) {
2094             mCachedNetworkScorer = mInjector.getActiveNetworkScorer();
2095             mCachedNetworkScorerAtMillis = now;
2096         }
2097         return packageName.equals(mCachedNetworkScorer);
2098     }
2099 
informListeners(String packageName, int userId, int bucket, int reason, boolean userInteraction)2100     private void informListeners(String packageName, int userId, int bucket, int reason,
2101             boolean userInteraction) {
2102         final boolean idle = bucket >= STANDBY_BUCKET_RARE;
2103         synchronized (mPackageAccessListeners) {
2104             for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
2105                 listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
2106                 if (userInteraction) {
2107                     listener.onUserInteractionStarted(packageName, userId);
2108                 }
2109             }
2110         }
2111     }
2112 
informParoleStateChanged()2113     private void informParoleStateChanged() {
2114         final boolean paroled = isInParole();
2115         synchronized (mPackageAccessListeners) {
2116             for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
2117                 listener.onParoleStateChanged(paroled);
2118             }
2119         }
2120     }
2121 
2122     @Override
getBroadcastResponseWindowDurationMs()2123     public long getBroadcastResponseWindowDurationMs() {
2124         return mBroadcastResponseWindowDurationMillis;
2125     }
2126 
2127     @Override
getBroadcastResponseFgThresholdState()2128     public int getBroadcastResponseFgThresholdState() {
2129         return mBroadcastResponseFgThresholdState;
2130     }
2131 
2132     @Override
getBroadcastSessionsDurationMs()2133     public long getBroadcastSessionsDurationMs() {
2134         return mBroadcastSessionsDurationMs;
2135     }
2136 
2137     @Override
getBroadcastSessionsWithResponseDurationMs()2138     public long getBroadcastSessionsWithResponseDurationMs() {
2139         return mBroadcastSessionsWithResponseDurationMs;
2140     }
2141 
2142     @Override
shouldNoteResponseEventForAllBroadcastSessions()2143     public boolean shouldNoteResponseEventForAllBroadcastSessions() {
2144         return mNoteResponseEventForAllBroadcastSessions;
2145     }
2146 
2147     @Override
2148     @NonNull
getBroadcastResponseExemptedRoles()2149     public List<String> getBroadcastResponseExemptedRoles() {
2150         return mBroadcastResponseExemptedRolesList;
2151     }
2152 
2153     @Override
2154     @NonNull
getBroadcastResponseExemptedPermissions()2155     public List<String> getBroadcastResponseExemptedPermissions() {
2156         return mBroadcastResponseExemptedPermissionsList;
2157     }
2158 
2159     @Override
2160     @Nullable
getAppStandbyConstant(@onNull String key)2161     public String getAppStandbyConstant(@NonNull String key) {
2162         return mAppStandbyProperties.get(key);
2163     }
2164 
2165     @Override
clearLastUsedTimestampsForTest(@onNull String packageName, @UserIdInt int userId)2166     public void clearLastUsedTimestampsForTest(@NonNull String packageName, @UserIdInt int userId) {
2167         synchronized (mAppIdleLock) {
2168             mAppIdleHistory.clearLastUsedTimestamps(packageName, userId);
2169         }
2170     }
2171 
2172     /**
2173      * Flush the handler.
2174      * Returns true if successfully flushed within the timeout, otherwise return false.
2175      */
2176     @VisibleForTesting
flushHandler(@urationMillisLong long timeoutMillis)2177     boolean flushHandler(@DurationMillisLong long timeoutMillis) {
2178         return mHandler.runWithScissors(() -> {}, timeoutMillis);
2179     }
2180 
2181     @Override
flushToDisk()2182     public void flushToDisk() {
2183         synchronized (mAppIdleLock) {
2184             mAppIdleHistory.writeAppIdleTimes(mInjector.elapsedRealtime());
2185             mAppIdleHistory.writeAppIdleDurations();
2186         }
2187     }
2188 
isDisplayOn()2189     private boolean isDisplayOn() {
2190         return mInjector.isDefaultDisplayOn();
2191     }
2192 
2193     @VisibleForTesting
clearAppIdleForPackage(String packageName, int userId)2194     void clearAppIdleForPackage(String packageName, int userId) {
2195         synchronized (mAppIdleLock) {
2196             mAppIdleHistory.clearUsage(packageName, userId);
2197         }
2198     }
2199 
2200     /**
2201      * Remove an app from the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
2202      * bucket if it was forced into the bucket by the system because it was buggy.
2203      */
2204     @VisibleForTesting
maybeUnrestrictBuggyApp(@onNull String packageName, int userId)2205     void maybeUnrestrictBuggyApp(@NonNull String packageName, int userId) {
2206         maybeUnrestrictApp(packageName, userId,
2207                 REASON_MAIN_FORCED_BY_SYSTEM, REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
2208                 REASON_MAIN_DEFAULT, REASON_SUB_DEFAULT_APP_UPDATE);
2209     }
2210 
2211     @Override
maybeUnrestrictApp(@onNull String packageName, int userId, int prevMainReasonRestrict, int prevSubReasonRestrict, int mainReasonUnrestrict, int subReasonUnrestrict)2212     public void maybeUnrestrictApp(@NonNull String packageName, int userId,
2213             int prevMainReasonRestrict, int prevSubReasonRestrict,
2214             int mainReasonUnrestrict, int subReasonUnrestrict) {
2215         synchronized (mAppIdleLock) {
2216             final long elapsedRealtime = mInjector.elapsedRealtime();
2217             final AppIdleHistory.AppUsageHistory app =
2218                     mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime);
2219             if (app.currentBucket != STANDBY_BUCKET_RESTRICTED
2220                     || (app.bucketingReason & REASON_MAIN_MASK) != prevMainReasonRestrict) {
2221                 return;
2222             }
2223 
2224             final int newBucket;
2225             final int newReason;
2226             if ((app.bucketingReason & REASON_SUB_MASK) == prevSubReasonRestrict) {
2227                 // If it was the only reason the app should be restricted, then lift it out.
2228                 newBucket = STANDBY_BUCKET_RARE;
2229                 newReason = mainReasonUnrestrict | subReasonUnrestrict;
2230             } else {
2231                 // There's another reason the app was restricted. Remove the subreason bit and call
2232                 // it a day.
2233                 newBucket = STANDBY_BUCKET_RESTRICTED;
2234                 newReason = app.bucketingReason & ~prevSubReasonRestrict;
2235             }
2236             mAppIdleHistory.setAppStandbyBucket(
2237                     packageName, userId, elapsedRealtime, newBucket, newReason);
2238             maybeInformListeners(packageName, userId, elapsedRealtime, newBucket,
2239                     newReason, false);
2240         }
2241     }
2242 
updatePowerWhitelistCache()2243     private void updatePowerWhitelistCache() {
2244         if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
2245             return;
2246         }
2247         mInjector.updatePowerWhitelistCache();
2248         postCheckIdleStates(UserHandle.USER_ALL);
2249     }
2250 
2251     private class PackageReceiver extends BroadcastReceiver {
2252         @Override
onReceive(Context context, Intent intent)2253         public void onReceive(Context context, Intent intent) {
2254             final String action = intent.getAction();
2255             final String pkgName = intent.getData().getSchemeSpecificPart();
2256             final int userId = getSendingUserId();
2257             if (Intent.ACTION_PACKAGE_ADDED.equals(action)
2258                     || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
2259                 final String[] cmpList = intent.getStringArrayExtra(
2260                         Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
2261                 // If this is PACKAGE_ADDED (cmpList == null), or if it's a whole-package
2262                 // enable/disable event (cmpList is just the package name itself), drop
2263                 // our carrier privileged app & system-app caches and let them refresh
2264                 if (cmpList == null
2265                         || (cmpList.length == 1 && pkgName.equals(cmpList[0]))) {
2266                     clearCarrierPrivilegedApps();
2267                     evaluateSystemAppException(pkgName, userId);
2268                 }
2269                 // component-level enable/disable can affect bucketing, so we always
2270                 // reevaluate that for any PACKAGE_CHANGED
2271                 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
2272                     mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkgName)
2273                             .sendToTarget();
2274                 }
2275             }
2276             if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
2277                     Intent.ACTION_PACKAGE_ADDED.equals(action))) {
2278                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
2279                     maybeUnrestrictBuggyApp(pkgName, userId);
2280                 } else if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) {
2281                     clearAppIdleForPackage(pkgName, userId);
2282                 } else {
2283                     // Do a lazy read of the persisted list, if necessary.
2284                     if (Flags.persistRestoreToRareAppsList()
2285                             && mAppsToRestoreToRare.get(userId) == null) {
2286                         synchronized (mAppIdleLock) {
2287                             final ArraySet<String> restoredApps =
2288                                     mAppIdleHistory.readRestoreToRareAppsList(userId);
2289                             if (restoredApps != null) {
2290                                 mAppsToRestoreToRare.addAll(userId, restoredApps);
2291                                 // Clear out the list of restored apps if they still haven't been
2292                                 // installed in two days - at worst, we are allowing for up to
2293                                 // 4 days for reinstallation (device reboots just before 2 days)
2294                                 mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId),
2295                                         AppIdleHistory.RESTORE_TO_RARE_APPS_LIST_EXPIRY);
2296                             }
2297                         }
2298                     }
2299                     // Package was just added and it's not being replaced.
2300                     if (mAppsToRestoreToRare.contains(userId, pkgName)) {
2301                         restoreAppToRare(pkgName, userId, mInjector.elapsedRealtime(),
2302                                 REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_RESTORED);
2303                         mAppsToRestoreToRare.remove(userId, pkgName);
2304                     }
2305                 }
2306             }
2307             synchronized (mSystemExemptionAppOpMode) {
2308                 if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
2309                     final int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
2310                     mSystemExemptionAppOpMode.delete(uid);
2311                 }
2312             }
2313 
2314         }
2315     }
2316 
evaluateSystemAppException(String packageName, int userId)2317     private void evaluateSystemAppException(String packageName, int userId) {
2318         if (!mSystemServicesReady) {
2319             // The app will be evaluated in when services are ready.
2320             return;
2321         }
2322         try {
2323             PackageInfo pi = mPackageManager.getPackageInfoAsUser(
2324                     packageName, HEADLESS_APP_CHECK_FLAGS, userId);
2325             maybeUpdateHeadlessSystemAppCache(pi);
2326         } catch (PackageManager.NameNotFoundException e) {
2327             synchronized (mHeadlessSystemApps) {
2328                 mHeadlessSystemApps.remove(packageName);
2329             }
2330         }
2331     }
2332 
2333     /**
2334      * Update the "headless system app" cache.
2335      *
2336      * @return true if the cache is updated.
2337      */
maybeUpdateHeadlessSystemAppCache(@ullable PackageInfo pkgInfo)2338     private boolean maybeUpdateHeadlessSystemAppCache(@Nullable PackageInfo pkgInfo) {
2339         if (pkgInfo == null || pkgInfo.applicationInfo == null
2340                 || (!pkgInfo.applicationInfo.isSystemApp()
2341                         && !pkgInfo.applicationInfo.isUpdatedSystemApp())) {
2342             return false;
2343         }
2344         final Intent frontDoorActivityIntent = new Intent(Intent.ACTION_MAIN)
2345                 .addCategory(Intent.CATEGORY_LAUNCHER)
2346                 .setPackage(pkgInfo.packageName);
2347         List<ResolveInfo> res = mPackageManager.queryIntentActivitiesAsUser(frontDoorActivityIntent,
2348                 HEADLESS_APP_CHECK_FLAGS, UserHandle.USER_SYSTEM);
2349         return updateHeadlessSystemAppCache(pkgInfo.packageName, ArrayUtils.isEmpty(res));
2350     }
2351 
updateHeadlessSystemAppCache(String packageName, boolean add)2352     private boolean updateHeadlessSystemAppCache(String packageName, boolean add) {
2353         synchronized (mHeadlessSystemApps) {
2354             if (add) {
2355                 return mHeadlessSystemApps.add(packageName);
2356             } else {
2357                 return mHeadlessSystemApps.remove(packageName);
2358             }
2359         }
2360     }
2361 
2362     /** Call on a system version update to temporarily reset system app buckets. */
2363     @Override
initializeDefaultsForSystemApps(int userId)2364     public void initializeDefaultsForSystemApps(int userId) {
2365         if (!mSystemServicesReady) {
2366             // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
2367             mPendingInitializeDefaults = true;
2368             return;
2369         }
2370         Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", "
2371                 + "appIdleEnabled=" + mAppIdleEnabled);
2372         final long elapsedRealtime = mInjector.elapsedRealtime();
2373         List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
2374                 PackageManager.MATCH_DISABLED_COMPONENTS,
2375                 userId);
2376         final int packageCount = packages.size();
2377         synchronized (mAppIdleLock) {
2378             for (int i = 0; i < packageCount; i++) {
2379                 final PackageInfo pi = packages.get(i);
2380                 String packageName = pi.packageName;
2381                 if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
2382                     // Mark app as used for 2 hours. After that it can timeout to whatever the
2383                     // past usage pattern was.
2384                     mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
2385                             REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
2386                             elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
2387                 }
2388             }
2389             // Immediately persist defaults to disk
2390             mAppIdleHistory.writeAppIdleTimes(userId, elapsedRealtime);
2391         }
2392     }
2393 
2394     /** Returns the packages that have launcher icons. */
getSystemPackagesWithLauncherActivities()2395     private Set<String> getSystemPackagesWithLauncherActivities() {
2396         final Intent intent = new Intent(Intent.ACTION_MAIN)
2397                 .addCategory(Intent.CATEGORY_LAUNCHER);
2398         List<ResolveInfo> activities = mPackageManager.queryIntentActivitiesAsUser(intent,
2399                 HEADLESS_APP_CHECK_FLAGS, UserHandle.USER_SYSTEM);
2400         final ArraySet<String> ret = new ArraySet<>();
2401         for (ResolveInfo ri : activities) {
2402             ret.add(ri.activityInfo.packageName);
2403         }
2404         return ret;
2405     }
2406 
2407     /** Call on system boot to get the initial set of headless system apps. */
loadHeadlessSystemAppCache()2408     private void loadHeadlessSystemAppCache() {
2409         final long start = SystemClock.uptimeMillis();
2410         final List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
2411                 HEADLESS_APP_CHECK_FLAGS, UserHandle.USER_SYSTEM);
2412 
2413         final Set<String> systemLauncherActivities = getSystemPackagesWithLauncherActivities();
2414 
2415         final int packageCount = packages.size();
2416         for (int i = 0; i < packageCount; i++) {
2417             final PackageInfo pkgInfo = packages.get(i);
2418             if (pkgInfo == null) {
2419                 continue;
2420             }
2421             final String pkg = pkgInfo.packageName;
2422             final boolean isHeadLess = !systemLauncherActivities.contains(pkg);
2423 
2424             if (updateHeadlessSystemAppCache(pkg, isHeadLess)) {
2425                 if (!Flags.avoidIdleCheck()) {
2426                     // Checking idle state for the each individual headless system app
2427                     // during the boot up is not necessary, a full idle check for all
2428                     // usres will be scheduled after boot completed.
2429                     mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE,
2430                                     UserHandle.USER_SYSTEM, -1, pkg)
2431                             .sendToTarget();
2432                 }
2433             }
2434         }
2435         final long end = SystemClock.uptimeMillis();
2436         Slog.d(TAG, "Loaded headless system app cache in " + (end - start) + " ms:"
2437                 + " appIdleEnabled=" + mAppIdleEnabled);
2438     }
2439 
2440     @Override
postReportContentProviderUsage(String name, String packageName, int userId)2441     public void postReportContentProviderUsage(String name, String packageName, int userId) {
2442         ContentProviderUsageRecord record = ContentProviderUsageRecord.obtain(name, packageName,
2443                 userId);
2444         mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, record)
2445                 .sendToTarget();
2446     }
2447 
2448     @Override
postReportSyncScheduled(String packageName, int userId, boolean exempted)2449     public void postReportSyncScheduled(String packageName, int userId, boolean exempted) {
2450         mHandler.obtainMessage(MSG_REPORT_SYNC_SCHEDULED, userId, exempted ? 1 : 0, packageName)
2451                 .sendToTarget();
2452     }
2453 
2454     @Override
postReportExemptedSyncStart(String packageName, int userId)2455     public void postReportExemptedSyncStart(String packageName, int userId) {
2456         mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
2457                 .sendToTarget();
2458     }
2459 
2460     @VisibleForTesting
getAppIdleHistoryForTest()2461     AppIdleHistory getAppIdleHistoryForTest() {
2462         synchronized (mAppIdleLock) {
2463             return mAppIdleHistory;
2464         }
2465     }
2466 
2467     @Override
dumpUsers(IndentingPrintWriter idpw, int[] userIds, List<String> pkgs)2468     public void dumpUsers(IndentingPrintWriter idpw, int[] userIds, List<String> pkgs) {
2469         synchronized (mAppIdleLock) {
2470             mAppIdleHistory.dumpUsers(idpw, userIds, pkgs);
2471         }
2472     }
2473 
2474     @Override
dumpState(String[] args, PrintWriter pw)2475     public void dumpState(String[] args, PrintWriter pw) {
2476         pw.println("Flags: ");
2477         pw.println("    " + Flags.FLAG_AVOID_IDLE_CHECK
2478                 + ": " + Flags.avoidIdleCheck());
2479         pw.println("    " + Flags.FLAG_ADJUST_DEFAULT_BUCKET_ELEVATION_PARAMS
2480                 + ": " + Flags.adjustDefaultBucketElevationParams());
2481         pw.println("    " + Flags.FLAG_PERSIST_RESTORE_TO_RARE_APPS_LIST
2482                 + ": " + Flags.persistRestoreToRareAppsList());
2483         pw.println();
2484 
2485         synchronized (mCarrierPrivilegedLock) {
2486             pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
2487                     + "): " + mCarrierPrivilegedApps);
2488         }
2489 
2490         pw.println();
2491         pw.println("Settings:");
2492 
2493         pw.print("  mCheckIdleIntervalMillis=");
2494         TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
2495         pw.println();
2496 
2497         pw.print("  mStrongUsageTimeoutMillis=");
2498         TimeUtils.formatDuration(mStrongUsageTimeoutMillis, pw);
2499         pw.println();
2500         pw.print("  mNotificationSeenTimeoutMillis=");
2501         TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
2502         pw.println();
2503         pw.print("  mNotificationSeenPromotedBucket=");
2504         pw.print(standbyBucketToString(mNotificationSeenPromotedBucket));
2505         pw.println();
2506         pw.print("  mTriggerQuotaBumpOnNotificationSeen=");
2507         pw.print(mTriggerQuotaBumpOnNotificationSeen);
2508         pw.println();
2509         pw.print("  mRetainNotificationSeenImpactForPreTApps=");
2510         pw.print(mRetainNotificationSeenImpactForPreTApps);
2511         pw.println();
2512         pw.print("  mSlicePinnedTimeoutMillis=");
2513         TimeUtils.formatDuration(mSlicePinnedTimeoutMillis, pw);
2514         pw.println();
2515         pw.print("  mSyncAdapterTimeoutMillis=");
2516         TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
2517         pw.println();
2518         pw.print("  mSyncAdapaterPromotedBucket=");
2519         pw.print(standbyBucketToString(mSyncAdapaterPromotedBucket));
2520         pw.println();
2521         pw.print("  mSystemInteractionTimeoutMillis=");
2522         TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
2523         pw.println();
2524         pw.print("  mInitialForegroundServiceStartTimeoutMillis=");
2525         TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw);
2526         pw.println();
2527 
2528         pw.print("  mPredictionTimeoutMillis=");
2529         TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
2530         pw.println();
2531 
2532         pw.print("  mExemptedSyncScheduledNonDozeTimeoutMillis=");
2533         TimeUtils.formatDuration(mExemptedSyncScheduledNonDozeTimeoutMillis, pw);
2534         pw.println();
2535         pw.print("  mExemptedSyncScheduledDozeTimeoutMillis=");
2536         TimeUtils.formatDuration(mExemptedSyncScheduledDozeTimeoutMillis, pw);
2537         pw.println();
2538         pw.print("  mExemptedSyncStartTimeoutMillis=");
2539         TimeUtils.formatDuration(mExemptedSyncStartTimeoutMillis, pw);
2540         pw.println();
2541         pw.print("  mUnexemptedSyncScheduledTimeoutMillis=");
2542         TimeUtils.formatDuration(mUnexemptedSyncScheduledTimeoutMillis, pw);
2543         pw.println();
2544 
2545         pw.print("  mSystemUpdateUsageTimeoutMillis=");
2546         TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
2547         pw.println();
2548 
2549         pw.print("  mBroadcastResponseWindowDurationMillis=");
2550         TimeUtils.formatDuration(mBroadcastResponseWindowDurationMillis, pw);
2551         pw.println();
2552 
2553         pw.print("  mBroadcastResponseFgThresholdState=");
2554         pw.print(ActivityManager.procStateToString(mBroadcastResponseFgThresholdState));
2555         pw.println();
2556 
2557         pw.print("  mBroadcastSessionsDurationMs=");
2558         TimeUtils.formatDuration(mBroadcastSessionsDurationMs, pw);
2559         pw.println();
2560 
2561         pw.print("  mBroadcastSessionsWithResponseDurationMs=");
2562         TimeUtils.formatDuration(mBroadcastSessionsWithResponseDurationMs, pw);
2563         pw.println();
2564 
2565         pw.print("  mNoteResponseEventForAllBroadcastSessions=");
2566         pw.print(mNoteResponseEventForAllBroadcastSessions);
2567         pw.println();
2568 
2569         pw.print("  mBroadcastResponseExemptedRoles=");
2570         pw.print(mBroadcastResponseExemptedRoles);
2571         pw.println();
2572 
2573         pw.print("  mBroadcastResponseExemptedPermissions=");
2574         pw.print(mBroadcastResponseExemptedPermissions);
2575         pw.println();
2576 
2577         pw.println();
2578         pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
2579         pw.print(" mIsCharging=");
2580         pw.print(mIsCharging);
2581         pw.println();
2582         pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
2583         pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
2584         pw.println();
2585 
2586         pw.println("mHeadlessSystemApps=[");
2587         synchronized (mHeadlessSystemApps) {
2588             for (int i = mHeadlessSystemApps.size() - 1; i >= 0; --i) {
2589                 pw.print("  ");
2590                 pw.print(mHeadlessSystemApps.valueAt(i));
2591                 if (i != 0) pw.println(",");
2592             }
2593         }
2594         pw.println("]");
2595         pw.println();
2596 
2597         pw.println("mSystemPackagesAppIds=[");
2598         synchronized (mSystemPackagesAppIds) {
2599             for (int i = mSystemPackagesAppIds.size() - 1; i >= 0; --i) {
2600                 pw.print("  ");
2601                 pw.print(mSystemPackagesAppIds.get(i));
2602                 if (i != 0) pw.println(",");
2603             }
2604         }
2605         pw.println("]");
2606         pw.println();
2607 
2608         pw.println("mActiveAdminApps=[");
2609         synchronized (mActiveAdminApps) {
2610             final int size = mActiveAdminApps.size();
2611             for (int i = 0; i < size; ++i) {
2612                 final int userId = mActiveAdminApps.keyAt(i);
2613                 pw.print(" ");
2614                 pw.print(userId);
2615                 pw.print(": ");
2616                 pw.print(mActiveAdminApps.valueAt(i));
2617                 if (i != size - 1) pw.print(",");
2618                 pw.println();
2619             }
2620         }
2621         pw.println("]");
2622         pw.println();
2623 
2624         pw.println("mAdminProtectedPackages=[");
2625         synchronized (mAdminProtectedPackages) {
2626             final int size = mAdminProtectedPackages.size();
2627             for (int i = 0; i < size; ++i) {
2628                 final int userId = mAdminProtectedPackages.keyAt(i);
2629                 pw.print(" ");
2630                 pw.print(userId);
2631                 pw.print(": ");
2632                 pw.print(mAdminProtectedPackages.valueAt(i));
2633                 if (i != size - 1) pw.print(",");
2634                 pw.println();
2635             }
2636         }
2637         pw.println("]");
2638         pw.println();
2639 
2640         mInjector.dump(pw);
2641     }
2642 
2643     /**
2644      * Injector for interaction with external code. Override methods to provide a mock
2645      * implementation for tests.
2646      * onBootPhase() must be called with at least the PHASE_SYSTEM_SERVICES_READY
2647      */
2648     static class Injector {
2649 
2650         private final Context mContext;
2651         private final Looper mLooper;
2652         private IBatteryStats mBatteryStats;
2653         private BatteryManager mBatteryManager;
2654         private PackageManagerInternal mPackageManagerInternal;
2655         private DisplayManager mDisplayManager;
2656         private PowerManager mPowerManager;
2657         private IDeviceIdleController mDeviceIdleController;
2658         private CrossProfileAppsInternal mCrossProfileAppsInternal;
2659         private AlarmManagerInternal mAlarmManagerInternal;
2660         int mBootPhase;
2661         /**
2662          * The minimum amount of time required since the last user interaction before an app can be
2663          * automatically placed in the RESTRICTED bucket.
2664          */
2665         long mAutoRestrictedBucketDelayMs =
2666                 ConstantsObserver.DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS;
2667         /**
2668          * Cached set of apps that are power whitelisted, including those not whitelisted from idle.
2669          */
2670         @GuardedBy("mPowerWhitelistedApps")
2671         private final ArraySet<String> mPowerWhitelistedApps = new ArraySet<>();
2672         private String mWellbeingApp = null;
2673 
Injector(Context context, Looper looper)2674         Injector(Context context, Looper looper) {
2675             mContext = context;
2676             mLooper = looper;
2677         }
2678 
getContext()2679         Context getContext() {
2680             return mContext;
2681         }
2682 
getLooper()2683         Looper getLooper() {
2684             return mLooper;
2685         }
2686 
onBootPhase(int phase)2687         void onBootPhase(int phase) {
2688             if (phase == PHASE_SYSTEM_SERVICES_READY) {
2689                 mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
2690                         ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
2691                 mBatteryStats = IBatteryStats.Stub.asInterface(
2692                         ServiceManager.getService(BatteryStats.SERVICE_NAME));
2693                 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
2694                 mDisplayManager = (DisplayManager) mContext.getSystemService(
2695                         Context.DISPLAY_SERVICE);
2696                 mPowerManager = mContext.getSystemService(PowerManager.class);
2697                 mBatteryManager = mContext.getSystemService(BatteryManager.class);
2698                 mCrossProfileAppsInternal = LocalServices.getService(
2699                         CrossProfileAppsInternal.class);
2700                 mAlarmManagerInternal = LocalServices.getService(AlarmManagerInternal.class);
2701 
2702                 final ActivityManager activityManager =
2703                         (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
2704                 if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
2705                     mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
2706                 }
2707             } else if (phase == PHASE_BOOT_COMPLETED) {
2708                 // mWellbeingApp needs to be initialized lazily after boot to allow for roles to be
2709                 // parsed and the wellbeing role-holder to be assigned
2710                 final PackageManager packageManager = mContext.getPackageManager();
2711                 mWellbeingApp = packageManager.getWellbeingPackageName();
2712             }
2713             mBootPhase = phase;
2714         }
2715 
getBootPhase()2716         int getBootPhase() {
2717             return mBootPhase;
2718         }
2719 
2720         /**
2721          * Returns the elapsed realtime since the device started. Override this
2722          * to control the clock.
2723          * @return elapsed realtime
2724          */
elapsedRealtime()2725         long elapsedRealtime() {
2726             return SystemClock.elapsedRealtime();
2727         }
2728 
currentTimeMillis()2729         long currentTimeMillis() {
2730             return System.currentTimeMillis();
2731         }
2732 
isAppIdleEnabled()2733         boolean isAppIdleEnabled() {
2734             final boolean buildFlag = mContext.getResources().getBoolean(
2735                     com.android.internal.R.bool.config_enableAutoPowerModes);
2736             final boolean runtimeFlag = Global.getInt(mContext.getContentResolver(),
2737                     Global.APP_STANDBY_ENABLED, 1) == 1
2738                     && Global.getInt(mContext.getContentResolver(),
2739                     Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 1;
2740             return buildFlag && runtimeFlag;
2741         }
2742 
isCharging()2743         boolean isCharging() {
2744             return mBatteryManager.isCharging();
2745         }
2746 
isNonIdleWhitelisted(String packageName)2747         boolean isNonIdleWhitelisted(String packageName) {
2748             if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) {
2749                 return false;
2750             }
2751             synchronized (mPowerWhitelistedApps) {
2752                 return mPowerWhitelistedApps.contains(packageName);
2753             }
2754         }
2755 
getAppOpsService()2756         IAppOpsService getAppOpsService() {
2757             return IAppOpsService.Stub.asInterface(
2758                     ServiceManager.getService(Context.APP_OPS_SERVICE));
2759         }
2760 
2761         /**
2762          * Returns {@code true} if the supplied package is the wellbeing app. Otherwise,
2763          * returns {@code false}.
2764          */
isWellbeingPackage(@onNull String packageName)2765         boolean isWellbeingPackage(@NonNull String packageName) {
2766             return packageName.equals(mWellbeingApp);
2767         }
2768 
shouldGetExactAlarmBucketElevation(String packageName, int uid)2769         boolean shouldGetExactAlarmBucketElevation(String packageName, int uid) {
2770             return mAlarmManagerInternal.shouldGetBucketElevation(packageName, uid);
2771         }
2772 
updatePowerWhitelistCache()2773         void updatePowerWhitelistCache() {
2774             try {
2775                 // Don't call out to DeviceIdleController with the lock held.
2776                 final String[] whitelistedPkgs =
2777                         mDeviceIdleController.getFullPowerWhitelistExceptIdle();
2778                 synchronized (mPowerWhitelistedApps) {
2779                     mPowerWhitelistedApps.clear();
2780                     final int len = whitelistedPkgs.length;
2781                     for (int i = 0; i < len; ++i) {
2782                         mPowerWhitelistedApps.add(whitelistedPkgs[i]);
2783                     }
2784                 }
2785             } catch (RemoteException e) {
2786                 // Should not happen.
2787                 Slog.wtf(TAG, "Failed to get power whitelist", e);
2788             }
2789         }
2790 
getDataSystemDirectory()2791         File getDataSystemDirectory() {
2792             return Environment.getDataSystemDirectory();
2793         }
2794 
2795         /**
2796          * Return the minimum amount of time that must have passed since the last user usage before
2797          * an app can be automatically put into the
2798          * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
2799          */
getAutoRestrictedBucketDelayMs()2800         long getAutoRestrictedBucketDelayMs() {
2801             return mAutoRestrictedBucketDelayMs;
2802         }
2803 
noteEvent(int event, String packageName, int uid)2804         void noteEvent(int event, String packageName, int uid) throws RemoteException {
2805             if (mBatteryStats != null) {
2806                 mBatteryStats.noteEvent(event, packageName, uid);
2807             }
2808         }
2809 
getPackageManagerInternal()2810         PackageManagerInternal getPackageManagerInternal() {
2811             return mPackageManagerInternal;
2812         }
2813 
isPackageEphemeral(int userId, String packageName)2814         boolean isPackageEphemeral(int userId, String packageName) {
2815             return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
2816         }
2817 
isPackageInstalled(String packageName, int flags, int userId)2818         boolean isPackageInstalled(String packageName, int flags, int userId) {
2819             return mPackageManagerInternal.getPackageUid(packageName, flags, userId) >= 0;
2820         }
2821 
getRunningUserIds()2822         int[] getRunningUserIds() throws RemoteException {
2823             return ActivityManager.getService().getRunningUserIds();
2824         }
2825 
isDefaultDisplayOn()2826         boolean isDefaultDisplayOn() {
2827             return mDisplayManager
2828                     .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
2829         }
2830 
registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler)2831         void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
2832             mDisplayManager.registerDisplayListener(listener, handler);
2833         }
2834 
getActiveNetworkScorer()2835         String getActiveNetworkScorer() {
2836             NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
2837                     Context.NETWORK_SCORE_SERVICE);
2838             return nsm.getActiveScorerPackage();
2839         }
2840 
isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName, int userId)2841         public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
2842                 int userId) {
2843             return appWidgetManager.isBoundWidgetPackage(packageName, userId);
2844         }
2845 
2846         @NonNull
getDeviceConfigProperties(String... keys)2847         DeviceConfig.Properties getDeviceConfigProperties(String... keys) {
2848             return DeviceConfig.getProperties(DeviceConfig.NAMESPACE_APP_STANDBY, keys);
2849         }
2850 
2851         /** Whether the device is in doze or not. */
isDeviceIdleMode()2852         public boolean isDeviceIdleMode() {
2853             return mPowerManager.isDeviceIdleMode();
2854         }
2855 
getValidCrossProfileTargets(String pkg, int userId)2856         public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) {
2857             final int uid = mPackageManagerInternal.getPackageUid(pkg, /* flags= */ 0, userId);
2858             final AndroidPackage aPkg = mPackageManagerInternal.getPackage(uid);
2859             if (uid < 0
2860                     || aPkg == null
2861                     || !aPkg.isCrossProfile()
2862                     || !mCrossProfileAppsInternal
2863                             .verifyUidHasInteractAcrossProfilePermission(pkg, uid)) {
2864                 if (uid >= 0 && aPkg == null) {
2865                     Slog.wtf(TAG, "Null package retrieved for UID " + uid);
2866                 }
2867                 return Collections.emptyList();
2868             }
2869             return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId);
2870         }
2871 
registerDeviceConfigPropertiesChangedListener( @onNull DeviceConfig.OnPropertiesChangedListener listener)2872         void registerDeviceConfigPropertiesChangedListener(
2873                 @NonNull DeviceConfig.OnPropertiesChangedListener listener) {
2874             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_STANDBY,
2875                     AppSchedulingModuleThread.getExecutor(), listener);
2876         }
2877 
dump(PrintWriter pw)2878         void dump(PrintWriter pw) {
2879             pw.println("mPowerWhitelistedApps=[");
2880             synchronized (mPowerWhitelistedApps) {
2881                 for (int i = mPowerWhitelistedApps.size() - 1; i >= 0; --i) {
2882                     pw.print("  ");
2883                     pw.print(mPowerWhitelistedApps.valueAt(i));
2884                     pw.println(",");
2885                 }
2886             }
2887             pw.println("]");
2888             pw.println();
2889         }
2890     }
2891 
2892     class AppStandbyHandler extends Handler {
2893 
AppStandbyHandler(Looper looper)2894         AppStandbyHandler(Looper looper) {
2895             super(looper);
2896         }
2897 
2898         @Override
handleMessage(Message msg)2899         public void handleMessage(Message msg) {
2900             switch (msg.what) {
2901                 case MSG_INFORM_LISTENERS:
2902                     // TODO(230875908): Properly notify BatteryStats when apps change from active to
2903                     // idle, and vice versa
2904                     StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
2905                     informListeners(r.packageName, r.userId, r.bucket, r.reason,
2906                             r.isUserInteraction);
2907                     r.recycle();
2908                     break;
2909 
2910                 case MSG_FORCE_IDLE_STATE:
2911                     forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
2912                     break;
2913 
2914                 case MSG_CHECK_IDLE_STATES:
2915                     removeMessages(MSG_CHECK_IDLE_STATES);
2916 
2917                     long earliestCheck = Long.MAX_VALUE;
2918                     final long nowElapsed = mInjector.elapsedRealtime();
2919                     synchronized (mPendingIdleStateChecks) {
2920                         for (int i = mPendingIdleStateChecks.size() - 1; i >= 0; --i) {
2921                             long expirationTime = mPendingIdleStateChecks.valueAt(i);
2922 
2923                             if (expirationTime <= nowElapsed) {
2924                                 final int userId = mPendingIdleStateChecks.keyAt(i);
2925                                 if (checkIdleStates(userId) && mAppIdleEnabled) {
2926                                     expirationTime = nowElapsed + mCheckIdleIntervalMillis;
2927                                     mPendingIdleStateChecks.put(userId, expirationTime);
2928                                 } else {
2929                                     mPendingIdleStateChecks.removeAt(i);
2930                                     continue;
2931                                 }
2932                             }
2933 
2934                             earliestCheck = Math.min(earliestCheck, expirationTime);
2935                         }
2936                     }
2937                     if (earliestCheck != Long.MAX_VALUE) {
2938                         mHandler.sendMessageDelayed(
2939                                 mHandler.obtainMessage(MSG_CHECK_IDLE_STATES),
2940                                 earliestCheck - nowElapsed);
2941                     }
2942                     break;
2943 
2944                 case MSG_ONE_TIME_CHECK_IDLE_STATES:
2945                     mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
2946                     waitForAdminData();
2947                     checkIdleStates(UserHandle.USER_ALL);
2948                     break;
2949 
2950                 case MSG_TRIGGER_LISTENER_QUOTA_BUMP:
2951                     triggerListenerQuotaBump((String) msg.obj, msg.arg1);
2952                     break;
2953 
2954                 case MSG_REPORT_CONTENT_PROVIDER_USAGE:
2955                     ContentProviderUsageRecord record = (ContentProviderUsageRecord) msg.obj;
2956                     reportContentProviderUsage(record.name, record.packageName, record.userId);
2957                     record.recycle();
2958                     break;
2959 
2960                 case MSG_PAROLE_STATE_CHANGED:
2961                     if (DEBUG) Slog.d(TAG, "Parole state: " + isInParole());
2962                     informParoleStateChanged();
2963                     break;
2964 
2965                 case MSG_CHECK_PACKAGE_IDLE_STATE:
2966                     checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
2967                             mInjector.elapsedRealtime());
2968                     break;
2969 
2970                 case MSG_REPORT_SYNC_SCHEDULED:
2971                     final boolean exempted = msg.arg2 > 0 ? true : false;
2972                     if (exempted) {
2973                         reportExemptedSyncScheduled((String) msg.obj, msg.arg1);
2974                     } else {
2975                         reportUnexemptedSyncScheduled((String) msg.obj, msg.arg1);
2976                     }
2977                     break;
2978 
2979                 case MSG_REPORT_EXEMPTED_SYNC_START:
2980                     reportExemptedSyncStart((String) msg.obj, msg.arg1);
2981                     break;
2982 
2983                 default:
2984                     super.handleMessage(msg);
2985                     break;
2986 
2987             }
2988         }
2989     };
2990 
2991     private class DeviceStateReceiver extends BroadcastReceiver {
2992         @Override
onReceive(Context context, Intent intent)2993         public void onReceive(Context context, Intent intent) {
2994             switch (intent.getAction()) {
2995                 case BatteryManager.ACTION_CHARGING:
2996                     setChargingState(true);
2997                     break;
2998                 case BatteryManager.ACTION_DISCHARGING:
2999                     setChargingState(false);
3000                     break;
3001                 case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
3002                     if (mSystemServicesReady) {
3003                         mHandler.post(AppStandbyController.this::updatePowerWhitelistCache);
3004                     }
3005                     break;
3006             }
3007         }
3008     }
3009 
3010     private final DisplayManager.DisplayListener mDisplayListener
3011             = new DisplayManager.DisplayListener() {
3012 
3013         @Override public void onDisplayAdded(int displayId) {
3014         }
3015 
3016         @Override public void onDisplayRemoved(int displayId) {
3017         }
3018 
3019         @Override public void onDisplayChanged(int displayId) {
3020             if (displayId == Display.DEFAULT_DISPLAY) {
3021                 final boolean displayOn = isDisplayOn();
3022                 synchronized (mAppIdleLock) {
3023                     mAppIdleHistory.updateDisplay(displayOn, mInjector.elapsedRealtime());
3024                 }
3025             }
3026         }
3027     };
3028 
3029     /**
3030      * Observe changes for {@link DeviceConfig#NAMESPACE_APP_STANDBY} and other standby related
3031      * Settings constants.
3032      */
3033     private class ConstantsObserver extends ContentObserver implements
3034             DeviceConfig.OnPropertiesChangedListener {
3035         private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
3036         private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
3037                 "notification_seen_duration";
3038         private static final String KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET =
3039                 "notification_seen_promoted_bucket";
3040         private static final String KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS =
3041                 "retain_notification_seen_impact_for_pre_t_apps";
3042         private static final String KEY_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN =
3043                 "trigger_quota_bump_on_notification_seen";
3044         private static final String KEY_SLICE_PINNED_HOLD_DURATION =
3045                 "slice_pinned_duration";
3046         private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
3047                 "system_update_usage_duration";
3048         private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
3049         private static final String KEY_SYNC_ADAPTER_HOLD_DURATION = "sync_adapter_duration";
3050         private static final String KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION =
3051                 "exempted_sync_scheduled_nd_duration";
3052         private static final String KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION =
3053                 "exempted_sync_scheduled_d_duration";
3054         private static final String KEY_EXEMPTED_SYNC_START_HOLD_DURATION =
3055                 "exempted_sync_start_duration";
3056         private static final String KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION =
3057                 "unexempted_sync_scheduled_duration";
3058         private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
3059                 "system_interaction_duration";
3060         private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
3061                 "initial_foreground_service_start_duration";
3062         private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS =
3063                 "auto_restricted_bucket_delay_ms";
3064         private static final String KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS =
3065                 "cross_profile_apps_share_standby_buckets";
3066         private static final String KEY_PREFIX_SCREEN_TIME_THRESHOLD = "screen_threshold_";
3067         private final String[] KEYS_SCREEN_TIME_THRESHOLDS = {
3068                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "active",
3069                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "working_set",
3070                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "frequent",
3071                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "rare",
3072                 KEY_PREFIX_SCREEN_TIME_THRESHOLD + "restricted"
3073         };
3074         private static final String KEY_PREFIX_ELAPSED_TIME_THRESHOLD = "elapsed_threshold_";
3075         private final String[] KEYS_ELAPSED_TIME_THRESHOLDS = {
3076                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "active",
3077                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "working_set",
3078                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "frequent",
3079                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "rare",
3080                 KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "restricted"
3081         };
3082         private static final String KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
3083                 "broadcast_response_window_timeout_ms";
3084         private static final String KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
3085                 "broadcast_response_fg_threshold_state";
3086         private static final String KEY_BROADCAST_SESSIONS_DURATION_MS =
3087                 "broadcast_sessions_duration_ms";
3088         private static final String KEY_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS =
3089                 "broadcast_sessions_with_response_duration_ms";
3090         private static final String KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS =
3091                 "note_response_event_for_all_broadcast_sessions";
3092         private static final String KEY_BROADCAST_RESPONSE_EXEMPTED_ROLES =
3093                 "brodacast_response_exempted_roles";
3094         private static final String KEY_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS =
3095                 "brodacast_response_exempted_permissions";
3096 
3097         public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
3098                 COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
3099         public static final long DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT =
3100                 COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR;
3101 
3102         public static final long DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT =
3103                 COMPRESS_TIME ? ONE_MINUTE : 5 * ONE_MINUTE;
3104         public static final long DEFAULT_NOTIFICATION_TIMEOUT =
3105                 COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
3106         public static final long DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT =
3107                 COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
3108 
3109         public static final long DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT =
3110                 COMPRESS_TIME ? 12 * ONE_MINUTE : 2 * ONE_HOUR;
3111         public static final int DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET =
3112                 STANDBY_BUCKET_WORKING_SET;
3113         public static final boolean DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS = false;
3114         public static final boolean DEFAULT_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN = false;
3115         public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT =
3116                 COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR;
3117         public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT =
3118                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
3119         public static final long DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT =
3120                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
3121 
3122         public static final long DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT =
3123                 COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR;
3124         public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT =
3125                 COMPRESS_TIME ? (ONE_MINUTE / 2) : 10 * ONE_MINUTE;
3126         public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT =
3127                 COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
3128         public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT =
3129                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
3130         public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT =
3131                 COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE;
3132         public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT =
3133                 COMPRESS_TIME ? ONE_MINUTE : 30 * ONE_MINUTE;
3134         public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS =
3135                 COMPRESS_TIME ? ONE_MINUTE : ONE_HOUR;
3136         public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
3137         public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
3138                 2 * ONE_MINUTE;
3139         public static final int DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
3140                 ActivityManager.PROCESS_STATE_TOP;
3141         public static final long DEFAULT_BROADCAST_SESSIONS_DURATION_MS =
3142                 2 * ONE_MINUTE;
3143         public static final long DEFAULT_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS =
3144                 2 * ONE_MINUTE;
3145         public static final boolean DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS =
3146                 true;
3147         private static final String DEFAULT_BROADCAST_RESPONSE_EXEMPTED_ROLES = "";
3148         private static final String DEFAULT_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS = "";
3149 
3150         private final TextUtils.SimpleStringSplitter mStringPipeSplitter =
3151                 new TextUtils.SimpleStringSplitter('|');
3152 
ConstantsObserver(Handler handler)3153         ConstantsObserver(Handler handler) {
3154             super(handler);
3155         }
3156 
start()3157         public void start() {
3158             final ContentResolver cr = mContext.getContentResolver();
3159             // APP_STANDBY_ENABLED is a SystemApi that some apps may be watching, so best to
3160             // leave it in Settings.
3161             cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
3162             // ADAPTIVE_BATTERY_MANAGEMENT_ENABLED is a user setting, so it has to stay in Settings.
3163             cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
3164                     false, this);
3165             mInjector.registerDeviceConfigPropertiesChangedListener(this);
3166 
3167             processDefaultConstants();
3168 
3169             // Load all the constants.
3170             // postOneTimeCheckIdleStates() doesn't need to be called on boot.
3171             processProperties(mInjector.getDeviceConfigProperties());
3172             updateSettings();
3173         }
3174 
3175         @Override
onChange(boolean selfChange)3176         public void onChange(boolean selfChange) {
3177             updateSettings();
3178             postOneTimeCheckIdleStates();
3179         }
3180 
3181         @Override
onPropertiesChanged(DeviceConfig.Properties properties)3182         public void onPropertiesChanged(DeviceConfig.Properties properties) {
3183             processProperties(properties);
3184             postOneTimeCheckIdleStates();
3185         }
3186 
processDefaultConstants()3187         private void processDefaultConstants() {
3188             if (!Flags.adjustDefaultBucketElevationParams()) {
3189                 return;
3190             }
3191 
3192             mSlicePinnedTimeoutMillis = DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT;
3193             mSyncAdapterTimeoutMillis = DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT;
3194             mSyncAdapaterPromotedBucket = STANDBY_BUCKET_WORKING_SET;
3195             mStrongUsageTimeoutMillis = DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT;
3196         }
3197 
processProperties(DeviceConfig.Properties properties)3198         private void processProperties(DeviceConfig.Properties properties) {
3199             boolean timeThresholdsUpdated = false;
3200             synchronized (mAppIdleLock) {
3201                 for (String name : properties.getKeyset()) {
3202                     if (name == null) {
3203                         continue;
3204                     }
3205                     switch (name) {
3206                         case KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS:
3207                             mInjector.mAutoRestrictedBucketDelayMs = Math.max(
3208                                     COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR,
3209                                     properties.getLong(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
3210                                             DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
3211                             break;
3212                         case KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS:
3213                             mLinkCrossProfileApps = properties.getBoolean(
3214                                     KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS,
3215                                     DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS);
3216                             break;
3217                         case KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION:
3218                             mInitialForegroundServiceStartTimeoutMillis = properties.getLong(
3219                                     KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
3220                                     DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
3221                             break;
3222                         case KEY_NOTIFICATION_SEEN_HOLD_DURATION:
3223                             mNotificationSeenTimeoutMillis = properties.getLong(
3224                                     KEY_NOTIFICATION_SEEN_HOLD_DURATION,
3225                                     DEFAULT_NOTIFICATION_TIMEOUT);
3226                             break;
3227                         case KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET:
3228                             mNotificationSeenPromotedBucket = properties.getInt(
3229                                     KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET,
3230                                     DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET);
3231                             break;
3232                         case KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS:
3233                             mRetainNotificationSeenImpactForPreTApps = properties.getBoolean(
3234                                     KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS,
3235                                     DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS);
3236                             break;
3237                         case KEY_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN:
3238                             mTriggerQuotaBumpOnNotificationSeen = properties.getBoolean(
3239                                     KEY_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN,
3240                                     DEFAULT_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN);
3241                             break;
3242                         case KEY_SLICE_PINNED_HOLD_DURATION:
3243                             mSlicePinnedTimeoutMillis = properties.getLong(
3244                                     KEY_SLICE_PINNED_HOLD_DURATION,
3245                                     Flags.adjustDefaultBucketElevationParams()
3246                                             ? DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT
3247                                             : DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT);
3248                             break;
3249                         case KEY_STRONG_USAGE_HOLD_DURATION:
3250                             mStrongUsageTimeoutMillis = properties.getLong(
3251                                     KEY_STRONG_USAGE_HOLD_DURATION,
3252                                     Flags.adjustDefaultBucketElevationParams()
3253                                             ? DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT
3254                                             : DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT);
3255                             break;
3256                         case KEY_PREDICTION_TIMEOUT:
3257                             mPredictionTimeoutMillis = properties.getLong(
3258                                     KEY_PREDICTION_TIMEOUT, DEFAULT_PREDICTION_TIMEOUT);
3259                             break;
3260                         case KEY_SYSTEM_INTERACTION_HOLD_DURATION:
3261                             mSystemInteractionTimeoutMillis = properties.getLong(
3262                                     KEY_SYSTEM_INTERACTION_HOLD_DURATION,
3263                                     DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
3264                             break;
3265                         case KEY_SYSTEM_UPDATE_HOLD_DURATION:
3266                             mSystemUpdateUsageTimeoutMillis = properties.getLong(
3267                                     KEY_SYSTEM_UPDATE_HOLD_DURATION, DEFAULT_SYSTEM_UPDATE_TIMEOUT);
3268                             break;
3269                         case KEY_SYNC_ADAPTER_HOLD_DURATION:
3270                             mSyncAdapterTimeoutMillis = properties.getLong(
3271                                     KEY_SYNC_ADAPTER_HOLD_DURATION,
3272                                     Flags.adjustDefaultBucketElevationParams()
3273                                             ? DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT
3274                                             : DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT);
3275                             break;
3276                         case KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION:
3277                             mExemptedSyncScheduledDozeTimeoutMillis = properties.getLong(
3278                                     KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
3279                                     DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT);
3280                             break;
3281                         case KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION:
3282                             mExemptedSyncScheduledNonDozeTimeoutMillis = properties.getLong(
3283                                     KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION,
3284                                     DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT);
3285                             break;
3286                         case KEY_EXEMPTED_SYNC_START_HOLD_DURATION:
3287                             mExemptedSyncStartTimeoutMillis = properties.getLong(
3288                                     KEY_EXEMPTED_SYNC_START_HOLD_DURATION,
3289                                     DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
3290                             break;
3291                         case KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION:
3292                             mUnexemptedSyncScheduledTimeoutMillis = properties.getLong(
3293                                     KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
3294                                     DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
3295                             break;
3296                         case KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS:
3297                             mBroadcastResponseWindowDurationMillis = properties.getLong(
3298                                     KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS,
3299                                     DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS);
3300                             break;
3301                         case KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE:
3302                             mBroadcastResponseFgThresholdState = properties.getInt(
3303                                     KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE,
3304                                     DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE);
3305                             break;
3306                         case KEY_BROADCAST_SESSIONS_DURATION_MS:
3307                             mBroadcastSessionsDurationMs = properties.getLong(
3308                                     KEY_BROADCAST_SESSIONS_DURATION_MS,
3309                                     DEFAULT_BROADCAST_SESSIONS_DURATION_MS);
3310                             break;
3311                         case KEY_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS:
3312                             mBroadcastSessionsWithResponseDurationMs = properties.getLong(
3313                                     KEY_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS,
3314                                     DEFAULT_BROADCAST_SESSIONS_WITH_RESPONSE_DURATION_MS);
3315                             break;
3316                         case KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS:
3317                             mNoteResponseEventForAllBroadcastSessions = properties.getBoolean(
3318                                     KEY_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS,
3319                                     DEFAULT_NOTE_RESPONSE_EVENT_FOR_ALL_BROADCAST_SESSIONS);
3320                             break;
3321                         case KEY_BROADCAST_RESPONSE_EXEMPTED_ROLES:
3322                             mBroadcastResponseExemptedRoles = properties.getString(
3323                                     KEY_BROADCAST_RESPONSE_EXEMPTED_ROLES,
3324                                     DEFAULT_BROADCAST_RESPONSE_EXEMPTED_ROLES);
3325                             mBroadcastResponseExemptedRolesList = splitPipeSeparatedString(
3326                                     mBroadcastResponseExemptedRoles);
3327                             break;
3328                         case KEY_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS:
3329                             mBroadcastResponseExemptedPermissions = properties.getString(
3330                                     KEY_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS,
3331                                     DEFAULT_BROADCAST_RESPONSE_EXEMPTED_PERMISSIONS);
3332                             mBroadcastResponseExemptedPermissionsList = splitPipeSeparatedString(
3333                                     mBroadcastResponseExemptedPermissions);
3334                             break;
3335                         default:
3336                             if (!timeThresholdsUpdated
3337                                     && (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
3338                                     || name.startsWith(KEY_PREFIX_ELAPSED_TIME_THRESHOLD))) {
3339                                 updateTimeThresholds();
3340                                 timeThresholdsUpdated = true;
3341                             }
3342                             break;
3343                     }
3344                     mAppStandbyProperties.put(name, properties.getString(name, null));
3345                 }
3346             }
3347         }
3348 
splitPipeSeparatedString(String string)3349         private List<String> splitPipeSeparatedString(String string) {
3350             final List<String> values = new ArrayList<>();
3351             mStringPipeSplitter.setString(string);
3352             while (mStringPipeSplitter.hasNext()) {
3353                 values.add(mStringPipeSplitter.next());
3354             }
3355             return values;
3356         }
3357 
updateTimeThresholds()3358         private void updateTimeThresholds() {
3359             // Query the values as an atomic set.
3360             final DeviceConfig.Properties screenThresholdProperties =
3361                     mInjector.getDeviceConfigProperties(KEYS_SCREEN_TIME_THRESHOLDS);
3362             final DeviceConfig.Properties elapsedThresholdProperties =
3363                     mInjector.getDeviceConfigProperties(KEYS_ELAPSED_TIME_THRESHOLDS);
3364             mAppStandbyScreenThresholds = generateThresholdArray(
3365                     screenThresholdProperties, KEYS_SCREEN_TIME_THRESHOLDS,
3366                     DEFAULT_SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS);
3367             mAppStandbyElapsedThresholds = generateThresholdArray(
3368                     elapsedThresholdProperties, KEYS_ELAPSED_TIME_THRESHOLDS,
3369                     DEFAULT_ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS);
3370             mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
3371                     DEFAULT_CHECK_IDLE_INTERVAL_MS);
3372         }
3373 
updateSettings()3374         void updateSettings() {
3375             if (DEBUG) {
3376                 Slog.d(TAG,
3377                         "appidle=" + Global.getString(mContext.getContentResolver(),
3378                                 Global.APP_STANDBY_ENABLED));
3379                 Slog.d(TAG,
3380                         "adaptivebat=" + Global.getString(mContext.getContentResolver(),
3381                                 Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
3382             }
3383 
3384             setAppIdleEnabled(mInjector.isAppIdleEnabled());
3385         }
3386 
generateThresholdArray(@onNull DeviceConfig.Properties properties, @NonNull String[] keys, long[] defaults, long[] minValues)3387         long[] generateThresholdArray(@NonNull DeviceConfig.Properties properties,
3388                 @NonNull String[] keys, long[] defaults, long[] minValues) {
3389             if (properties.getKeyset().isEmpty()) {
3390                 // Reset to defaults
3391                 return defaults;
3392             }
3393             if (keys.length != THRESHOLD_BUCKETS.length) {
3394                 // This should only happen in development.
3395                 throw new IllegalStateException(
3396                         "# keys (" + keys.length + ") != # buckets ("
3397                                 + THRESHOLD_BUCKETS.length + ")");
3398             }
3399             if (defaults.length != THRESHOLD_BUCKETS.length) {
3400                 // This should only happen in development.
3401                 throw new IllegalStateException(
3402                         "# defaults (" + defaults.length + ") != # buckets ("
3403                                 + THRESHOLD_BUCKETS.length + ")");
3404             }
3405             if (minValues.length != THRESHOLD_BUCKETS.length) {
3406                 Slog.wtf(TAG, "minValues array is the wrong size");
3407                 // Use zeroes as the minimums.
3408                 minValues = new long[THRESHOLD_BUCKETS.length];
3409             }
3410             long[] array = new long[THRESHOLD_BUCKETS.length];
3411             for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
3412                 array[i] = Math.max(minValues[i], properties.getLong(keys[i], defaults[i]));
3413             }
3414             return array;
3415         }
3416     }
3417 }
3418