blob: 5ceea2aedb8531b8c31b8170ad6c156a8be8e0c1 [file] [log] [blame]
Amith Yamasani7e528f32017-10-05 16:06:16 -07001/**
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
17package com.android.server.usage;
18
Amith Yamasani119be9a2018-02-18 22:23:00 -080019import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
Kweku Adamsc182d5e2020-01-08 18:37:26 -080020import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
21import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
Amith Yamasani119be9a2018-02-18 22:23:00 -080022import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
23import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
24import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
25import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
Kweku Adams917f8a42020-02-26 17:18:03 -080026import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
27import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
Kweku Adamsc6a9b342020-01-08 18:37:26 -080028import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
Amith Yamasani3154dcf2018-03-27 18:24:04 -070029import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
Amith Yamasani119be9a2018-02-18 22:23:00 -080030import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
Makoto Onukid5f25d22018-05-22 16:02:17 -070031import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
32import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
Makoto Onuki75ad2492018-03-28 14:42:42 -070033import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
Michael Wachenschwanz6ced0ee2019-04-15 16:43:28 -070034import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
Amith Yamasani119be9a2018-02-18 22:23:00 -080035import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
36import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
37import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
Kweku Adams1e8947c2018-11-05 18:06:13 -080038import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
39import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
Amith Yamasani119be9a2018-02-18 22:23:00 -080040import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
41import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
42import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
Michael Wachenschwanzc3295202019-02-20 17:19:52 -080043import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED;
Amith Yamasani119be9a2018-02-18 22:23:00 -080044import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080045import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
Amith Yamasani5a8cc722017-12-06 10:26:10 -080046import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080047import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
Amith Yamasani93885192017-12-13 11:52:10 -080048import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080049import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
Kweku Adamsc6a9b342020-01-08 18:37:26 -080050import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080051import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
Kweku Adams1e8947c2018-11-05 18:06:13 -080052
Kweku Adamsf35ed3702020-02-11 17:32:54 -080053import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
Amith Yamasani7e528f32017-10-05 16:06:16 -070054import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
Amith Yamasani7e528f32017-10-05 16:06:16 -070055
Kweku Adamsc4ee9982020-01-08 11:14:39 -080056import android.annotation.NonNull;
Kweku Adams95cd9522020-05-08 09:56:53 -070057import android.annotation.Nullable;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -080058import android.annotation.UserIdInt;
Amith Yamasani7e528f32017-10-05 16:06:16 -070059import android.app.ActivityManager;
60import android.app.AppGlobals;
Suprabh Shukla868bde22018-02-20 20:59:52 -080061import android.app.usage.AppStandbyInfo;
Amith Yamasani7e528f32017-10-05 16:06:16 -070062import android.app.usage.UsageEvents;
Jason Monk1918ef72018-03-14 09:20:39 -040063import android.app.usage.UsageStatsManager.StandbyBuckets;
Kweku Adamsaa461942020-03-16 11:59:05 -070064import android.app.usage.UsageStatsManager.SystemForcedReasons;
Amith Yamasani7e528f32017-10-05 16:06:16 -070065import android.appwidget.AppWidgetManager;
66import android.content.BroadcastReceiver;
67import android.content.ContentResolver;
68import android.content.Context;
69import android.content.Intent;
70import android.content.IntentFilter;
71import android.content.pm.ApplicationInfo;
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -080072import android.content.pm.CrossProfileAppsInternal;
Amith Yamasani7e528f32017-10-05 16:06:16 -070073import android.content.pm.PackageInfo;
74import android.content.pm.PackageManager;
75import android.content.pm.PackageManagerInternal;
76import android.content.pm.ParceledListSlice;
77import android.database.ContentObserver;
78import android.hardware.display.DisplayManager;
79import android.net.NetworkScoreManager;
Kweku Adamsf35ed3702020-02-11 17:32:54 -080080import android.os.BatteryManager;
Amith Yamasani7e528f32017-10-05 16:06:16 -070081import android.os.BatteryStats;
Kweku Adamsc6a9b342020-01-08 18:37:26 -080082import android.os.Build;
Amith Yamasani17fffee2017-09-29 13:17:43 -070083import android.os.Environment;
Amith Yamasani7e528f32017-10-05 16:06:16 -070084import android.os.Handler;
Kweku Adamseffeb2a2020-06-16 10:11:56 -070085import android.os.IDeviceIdleController;
Amith Yamasani7e528f32017-10-05 16:06:16 -070086import android.os.Looper;
87import android.os.Message;
88import android.os.PowerManager;
89import android.os.Process;
90import android.os.RemoteException;
91import android.os.ServiceManager;
92import android.os.SystemClock;
93import android.os.UserHandle;
Lei Yu4b976ad2018-04-19 10:38:58 -070094import android.provider.Settings.Global;
Amith Yamasani7e528f32017-10-05 16:06:16 -070095import android.telephony.TelephonyManager;
Sudheer Shanka101c3532018-01-08 16:28:42 -080096import android.util.ArraySet;
Amith Yamasani7e528f32017-10-05 16:06:16 -070097import android.util.KeyValueListParser;
98import android.util.Slog;
Sudheer Shanka101c3532018-01-08 16:28:42 -080099import android.util.SparseArray;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700100import android.util.SparseIntArray;
101import android.util.TimeUtils;
102import android.view.Display;
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800103import android.widget.Toast;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700104
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800105import com.android.internal.R;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700106import com.android.internal.annotations.GuardedBy;
Sudheer Shanka101c3532018-01-08 16:28:42 -0800107import com.android.internal.annotations.VisibleForTesting;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700108import com.android.internal.app.IBatteryStats;
109import com.android.internal.os.SomeArgs;
110import com.android.internal.util.ArrayUtils;
Sudheer Shankac53c47f2018-01-16 12:01:00 -0800111import com.android.internal.util.ConcurrentUtils;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700112import com.android.internal.util.IndentingPrintWriter;
113import com.android.server.LocalServices;
Kweku Adams259403e2020-04-27 09:25:07 -0700114import com.android.server.pm.parsing.pkg.AndroidPackage;
Christopher Tated117b292018-01-05 17:32:36 -0800115import com.android.server.usage.AppIdleHistory.AppUsageHistory;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700116
Amith Yamasani17fffee2017-09-29 13:17:43 -0700117import java.io.File;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700118import java.io.PrintWriter;
Amith Yamasani761d3ff2017-12-14 17:50:03 -0800119import java.time.Duration;
120import java.time.format.DateTimeParseException;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700121import java.util.ArrayList;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700122import java.util.Arrays;
Kweku Adamsc4ee9982020-01-08 11:14:39 -0800123import java.util.Collections;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700124import java.util.List;
Sudheer Shanka101c3532018-01-08 16:28:42 -0800125import java.util.Set;
Sudheer Shankac53c47f2018-01-16 12:01:00 -0800126import java.util.concurrent.CountDownLatch;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700127
128/**
129 * Manages the standby state of an app, listening to various events.
Makoto Onukia0058b42018-05-22 16:32:23 -0700130 *
131 * Unit test:
Makoto Onukia72e04f2019-10-03 11:10:45 -0700132 atest com.android.server.usage.AppStandbyControllerTests
Amith Yamasani7e528f32017-10-05 16:06:16 -0700133 */
Makoto Onukia72e04f2019-10-03 11:10:45 -0700134public class AppStandbyController implements AppStandbyInternal {
Amith Yamasani7e528f32017-10-05 16:06:16 -0700135
136 private static final String TAG = "AppStandbyController";
Kweku Adamsa52d5332020-03-04 10:37:57 -0800137 // Do not submit with true.
138 static final boolean DEBUG = false;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700139
140 static final boolean COMPRESS_TIME = false;
141 private static final long ONE_MINUTE = 60 * 1000;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700142 private static final long ONE_HOUR = ONE_MINUTE * 60;
143 private static final long ONE_DAY = ONE_HOUR * 24;
144
Kweku Adams4297b5d2020-02-06 15:56:12 -0800145 /**
146 * The minimum amount of time the screen must have been on before an app can time out from its
147 * current bucket to the next bucket.
148 */
149 private static final long[] SCREEN_TIME_THRESHOLDS = {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700150 0,
151 0,
Kweku Adams4297b5d2020-02-06 15:56:12 -0800152 COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR,
153 COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR,
154 COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR
Amith Yamasani17fffee2017-09-29 13:17:43 -0700155 };
156
Kweku Adams4297b5d2020-02-06 15:56:12 -0800157 /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */
158 private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME
159 ? new long[SCREEN_TIME_THRESHOLDS.length]
160 : new long[]{
161 0,
162 0,
163 0,
164 30 * ONE_MINUTE,
165 ONE_HOUR
166 };
167
168 /**
169 * The minimum amount of elapsed time that must have passed before an app can time out from its
170 * current bucket to the next bucket.
171 */
172 private static final long[] ELAPSED_TIME_THRESHOLDS = {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700173 0,
174 COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
Amith Yamasani301e94a2017-11-17 16:35:44 -0800175 COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
Kweku Adams4297b5d2020-02-06 15:56:12 -0800176 COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
Kweku Adams23aeb082020-04-13 08:34:36 -0700177 COMPRESS_TIME ? 32 * ONE_MINUTE : 30 * ONE_DAY
Amith Yamasani17fffee2017-09-29 13:17:43 -0700178 };
179
Kweku Adams4297b5d2020-02-06 15:56:12 -0800180 /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */
181 private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME
182 ? new long[ELAPSED_TIME_THRESHOLDS.length]
183 : new long[]{
184 0,
185 ONE_HOUR,
186 ONE_HOUR,
187 2 * ONE_HOUR,
188 4 * ONE_DAY
189 };
190
191 private static final int[] THRESHOLD_BUCKETS = {
Amith Yamasaniafbccb72017-11-27 10:44:24 -0800192 STANDBY_BUCKET_ACTIVE,
193 STANDBY_BUCKET_WORKING_SET,
194 STANDBY_BUCKET_FREQUENT,
Kweku Adams4297b5d2020-02-06 15:56:12 -0800195 STANDBY_BUCKET_RARE,
196 STANDBY_BUCKET_RESTRICTED
Amith Yamasani17fffee2017-09-29 13:17:43 -0700197 };
Amith Yamasani7e528f32017-10-05 16:06:16 -0700198
Amith Yamasani119be9a2018-02-18 22:23:00 -0800199 /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
200 private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800201
Sudheer Shankac53c47f2018-01-16 12:01:00 -0800202 /**
203 * Indicates the maximum wait time for admin data to be available;
204 */
205 private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
206
Kweku Adamsf197eef2020-06-16 11:59:37 -0700207 private static final int HEADLESS_APP_CHECK_FLAGS =
208 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
209 | PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS;
210
Amith Yamasani7e528f32017-10-05 16:06:16 -0700211 // To name the lock for stack traces
212 static class Lock {}
213
214 /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
215 private final Object mAppIdleLock = new Lock();
216
217 /** Keeps the history and state for each app. */
218 @GuardedBy("mAppIdleLock")
219 private AppIdleHistory mAppIdleHistory;
220
Amith Yamasani93885192017-12-13 11:52:10 -0800221 @GuardedBy("mPackageAccessListeners")
Kweku Adamse6601762020-05-01 15:06:18 -0700222 private final ArrayList<AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>();
Amith Yamasani7e528f32017-10-05 16:06:16 -0700223
224 /** Whether we've queried the list of carrier privileged apps. */
225 @GuardedBy("mAppIdleLock")
226 private boolean mHaveCarrierPrivilegedApps;
227
228 /** List of carrier-privileged apps that should be excluded from standby */
229 @GuardedBy("mAppIdleLock")
230 private List<String> mCarrierPrivilegedApps;
231
Sudheer Shanka101c3532018-01-08 16:28:42 -0800232 @GuardedBy("mActiveAdminApps")
233 private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
234
Kweku Adams95cd9522020-05-08 09:56:53 -0700235 /**
236 * Set of system apps that are headless (don't have any declared activities, enabled or
237 * disabled). Presence in this map indicates that the app is a headless system app.
238 */
Kweku Adams8604fd72020-06-02 14:04:27 -0700239 @GuardedBy("mHeadlessSystemApps")
Kweku Adamsf197eef2020-06-16 11:59:37 -0700240 private final ArraySet<String> mHeadlessSystemApps = new ArraySet<>();
Kweku Adams95cd9522020-05-08 09:56:53 -0700241
Sudheer Shankac53c47f2018-01-16 12:01:00 -0800242 private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
243
Amith Yamasani7e528f32017-10-05 16:06:16 -0700244 // Messages for the handler
245 static final int MSG_INFORM_LISTENERS = 3;
246 static final int MSG_FORCE_IDLE_STATE = 4;
247 static final int MSG_CHECK_IDLE_STATES = 5;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700248 static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
Kweku Adamse6601762020-05-01 15:06:18 -0700249 static final int MSG_PAROLE_STATE_CHANGED = 9;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700250 static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800251 /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
252 static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
Michael Wachenschwanzc3295202019-02-20 17:19:52 -0800253 static final int MSG_REPORT_SYNC_SCHEDULED = 12;
Makoto Onukid5f25d22018-05-22 16:02:17 -0700254 static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700255
Amith Yamasani7e528f32017-10-05 16:06:16 -0700256 long mCheckIdleIntervalMillis;
Kweku Adams4297b5d2020-02-06 15:56:12 -0800257 /**
258 * The minimum amount of time the screen must have been on before an app can time out from its
259 * current bucket to the next bucket.
260 */
Amith Yamasani17fffee2017-09-29 13:17:43 -0700261 long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
Kweku Adams4297b5d2020-02-06 15:56:12 -0800262 /**
263 * The minimum amount of elapsed time that must have passed before an app can time out from its
264 * current bucket to the next bucket.
265 */
Amith Yamasani17fffee2017-09-29 13:17:43 -0700266 long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
Amith Yamasani7ec89412018-02-07 08:48:49 -0800267 /** Minimum time a strong usage event should keep the bucket elevated. */
268 long mStrongUsageTimeoutMillis;
269 /** Minimum time a notification seen event should keep the bucket elevated. */
270 long mNotificationSeenTimeoutMillis;
271 /** Minimum time a system update event should keep the buckets elevated. */
272 long mSystemUpdateUsageTimeoutMillis;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800273 /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
274 long mPredictionTimeoutMillis;
Amith Yamasani7f53c7b2018-03-25 21:55:50 -0700275 /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
276 long mSyncAdapterTimeoutMillis;
Makoto Onukid5f25d22018-05-22 16:02:17 -0700277 /**
278 * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
279 * non-doze
280 */
281 long mExemptedSyncScheduledNonDozeTimeoutMillis;
282 /**
283 * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in
284 * doze
285 */
286 long mExemptedSyncScheduledDozeTimeoutMillis;
287 /**
288 * Maximum time an exempted sync should keep the buckets elevated, when sync is started.
289 */
290 long mExemptedSyncStartTimeoutMillis;
Michael Wachenschwanzc3295202019-02-20 17:19:52 -0800291 /**
292 * Maximum time an unexempted sync should keep the buckets elevated, when sync is scheduled
293 */
294 long mUnexemptedSyncScheduledTimeoutMillis;
Amith Yamasani7f53c7b2018-03-25 21:55:50 -0700295 /** Maximum time a system interaction should keep the buckets elevated. */
296 long mSystemInteractionTimeoutMillis;
Michael Wachenschwanz6ced0ee2019-04-15 16:43:28 -0700297 /**
298 * Maximum time a foreground service start should keep the buckets elevated if the service
299 * start is the first usage of the app
300 */
301 long mInitialForegroundServiceStartTimeoutMillis;
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800302 /**
303 * User usage that would elevate an app's standby bucket will also elevate the standby bucket of
304 * cross profile connected apps. Explicit standby bucket setting via
305 * {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated.
306 */
307 boolean mLinkCrossProfileApps;
Kweku Adams2d79ce52020-05-05 12:31:35 -0700308 /**
309 * Whether we should allow apps into the
310 * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
311 * If false, any attempts to put an app into the bucket will put the app into the
312 * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE} bucket instead.
313 */
314 private boolean mAllowRestrictedBucket;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700315
Makoto Onukia72e04f2019-10-03 11:10:45 -0700316 private volatile boolean mAppIdleEnabled;
Kweku Adamsf35ed3702020-02-11 17:32:54 -0800317 private boolean mIsCharging;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700318 private boolean mSystemServicesReady = false;
Amith Yamasani777b1532018-01-28 23:20:07 +0000319 // There was a system update, defaults need to be initialized after services are ready
320 private boolean mPendingInitializeDefaults;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700321
322 private volatile boolean mPendingOneTimeCheckIdleStates;
323
Christopher Tatea732f012017-10-26 17:26:53 -0700324 private final AppStandbyHandler mHandler;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700325 private final Context mContext;
326
Amith Yamasani7e528f32017-10-05 16:06:16 -0700327 private AppWidgetManager mAppWidgetManager;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700328 private PackageManager mPackageManager;
Amith Yamasani172612c2017-12-15 10:51:53 -0800329 Injector mInjector;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700330
Christopher Tated117b292018-01-05 17:32:36 -0800331 static final ArrayList<StandbyUpdateRecord> sStandbyUpdatePool = new ArrayList<>(4);
332
333 public static class StandbyUpdateRecord {
334 // Identity of the app whose standby state has changed
335 String packageName;
336 int userId;
337
338 // What the standby bucket the app is now in
339 int bucket;
340
341 // Whether the bucket change is because the user has started interacting with the app
342 boolean isUserInteraction;
343
Amith Yamasani119be9a2018-02-18 22:23:00 -0800344 // Reason for bucket change
345 int reason;
346
347 StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
348 boolean isInteraction) {
Christopher Tated117b292018-01-05 17:32:36 -0800349 this.packageName = pkgName;
350 this.userId = userId;
351 this.bucket = bucket;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800352 this.reason = reason;
Christopher Tated117b292018-01-05 17:32:36 -0800353 this.isUserInteraction = isInteraction;
354 }
355
356 public static StandbyUpdateRecord obtain(String pkgName, int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -0800357 int bucket, int reason, boolean isInteraction) {
Christopher Tated117b292018-01-05 17:32:36 -0800358 synchronized (sStandbyUpdatePool) {
359 final int size = sStandbyUpdatePool.size();
360 if (size < 1) {
Amith Yamasani119be9a2018-02-18 22:23:00 -0800361 return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
Christopher Tated117b292018-01-05 17:32:36 -0800362 }
363 StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
364 r.packageName = pkgName;
365 r.userId = userId;
366 r.bucket = bucket;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800367 r.reason = reason;
Christopher Tated117b292018-01-05 17:32:36 -0800368 r.isUserInteraction = isInteraction;
369 return r;
370 }
371 }
372
373 public void recycle() {
374 synchronized (sStandbyUpdatePool) {
375 sStandbyUpdatePool.add(this);
376 }
377 }
378 }
Amith Yamasani7e528f32017-10-05 16:06:16 -0700379
Makoto Onukia72e04f2019-10-03 11:10:45 -0700380 public AppStandbyController(Context context, Looper looper) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700381 this(new Injector(context, looper));
382 }
383
384 AppStandbyController(Injector injector) {
385 mInjector = injector;
386 mContext = mInjector.getContext();
387 mHandler = new AppStandbyHandler(mInjector.getLooper());
Amith Yamasani7e528f32017-10-05 16:06:16 -0700388 mPackageManager = mContext.getPackageManager();
Amith Yamasani172612c2017-12-15 10:51:53 -0800389
Kweku Adamsf35ed3702020-02-11 17:32:54 -0800390 DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver();
391 IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
392 deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
Kweku Adamseffeb2a2020-06-16 10:11:56 -0700393 deviceStates.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
Kweku Adamsf35ed3702020-02-11 17:32:54 -0800394 mContext.registerReceiver(deviceStateReceiver, deviceStates);
395
Amith Yamasani7e528f32017-10-05 16:06:16 -0700396 synchronized (mAppIdleLock) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700397 mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(),
398 mInjector.elapsedRealtime());
Amith Yamasani7e528f32017-10-05 16:06:16 -0700399 }
400
401 IntentFilter packageFilter = new IntentFilter();
402 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
403 packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
404 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
405 packageFilter.addDataScheme("package");
406
407 mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
408 null, mHandler);
409 }
410
Makoto Onukia72e04f2019-10-03 11:10:45 -0700411 @VisibleForTesting
Amith Yamasani172612c2017-12-15 10:51:53 -0800412 void setAppIdleEnabled(boolean enabled) {
Kweku Adamse6601762020-05-01 15:06:18 -0700413 synchronized (mAppIdleLock) {
414 if (mAppIdleEnabled != enabled) {
415 final boolean oldParoleState = isInParole();
416 mAppIdleEnabled = enabled;
417 if (isInParole() != oldParoleState) {
418 postParoleStateChanged();
419 }
420 }
421 }
422
Amith Yamasani172612c2017-12-15 10:51:53 -0800423 }
424
Makoto Onukia72e04f2019-10-03 11:10:45 -0700425 @Override
426 public boolean isAppIdleEnabled() {
427 return mAppIdleEnabled;
428 }
429
430 @Override
Amith Yamasani7e528f32017-10-05 16:06:16 -0700431 public void onBootPhase(int phase) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700432 mInjector.onBootPhase(phase);
Amith Yamasani7e528f32017-10-05 16:06:16 -0700433 if (phase == PHASE_SYSTEM_SERVICES_READY) {
Amith Yamasani777b1532018-01-28 23:20:07 +0000434 Slog.d(TAG, "Setting app idle enabled state");
Amith Yamasani7e528f32017-10-05 16:06:16 -0700435 // Observe changes to the threshold
436 SettingsObserver settingsObserver = new SettingsObserver(mHandler);
437 settingsObserver.registerObserver();
438 settingsObserver.updateSettings();
439
440 mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
Amith Yamasani7e528f32017-10-05 16:06:16 -0700441
Amith Yamasani17fffee2017-09-29 13:17:43 -0700442 mInjector.registerDisplayListener(mDisplayListener, mHandler);
Amith Yamasani7e528f32017-10-05 16:06:16 -0700443 synchronized (mAppIdleLock) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700444 mAppIdleHistory.updateDisplay(isDisplayOn(), mInjector.elapsedRealtime());
Amith Yamasani7e528f32017-10-05 16:06:16 -0700445 }
446
Amith Yamasani777b1532018-01-28 23:20:07 +0000447 mSystemServicesReady = true;
448
Kweku Adamseffeb2a2020-06-16 10:11:56 -0700449 // Offload to handler thread to avoid boot time impact.
450 mHandler.post(mInjector::updatePowerWhitelistCache);
451
Michael Wachenschwanzd1d8aa62019-02-28 16:38:37 -0800452 boolean userFileExists;
453 synchronized (mAppIdleLock) {
454 userFileExists = mAppIdleHistory.userFileExists(UserHandle.USER_SYSTEM);
455 }
456
Kweku Adams8604fd72020-06-02 14:04:27 -0700457 // Offload to handler thread to avoid boottime impact.
458 mHandler.post(this::loadHeadlessSystemAppCache);
Kweku Adamsc86da5f2020-05-28 12:26:35 -0700459
Michael Wachenschwanzd1d8aa62019-02-28 16:38:37 -0800460 if (mPendingInitializeDefaults || !userFileExists) {
Amith Yamasani777b1532018-01-28 23:20:07 +0000461 initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
462 }
463
Amith Yamasani7e528f32017-10-05 16:06:16 -0700464 if (mPendingOneTimeCheckIdleStates) {
465 postOneTimeCheckIdleStates();
466 }
Kweku Adamsf35ed3702020-02-11 17:32:54 -0800467 } else if (phase == PHASE_BOOT_COMPLETED) {
468 setChargingState(mInjector.isCharging());
Amith Yamasani7e528f32017-10-05 16:06:16 -0700469 }
470 }
471
Makoto Onukia72e04f2019-10-03 11:10:45 -0700472 private void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
Amith Yamasani172612c2017-12-15 10:51:53 -0800473 if (!mAppIdleEnabled) return;
474
Amith Yamasani7e528f32017-10-05 16:06:16 -0700475 // Get sync adapters for the authority
476 String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
477 authority, userId);
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800478 final long elapsedRealtime = mInjector.elapsedRealtime();
Amith Yamasani7e528f32017-10-05 16:06:16 -0700479 for (String packageName: packages) {
480 // Only force the sync adapters to active if the provider is not in the same package and
481 // the sync adapter is a system package.
482 try {
483 PackageInfo pi = mPackageManager.getPackageInfoAsUser(
484 packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
485 if (pi == null || pi.applicationInfo == null) {
486 continue;
487 }
488 if (!packageName.equals(providerPkgName)) {
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800489 final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName,
490 userId);
Amith Yamasani803eab692017-11-09 17:47:04 -0800491 synchronized (mAppIdleLock) {
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800492 reportNoninteractiveUsageCrossUserLocked(packageName, userId,
493 STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
494 elapsedRealtime, mSyncAdapterTimeoutMillis, linkedProfiles);
Amith Yamasani803eab692017-11-09 17:47:04 -0800495 }
Amith Yamasani7e528f32017-10-05 16:06:16 -0700496 }
497 } catch (PackageManager.NameNotFoundException e) {
498 // Shouldn't happen
499 }
500 }
501 }
502
Makoto Onukia72e04f2019-10-03 11:10:45 -0700503 private void reportExemptedSyncScheduled(String packageName, int userId) {
Makoto Onukid5f25d22018-05-22 16:02:17 -0700504 if (!mAppIdleEnabled) return;
505
506 final int bucketToPromote;
507 final int usageReason;
508 final long durationMillis;
509
510 if (!mInjector.isDeviceIdleMode()) {
511 // Not dozing.
512 bucketToPromote = STANDBY_BUCKET_ACTIVE;
513 usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
514 durationMillis = mExemptedSyncScheduledNonDozeTimeoutMillis;
515 } else {
516 // Dozing.
517 bucketToPromote = STANDBY_BUCKET_WORKING_SET;
518 usageReason = REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
519 durationMillis = mExemptedSyncScheduledDozeTimeoutMillis;
520 }
521
522 final long elapsedRealtime = mInjector.elapsedRealtime();
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800523 final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
Makoto Onukid5f25d22018-05-22 16:02:17 -0700524 synchronized (mAppIdleLock) {
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800525 reportNoninteractiveUsageCrossUserLocked(packageName, userId, bucketToPromote,
526 usageReason, elapsedRealtime, durationMillis, linkedProfiles);
Makoto Onukid5f25d22018-05-22 16:02:17 -0700527 }
528 }
529
Makoto Onukia72e04f2019-10-03 11:10:45 -0700530 private void reportUnexemptedSyncScheduled(String packageName, int userId) {
Michael Wachenschwanzc3295202019-02-20 17:19:52 -0800531 if (!mAppIdleEnabled) return;
532
533 final long elapsedRealtime = mInjector.elapsedRealtime();
534 synchronized (mAppIdleLock) {
535 final int currentBucket =
536 mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
537 if (currentBucket == STANDBY_BUCKET_NEVER) {
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800538 final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
Michael Wachenschwanzc3295202019-02-20 17:19:52 -0800539 // Bring the app out of the never bucket
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800540 reportNoninteractiveUsageCrossUserLocked(packageName, userId,
541 STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED,
542 elapsedRealtime, mUnexemptedSyncScheduledTimeoutMillis, linkedProfiles);
Michael Wachenschwanzc3295202019-02-20 17:19:52 -0800543 }
544 }
545 }
546
Makoto Onukia72e04f2019-10-03 11:10:45 -0700547 private void reportExemptedSyncStart(String packageName, int userId) {
Makoto Onuki75ad2492018-03-28 14:42:42 -0700548 if (!mAppIdleEnabled) return;
549
550 final long elapsedRealtime = mInjector.elapsedRealtime();
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800551 final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
Makoto Onuki75ad2492018-03-28 14:42:42 -0700552 synchronized (mAppIdleLock) {
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800553 reportNoninteractiveUsageCrossUserLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800554 REASON_SUB_USAGE_EXEMPTED_SYNC_START, elapsedRealtime,
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800555 mExemptedSyncStartTimeoutMillis, linkedProfiles);
Makoto Onuki75ad2492018-03-28 14:42:42 -0700556 }
557 }
558
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800559 /**
560 * Helper method to report indirect user usage of an app and handle reporting the usage
561 * against cross profile connected apps. <br>
562 * Use {@link #reportNoninteractiveUsageLocked(String, int, int, int, long, long)} if
563 * cross profile connected apps do not need to be handled.
564 */
565 private void reportNoninteractiveUsageCrossUserLocked(String packageName, int userId,
566 int bucket, int subReason, long elapsedRealtime, long nextCheckDelay,
567 List<UserHandle> otherProfiles) {
568 reportNoninteractiveUsageLocked(packageName, userId, bucket, subReason, elapsedRealtime,
569 nextCheckDelay);
570 final int size = otherProfiles.size();
571 for (int profileIndex = 0; profileIndex < size; profileIndex++) {
572 final int otherUserId = otherProfiles.get(profileIndex).getIdentifier();
573 reportNoninteractiveUsageLocked(packageName, otherUserId, bucket, subReason,
574 elapsedRealtime, nextCheckDelay);
575 }
576 }
577
578 /**
579 * Helper method to report indirect user usage of an app. <br>
580 * Use
581 * {@link #reportNoninteractiveUsageCrossUserLocked(String, int, int, int, long, long, List)}
582 * if cross profile connected apps need to be handled.
583 */
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800584 private void reportNoninteractiveUsageLocked(String packageName, int userId, int bucket,
585 int subReason, long elapsedRealtime, long nextCheckDelay) {
586 final AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, bucket,
587 subReason, 0, elapsedRealtime + nextCheckDelay);
588 mHandler.sendMessageDelayed(
589 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
590 nextCheckDelay);
591 maybeInformListeners(packageName, userId, elapsedRealtime, appUsage.currentBucket,
592 appUsage.bucketingReason, false);
593 }
594
Kweku Adamsf35ed3702020-02-11 17:32:54 -0800595 @VisibleForTesting
596 void setChargingState(boolean isCharging) {
597 synchronized (mAppIdleLock) {
598 if (mIsCharging != isCharging) {
599 if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging);
600 mIsCharging = isCharging;
Kweku Adamse6601762020-05-01 15:06:18 -0700601 postParoleStateChanged();
Kweku Adamsf35ed3702020-02-11 17:32:54 -0800602 }
603 }
604 }
605
Makoto Onukia72e04f2019-10-03 11:10:45 -0700606 @Override
Kweku Adamse6601762020-05-01 15:06:18 -0700607 public boolean isInParole() {
608 return !mAppIdleEnabled || mIsCharging;
609 }
610
611 private void postParoleStateChanged() {
612 if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
613 mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
614 mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
615 }
616
617 @Override
Makoto Onukia72e04f2019-10-03 11:10:45 -0700618 public void postCheckIdleStates(int userId) {
Amith Yamasani7e528f32017-10-05 16:06:16 -0700619 mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
620 }
621
Makoto Onukia72e04f2019-10-03 11:10:45 -0700622 @Override
623 public void postOneTimeCheckIdleStates() {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700624 if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) {
Amith Yamasani7e528f32017-10-05 16:06:16 -0700625 // Not booted yet; wait for it!
626 mPendingOneTimeCheckIdleStates = true;
627 } else {
628 mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
629 mPendingOneTimeCheckIdleStates = false;
630 }
631 }
632
Makoto Onukia72e04f2019-10-03 11:10:45 -0700633 @VisibleForTesting
Amith Yamasani7e528f32017-10-05 16:06:16 -0700634 boolean checkIdleStates(int checkUserId) {
635 if (!mAppIdleEnabled) {
636 return false;
637 }
638
639 final int[] runningUserIds;
640 try {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700641 runningUserIds = mInjector.getRunningUserIds();
Amith Yamasani7e528f32017-10-05 16:06:16 -0700642 if (checkUserId != UserHandle.USER_ALL
643 && !ArrayUtils.contains(runningUserIds, checkUserId)) {
644 return false;
645 }
646 } catch (RemoteException re) {
647 throw re.rethrowFromSystemServer();
648 }
649
Amith Yamasani17fffee2017-09-29 13:17:43 -0700650 final long elapsedRealtime = mInjector.elapsedRealtime();
Amith Yamasani7e528f32017-10-05 16:06:16 -0700651 for (int i = 0; i < runningUserIds.length; i++) {
652 final int userId = runningUserIds[i];
653 if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
654 continue;
655 }
656 if (DEBUG) {
657 Slog.d(TAG, "Checking idle state for user " + userId);
658 }
659 List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
660 PackageManager.MATCH_DISABLED_COMPONENTS,
661 userId);
662 final int packageCount = packages.size();
663 for (int p = 0; p < packageCount; p++) {
664 final PackageInfo pi = packages.get(p);
665 final String packageName = pi.packageName;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800666 checkAndUpdateStandbyState(packageName, userId, pi.applicationInfo.uid,
667 elapsedRealtime);
Amith Yamasani7e528f32017-10-05 16:06:16 -0700668 }
669 }
670 if (DEBUG) {
671 Slog.d(TAG, "checkIdleStates took "
Amith Yamasani17fffee2017-09-29 13:17:43 -0700672 + (mInjector.elapsedRealtime() - elapsedRealtime));
Amith Yamasani7e528f32017-10-05 16:06:16 -0700673 }
674 return true;
675 }
676
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800677 /** Check if we need to update the standby state of a specific app. */
678 private void checkAndUpdateStandbyState(String packageName, @UserIdInt int userId,
679 int uid, long elapsedRealtime) {
680 if (uid <= 0) {
681 try {
682 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
683 } catch (PackageManager.NameNotFoundException e) {
684 // Not a valid package for this user, nothing to do
685 // TODO: Remove any history of removed packages
686 return;
687 }
688 }
Kweku Adams95cd9522020-05-08 09:56:53 -0700689 final int minBucket = getAppMinBucket(packageName,
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800690 UserHandle.getAppId(uid),
691 userId);
692 if (DEBUG) {
Kweku Adams95cd9522020-05-08 09:56:53 -0700693 Slog.d(TAG, " Checking idle state for " + packageName
694 + " minBucket=" + minBucket);
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800695 }
Kweku Adams95cd9522020-05-08 09:56:53 -0700696 if (minBucket <= STANDBY_BUCKET_ACTIVE) {
697 // No extra processing needed for ACTIVE or higher since apps can't drop into lower
698 // buckets.
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800699 synchronized (mAppIdleLock) {
700 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
Kweku Adams95cd9522020-05-08 09:56:53 -0700701 minBucket, REASON_MAIN_DEFAULT);
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800702 }
703 maybeInformListeners(packageName, userId, elapsedRealtime,
Kweku Adams95cd9522020-05-08 09:56:53 -0700704 minBucket, REASON_MAIN_DEFAULT, false);
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800705 } else {
706 synchronized (mAppIdleLock) {
707 final AppIdleHistory.AppUsageHistory app =
708 mAppIdleHistory.getAppUsageHistory(packageName,
709 userId, elapsedRealtime);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800710 int reason = app.bucketingReason;
711 final int oldMainReason = reason & REASON_MAIN_MASK;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800712
713 // If the bucket was forced by the user/developer, leave it alone.
714 // A usage event will be the only way to bring it out of this forced state
Kweku Adamsc182d5e2020-01-08 18:37:26 -0800715 if (oldMainReason == REASON_MAIN_FORCED_BY_USER) {
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800716 return;
717 }
718 final int oldBucket = app.currentBucket;
Kweku Adams2d79ce52020-05-05 12:31:35 -0700719 if (oldBucket == STANDBY_BUCKET_NEVER) {
720 // None of this should bring an app out of the NEVER bucket.
721 return;
722 }
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800723 int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
Amith Yamasani3154dcf2018-03-27 18:24:04 -0700724 boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800725 // Compute age-based bucket
Amith Yamasani119be9a2018-02-18 22:23:00 -0800726 if (oldMainReason == REASON_MAIN_DEFAULT
727 || oldMainReason == REASON_MAIN_USAGE
728 || oldMainReason == REASON_MAIN_TIMEOUT
Amith Yamasani3154dcf2018-03-27 18:24:04 -0700729 || predictionLate) {
730
731 if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
732 && app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
733 newBucket = app.lastPredictedBucket;
734 reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
735 if (DEBUG) {
736 Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
737 }
738 } else {
739 newBucket = getBucketForLocked(packageName, userId,
740 elapsedRealtime);
741 if (DEBUG) {
742 Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
743 }
744 reason = REASON_MAIN_TIMEOUT;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800745 }
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800746 }
Amith Yamasani3154dcf2018-03-27 18:24:04 -0700747
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800748 // Check if the app is within one of the timeouts for forced bucket elevation
749 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
750 if (newBucket >= STANDBY_BUCKET_ACTIVE
751 && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
752 newBucket = STANDBY_BUCKET_ACTIVE;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800753 reason = app.bucketingReason;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800754 if (DEBUG) {
755 Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
756 }
757 } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
758 && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
759 newBucket = STANDBY_BUCKET_WORKING_SET;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800760 // If it was already there, keep the reason, else assume timeout to WS
761 reason = (newBucket == oldBucket)
762 ? app.bucketingReason
763 : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800764 if (DEBUG) {
765 Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
766 }
767 }
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800768
769 if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
770 && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
Kweku Adams109cd9c2020-02-11 11:10:52 -0800771 >= mInjector.getAutoRestrictedBucketDelayMs()) {
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800772 newBucket = STANDBY_BUCKET_RESTRICTED;
773 reason = app.lastRestrictReason;
774 if (DEBUG) {
775 Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
776 }
777 }
Kweku Adams2d79ce52020-05-05 12:31:35 -0700778 if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
779 newBucket = STANDBY_BUCKET_RARE;
780 // Leave the reason alone.
781 if (DEBUG) {
782 Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch");
783 }
784 }
Kweku Adams95cd9522020-05-08 09:56:53 -0700785 if (newBucket > minBucket) {
786 newBucket = minBucket;
787 // Leave the reason alone.
788 if (DEBUG) {
789 Slog.d(TAG, "Bringing up from " + newBucket + " to " + minBucket
790 + " due to min bucketing");
791 }
792 }
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800793 if (DEBUG) {
794 Slog.d(TAG, " Old bucket=" + oldBucket
795 + ", newBucket=" + newBucket);
796 }
Kweku Adams2d79ce52020-05-05 12:31:35 -0700797 if (oldBucket != newBucket || predictionLate) {
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800798 mAppIdleHistory.setAppStandbyBucket(packageName, userId,
799 elapsedRealtime, newBucket, reason);
800 maybeInformListeners(packageName, userId, elapsedRealtime,
Amith Yamasani119be9a2018-02-18 22:23:00 -0800801 newBucket, reason, false);
Amith Yamasanibbbad9c2018-02-10 16:46:38 -0800802 }
803 }
804 }
805 }
806
Amith Yamasani119be9a2018-02-18 22:23:00 -0800807 /** Returns true if there hasn't been a prediction for the app in a while. */
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800808 private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
Amith Yamasani3154dcf2018-03-27 18:24:04 -0700809 return app.lastPredictedTime > 0
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800810 && mAppIdleHistory.getElapsedTime(elapsedRealtime)
Amith Yamasani119be9a2018-02-18 22:23:00 -0800811 - app.lastPredictedTime > mPredictionTimeoutMillis;
Amith Yamasanibd7b3022017-12-06 17:40:25 -0800812 }
813
Amith Yamasani119be9a2018-02-18 22:23:00 -0800814 /** Inform listeners if the bucket has changed since it was last reported to listeners */
Amith Yamasani17fffee2017-09-29 13:17:43 -0700815 private void maybeInformListeners(String packageName, int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -0800816 long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700817 synchronized (mAppIdleLock) {
818 if (mAppIdleHistory.shouldInformListeners(packageName, userId,
Amith Yamasani84cd7b72017-11-07 13:59:37 -0800819 elapsedRealtime, bucket)) {
Amith Yamasani119be9a2018-02-18 22:23:00 -0800820 final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
821 bucket, reason, userStartedInteracting);
Amith Yamasani777b1532018-01-28 23:20:07 +0000822 if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
Amith Yamasani119be9a2018-02-18 22:23:00 -0800823 mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
Amith Yamasani17fffee2017-09-29 13:17:43 -0700824 }
825 }
826 }
827
Amith Yamasani119be9a2018-02-18 22:23:00 -0800828 /**
829 * Evaluates next bucket based on time since last used and the bucketing thresholds.
830 * @param packageName the app
831 * @param userId the user
832 * @param elapsedRealtime as the name suggests, current elapsed time
833 * @return the bucket for the app, based on time since last used
834 */
Andreas Gampebbab23f2018-02-07 15:34:27 -0800835 @GuardedBy("mAppIdleLock")
Makoto Onukia72e04f2019-10-03 11:10:45 -0700836 @StandbyBuckets
837 private int getBucketForLocked(String packageName, int userId,
Amith Yamasani17fffee2017-09-29 13:17:43 -0700838 long elapsedRealtime) {
839 int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
840 elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
841 return THRESHOLD_BUCKETS[bucketIndex];
842 }
843
Amith Yamasani7e528f32017-10-05 16:06:16 -0700844 private void notifyBatteryStats(String packageName, int userId, boolean idle) {
845 try {
846 final int uid = mPackageManager.getPackageUidAsUser(packageName,
847 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
848 if (idle) {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700849 mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
Amith Yamasani7e528f32017-10-05 16:06:16 -0700850 packageName, uid);
851 } else {
Amith Yamasani17fffee2017-09-29 13:17:43 -0700852 mInjector.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
Amith Yamasani7e528f32017-10-05 16:06:16 -0700853 packageName, uid);
854 }
855 } catch (PackageManager.NameNotFoundException | RemoteException e) {
856 }
857 }
858
Makoto Onukia72e04f2019-10-03 11:10:45 -0700859 @Override
860 public void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
Amith Yamasani172612c2017-12-15 10:51:53 -0800861 if (!mAppIdleEnabled) return;
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800862 final int eventType = event.getEventType();
863 if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED
864 || eventType == UsageEvents.Event.ACTIVITY_PAUSED
865 || eventType == UsageEvents.Event.SYSTEM_INTERACTION
866 || eventType == UsageEvents.Event.USER_INTERACTION
867 || eventType == UsageEvents.Event.NOTIFICATION_SEEN
868 || eventType == UsageEvents.Event.SLICE_PINNED
869 || eventType == UsageEvents.Event.SLICE_PINNED_PRIV
870 || eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800871 final String pkg = event.getPackageName();
872 final List<UserHandle> linkedProfiles = getCrossProfileTargets(pkg, userId);
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800873 synchronized (mAppIdleLock) {
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800874 reportEventLocked(pkg, eventType, elapsedRealtime, userId);
875
876 final int size = linkedProfiles.size();
877 for (int profileIndex = 0; profileIndex < size; profileIndex++) {
878 final int linkedUserId = linkedProfiles.get(profileIndex).getIdentifier();
879 reportEventLocked(pkg, eventType, elapsedRealtime, linkedUserId);
880 }
Amith Yamasani7e528f32017-10-05 16:06:16 -0700881 }
882 }
Amith Yamasani7e528f32017-10-05 16:06:16 -0700883 }
884
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800885 private void reportEventLocked(String pkg, int eventType, long elapsedRealtime, int userId) {
886 // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
887 // about apps that are on some kind of whitelist anyway.
888 final boolean previouslyIdle = mAppIdleHistory.isIdle(
889 pkg, userId, elapsedRealtime);
890
891 final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
892 pkg, userId, elapsedRealtime);
893 final int prevBucket = appHistory.currentBucket;
894 final int prevBucketReason = appHistory.bucketingReason;
895 final long nextCheckDelay;
896 final int subReason = usageEventToSubReason(eventType);
897 final int reason = REASON_MAIN_USAGE | subReason;
898 if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
899 || eventType == UsageEvents.Event.SLICE_PINNED) {
900 // Mild usage elevates to WORKING_SET but doesn't change usage time.
Kweku Adams12752132020-02-18 15:36:48 -0800901 mAppIdleHistory.reportUsage(appHistory, pkg, userId,
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800902 STANDBY_BUCKET_WORKING_SET, subReason,
903 0, elapsedRealtime + mNotificationSeenTimeoutMillis);
904 nextCheckDelay = mNotificationSeenTimeoutMillis;
905 } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
Kweku Adams12752132020-02-18 15:36:48 -0800906 mAppIdleHistory.reportUsage(appHistory, pkg, userId,
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800907 STANDBY_BUCKET_ACTIVE, subReason,
908 0, elapsedRealtime + mSystemInteractionTimeoutMillis);
909 nextCheckDelay = mSystemInteractionTimeoutMillis;
910 } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
911 // Only elevate bucket if this is the first usage of the app
912 if (prevBucket != STANDBY_BUCKET_NEVER) return;
Kweku Adams12752132020-02-18 15:36:48 -0800913 mAppIdleHistory.reportUsage(appHistory, pkg, userId,
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800914 STANDBY_BUCKET_ACTIVE, subReason,
915 0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
916 nextCheckDelay = mInitialForegroundServiceStartTimeoutMillis;
917 } else {
Kweku Adams12752132020-02-18 15:36:48 -0800918 mAppIdleHistory.reportUsage(appHistory, pkg, userId,
Michael Wachenschwanz081ce5d2020-02-10 12:47:53 -0800919 STANDBY_BUCKET_ACTIVE, subReason,
920 elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
921 nextCheckDelay = mStrongUsageTimeoutMillis;
922 }
923 if (appHistory.currentBucket != prevBucket) {
924 mHandler.sendMessageDelayed(
925 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkg),
926 nextCheckDelay);
927 final boolean userStartedInteracting =
928 appHistory.currentBucket == STANDBY_BUCKET_ACTIVE
929 && (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
930 maybeInformListeners(pkg, userId, elapsedRealtime,
931 appHistory.currentBucket, reason, userStartedInteracting);
932 }
933
934 if (previouslyIdle) {
935 notifyBatteryStats(pkg, userId, false);
936 }
937 }
938
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -0800939 /**
940 * Note: don't call this with the lock held since it makes calls to other system services.
941 */
942 private @NonNull List<UserHandle> getCrossProfileTargets(String pkg, int userId) {
943 synchronized (mAppIdleLock) {
944 if (!mLinkCrossProfileApps) return Collections.emptyList();
945 }
946 return mInjector.getValidCrossProfileTargets(pkg, userId);
947 }
948
Amith Yamasani119be9a2018-02-18 22:23:00 -0800949 private int usageEventToSubReason(int eventType) {
950 switch (eventType) {
Hui Yu03d12402018-12-06 18:00:37 -0800951 case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
952 case UsageEvents.Event.ACTIVITY_PAUSED: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800953 case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
954 case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
955 case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
Amith Yamasani80c4be82018-03-26 10:54:04 -0700956 case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
957 case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
Michael Wachenschwanz6ced0ee2019-04-15 16:43:28 -0700958 case UsageEvents.Event.FOREGROUND_SERVICE_START:
959 return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
Amith Yamasani119be9a2018-02-18 22:23:00 -0800960 default: return 0;
961 }
962 }
963
Makoto Onukia72e04f2019-10-03 11:10:45 -0700964 @VisibleForTesting
Amith Yamasani7e528f32017-10-05 16:06:16 -0700965 void forceIdleState(String packageName, int userId, boolean idle) {
Amith Yamasani172612c2017-12-15 10:51:53 -0800966 if (!mAppIdleEnabled) return;
967
Amith Yamasani7e528f32017-10-05 16:06:16 -0700968 final int appId = getAppId(packageName);
969 if (appId < 0) return;
Amith Yamasani17fffee2017-09-29 13:17:43 -0700970 final long elapsedRealtime = mInjector.elapsedRealtime();
Amith Yamasani7e528f32017-10-05 16:06:16 -0700971
972 final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
973 userId, elapsedRealtime);
Christopher Tatea732f012017-10-26 17:26:53 -0700974 final int standbyBucket;
Amith Yamasani7e528f32017-10-05 16:06:16 -0700975 synchronized (mAppIdleLock) {
Christopher Tatea732f012017-10-26 17:26:53 -0700976 standbyBucket = mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
Amith Yamasani7e528f32017-10-05 16:06:16 -0700977 }
978 final boolean stillIdle = isAppIdleFiltered(packageName, appId,
979 userId, elapsedRealtime);
980 // Inform listeners if necessary
981 if (previouslyIdle != stillIdle) {
Amith Yamasani119be9a2018-02-18 22:23:00 -0800982 maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
Kweku Adamsc182d5e2020-01-08 18:37:26 -0800983 REASON_MAIN_FORCED_BY_USER, false);
Amith Yamasani7e528f32017-10-05 16:06:16 -0700984 if (!stillIdle) {
985 notifyBatteryStats(packageName, userId, idle);
986 }
987 }
988 }
989
Makoto Onukia72e04f2019-10-03 11:10:45 -0700990 @Override
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800991 public void setLastJobRunTime(String packageName, int userId, long elapsedRealtime) {
992 synchronized (mAppIdleLock) {
993 mAppIdleHistory.setLastJobRunTime(packageName, userId, elapsedRealtime);
994 }
995 }
996
Makoto Onukia72e04f2019-10-03 11:10:45 -0700997 @Override
Amith Yamasani53f06ea2018-01-05 17:53:46 -0800998 public long getTimeSinceLastJobRun(String packageName, int userId) {
999 final long elapsedRealtime = mInjector.elapsedRealtime();
1000 synchronized (mAppIdleLock) {
1001 return mAppIdleHistory.getTimeSinceLastJobRun(packageName, userId, elapsedRealtime);
1002 }
1003 }
1004
Makoto Onukia72e04f2019-10-03 11:10:45 -07001005 @Override
Amith Yamasani7e528f32017-10-05 16:06:16 -07001006 public void onUserRemoved(int userId) {
1007 synchronized (mAppIdleLock) {
1008 mAppIdleHistory.onUserRemoved(userId);
Sudheer Shanka101c3532018-01-08 16:28:42 -08001009 synchronized (mActiveAdminApps) {
1010 mActiveAdminApps.remove(userId);
1011 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07001012 }
1013 }
1014
1015 private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
1016 synchronized (mAppIdleLock) {
1017 return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
1018 }
1019 }
1020
Makoto Onukia72e04f2019-10-03 11:10:45 -07001021 @Override
1022 public void addListener(AppIdleStateChangeListener listener) {
Amith Yamasani93885192017-12-13 11:52:10 -08001023 synchronized (mPackageAccessListeners) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001024 if (!mPackageAccessListeners.contains(listener)) {
1025 mPackageAccessListeners.add(listener);
1026 }
1027 }
1028 }
1029
Makoto Onukia72e04f2019-10-03 11:10:45 -07001030 @Override
1031 public void removeListener(AppIdleStateChangeListener listener) {
Amith Yamasani93885192017-12-13 11:52:10 -08001032 synchronized (mPackageAccessListeners) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001033 mPackageAccessListeners.remove(listener);
1034 }
1035 }
1036
Makoto Onukia72e04f2019-10-03 11:10:45 -07001037 @Override
1038 public int getAppId(String packageName) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001039 try {
1040 ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
1041 PackageManager.MATCH_ANY_USER
1042 | PackageManager.MATCH_DISABLED_COMPONENTS);
1043 return ai.uid;
1044 } catch (PackageManager.NameNotFoundException re) {
1045 return -1;
1046 }
1047 }
1048
Makoto Onukia72e04f2019-10-03 11:10:45 -07001049 @Override
Kweku Adamsdf33ae12019-10-08 11:51:41 -07001050 public boolean isAppIdleFiltered(String packageName, int userId, long elapsedRealtime,
Amith Yamasani7e528f32017-10-05 16:06:16 -07001051 boolean shouldObfuscateInstantApps) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001052 if (shouldObfuscateInstantApps &&
Amith Yamasani17fffee2017-09-29 13:17:43 -07001053 mInjector.isPackageEphemeral(userId, packageName)) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001054 return false;
1055 }
1056 return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
1057 }
1058
Kweku Adams95cd9522020-05-08 09:56:53 -07001059 @StandbyBuckets
1060 private int getAppMinBucket(String packageName, int userId) {
1061 try {
1062 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
1063 return getAppMinBucket(packageName, UserHandle.getAppId(uid), userId);
1064 } catch (PackageManager.NameNotFoundException e) {
1065 // Not a valid package for this user, nothing to do
1066 return STANDBY_BUCKET_NEVER;
1067 }
1068 }
1069
1070 /**
1071 * Return the lowest bucket this app should ever enter.
1072 */
1073 @StandbyBuckets
1074 private int getAppMinBucket(String packageName, int appId, int userId) {
1075 if (packageName == null) return STANDBY_BUCKET_NEVER;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001076 // If not enabled at all, of course nobody is ever idle.
1077 if (!mAppIdleEnabled) {
Kweku Adams95cd9522020-05-08 09:56:53 -07001078 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001079 }
1080 if (appId < Process.FIRST_APPLICATION_UID) {
1081 // System uids never go idle.
Kweku Adams95cd9522020-05-08 09:56:53 -07001082 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001083 }
1084 if (packageName.equals("android")) {
1085 // Nor does the framework (which should be redundant with the above, but for MR1 we will
1086 // retain this for safety).
Kweku Adams95cd9522020-05-08 09:56:53 -07001087 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001088 }
1089 if (mSystemServicesReady) {
Kweku Adamseffeb2a2020-06-16 10:11:56 -07001090 // We allow all whitelisted apps, including those that don't want to be whitelisted
1091 // for idle mode, because app idle (aka app standby) is really not as big an issue
1092 // for controlling who participates vs. doze mode.
1093 if (mInjector.isNonIdleWhitelisted(packageName)) {
1094 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001095 }
1096
1097 if (isActiveDeviceAdmin(packageName, userId)) {
Kweku Adams95cd9522020-05-08 09:56:53 -07001098 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001099 }
1100
1101 if (isActiveNetworkScorer(packageName)) {
Kweku Adams95cd9522020-05-08 09:56:53 -07001102 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001103 }
1104
1105 if (mAppWidgetManager != null
1106 && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) {
Kweku Adams107be822020-05-14 08:02:45 -07001107 return STANDBY_BUCKET_ACTIVE;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001108 }
1109
1110 if (isDeviceProvisioningPackage(packageName)) {
Kweku Adams95cd9522020-05-08 09:56:53 -07001111 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001112 }
1113 }
1114
1115 // Check this last, as it can be the most expensive check
1116 if (isCarrierApp(packageName)) {
Kweku Adams95cd9522020-05-08 09:56:53 -07001117 return STANDBY_BUCKET_EXEMPTED;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001118 }
1119
Kweku Adams95cd9522020-05-08 09:56:53 -07001120 if (isHeadlessSystemApp(packageName)) {
1121 return STANDBY_BUCKET_ACTIVE;
1122 }
1123
1124 return STANDBY_BUCKET_NEVER;
1125 }
1126
1127 private boolean isHeadlessSystemApp(String packageName) {
Kweku Adams8604fd72020-06-02 14:04:27 -07001128 synchronized (mHeadlessSystemApps) {
Kweku Adamsf197eef2020-06-16 11:59:37 -07001129 return mHeadlessSystemApps.contains(packageName);
Kweku Adams8604fd72020-06-02 14:04:27 -07001130 }
Amith Yamasani17fffee2017-09-29 13:17:43 -07001131 }
1132
Makoto Onukia72e04f2019-10-03 11:10:45 -07001133 @Override
1134 public boolean isAppIdleFiltered(String packageName, int appId, int userId,
Amith Yamasani7e528f32017-10-05 16:06:16 -07001135 long elapsedRealtime) {
Kweku Adams95cd9522020-05-08 09:56:53 -07001136 if (getAppMinBucket(packageName, appId, userId) < AppIdleHistory.IDLE_BUCKET_CUTOFF) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001137 return false;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001138 } else {
Kweku Adamsf35ed3702020-02-11 17:32:54 -08001139 synchronized (mAppIdleLock) {
1140 if (!mAppIdleEnabled || mIsCharging) {
1141 return false;
1142 }
1143 }
Amith Yamasani17fffee2017-09-29 13:17:43 -07001144 return isAppIdleUnfiltered(packageName, userId, elapsedRealtime);
Amith Yamasani7e528f32017-10-05 16:06:16 -07001145 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07001146 }
1147
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001148 static boolean isUserUsage(int reason) {
1149 if ((reason & REASON_MAIN_MASK) == REASON_MAIN_USAGE) {
1150 final int subReason = reason & REASON_SUB_MASK;
1151 return subReason == REASON_SUB_USAGE_USER_INTERACTION
1152 || subReason == REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
1153 }
1154 return false;
1155 }
1156
Makoto Onukia72e04f2019-10-03 11:10:45 -07001157 @Override
1158 public int[] getIdleUidsForUser(int userId) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001159 if (!mAppIdleEnabled) {
1160 return new int[0];
1161 }
1162
Amith Yamasani17fffee2017-09-29 13:17:43 -07001163 final long elapsedRealtime = mInjector.elapsedRealtime();
Amith Yamasani7e528f32017-10-05 16:06:16 -07001164
1165 List<ApplicationInfo> apps;
1166 try {
1167 ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
1168 .getInstalledApplications(/* flags= */ 0, userId);
1169 if (slice == null) {
1170 return new int[0];
1171 }
1172 apps = slice.getList();
1173 } catch (RemoteException e) {
1174 throw e.rethrowFromSystemServer();
1175 }
1176
1177 // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
1178 // associated with that uid, upper 16 bits is the number of those apps that is idle.
1179 SparseIntArray uidStates = new SparseIntArray();
1180
1181 // Now resolve all app state. Iterating over all apps, keeping track of how many
1182 // we find for each uid and how many of those are idle.
1183 for (int i = apps.size() - 1; i >= 0; i--) {
1184 ApplicationInfo ai = apps.get(i);
1185
1186 // Check whether this app is idle.
1187 boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
1188 userId, elapsedRealtime);
1189
1190 int index = uidStates.indexOfKey(ai.uid);
1191 if (index < 0) {
1192 uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
1193 } else {
1194 int value = uidStates.valueAt(index);
1195 uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
1196 }
1197 }
1198 if (DEBUG) {
Amith Yamasani17fffee2017-09-29 13:17:43 -07001199 Slog.d(TAG, "getIdleUids took " + (mInjector.elapsedRealtime() - elapsedRealtime));
Amith Yamasani7e528f32017-10-05 16:06:16 -07001200 }
1201 int numIdle = 0;
1202 for (int i = uidStates.size() - 1; i >= 0; i--) {
1203 int value = uidStates.valueAt(i);
1204 if ((value&0x7fff) == (value>>16)) {
1205 numIdle++;
1206 }
1207 }
1208
1209 int[] res = new int[numIdle];
1210 numIdle = 0;
1211 for (int i = uidStates.size() - 1; i >= 0; i--) {
1212 int value = uidStates.valueAt(i);
1213 if ((value&0x7fff) == (value>>16)) {
1214 res[numIdle] = uidStates.keyAt(i);
1215 numIdle++;
1216 }
1217 }
1218
1219 return res;
1220 }
1221
Makoto Onukia72e04f2019-10-03 11:10:45 -07001222 @Override
1223 public void setAppIdleAsync(String packageName, boolean idle, int userId) {
Amith Yamasani172612c2017-12-15 10:51:53 -08001224 if (packageName == null || !mAppIdleEnabled) return;
Amith Yamasani7e528f32017-10-05 16:06:16 -07001225
1226 mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
1227 .sendToTarget();
1228 }
1229
Makoto Onukia72e04f2019-10-03 11:10:45 -07001230 @Override
Christopher Tatea732f012017-10-26 17:26:53 -07001231 @StandbyBuckets public int getAppStandbyBucket(String packageName, int userId,
Amith Yamasani17fffee2017-09-29 13:17:43 -07001232 long elapsedRealtime, boolean shouldObfuscateInstantApps) {
Amith Yamasani172612c2017-12-15 10:51:53 -08001233 if (!mAppIdleEnabled || (shouldObfuscateInstantApps
1234 && mInjector.isPackageEphemeral(userId, packageName))) {
Amith Yamasaniafbccb72017-11-27 10:44:24 -08001235 return STANDBY_BUCKET_ACTIVE;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001236 }
1237
Amith Yamasanie8789312017-12-10 14:34:26 -08001238 synchronized (mAppIdleLock) {
1239 return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
1240 }
1241 }
1242
Kweku Adamsaa461942020-03-16 11:59:05 -07001243 @VisibleForTesting
1244 int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) {
1245 synchronized (mAppIdleLock) {
1246 return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime);
1247 }
1248 }
1249
Makoto Onukia72e04f2019-10-03 11:10:45 -07001250 @Override
Suprabh Shukla868bde22018-02-20 20:59:52 -08001251 public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
Amith Yamasanie8789312017-12-10 14:34:26 -08001252 synchronized (mAppIdleLock) {
Suprabh Shukla868bde22018-02-20 20:59:52 -08001253 return mAppIdleHistory.getAppStandbyBuckets(userId, mAppIdleEnabled);
Amith Yamasanie8789312017-12-10 14:34:26 -08001254 }
Amith Yamasani17fffee2017-09-29 13:17:43 -07001255 }
1256
Kweku Adamsc4ee9982020-01-08 11:14:39 -08001257 @Override
Kweku Adamsaa461942020-03-16 11:59:05 -07001258 public void restrictApp(@NonNull String packageName, int userId,
1259 @SystemForcedReasons int restrictReason) {
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001260 // If the package is not installed, don't allow the bucket to be set.
1261 if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
1262 Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
1263 return;
1264 }
1265
1266 final int reason = REASON_MAIN_FORCED_BY_SYSTEM | (REASON_SUB_MASK & restrictReason);
1267 final long nowElapsed = mInjector.elapsedRealtime();
Kweku Adams2d79ce52020-05-05 12:31:35 -07001268 final int bucket = mAllowRestrictedBucket ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE;
1269 setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false);
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001270 }
1271
1272 @Override
Kweku Adamsc4ee9982020-01-08 11:14:39 -08001273 public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
1274 int callingUid, int callingPid) {
1275 setAppStandbyBuckets(
1276 Collections.singletonList(new AppStandbyInfo(packageName, bucket)),
1277 userId, callingUid, callingPid);
Makoto Onukia0058b42018-05-22 16:32:23 -07001278 }
1279
Makoto Onukia72e04f2019-10-03 11:10:45 -07001280 @Override
Kweku Adamsc4ee9982020-01-08 11:14:39 -08001281 public void setAppStandbyBuckets(@NonNull List<AppStandbyInfo> appBuckets, int userId,
1282 int callingUid, int callingPid) {
1283 userId = ActivityManager.handleIncomingUser(
1284 callingPid, callingUid, userId, false, true, "setAppStandbyBucket", null);
1285 final boolean shellCaller = callingUid == Process.ROOT_UID
1286 || callingUid == Process.SHELL_UID;
Kweku Adamsc182d5e2020-01-08 18:37:26 -08001287 final int reason;
1288 // The Settings app runs in the system UID but in a separate process. Assume
1289 // things coming from other processes are due to the user.
1290 if ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) && callingPid != Process.myPid())
1291 || shellCaller) {
1292 reason = REASON_MAIN_FORCED_BY_USER;
1293 } else if (UserHandle.isCore(callingUid)) {
1294 reason = REASON_MAIN_FORCED_BY_SYSTEM;
1295 } else {
1296 reason = REASON_MAIN_PREDICTED;
1297 }
Kweku Adamsc4ee9982020-01-08 11:14:39 -08001298 final int packageFlags = PackageManager.MATCH_ANY_USER
1299 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
1300 | PackageManager.MATCH_DIRECT_BOOT_AWARE;
1301 final int numApps = appBuckets.size();
1302 final long elapsedRealtime = mInjector.elapsedRealtime();
1303 for (int i = 0; i < numApps; ++i) {
1304 final AppStandbyInfo bucketInfo = appBuckets.get(i);
1305 final String packageName = bucketInfo.mPackageName;
1306 final int bucket = bucketInfo.mStandbyBucket;
1307 if (bucket < STANDBY_BUCKET_ACTIVE || bucket > STANDBY_BUCKET_NEVER) {
1308 throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
1309 }
1310 final int packageUid = mInjector.getPackageManagerInternal()
1311 .getPackageUid(packageName, packageFlags, userId);
1312 // Caller cannot set their own standby state
1313 if (packageUid == callingUid) {
1314 throw new IllegalArgumentException("Cannot set your own standby bucket");
1315 }
1316 if (packageUid < 0) {
1317 throw new IllegalArgumentException(
1318 "Cannot set standby bucket for non existent package (" + packageName + ")");
1319 }
1320 setAppStandbyBucket(packageName, userId, bucket, reason, elapsedRealtime, shellCaller);
1321 }
1322 }
1323
1324 @VisibleForTesting
1325 void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
1326 int reason) {
1327 setAppStandbyBucket(
1328 packageName, userId, newBucket, reason, mInjector.elapsedRealtime(), false);
1329 }
1330
1331 private void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
Makoto Onukia0058b42018-05-22 16:32:23 -07001332 int reason, long elapsedRealtime, boolean resetTimeout) {
Kweku Adams52b52a62020-05-26 12:21:16 -07001333 if (!mAppIdleEnabled) return;
1334
Amith Yamasanie8789312017-12-10 14:34:26 -08001335 synchronized (mAppIdleLock) {
Varun Shah7609b752018-10-15 15:07:47 -07001336 // If the package is not installed, don't allow the bucket to be set.
1337 if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001338 Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
Varun Shah7609b752018-10-15 15:07:47 -07001339 return;
1340 }
Kweku Adams2d79ce52020-05-05 12:31:35 -07001341 if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
1342 newBucket = STANDBY_BUCKET_RARE;
1343 }
Amith Yamasani93885192017-12-13 11:52:10 -08001344 AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
1345 userId, elapsedRealtime);
Amith Yamasani119be9a2018-02-18 22:23:00 -08001346 boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
Amith Yamasani53f06ea2018-01-05 17:53:46 -08001347
Amith Yamasani93885192017-12-13 11:52:10 -08001348 // Don't allow changing bucket if higher than ACTIVE
1349 if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
Amith Yamasani53f06ea2018-01-05 17:53:46 -08001350
Kweku Adams496594c2020-03-16 12:26:01 -07001351 // Don't allow prediction to change from/to NEVER.
1352 if ((app.currentBucket == STANDBY_BUCKET_NEVER || newBucket == STANDBY_BUCKET_NEVER)
Amith Yamasani93885192017-12-13 11:52:10 -08001353 && predicted) {
1354 return;
1355 }
Amith Yamasani53f06ea2018-01-05 17:53:46 -08001356
Kweku Adamsaa461942020-03-16 11:59:05 -07001357 final boolean wasForcedBySystem =
1358 (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
1359
Amith Yamasani93885192017-12-13 11:52:10 -08001360 // If the bucket was forced, don't allow prediction to override
Kweku Adamsc182d5e2020-01-08 18:37:26 -08001361 if (predicted
1362 && ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER
Kweku Adamsaa461942020-03-16 11:59:05 -07001363 || wasForcedBySystem)) {
1364 return;
1365 }
1366
1367 final boolean isForcedBySystem =
1368 (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
1369
1370 if (app.currentBucket == newBucket && wasForcedBySystem && isForcedBySystem) {
1371 mAppIdleHistory
1372 .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
1373 // Keep track of all restricting reasons
1374 reason = REASON_MAIN_FORCED_BY_SYSTEM
1375 | (app.bucketingReason & REASON_SUB_MASK)
1376 | (reason & REASON_SUB_MASK);
1377 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
1378 newBucket, reason, resetTimeout);
Kweku Adamsc182d5e2020-01-08 18:37:26 -08001379 return;
1380 }
Amith Yamasani53f06ea2018-01-05 17:53:46 -08001381
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001382 final boolean isForcedByUser =
1383 (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
1384
Kweku Adams496594c2020-03-16 12:26:01 -07001385 if (app.currentBucket == STANDBY_BUCKET_RESTRICTED) {
1386 if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_TIMEOUT) {
1387 if (predicted && newBucket >= STANDBY_BUCKET_RARE) {
1388 // Predicting into RARE or below means we don't expect the user to use the
1389 // app anytime soon, so don't elevate it from RESTRICTED.
1390 return;
1391 }
1392 } else if (!isUserUsage(reason) && !isForcedByUser) {
1393 // If the current bucket is RESTRICTED, only user force or usage should bring
1394 // it out, unless the app was put into the bucket due to timing out.
1395 return;
1396 }
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001397 }
1398
1399 if (newBucket == STANDBY_BUCKET_RESTRICTED) {
1400 mAppIdleHistory
1401 .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
1402
1403 if (isForcedByUser) {
1404 // Only user force can bypass the delay restriction. If the user forced the
1405 // app into the RESTRICTED bucket, then a toast confirming the action
1406 // shouldn't be surprising.
1407 if (Build.IS_DEBUGGABLE) {
1408 Toast.makeText(mContext,
1409 // Since AppStandbyController sits low in the lock hierarchy,
1410 // make sure not to call out with the lock held.
1411 mHandler.getLooper(),
1412 mContext.getResources().getString(
1413 R.string.as_app_forced_to_restricted_bucket, packageName),
1414 Toast.LENGTH_SHORT)
1415 .show();
1416 } else {
1417 Slog.i(TAG, packageName + " restricted by user");
1418 }
1419 } else {
1420 final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
Kweku Adams109cd9c2020-02-11 11:10:52 -08001421 + mInjector.getAutoRestrictedBucketDelayMs() - elapsedRealtime;
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001422 if (timeUntilRestrictPossibleMs > 0) {
1423 Slog.w(TAG, "Tried to restrict recently used app: " + packageName
1424 + " due to " + reason);
1425 mHandler.sendMessageDelayed(
1426 mHandler.obtainMessage(
1427 MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, packageName),
1428 timeUntilRestrictPossibleMs);
1429 return;
1430 }
1431 }
1432 }
1433
Amith Yamasani53f06ea2018-01-05 17:53:46 -08001434 // If the bucket is required to stay in a higher state for a specified duration, don't
1435 // override unless the duration has passed
Amith Yamasanibbbad9c2018-02-10 16:46:38 -08001436 if (predicted) {
1437 // Check if the app is within one of the timeouts for forced bucket elevation
1438 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
Amith Yamasani3154dcf2018-03-27 18:24:04 -07001439 // In case of not using the prediction, just keep track of it for applying after
1440 // ACTIVE or WORKING_SET timeout.
1441 mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
1442
Amith Yamasanibbbad9c2018-02-10 16:46:38 -08001443 if (newBucket > STANDBY_BUCKET_ACTIVE
1444 && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
1445 newBucket = STANDBY_BUCKET_ACTIVE;
Amith Yamasani119be9a2018-02-18 22:23:00 -08001446 reason = app.bucketingReason;
Amith Yamasanibbbad9c2018-02-10 16:46:38 -08001447 if (DEBUG) {
1448 Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
1449 }
1450 } else if (newBucket > STANDBY_BUCKET_WORKING_SET
1451 && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
1452 newBucket = STANDBY_BUCKET_WORKING_SET;
Amith Yamasani119be9a2018-02-18 22:23:00 -08001453 if (app.currentBucket != newBucket) {
1454 reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
1455 } else {
1456 reason = app.bucketingReason;
1457 }
Amith Yamasanibbbad9c2018-02-10 16:46:38 -08001458 if (DEBUG) {
1459 Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
1460 }
Kweku Adams54391332020-04-08 14:56:46 -07001461 } else if (newBucket == STANDBY_BUCKET_RARE
Kweku Adams2d79ce52020-05-05 12:31:35 -07001462 && mAllowRestrictedBucket
Kweku Adams54391332020-04-08 14:56:46 -07001463 && getBucketForLocked(packageName, userId, elapsedRealtime)
1464 == STANDBY_BUCKET_RESTRICTED) {
1465 // Prediction doesn't think the app will be used anytime soon and
1466 // it's been long enough that it could just time out into restricted,
1467 // so time it out there instead. Using TIMEOUT will allow prediction
1468 // to raise the bucket when it needs to.
1469 newBucket = STANDBY_BUCKET_RESTRICTED;
1470 reason = REASON_MAIN_TIMEOUT;
1471 if (DEBUG) {
1472 Slog.d(TAG,
1473 "Prediction to RARE overridden by timeout into RESTRICTED");
1474 }
Amith Yamasanibbbad9c2018-02-10 16:46:38 -08001475 }
Amith Yamasani53f06ea2018-01-05 17:53:46 -08001476 }
1477
Kweku Adams95cd9522020-05-08 09:56:53 -07001478 // Make sure we don't put the app in a lower bucket than it's supposed to be in.
1479 newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId));
Amith Yamasanie8789312017-12-10 14:34:26 -08001480 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
Makoto Onukia0058b42018-05-22 16:32:23 -07001481 reason, resetTimeout);
Amith Yamasanie8789312017-12-10 14:34:26 -08001482 }
Amith Yamasani119be9a2018-02-18 22:23:00 -08001483 maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
Amith Yamasani17fffee2017-09-29 13:17:43 -07001484 }
1485
Sudheer Shanka101c3532018-01-08 16:28:42 -08001486 @VisibleForTesting
1487 boolean isActiveDeviceAdmin(String packageName, int userId) {
1488 synchronized (mActiveAdminApps) {
1489 final Set<String> adminPkgs = mActiveAdminApps.get(userId);
1490 return adminPkgs != null && adminPkgs.contains(packageName);
1491 }
1492 }
1493
Makoto Onukia72e04f2019-10-03 11:10:45 -07001494 @Override
Sudheer Shanka101c3532018-01-08 16:28:42 -08001495 public void addActiveDeviceAdmin(String adminPkg, int userId) {
1496 synchronized (mActiveAdminApps) {
1497 Set<String> adminPkgs = mActiveAdminApps.get(userId);
1498 if (adminPkgs == null) {
1499 adminPkgs = new ArraySet<>();
1500 mActiveAdminApps.put(userId, adminPkgs);
1501 }
1502 adminPkgs.add(adminPkg);
1503 }
1504 }
1505
Makoto Onukia72e04f2019-10-03 11:10:45 -07001506 @Override
Sudheer Shanka101c3532018-01-08 16:28:42 -08001507 public void setActiveAdminApps(Set<String> adminPkgs, int userId) {
1508 synchronized (mActiveAdminApps) {
1509 if (adminPkgs == null) {
1510 mActiveAdminApps.remove(userId);
1511 } else {
1512 mActiveAdminApps.put(userId, adminPkgs);
1513 }
1514 }
1515 }
1516
Makoto Onukia72e04f2019-10-03 11:10:45 -07001517 @Override
Sudheer Shankac53c47f2018-01-16 12:01:00 -08001518 public void onAdminDataAvailable() {
1519 mAdminDataAvailableLatch.countDown();
1520 }
1521
1522 /**
1523 * This will only ever be called once - during device boot.
1524 */
1525 private void waitForAdminData() {
1526 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
1527 ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
1528 WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
1529 }
1530 }
1531
Makoto Onukia72e04f2019-10-03 11:10:45 -07001532 @VisibleForTesting
Sudheer Shanka101c3532018-01-08 16:28:42 -08001533 Set<String> getActiveAdminAppsForTest(int userId) {
1534 synchronized (mActiveAdminApps) {
1535 return mActiveAdminApps.get(userId);
1536 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07001537 }
1538
1539 /**
1540 * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
1541 * returns {@code false}.
1542 */
1543 private boolean isDeviceProvisioningPackage(String packageName) {
1544 String deviceProvisioningPackage = mContext.getResources().getString(
1545 com.android.internal.R.string.config_deviceProvisioningPackage);
1546 return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
1547 }
1548
1549 private boolean isCarrierApp(String packageName) {
1550 synchronized (mAppIdleLock) {
1551 if (!mHaveCarrierPrivilegedApps) {
Amith Yamasani17fffee2017-09-29 13:17:43 -07001552 fetchCarrierPrivilegedAppsLocked();
Amith Yamasani7e528f32017-10-05 16:06:16 -07001553 }
1554 if (mCarrierPrivilegedApps != null) {
1555 return mCarrierPrivilegedApps.contains(packageName);
1556 }
1557 return false;
1558 }
1559 }
1560
Makoto Onukia72e04f2019-10-03 11:10:45 -07001561 @Override
1562 public void clearCarrierPrivilegedApps() {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001563 if (DEBUG) {
1564 Slog.i(TAG, "Clearing carrier privileged apps list");
1565 }
1566 synchronized (mAppIdleLock) {
1567 mHaveCarrierPrivilegedApps = false;
1568 mCarrierPrivilegedApps = null; // Need to be refetched.
1569 }
1570 }
1571
1572 @GuardedBy("mAppIdleLock")
Amith Yamasani17fffee2017-09-29 13:17:43 -07001573 private void fetchCarrierPrivilegedAppsLocked() {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001574 TelephonyManager telephonyManager =
1575 mContext.getSystemService(TelephonyManager.class);
Shuo Qian6f27c572019-12-03 23:52:13 +00001576 mCarrierPrivilegedApps =
1577 telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions();
Amith Yamasani7e528f32017-10-05 16:06:16 -07001578 mHaveCarrierPrivilegedApps = true;
1579 if (DEBUG) {
1580 Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
1581 }
1582 }
1583
1584 private boolean isActiveNetworkScorer(String packageName) {
Amith Yamasani17fffee2017-09-29 13:17:43 -07001585 String activeScorer = mInjector.getActiveNetworkScorer();
1586 return packageName != null && packageName.equals(activeScorer);
Amith Yamasani7e528f32017-10-05 16:06:16 -07001587 }
1588
Makoto Onukia72e04f2019-10-03 11:10:45 -07001589 private void informListeners(String packageName, int userId, int bucket, int reason,
Amith Yamasani119be9a2018-02-18 22:23:00 -08001590 boolean userInteraction) {
Amith Yamasaniafbccb72017-11-27 10:44:24 -08001591 final boolean idle = bucket >= STANDBY_BUCKET_RARE;
Amith Yamasani93885192017-12-13 11:52:10 -08001592 synchronized (mPackageAccessListeners) {
1593 for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
Amith Yamasani119be9a2018-02-18 22:23:00 -08001594 listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
Christopher Tated117b292018-01-05 17:32:36 -08001595 if (userInteraction) {
1596 listener.onUserInteractionStarted(packageName, userId);
1597 }
Amith Yamasani93885192017-12-13 11:52:10 -08001598 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07001599 }
1600 }
1601
Kweku Adamse6601762020-05-01 15:06:18 -07001602 private void informParoleStateChanged() {
1603 final boolean paroled = isInParole();
1604 synchronized (mPackageAccessListeners) {
1605 for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
1606 listener.onParoleStateChanged(paroled);
1607 }
1608 }
1609 }
1610
Makoto Onukia72e04f2019-10-03 11:10:45 -07001611 @Override
1612 public void flushToDisk(int userId) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001613 synchronized (mAppIdleLock) {
1614 mAppIdleHistory.writeAppIdleTimes(userId);
1615 }
1616 }
1617
Makoto Onukia72e04f2019-10-03 11:10:45 -07001618 @Override
1619 public void flushDurationsToDisk() {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001620 // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
1621 // considered not-idle, which is the safest outcome in such an event.
1622 synchronized (mAppIdleLock) {
1623 mAppIdleHistory.writeAppIdleDurations();
1624 }
1625 }
1626
Makoto Onukia72e04f2019-10-03 11:10:45 -07001627 private boolean isDisplayOn() {
Amith Yamasani17fffee2017-09-29 13:17:43 -07001628 return mInjector.isDefaultDisplayOn();
Amith Yamasani7e528f32017-10-05 16:06:16 -07001629 }
1630
Makoto Onukia72e04f2019-10-03 11:10:45 -07001631 @VisibleForTesting
Amith Yamasani7e528f32017-10-05 16:06:16 -07001632 void clearAppIdleForPackage(String packageName, int userId) {
1633 synchronized (mAppIdleLock) {
1634 mAppIdleHistory.clearUsage(packageName, userId);
1635 }
1636 }
1637
Kweku Adams917f8a42020-02-26 17:18:03 -08001638 /**
1639 * Remove an app from the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
1640 * bucket if it was forced into the bucket by the system because it was buggy.
1641 */
1642 @VisibleForTesting
1643 void maybeUnrestrictBuggyApp(String packageName, int userId) {
1644 synchronized (mAppIdleLock) {
1645 final long elapsedRealtime = mInjector.elapsedRealtime();
1646 final AppIdleHistory.AppUsageHistory app =
1647 mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime);
1648 if (app.currentBucket != STANDBY_BUCKET_RESTRICTED
1649 || (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_FORCED_BY_SYSTEM) {
1650 return;
1651 }
1652
1653 final int newBucket;
1654 final int newReason;
1655 if ((app.bucketingReason & REASON_SUB_MASK) == REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY) {
1656 // If bugginess was the only reason the app should be restricted, then lift it out.
1657 newBucket = STANDBY_BUCKET_RARE;
1658 newReason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_UPDATE;
1659 } else {
1660 // There's another reason the app was restricted. Remove the buggy bit and call
1661 // it a day.
1662 newBucket = STANDBY_BUCKET_RESTRICTED;
1663 newReason = app.bucketingReason & ~REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
1664 }
1665 mAppIdleHistory.setAppStandbyBucket(
1666 packageName, userId, elapsedRealtime, newBucket, newReason);
1667 }
1668 }
1669
Amith Yamasani7e528f32017-10-05 16:06:16 -07001670 private class PackageReceiver extends BroadcastReceiver {
1671 @Override
1672 public void onReceive(Context context, Intent intent) {
1673 final String action = intent.getAction();
Kweku Adams95cd9522020-05-08 09:56:53 -07001674 final String pkgName = intent.getData().getSchemeSpecificPart();
1675 final int userId = getSendingUserId();
Amith Yamasani7e528f32017-10-05 16:06:16 -07001676 if (Intent.ACTION_PACKAGE_ADDED.equals(action)
1677 || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1678 clearCarrierPrivilegedApps();
Kweku Adams95cd9522020-05-08 09:56:53 -07001679 // ACTION_PACKAGE_ADDED is called even for system app downgrades.
1680 evaluateSystemAppException(pkgName, userId);
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001681 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, pkgName)
1682 .sendToTarget();
Amith Yamasani7e528f32017-10-05 16:06:16 -07001683 }
1684 if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
Kweku Adams917f8a42020-02-26 17:18:03 -08001685 Intent.ACTION_PACKAGE_ADDED.equals(action))) {
Kweku Adams917f8a42020-02-26 17:18:03 -08001686 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1687 maybeUnrestrictBuggyApp(pkgName, userId);
1688 } else {
1689 clearAppIdleForPackage(pkgName, userId);
1690 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07001691 }
1692 }
1693 }
1694
Kweku Adams95cd9522020-05-08 09:56:53 -07001695 private void evaluateSystemAppException(String packageName, int userId) {
1696 if (!mSystemServicesReady) {
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001697 // The app will be evaluated in when services are ready.
Kweku Adams95cd9522020-05-08 09:56:53 -07001698 return;
1699 }
1700 try {
Kweku Adamsf197eef2020-06-16 11:59:37 -07001701 PackageInfo pi = mPackageManager.getPackageInfoAsUser(
1702 packageName, HEADLESS_APP_CHECK_FLAGS, userId);
Kweku Adams95cd9522020-05-08 09:56:53 -07001703 evaluateSystemAppException(pi);
1704 } catch (PackageManager.NameNotFoundException e) {
Kweku Adams8604fd72020-06-02 14:04:27 -07001705 synchronized (mHeadlessSystemApps) {
1706 mHeadlessSystemApps.remove(packageName);
1707 }
Kweku Adams95cd9522020-05-08 09:56:53 -07001708 }
1709 }
1710
Kweku Adams8604fd72020-06-02 14:04:27 -07001711 /** Returns true if the exception status changed. */
1712 private boolean evaluateSystemAppException(@Nullable PackageInfo pkgInfo) {
1713 if (pkgInfo == null || pkgInfo.applicationInfo == null
Kweku Adamsf197eef2020-06-16 11:59:37 -07001714 || (!pkgInfo.applicationInfo.isSystemApp()
1715 && !pkgInfo.applicationInfo.isUpdatedSystemApp())) {
Kweku Adams8604fd72020-06-02 14:04:27 -07001716 return false;
1717 }
1718 synchronized (mHeadlessSystemApps) {
1719 if (pkgInfo.activities == null || pkgInfo.activities.length == 0) {
1720 // Headless system app.
Kweku Adamsf197eef2020-06-16 11:59:37 -07001721 return mHeadlessSystemApps.add(pkgInfo.packageName);
Kweku Adams8604fd72020-06-02 14:04:27 -07001722 } else {
Kweku Adamsf197eef2020-06-16 11:59:37 -07001723 return mHeadlessSystemApps.remove(pkgInfo.packageName);
Kweku Adams95cd9522020-05-08 09:56:53 -07001724 }
1725 }
1726 }
1727
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001728 /** Call on a system version update to temporarily reset system app buckets. */
Makoto Onukia72e04f2019-10-03 11:10:45 -07001729 @Override
1730 public void initializeDefaultsForSystemApps(int userId) {
Amith Yamasani777b1532018-01-28 23:20:07 +00001731 if (!mSystemServicesReady) {
1732 // Do it later, since SettingsProvider wasn't queried yet for app_standby_enabled
1733 mPendingInitializeDefaults = true;
1734 return;
1735 }
1736 Slog.d(TAG, "Initializing defaults for system apps on user " + userId + ", "
1737 + "appIdleEnabled=" + mAppIdleEnabled);
Amith Yamasani17fffee2017-09-29 13:17:43 -07001738 final long elapsedRealtime = mInjector.elapsedRealtime();
Amith Yamasani7e528f32017-10-05 16:06:16 -07001739 List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001740 PackageManager.MATCH_DISABLED_COMPONENTS,
Amith Yamasani7e528f32017-10-05 16:06:16 -07001741 userId);
1742 final int packageCount = packages.size();
1743 synchronized (mAppIdleLock) {
1744 for (int i = 0; i < packageCount; i++) {
1745 final PackageInfo pi = packages.get(i);
1746 String packageName = pi.packageName;
1747 if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
Amith Yamasani7ec89412018-02-07 08:48:49 -08001748 // Mark app as used for 2 hours. After that it can timeout to whatever the
Amith Yamasani53f06ea2018-01-05 17:53:46 -08001749 // past usage pattern was.
Amith Yamasani119be9a2018-02-18 22:23:00 -08001750 mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
1751 REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
Amith Yamasani7ec89412018-02-07 08:48:49 -08001752 elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
Amith Yamasani7e528f32017-10-05 16:06:16 -07001753 }
1754 }
Michael Wachenschwanzd1d8aa62019-02-28 16:38:37 -08001755 // Immediately persist defaults to disk
1756 mAppIdleHistory.writeAppIdleTimes(userId);
Amith Yamasani7e528f32017-10-05 16:06:16 -07001757 }
1758 }
1759
Kweku Adamseffeb2a2020-06-16 10:11:56 -07001760 /** Call on system boot to get the initial set of headless system apps. */
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001761 private void loadHeadlessSystemAppCache() {
1762 Slog.d(TAG, "Loading headless system app cache. appIdleEnabled=" + mAppIdleEnabled);
1763 final List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
Kweku Adamsf197eef2020-06-16 11:59:37 -07001764 HEADLESS_APP_CHECK_FLAGS, UserHandle.USER_SYSTEM);
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001765 final int packageCount = packages.size();
1766 for (int i = 0; i < packageCount; i++) {
Kweku Adams8604fd72020-06-02 14:04:27 -07001767 PackageInfo pkgInfo = packages.get(i);
1768 if (pkgInfo != null && evaluateSystemAppException(pkgInfo)) {
1769 mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE,
1770 UserHandle.USER_SYSTEM, -1, pkgInfo.packageName)
1771 .sendToTarget();
1772 }
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001773 }
1774 }
1775
Makoto Onukia72e04f2019-10-03 11:10:45 -07001776 @Override
1777 public void postReportContentProviderUsage(String name, String packageName, int userId) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001778 SomeArgs args = SomeArgs.obtain();
1779 args.arg1 = name;
1780 args.arg2 = packageName;
1781 args.arg3 = userId;
1782 mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
1783 .sendToTarget();
1784 }
1785
Makoto Onukia72e04f2019-10-03 11:10:45 -07001786 @Override
1787 public void postReportSyncScheduled(String packageName, int userId, boolean exempted) {
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08001788 mHandler.obtainMessage(MSG_REPORT_SYNC_SCHEDULED, userId, exempted ? 1 : 0, packageName)
Makoto Onukid5f25d22018-05-22 16:02:17 -07001789 .sendToTarget();
1790 }
1791
Makoto Onukia72e04f2019-10-03 11:10:45 -07001792 @Override
1793 public void postReportExemptedSyncStart(String packageName, int userId) {
Makoto Onuki75ad2492018-03-28 14:42:42 -07001794 mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
1795 .sendToTarget();
1796 }
1797
Makoto Onukia72e04f2019-10-03 11:10:45 -07001798 @Override
Sudheer Shankab497dd42020-02-16 22:23:24 -08001799 public void dumpUser(IndentingPrintWriter idpw, int userId, List<String> pkgs) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001800 synchronized (mAppIdleLock) {
Sudheer Shankab497dd42020-02-16 22:23:24 -08001801 mAppIdleHistory.dump(idpw, userId, pkgs);
Amith Yamasani7e528f32017-10-05 16:06:16 -07001802 }
1803 }
1804
Makoto Onukia72e04f2019-10-03 11:10:45 -07001805 @Override
1806 public void dumpState(String[] args, PrintWriter pw) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07001807 synchronized (mAppIdleLock) {
1808 pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
1809 + "): " + mCarrierPrivilegedApps);
1810 }
1811
1812 pw.println();
1813 pw.println("Settings:");
1814
Amith Yamasani7e528f32017-10-05 16:06:16 -07001815 pw.print(" mCheckIdleIntervalMillis=");
1816 TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
1817 pw.println();
1818
Esteban Talavera5b79bfa2018-07-05 15:15:22 +01001819 pw.print(" mStrongUsageTimeoutMillis=");
1820 TimeUtils.formatDuration(mStrongUsageTimeoutMillis, pw);
1821 pw.println();
1822 pw.print(" mNotificationSeenTimeoutMillis=");
1823 TimeUtils.formatDuration(mNotificationSeenTimeoutMillis, pw);
1824 pw.println();
1825 pw.print(" mSyncAdapterTimeoutMillis=");
1826 TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw);
1827 pw.println();
1828 pw.print(" mSystemInteractionTimeoutMillis=");
1829 TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw);
1830 pw.println();
Michael Wachenschwanz6ced0ee2019-04-15 16:43:28 -07001831 pw.print(" mInitialForegroundServiceStartTimeoutMillis=");
1832 TimeUtils.formatDuration(mInitialForegroundServiceStartTimeoutMillis, pw);
1833 pw.println();
Esteban Talavera5b79bfa2018-07-05 15:15:22 +01001834
1835 pw.print(" mPredictionTimeoutMillis=");
1836 TimeUtils.formatDuration(mPredictionTimeoutMillis, pw);
1837 pw.println();
1838
Makoto Onukid5f25d22018-05-22 16:02:17 -07001839 pw.print(" mExemptedSyncScheduledNonDozeTimeoutMillis=");
1840 TimeUtils.formatDuration(mExemptedSyncScheduledNonDozeTimeoutMillis, pw);
1841 pw.println();
1842 pw.print(" mExemptedSyncScheduledDozeTimeoutMillis=");
1843 TimeUtils.formatDuration(mExemptedSyncScheduledDozeTimeoutMillis, pw);
1844 pw.println();
1845 pw.print(" mExemptedSyncStartTimeoutMillis=");
1846 TimeUtils.formatDuration(mExemptedSyncStartTimeoutMillis, pw);
1847 pw.println();
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08001848 pw.print(" mUnexemptedSyncScheduledTimeoutMillis=");
1849 TimeUtils.formatDuration(mUnexemptedSyncScheduledTimeoutMillis, pw);
1850 pw.println();
Makoto Onukid5f25d22018-05-22 16:02:17 -07001851
Esteban Talavera5b79bfa2018-07-05 15:15:22 +01001852 pw.print(" mSystemUpdateUsageTimeoutMillis=");
1853 TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw);
1854 pw.println();
1855
Amith Yamasani7e528f32017-10-05 16:06:16 -07001856 pw.println();
1857 pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
Kweku Adams2d79ce52020-05-05 12:31:35 -07001858 pw.print(" mAllowRestrictedBucket=");
1859 pw.print(mAllowRestrictedBucket);
Kweku Adamsf35ed3702020-02-11 17:32:54 -08001860 pw.print(" mIsCharging=");
1861 pw.print(mIsCharging);
Amith Yamasani7e528f32017-10-05 16:06:16 -07001862 pw.println();
Amith Yamasani17fffee2017-09-29 13:17:43 -07001863 pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds));
1864 pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds));
Michael Wachenschwanz5ca5cb62018-04-25 16:12:59 -07001865 pw.println();
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001866
1867 pw.println("mHeadlessSystemApps=[");
Kweku Adams8604fd72020-06-02 14:04:27 -07001868 synchronized (mHeadlessSystemApps) {
1869 for (int i = mHeadlessSystemApps.size() - 1; i >= 0; --i) {
1870 pw.print(" ");
Kweku Adamsf197eef2020-06-16 11:59:37 -07001871 pw.print(mHeadlessSystemApps.valueAt(i));
Kweku Adams8604fd72020-06-02 14:04:27 -07001872 pw.println(",");
1873 }
Kweku Adamsc86da5f2020-05-28 12:26:35 -07001874 }
1875 pw.println("]");
1876 pw.println();
Kweku Adamseffeb2a2020-06-16 10:11:56 -07001877
1878 mInjector.dump(pw);
Amith Yamasani17fffee2017-09-29 13:17:43 -07001879 }
1880
1881 /**
1882 * Injector for interaction with external code. Override methods to provide a mock
1883 * implementation for tests.
1884 * onBootPhase() must be called with at least the PHASE_SYSTEM_SERVICES_READY
1885 */
1886 static class Injector {
1887
1888 private final Context mContext;
1889 private final Looper mLooper;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001890 private IBatteryStats mBatteryStats;
Kweku Adamsf35ed3702020-02-11 17:32:54 -08001891 private BatteryManager mBatteryManager;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001892 private PackageManagerInternal mPackageManagerInternal;
1893 private DisplayManager mDisplayManager;
Makoto Onukid5f25d22018-05-22 16:02:17 -07001894 private PowerManager mPowerManager;
Kweku Adamseffeb2a2020-06-16 10:11:56 -07001895 private IDeviceIdleController mDeviceIdleController;
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08001896 private CrossProfileAppsInternal mCrossProfileAppsInternal;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001897 int mBootPhase;
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001898 /**
1899 * The minimum amount of time required since the last user interaction before an app can be
Kweku Adams109cd9c2020-02-11 11:10:52 -08001900 * automatically placed in the RESTRICTED bucket.
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001901 */
Kweku Adams109cd9c2020-02-11 11:10:52 -08001902 long mAutoRestrictedBucketDelayMs = ONE_DAY;
Kweku Adamseffeb2a2020-06-16 10:11:56 -07001903 /**
1904 * Cached set of apps that are power whitelisted, including those not whitelisted from idle.
1905 */
1906 @GuardedBy("mPowerWhitelistedApps")
1907 private final ArraySet<String> mPowerWhitelistedApps = new ArraySet<>();
Amith Yamasani17fffee2017-09-29 13:17:43 -07001908
1909 Injector(Context context, Looper looper) {
1910 mContext = context;
1911 mLooper = looper;
1912 }
1913
1914 Context getContext() {
1915 return mContext;
1916 }
1917
1918 Looper getLooper() {
1919 return mLooper;
1920 }
1921
1922 void onBootPhase(int phase) {
1923 if (phase == PHASE_SYSTEM_SERVICES_READY) {
Kweku Adamseffeb2a2020-06-16 10:11:56 -07001924 mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
1925 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
Amith Yamasani17fffee2017-09-29 13:17:43 -07001926 mBatteryStats = IBatteryStats.Stub.asInterface(
1927 ServiceManager.getService(BatteryStats.SERVICE_NAME));
1928 mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
1929 mDisplayManager = (DisplayManager) mContext.getSystemService(
1930 Context.DISPLAY_SERVICE);
Makoto Onukid5f25d22018-05-22 16:02:17 -07001931 mPowerManager = mContext.getSystemService(PowerManager.class);
Kweku Adamsf35ed3702020-02-11 17:32:54 -08001932 mBatteryManager = mContext.getSystemService(BatteryManager.class);
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08001933 mCrossProfileAppsInternal = LocalServices.getService(
1934 CrossProfileAppsInternal.class);
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001935
1936 final ActivityManager activityManager =
1937 (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
1938 if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
Kweku Adams109cd9c2020-02-11 11:10:52 -08001939 mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001940 }
Amith Yamasani17fffee2017-09-29 13:17:43 -07001941 }
1942 mBootPhase = phase;
1943 }
1944
1945 int getBootPhase() {
1946 return mBootPhase;
1947 }
1948
1949 /**
1950 * Returns the elapsed realtime since the device started. Override this
1951 * to control the clock.
1952 * @return elapsed realtime
1953 */
1954 long elapsedRealtime() {
1955 return SystemClock.elapsedRealtime();
1956 }
1957
1958 long currentTimeMillis() {
1959 return System.currentTimeMillis();
1960 }
1961
1962 boolean isAppIdleEnabled() {
Amith Yamasani172612c2017-12-15 10:51:53 -08001963 final boolean buildFlag = mContext.getResources().getBoolean(
Amith Yamasani17fffee2017-09-29 13:17:43 -07001964 com.android.internal.R.bool.config_enableAutoPowerModes);
Lei Yu4b976ad2018-04-19 10:38:58 -07001965 final boolean runtimeFlag = Global.getInt(mContext.getContentResolver(),
1966 Global.APP_STANDBY_ENABLED, 1) == 1
1967 && Global.getInt(mContext.getContentResolver(),
1968 Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 1;
Amith Yamasani172612c2017-12-15 10:51:53 -08001969 return buildFlag && runtimeFlag;
Amith Yamasani17fffee2017-09-29 13:17:43 -07001970 }
1971
Kweku Adamsf35ed3702020-02-11 17:32:54 -08001972 boolean isCharging() {
1973 return mBatteryManager.isCharging();
1974 }
1975
Kweku Adamseffeb2a2020-06-16 10:11:56 -07001976 boolean isNonIdleWhitelisted(String packageName) {
1977 if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) {
1978 return false;
1979 }
1980 synchronized (mPowerWhitelistedApps) {
1981 return mPowerWhitelistedApps.contains(packageName);
1982 }
1983 }
1984
1985 private void updatePowerWhitelistCache() {
1986 if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) {
1987 return;
1988 }
1989 try {
1990 // Don't call out to DeviceIdleController with the lock held.
1991 final String[] whitelistedPkgs =
1992 mDeviceIdleController.getFullPowerWhitelistExceptIdle();
1993 synchronized (mPowerWhitelistedApps) {
1994 mPowerWhitelistedApps.clear();
1995 final int len = whitelistedPkgs.length;
1996 for (int i = 0; i < len; ++i) {
1997 mPowerWhitelistedApps.add(whitelistedPkgs[i]);
1998 }
1999 }
2000 } catch (RemoteException e) {
2001 // Should not happen.
2002 Slog.wtf(TAG, "Failed to get power whitelist", e);
2003 }
Amith Yamasani17fffee2017-09-29 13:17:43 -07002004 }
2005
Kweku Adams2d79ce52020-05-05 12:31:35 -07002006 boolean isRestrictedBucketEnabled() {
2007 return Global.getInt(mContext.getContentResolver(),
2008 Global.ENABLE_RESTRICTED_BUCKET,
2009 Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1;
2010 }
2011
Amith Yamasani17fffee2017-09-29 13:17:43 -07002012 File getDataSystemDirectory() {
2013 return Environment.getDataSystemDirectory();
2014 }
2015
Kweku Adams109cd9c2020-02-11 11:10:52 -08002016 /**
2017 * Return the minimum amount of time that must have passed since the last user usage before
2018 * an app can be automatically put into the
2019 * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
2020 */
2021 long getAutoRestrictedBucketDelayMs() {
2022 return mAutoRestrictedBucketDelayMs;
Kweku Adamsc6a9b342020-01-08 18:37:26 -08002023 }
2024
Amith Yamasani17fffee2017-09-29 13:17:43 -07002025 void noteEvent(int event, String packageName, int uid) throws RemoteException {
2026 mBatteryStats.noteEvent(event, packageName, uid);
2027 }
2028
Kweku Adamsc4ee9982020-01-08 11:14:39 -08002029 PackageManagerInternal getPackageManagerInternal() {
2030 return mPackageManagerInternal;
2031 }
2032
Amith Yamasani17fffee2017-09-29 13:17:43 -07002033 boolean isPackageEphemeral(int userId, String packageName) {
2034 return mPackageManagerInternal.isPackageEphemeral(userId, packageName);
2035 }
2036
Varun Shah7609b752018-10-15 15:07:47 -07002037 boolean isPackageInstalled(String packageName, int flags, int userId) {
2038 return mPackageManagerInternal.getPackageUid(packageName, flags, userId) >= 0;
2039 }
2040
Amith Yamasani17fffee2017-09-29 13:17:43 -07002041 int[] getRunningUserIds() throws RemoteException {
2042 return ActivityManager.getService().getRunningUserIds();
2043 }
2044
2045 boolean isDefaultDisplayOn() {
2046 return mDisplayManager
2047 .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
2048 }
2049
2050 void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
2051 mDisplayManager.registerDisplayListener(listener, handler);
2052 }
2053
2054 String getActiveNetworkScorer() {
2055 NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
2056 Context.NETWORK_SCORE_SERVICE);
2057 return nsm.getActiveScorerPackage();
2058 }
2059
2060 public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
2061 int userId) {
2062 return appWidgetManager.isBoundWidgetPackage(packageName, userId);
2063 }
Amith Yamasani301e94a2017-11-17 16:35:44 -08002064
2065 String getAppIdleSettings() {
Lei Yu4b976ad2018-04-19 10:38:58 -07002066 return Global.getString(mContext.getContentResolver(),
2067 Global.APP_IDLE_CONSTANTS);
Amith Yamasani301e94a2017-11-17 16:35:44 -08002068 }
Makoto Onukid5f25d22018-05-22 16:02:17 -07002069
2070 /** Whether the device is in doze or not. */
2071 public boolean isDeviceIdleMode() {
2072 return mPowerManager.isDeviceIdleMode();
2073 }
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08002074
2075 public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) {
2076 final int uid = mPackageManagerInternal.getPackageUidInternal(pkg, 0, userId);
Kweku Adams259403e2020-04-27 09:25:07 -07002077 final AndroidPackage aPkg = mPackageManagerInternal.getPackage(uid);
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08002078 if (uid < 0
Kweku Adams259403e2020-04-27 09:25:07 -07002079 || aPkg == null
2080 || !aPkg.isCrossProfile()
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08002081 || !mCrossProfileAppsInternal
2082 .verifyUidHasInteractAcrossProfilePermission(pkg, uid)) {
Kweku Adams259403e2020-04-27 09:25:07 -07002083 if (uid >= 0 && aPkg == null) {
2084 Slog.wtf(TAG, "Null package retrieved for UID " + uid);
2085 }
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08002086 return Collections.emptyList();
2087 }
2088 return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId);
2089 }
Kweku Adamseffeb2a2020-06-16 10:11:56 -07002090
2091 void dump(PrintWriter pw) {
2092 pw.println("mPowerWhitelistedApps=[");
2093 synchronized (mPowerWhitelistedApps) {
2094 for (int i = mPowerWhitelistedApps.size() - 1; i >= 0; --i) {
2095 pw.print(" ");
2096 pw.print(mPowerWhitelistedApps.valueAt(i));
2097 pw.println(",");
2098 }
2099 }
2100 pw.println("]");
2101 pw.println();
2102 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07002103 }
2104
2105 class AppStandbyHandler extends Handler {
2106
2107 AppStandbyHandler(Looper looper) {
2108 super(looper);
2109 }
2110
2111 @Override
2112 public void handleMessage(Message msg) {
2113 switch (msg.what) {
Amith Yamasani17fffee2017-09-29 13:17:43 -07002114 case MSG_INFORM_LISTENERS:
Christopher Tated117b292018-01-05 17:32:36 -08002115 StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
Amith Yamasani119be9a2018-02-18 22:23:00 -08002116 informListeners(r.packageName, r.userId, r.bucket, r.reason,
2117 r.isUserInteraction);
Christopher Tated117b292018-01-05 17:32:36 -08002118 r.recycle();
Amith Yamasani17fffee2017-09-29 13:17:43 -07002119 break;
2120
Amith Yamasani7e528f32017-10-05 16:06:16 -07002121 case MSG_FORCE_IDLE_STATE:
2122 forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
2123 break;
2124
2125 case MSG_CHECK_IDLE_STATES:
Amith Yamasani172612c2017-12-15 10:51:53 -08002126 if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07002127 mHandler.sendMessageDelayed(mHandler.obtainMessage(
2128 MSG_CHECK_IDLE_STATES, msg.arg1, 0),
2129 mCheckIdleIntervalMillis);
2130 }
2131 break;
2132
2133 case MSG_ONE_TIME_CHECK_IDLE_STATES:
2134 mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
Sudheer Shankac53c47f2018-01-16 12:01:00 -08002135 waitForAdminData();
Amith Yamasani7e528f32017-10-05 16:06:16 -07002136 checkIdleStates(UserHandle.USER_ALL);
2137 break;
2138
Amith Yamasani7e528f32017-10-05 16:06:16 -07002139 case MSG_REPORT_CONTENT_PROVIDER_USAGE:
2140 SomeArgs args = (SomeArgs) msg.obj;
2141 reportContentProviderUsage((String) args.arg1, // authority name
2142 (String) args.arg2, // package name
2143 (int) args.arg3); // userId
2144 args.recycle();
2145 break;
2146
Kweku Adamse6601762020-05-01 15:06:18 -07002147 case MSG_PAROLE_STATE_CHANGED:
2148 if (DEBUG) Slog.d(TAG, "Parole state: " + isInParole());
2149 informParoleStateChanged();
2150 break;
2151
Amith Yamasanibbbad9c2018-02-10 16:46:38 -08002152 case MSG_CHECK_PACKAGE_IDLE_STATE:
2153 checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
2154 mInjector.elapsedRealtime());
2155 break;
Makoto Onuki75ad2492018-03-28 14:42:42 -07002156
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002157 case MSG_REPORT_SYNC_SCHEDULED:
Michael Wachenschwanz1103ff22020-02-11 20:40:26 -08002158 final boolean exempted = msg.arg2 > 0 ? true : false;
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002159 if (exempted) {
2160 reportExemptedSyncScheduled((String) msg.obj, msg.arg1);
2161 } else {
2162 reportUnexemptedSyncScheduled((String) msg.obj, msg.arg1);
2163 }
Makoto Onukid5f25d22018-05-22 16:02:17 -07002164 break;
2165
Makoto Onuki75ad2492018-03-28 14:42:42 -07002166 case MSG_REPORT_EXEMPTED_SYNC_START:
2167 reportExemptedSyncStart((String) msg.obj, msg.arg1);
2168 break;
2169
Amith Yamasani7e528f32017-10-05 16:06:16 -07002170 default:
2171 super.handleMessage(msg);
2172 break;
2173
2174 }
2175 }
2176 };
2177
Kweku Adamsf35ed3702020-02-11 17:32:54 -08002178 private class DeviceStateReceiver extends BroadcastReceiver {
Michael Wachenschwanz113a0fa2018-03-30 12:45:18 -07002179 @Override
Kweku Adamsf35ed3702020-02-11 17:32:54 -08002180 public void onReceive(Context context, Intent intent) {
2181 switch (intent.getAction()) {
2182 case BatteryManager.ACTION_CHARGING:
2183 setChargingState(true);
2184 break;
2185 case BatteryManager.ACTION_DISCHARGING:
2186 setChargingState(false);
2187 break;
Kweku Adamseffeb2a2020-06-16 10:11:56 -07002188 case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
2189 if (mSystemServicesReady) {
2190 mHandler.post(mInjector::updatePowerWhitelistCache);
2191 }
2192 break;
Kweku Adamsf35ed3702020-02-11 17:32:54 -08002193 }
Michael Wachenschwanz113a0fa2018-03-30 12:45:18 -07002194 }
Kweku Adamsf35ed3702020-02-11 17:32:54 -08002195 }
Michael Wachenschwanz113a0fa2018-03-30 12:45:18 -07002196
Amith Yamasani7e528f32017-10-05 16:06:16 -07002197 private final DisplayManager.DisplayListener mDisplayListener
2198 = new DisplayManager.DisplayListener() {
2199
2200 @Override public void onDisplayAdded(int displayId) {
2201 }
2202
2203 @Override public void onDisplayRemoved(int displayId) {
2204 }
2205
2206 @Override public void onDisplayChanged(int displayId) {
2207 if (displayId == Display.DEFAULT_DISPLAY) {
2208 final boolean displayOn = isDisplayOn();
2209 synchronized (mAppIdleLock) {
Amith Yamasani17fffee2017-09-29 13:17:43 -07002210 mAppIdleHistory.updateDisplay(displayOn, mInjector.elapsedRealtime());
Amith Yamasani7e528f32017-10-05 16:06:16 -07002211 }
2212 }
2213 }
2214 };
2215
2216 /**
Lei Yu4b976ad2018-04-19 10:38:58 -07002217 * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}.
Amith Yamasani7e528f32017-10-05 16:06:16 -07002218 */
2219 private class SettingsObserver extends ContentObserver {
Amith Yamasani17fffee2017-09-29 13:17:43 -07002220 private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
2221 private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
Amith Yamasani7ec89412018-02-07 08:48:49 -08002222 private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
2223 private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
2224 "notification_seen_duration";
2225 private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
2226 "system_update_usage_duration";
Amith Yamasani119be9a2018-02-18 22:23:00 -08002227 private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002228 private static final String KEY_SYNC_ADAPTER_HOLD_DURATION = "sync_adapter_duration";
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002229 private static final String KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION =
2230 "exempted_sync_scheduled_nd_duration";
2231 private static final String KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION =
2232 "exempted_sync_scheduled_d_duration";
2233 private static final String KEY_EXEMPTED_SYNC_START_HOLD_DURATION =
2234 "exempted_sync_start_duration";
2235 private static final String KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION =
2236 "unexempted_sync_scheduled_duration";
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002237 private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
2238 "system_interaction_duration";
Michael Wachenschwanz6ced0ee2019-04-15 16:43:28 -07002239 private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
2240 "initial_foreground_service_start_duration";
Kweku Adams109cd9c2020-02-11 11:10:52 -08002241 private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS =
2242 "auto_restricted_bucket_delay_ms";
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08002243 private static final String KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS =
2244 "cross_profile_apps_share_standby_buckets";
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002245 public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
2246 public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
2247 public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
2248 public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
2249 public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
Makoto Onukid5f25d22018-05-22 16:02:17 -07002250 public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = 10 * ONE_MINUTE;
2251 public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = 4 * ONE_HOUR;
2252 public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002253 public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
Michael Wachenschwanz6ced0ee2019-04-15 16:43:28 -07002254 public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
Kweku Adams109cd9c2020-02-11 11:10:52 -08002255 public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY;
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08002256 public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
Amith Yamasani7e528f32017-10-05 16:06:16 -07002257
2258 private final KeyValueListParser mParser = new KeyValueListParser(',');
2259
2260 SettingsObserver(Handler handler) {
2261 super(handler);
2262 }
2263
2264 void registerObserver() {
Lei Yu4b976ad2018-04-19 10:38:58 -07002265 final ContentResolver cr = mContext.getContentResolver();
2266 cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this);
2267 cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
Kweku Adams2d79ce52020-05-05 12:31:35 -07002268 cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
2269 false, this);
Lei Yu4b976ad2018-04-19 10:38:58 -07002270 cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
2271 false, this);
Amith Yamasani7e528f32017-10-05 16:06:16 -07002272 }
2273
2274 @Override
2275 public void onChange(boolean selfChange) {
2276 updateSettings();
2277 postOneTimeCheckIdleStates();
2278 }
2279
2280 void updateSettings() {
Amith Yamasani172612c2017-12-15 10:51:53 -08002281 if (DEBUG) {
2282 Slog.d(TAG,
Lei Yu4b976ad2018-04-19 10:38:58 -07002283 "appidle=" + Global.getString(mContext.getContentResolver(),
2284 Global.APP_STANDBY_ENABLED));
2285 Slog.d(TAG,
2286 "adaptivebat=" + Global.getString(mContext.getContentResolver(),
2287 Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
2288 Slog.d(TAG, "appidleconstants=" + Global.getString(
Amith Yamasani172612c2017-12-15 10:51:53 -08002289 mContext.getContentResolver(),
Lei Yu4b976ad2018-04-19 10:38:58 -07002290 Global.APP_IDLE_CONSTANTS));
Amith Yamasani172612c2017-12-15 10:51:53 -08002291 }
Amith Yamasani172612c2017-12-15 10:51:53 -08002292
2293 // Look at global settings for this.
2294 // TODO: Maybe apply different thresholds for different users.
2295 try {
2296 mParser.setString(mInjector.getAppIdleSettings());
2297 } catch (IllegalArgumentException e) {
2298 Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
2299 // fallthrough, mParser is empty and all defaults will be returned.
2300 }
2301
Amith Yamasani7e528f32017-10-05 16:06:16 -07002302 synchronized (mAppIdleLock) {
Amith Yamasani7e528f32017-10-05 16:06:16 -07002303
Amith Yamasani17fffee2017-09-29 13:17:43 -07002304 String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
2305 mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
Kweku Adams4297b5d2020-02-06 15:56:12 -08002306 SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS);
Amith Yamasani17fffee2017-09-29 13:17:43 -07002307
Amith Yamasani84cd7b72017-11-07 13:59:37 -08002308 String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
2309 null);
Amith Yamasani17fffee2017-09-29 13:17:43 -07002310 mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
Kweku Adams4297b5d2020-02-06 15:56:12 -08002311 ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS);
Amith Yamasania0ef1ca2017-11-20 09:43:56 -08002312 mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
2313 COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002314 mStrongUsageTimeoutMillis = mParser.getDurationMillis(
2315 KEY_STRONG_USAGE_HOLD_DURATION,
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002316 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STRONG_USAGE_TIMEOUT);
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002317 mNotificationSeenTimeoutMillis = mParser.getDurationMillis(
2318 KEY_NOTIFICATION_SEEN_HOLD_DURATION,
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002319 COMPRESS_TIME ? 12 * ONE_MINUTE : DEFAULT_NOTIFICATION_TIMEOUT);
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002320 mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis(
2321 KEY_SYSTEM_UPDATE_HOLD_DURATION,
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002322 COMPRESS_TIME ? 2 * ONE_MINUTE : DEFAULT_SYSTEM_UPDATE_TIMEOUT);
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002323 mPredictionTimeoutMillis = mParser.getDurationMillis(
2324 KEY_PREDICTION_TIMEOUT,
Amith Yamasani119be9a2018-02-18 22:23:00 -08002325 COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002326 mSyncAdapterTimeoutMillis = mParser.getDurationMillis(
2327 KEY_SYNC_ADAPTER_HOLD_DURATION,
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002328 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYNC_ADAPTER_TIMEOUT);
Makoto Onukid5f25d22018-05-22 16:02:17 -07002329
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002330 mExemptedSyncScheduledNonDozeTimeoutMillis = mParser.getDurationMillis(
2331 KEY_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_HOLD_DURATION,
Makoto Onukid5f25d22018-05-22 16:02:17 -07002332 COMPRESS_TIME ? (ONE_MINUTE / 2)
2333 : DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT);
2334
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002335 mExemptedSyncScheduledDozeTimeoutMillis = mParser.getDurationMillis(
2336 KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION,
Makoto Onukid5f25d22018-05-22 16:02:17 -07002337 COMPRESS_TIME ? ONE_MINUTE
2338 : DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT);
2339
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002340 mExemptedSyncStartTimeoutMillis = mParser.getDurationMillis(
2341 KEY_EXEMPTED_SYNC_START_HOLD_DURATION,
Makoto Onukid5f25d22018-05-22 16:02:17 -07002342 COMPRESS_TIME ? ONE_MINUTE
2343 : DEFAULT_EXEMPTED_SYNC_START_TIMEOUT);
2344
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002345 mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis(
Kweku Adams0140ff82019-10-08 17:04:28 -07002346 KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
Kweku Adams4297b5d2020-02-06 15:56:12 -08002347 COMPRESS_TIME
2348 ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
Michael Wachenschwanzc3295202019-02-20 17:19:52 -08002349
2350 mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
2351 KEY_SYSTEM_INTERACTION_HOLD_DURATION,
Amith Yamasani7f53c7b2018-03-25 21:55:50 -07002352 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
Michael Wachenschwanz6ced0ee2019-04-15 16:43:28 -07002353
2354 mInitialForegroundServiceStartTimeoutMillis = mParser.getDurationMillis(
2355 KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
2356 COMPRESS_TIME ? ONE_MINUTE :
2357 DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
Kweku Adams109cd9c2020-02-11 11:10:52 -08002358
2359 mInjector.mAutoRestrictedBucketDelayMs = Math.max(
2360 COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
2361 mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
2362 COMPRESS_TIME
2363 ? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
Michael Wachenschwanz9a3501d2020-02-10 23:17:28 -08002364
2365 mLinkCrossProfileApps = mParser.getBoolean(
2366 KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS,
2367 DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS);
Kweku Adams2d79ce52020-05-05 12:31:35 -07002368
2369 mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled();
Amith Yamasani17fffee2017-09-29 13:17:43 -07002370 }
Kweku Adams1e8947c2018-11-05 18:06:13 -08002371
2372 // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
2373 // in case we need to change something based on the new values.
2374 setAppIdleEnabled(mInjector.isAppIdleEnabled());
Amith Yamasani17fffee2017-09-29 13:17:43 -07002375 }
2376
Kweku Adams4297b5d2020-02-06 15:56:12 -08002377 long[] parseLongArray(String values, long[] defaults, long[] minValues) {
Amith Yamasani17fffee2017-09-29 13:17:43 -07002378 if (values == null) return defaults;
2379 if (values.isEmpty()) {
2380 // Reset to defaults
2381 return defaults;
2382 } else {
2383 String[] thresholds = values.split("/");
2384 if (thresholds.length == THRESHOLD_BUCKETS.length) {
Kweku Adams4297b5d2020-02-06 15:56:12 -08002385 if (minValues.length != THRESHOLD_BUCKETS.length) {
2386 Slog.wtf(TAG, "minValues array is the wrong size");
2387 // Use zeroes as the minimums.
2388 minValues = new long[THRESHOLD_BUCKETS.length];
2389 }
Amith Yamasani17fffee2017-09-29 13:17:43 -07002390 long[] array = new long[THRESHOLD_BUCKETS.length];
2391 for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
Amith Yamasani761d3ff2017-12-14 17:50:03 -08002392 try {
2393 if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) {
Kweku Adams4297b5d2020-02-06 15:56:12 -08002394 array[i] = Math.max(minValues[i],
2395 Duration.parse(thresholds[i]).toMillis());
Amith Yamasani761d3ff2017-12-14 17:50:03 -08002396 } else {
Kweku Adams4297b5d2020-02-06 15:56:12 -08002397 array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i]));
Amith Yamasani761d3ff2017-12-14 17:50:03 -08002398 }
2399 } catch (NumberFormatException|DateTimeParseException e) {
2400 return defaults;
2401 }
Amith Yamasani17fffee2017-09-29 13:17:43 -07002402 }
2403 return array;
2404 } else {
2405 return defaults;
2406 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07002407 }
2408 }
2409 }
Amith Yamasani7e528f32017-10-05 16:06:16 -07002410}