blob: b7eb9e06359183604a5c56e63cef1c21d9cbd12b [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;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070030import android.util.Slog;
Suprabh Shukla106203b2017-11-02 21:23:44 -070031import android.util.SparseBooleanArray;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070032
33import com.android.internal.util.ArrayUtils;
34import com.android.server.DeviceIdleController;
35import com.android.server.LocalServices;
36import com.android.server.job.JobSchedulerService;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070037import com.android.server.job.JobStore;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070038
39import java.io.PrintWriter;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070040import java.util.Arrays;
41
42/**
43 * When device is dozing, set constraint for all jobs, except whitelisted apps, as not satisfied.
44 * When device is not dozing, set constraint for all jobs as satisfied.
45 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070046public final class DeviceIdleJobsController extends StateController {
Amith Yamasanicb926fc2016-03-14 17:15:20 -070047
48 private static final String LOG_TAG = "DeviceIdleJobsController";
49 private static final boolean LOG_DEBUG = false;
Suprabh Shukla106203b2017-11-02 21:23:44 -070050 private static final long BACKGROUND_JOBS_DELAY = 3000;
51
52 static final int PROCESS_BACKGROUND_JOBS = 1;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070053
54 // Singleton factory
55 private static Object sCreationLock = new Object();
Amith Yamasanicb926fc2016-03-14 17:15:20 -070056 private static DeviceIdleJobsController sController;
57
Suprabh Shukla106203b2017-11-02 21:23:44 -070058 /**
59 * These are jobs added with a special flag to indicate that they should be exempted from doze
60 * when the app is temp whitelisted or in the foreground.
61 */
62 private final ArraySet<JobStatus> mAllowInIdleJobs;
63 private final SparseBooleanArray mForegroundUids;
64 private final DeviceIdleUpdateFunctor mDeviceIdleUpdateFunctor;
65 private final DeviceIdleJobsDelayHandler mHandler;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070066 private final JobSchedulerService mJobSchedulerService;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070067 private final PowerManager mPowerManager;
68 private final DeviceIdleController.LocalService mLocalDeviceIdleController;
69
70 /**
71 * True when in device idle mode, so we don't want to schedule any jobs.
72 */
73 private boolean mDeviceIdleMode;
74 private int[] mDeviceIdleWhitelistAppIds;
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -070075 private int[] mPowerSaveTempWhitelistAppIds;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070076
Amith Yamasanicb926fc2016-03-14 17:15:20 -070077 /**
78 * Returns a singleton for the DeviceIdleJobsController
79 */
80 public static DeviceIdleJobsController get(JobSchedulerService service) {
81 synchronized (sCreationLock) {
82 if (sController == null) {
83 sController = new DeviceIdleJobsController(service, service.getContext(),
84 service.getLock());
85 }
86 return sController;
87 }
88 }
89
90 // onReceive
91 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
92 @Override
93 public void onReceive(Context context, Intent intent) {
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -070094 switch (intent.getAction()) {
95 case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
96 case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
97 updateIdleMode(mPowerManager != null && (mPowerManager.isDeviceIdleMode()
98 || mPowerManager.isLightDeviceIdleMode()));
99 break;
100 case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
101 synchronized (mLock) {
102 mDeviceIdleWhitelistAppIds =
103 mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds();
104 if (LOG_DEBUG) {
105 Slog.d(LOG_TAG, "Got whitelist "
106 + Arrays.toString(mDeviceIdleWhitelistAppIds));
107 }
108 }
109 break;
110 case PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED:
111 synchronized (mLock) {
112 mPowerSaveTempWhitelistAppIds =
113 mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
114 if (LOG_DEBUG) {
115 Slog.d(LOG_TAG, "Got temp whitelist "
116 + Arrays.toString(mPowerSaveTempWhitelistAppIds));
117 }
118 boolean changed = false;
Suprabh Shukla106203b2017-11-02 21:23:44 -0700119 for (int i = 0; i < mAllowInIdleJobs.size(); i++) {
120 changed |= updateTaskStateLocked(mAllowInIdleJobs.valueAt(i));
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700121 }
122 if (changed) {
123 mStateChangedListener.onControllerStateChanged();
124 }
125 }
126 break;
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700127 }
128 }
129 };
130
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700131 private DeviceIdleJobsController(JobSchedulerService jobSchedulerService, Context context,
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700132 Object lock) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700133 super(jobSchedulerService, context, lock);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700134
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700135 mJobSchedulerService = jobSchedulerService;
Suprabh Shukla106203b2017-11-02 21:23:44 -0700136 mHandler = new DeviceIdleJobsDelayHandler(context.getMainLooper());
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700137 // Register for device idle mode changes
138 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
139 mLocalDeviceIdleController =
140 LocalServices.getService(DeviceIdleController.LocalService.class);
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700141 mDeviceIdleWhitelistAppIds = mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds();
142 mPowerSaveTempWhitelistAppIds =
143 mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
Suprabh Shukla106203b2017-11-02 21:23:44 -0700144 mDeviceIdleUpdateFunctor = new DeviceIdleUpdateFunctor();
145 mAllowInIdleJobs = new ArraySet<>();
146 mForegroundUids = new SparseBooleanArray();
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700147 final IntentFilter filter = new IntentFilter();
148 filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
149 filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
150 filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700151 filter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700152 mContext.registerReceiverAsUser(
153 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
154 }
155
156 void updateIdleMode(boolean enabled) {
157 boolean changed = false;
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700158 synchronized (mLock) {
159 if (mDeviceIdleMode != enabled) {
160 changed = true;
161 }
162 mDeviceIdleMode = enabled;
163 if (LOG_DEBUG) Slog.d(LOG_TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
Suprabh Shukla106203b2017-11-02 21:23:44 -0700164 if (enabled) {
165 mHandler.removeMessages(PROCESS_BACKGROUND_JOBS);
166 mJobSchedulerService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
167 } else {
168 // When coming out of doze, process all foreground uids immediately, while others
169 // will be processed after a delay of 3 seconds.
170 for (int i = 0; i < mForegroundUids.size(); i++) {
171 if (mForegroundUids.valueAt(i)) {
172 mJobSchedulerService.getJobStore().forEachJobForSourceUid(
173 mForegroundUids.keyAt(i), mDeviceIdleUpdateFunctor);
174 }
175 }
176 mHandler.sendEmptyMessageDelayed(PROCESS_BACKGROUND_JOBS, BACKGROUND_JOBS_DELAY);
177 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700178 }
179 // Inform the job scheduler service about idle mode changes
180 if (changed) {
181 mStateChangedListener.onDeviceIdleStateChanged(enabled);
182 }
183 }
184
185 /**
Suprabh Shukla106203b2017-11-02 21:23:44 -0700186 * Called by jobscheduler service to report uid state changes between active and idle
187 */
188 public void setUidActiveLocked(int uid, boolean active) {
189 final boolean changed = (active != mForegroundUids.get(uid));
190 if (!changed) {
191 return;
192 }
193 if (LOG_DEBUG) {
194 Slog.d(LOG_TAG, "uid " + uid + " going " + (active ? "active" : "inactive"));
195 }
196 mForegroundUids.put(uid, active);
197 mDeviceIdleUpdateFunctor.mChanged = false;
198 mJobSchedulerService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor);
199 if (mDeviceIdleUpdateFunctor.mChanged) {
200 mStateChangedListener.onControllerStateChanged();
201 }
202 }
203
204 /**
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700205 * Checks if the given job's scheduling app id exists in the device idle user whitelist.
206 */
207 boolean isWhitelistedLocked(JobStatus job) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700208 return Arrays.binarySearch(mDeviceIdleWhitelistAppIds,
209 UserHandle.getAppId(job.getSourceUid())) >= 0;
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700210 }
211
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700212 /**
213 * Checks if the given job's scheduling app id exists in the device idle temp whitelist.
214 */
215 boolean isTempWhitelistedLocked(JobStatus job) {
216 return ArrayUtils.contains(mPowerSaveTempWhitelistAppIds,
217 UserHandle.getAppId(job.getSourceUid()));
218 }
219
220 private boolean updateTaskStateLocked(JobStatus task) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700221 final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0)
222 && (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task));
223 final boolean whitelisted = isWhitelistedLocked(task);
224 final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle;
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700225 return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700226 }
227
228 @Override
229 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700230 if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
231 mAllowInIdleJobs.add(jobStatus);
Suprabh Shuklaa78acfd2017-10-13 19:29:36 -0700232 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700233 updateTaskStateLocked(jobStatus);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700234 }
235
236 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700237 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
238 boolean forUpdate) {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700239 if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) {
240 mAllowInIdleJobs.remove(jobStatus);
241 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700242 }
243
244 @Override
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700245 public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700246 pw.println("DeviceIdleJobsController");
Suprabh Shukla106203b2017-11-02 21:23:44 -0700247 pw.println("mDeviceIdleMode=" + mDeviceIdleMode);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700248 mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
249 @Override public void process(JobStatus jobStatus) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700250 if (!jobStatus.shouldDump(filterUid)) {
251 return;
252 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700253 pw.print(" #");
254 jobStatus.printUniqueId(pw);
255 pw.print(" from ");
256 UserHandle.formatUid(pw, jobStatus.getSourceUid());
257 pw.print(": ");
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700258 pw.print(jobStatus.getSourcePackageName());
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700259 pw.print((jobStatus.satisfiedConstraints
260 & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0
261 ? " RUNNABLE" : " WAITING");
262 if (jobStatus.dozeWhitelisted) {
263 pw.print(" WHITELISTED");
264 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700265 if (mAllowInIdleJobs.contains(jobStatus)) {
266 pw.print(" ALLOWED_IN_DOZE");
267 }
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700268 pw.println();
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700269 }
270 });
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700271 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700272
273 final class DeviceIdleUpdateFunctor implements JobStore.JobStatusFunctor {
274 boolean mChanged;
275
276 @Override
277 public void process(JobStatus jobStatus) {
278 mChanged |= updateTaskStateLocked(jobStatus);
279 }
280 }
281
282 final class DeviceIdleJobsDelayHandler extends Handler {
283 public DeviceIdleJobsDelayHandler(Looper looper) {
284 super(looper);
285 }
286
287 @Override
288 public void handleMessage(Message msg) {
289 switch (msg.what) {
290 case PROCESS_BACKGROUND_JOBS:
291 // Just process all the jobs, the ones in foreground should already be running.
292 synchronized (mLock) {
293 mDeviceIdleUpdateFunctor.mChanged = false;
294 mJobSchedulerService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
295 if (mDeviceIdleUpdateFunctor.mChanged) {
296 mStateChangedListener.onControllerStateChanged();
297 }
298 }
299 break;
300 }
301 }
302 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700303}