blob: 323a12634b39192c5fc0c6570b2153fcae04579a [file] [log] [blame]
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.job.controllers;
18
Suprabh Shukla106203b2017-11-02 21:23:44 -070019import android.app.job.JobInfo;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070020import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
Suprabh Shukla106203b2017-11-02 21:23:44 -070024import android.os.Handler;
25import android.os.Looper;
26import android.os.Message;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070027import android.os.PowerManager;
28import android.os.UserHandle;
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -070029import android.util.ArraySet;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070030import android.util.Log;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070031import android.util.Slog;
Suprabh Shukla106203b2017-11-02 21:23:44 -070032import android.util.SparseBooleanArray;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080033import android.util.proto.ProtoOutputStream;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070034
35import com.android.internal.util.ArrayUtils;
36import com.android.server.DeviceIdleController;
37import com.android.server.LocalServices;
38import com.android.server.job.JobSchedulerService;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070039import com.android.server.job.JobStore;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080040import com.android.server.job.StateControllerProto;
41import com.android.server.job.StateControllerProto.DeviceIdleJobsController.TrackedJob;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070042
43import java.io.PrintWriter;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070044import java.util.Arrays;
45
46/**
47 * When device is dozing, set constraint for all jobs, except whitelisted apps, as not satisfied.
48 * When device is not dozing, set constraint for all jobs as satisfied.
49 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070050public final class DeviceIdleJobsController extends StateController {
Jeff Sharkey01bb5302018-02-21 20:12:40 -070051 private static final String TAG = "JobScheduler.DeviceIdle";
52 private static final boolean DEBUG = JobSchedulerService.DEBUG
53 || Log.isLoggable(TAG, Log.DEBUG);
Amith Yamasanicb926fc2016-03-14 17:15:20 -070054
Suprabh Shukla106203b2017-11-02 21:23:44 -070055 private static final long BACKGROUND_JOBS_DELAY = 3000;
56
57 static final int PROCESS_BACKGROUND_JOBS = 1;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070058
59 // Singleton factory
60 private static Object sCreationLock = new Object();
Amith Yamasanicb926fc2016-03-14 17:15:20 -070061 private static DeviceIdleJobsController sController;
62
Suprabh Shukla106203b2017-11-02 21:23:44 -070063 /**
64 * These are jobs added with a special flag to indicate that they should be exempted from doze
65 * when the app is temp whitelisted or in the foreground.
66 */
67 private final ArraySet<JobStatus> mAllowInIdleJobs;
68 private final SparseBooleanArray mForegroundUids;
69 private final DeviceIdleUpdateFunctor mDeviceIdleUpdateFunctor;
70 private final DeviceIdleJobsDelayHandler mHandler;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070071 private final JobSchedulerService mJobSchedulerService;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070072 private final PowerManager mPowerManager;
73 private final DeviceIdleController.LocalService mLocalDeviceIdleController;
74
75 /**
76 * True when in device idle mode, so we don't want to schedule any jobs.
77 */
78 private boolean mDeviceIdleMode;
79 private int[] mDeviceIdleWhitelistAppIds;
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -070080 private int[] mPowerSaveTempWhitelistAppIds;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070081
Amith Yamasanicb926fc2016-03-14 17:15:20 -070082 /**
83 * Returns a singleton for the DeviceIdleJobsController
84 */
85 public static DeviceIdleJobsController get(JobSchedulerService service) {
86 synchronized (sCreationLock) {
87 if (sController == null) {
88 sController = new DeviceIdleJobsController(service, service.getContext(),
89 service.getLock());
90 }
91 return sController;
92 }
93 }
94
95 // onReceive
96 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
97 @Override
98 public void onReceive(Context context, Intent intent) {
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -070099 switch (intent.getAction()) {
100 case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
101 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
102 updateIdleMode(mPowerManager != null && (mPowerManager.isDeviceIdleMode()
103 || mPowerManager.isLightDeviceIdleMode()));
104 break;
105 case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
106 synchronized (mLock) {
107 mDeviceIdleWhitelistAppIds =
108 mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds();
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700109 if (DEBUG) {
110 Slog.d(TAG, "Got whitelist "
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700111 + Arrays.toString(mDeviceIdleWhitelistAppIds));
112 }
113 }
114 break;
115 case PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED:
116 synchronized (mLock) {
117 mPowerSaveTempWhitelistAppIds =
118 mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700119 if (DEBUG) {
120 Slog.d(TAG, "Got temp whitelist "
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700121 + Arrays.toString(mPowerSaveTempWhitelistAppIds));
122 }
123 boolean changed = false;
Suprabh Shukla106203b2017-11-02 21:23:44 -0700124 for (int i = 0; i < mAllowInIdleJobs.size(); i++) {
125 changed |= updateTaskStateLocked(mAllowInIdleJobs.valueAt(i));
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700126 }
127 if (changed) {
128 mStateChangedListener.onControllerStateChanged();
129 }
130 }
131 break;
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700132 }
133 }
134 };
135
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700136 private DeviceIdleJobsController(JobSchedulerService jobSchedulerService, Context context,
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700137 Object lock) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700138 super(jobSchedulerService, context, lock);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700139
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700140 mJobSchedulerService = jobSchedulerService;
Suprabh Shukla106203b2017-11-02 21:23:44 -0700141 mHandler = new DeviceIdleJobsDelayHandler(context.getMainLooper());
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700142 // Register for device idle mode changes
143 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
144 mLocalDeviceIdleController =
145 LocalServices.getService(DeviceIdleController.LocalService.class);
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700146 mDeviceIdleWhitelistAppIds = mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds();
147 mPowerSaveTempWhitelistAppIds =
148 mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
Suprabh Shukla106203b2017-11-02 21:23:44 -0700149 mDeviceIdleUpdateFunctor = new DeviceIdleUpdateFunctor();
150 mAllowInIdleJobs = new ArraySet<>();
151 mForegroundUids = new SparseBooleanArray();
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700152 final IntentFilter filter = new IntentFilter();
153 filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
154 filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
155 filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700156 filter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700157 mContext.registerReceiverAsUser(
158 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
159 }
160
161 void updateIdleMode(boolean enabled) {
162 boolean changed = false;
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700163 synchronized (mLock) {
164 if (mDeviceIdleMode != enabled) {
165 changed = true;
166 }
167 mDeviceIdleMode = enabled;
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700168 if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
Suprabh Shukla106203b2017-11-02 21:23:44 -0700169 if (enabled) {
170 mHandler.removeMessages(PROCESS_BACKGROUND_JOBS);
171 mJobSchedulerService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
172 } else {
173 // When coming out of doze, process all foreground uids immediately, while others
174 // will be processed after a delay of 3 seconds.
175 for (int i = 0; i < mForegroundUids.size(); i++) {
176 if (mForegroundUids.valueAt(i)) {
177 mJobSchedulerService.getJobStore().forEachJobForSourceUid(
178 mForegroundUids.keyAt(i), mDeviceIdleUpdateFunctor);
179 }
180 }
181 mHandler.sendEmptyMessageDelayed(PROCESS_BACKGROUND_JOBS, BACKGROUND_JOBS_DELAY);
182 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700183 }
184 // Inform the job scheduler service about idle mode changes
185 if (changed) {
186 mStateChangedListener.onDeviceIdleStateChanged(enabled);
187 }
188 }
189
190 /**
Suprabh Shukla106203b2017-11-02 21:23:44 -0700191 * Called by jobscheduler service to report uid state changes between active and idle
192 */
193 public void setUidActiveLocked(int uid, boolean active) {
194 final boolean changed = (active != mForegroundUids.get(uid));
195 if (!changed) {
196 return;
197 }
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700198 if (DEBUG) {
199 Slog.d(TAG, "uid " + uid + " going " + (active ? "active" : "inactive"));
Suprabh Shukla106203b2017-11-02 21:23:44 -0700200 }
201 mForegroundUids.put(uid, active);
202 mDeviceIdleUpdateFunctor.mChanged = false;
203 mJobSchedulerService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor);
204 if (mDeviceIdleUpdateFunctor.mChanged) {
205 mStateChangedListener.onControllerStateChanged();
206 }
207 }
208
209 /**
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700210 * Checks if the given job's scheduling app id exists in the device idle user whitelist.
211 */
212 boolean isWhitelistedLocked(JobStatus job) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700213 return Arrays.binarySearch(mDeviceIdleWhitelistAppIds,
214 UserHandle.getAppId(job.getSourceUid())) >= 0;
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700215 }
216
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700217 /**
218 * Checks if the given job's scheduling app id exists in the device idle temp whitelist.
219 */
220 boolean isTempWhitelistedLocked(JobStatus job) {
221 return ArrayUtils.contains(mPowerSaveTempWhitelistAppIds,
222 UserHandle.getAppId(job.getSourceUid()));
223 }
224
225 private boolean updateTaskStateLocked(JobStatus task) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700226 final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0)
227 && (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task));
228 final boolean whitelisted = isWhitelistedLocked(task);
229 final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle;
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700230 return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700231 }
232
233 @Override
234 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700235 if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
236 mAllowInIdleJobs.add(jobStatus);
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700237 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700238 updateTaskStateLocked(jobStatus);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700239 }
240
241 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700242 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
243 boolean forUpdate) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700244 if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
245 mAllowInIdleJobs.remove(jobStatus);
246 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700247 }
248
249 @Override
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700250 public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700251 pw.println("DeviceIdleJobsController");
Suprabh Shukla106203b2017-11-02 21:23:44 -0700252 pw.println("mDeviceIdleMode=" + mDeviceIdleMode);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700253 mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
254 @Override public void process(JobStatus jobStatus) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700255 if (!jobStatus.shouldDump(filterUid)) {
256 return;
257 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700258 pw.print(" #");
259 jobStatus.printUniqueId(pw);
260 pw.print(" from ");
261 UserHandle.formatUid(pw, jobStatus.getSourceUid());
262 pw.print(": ");
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700263 pw.print(jobStatus.getSourcePackageName());
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700264 pw.print((jobStatus.satisfiedConstraints
265 & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0
266 ? " RUNNABLE" : " WAITING");
267 if (jobStatus.dozeWhitelisted) {
268 pw.print(" WHITELISTED");
269 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700270 if (mAllowInIdleJobs.contains(jobStatus)) {
271 pw.print(" ALLOWED_IN_DOZE");
272 }
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700273 pw.println();
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700274 }
275 });
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700276 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700277
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800278 @Override
279 public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
280 final long token = proto.start(fieldId);
281 final long mToken = proto.start(StateControllerProto.DEVICE_IDLE);
282
283 proto.write(StateControllerProto.DeviceIdleJobsController.IS_DEVICE_IDLE_MODE,
284 mDeviceIdleMode);
285 mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
286 @Override public void process(JobStatus jobStatus) {
287 if (!jobStatus.shouldDump(filterUid)) {
288 return;
289 }
290 final long jsToken =
291 proto.start(StateControllerProto.DeviceIdleJobsController.TRACKED_JOBS);
292
293 jobStatus.writeToShortProto(proto, TrackedJob.INFO);
294 proto.write(TrackedJob.SOURCE_UID, jobStatus.getSourceUid());
295 proto.write(TrackedJob.SOURCE_PACKAGE_NAME, jobStatus.getSourcePackageName());
296 proto.write(TrackedJob.ARE_CONSTRAINTS_SATISFIED,
297 (jobStatus.satisfiedConstraints &
298 JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0);
299 proto.write(TrackedJob.IS_DOZE_WHITELISTED, jobStatus.dozeWhitelisted);
300 proto.write(TrackedJob.IS_ALLOWED_IN_DOZE, mAllowInIdleJobs.contains(jobStatus));
301
302 proto.end(jsToken);
303 }
304 });
305
306 proto.end(mToken);
307 proto.end(token);
308 }
309
Suprabh Shukla106203b2017-11-02 21:23:44 -0700310 final class DeviceIdleUpdateFunctor implements JobStore.JobStatusFunctor {
311 boolean mChanged;
312
313 @Override
314 public void process(JobStatus jobStatus) {
315 mChanged |= updateTaskStateLocked(jobStatus);
316 }
317 }
318
319 final class DeviceIdleJobsDelayHandler extends Handler {
320 public DeviceIdleJobsDelayHandler(Looper looper) {
321 super(looper);
322 }
323
324 @Override
325 public void handleMessage(Message msg) {
326 switch (msg.what) {
327 case PROCESS_BACKGROUND_JOBS:
328 // Just process all the jobs, the ones in foreground should already be running.
329 synchronized (mLock) {
330 mDeviceIdleUpdateFunctor.mChanged = false;
331 mJobSchedulerService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
332 if (mDeviceIdleUpdateFunctor.mChanged) {
333 mStateChangedListener.onControllerStateChanged();
334 }
335 }
336 break;
337 }
338 }
339 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800340}