blob: a9bc7e0dfb24b2eb3d55e5aa76123b3e0d28de75 [file] [log] [blame]
Christopher Tate851f3d512014-05-14 17:01:58 -07001/*
2 * Copyright (C) 2014 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
Christopher Tate7060b042014-06-09 19:50:00 -070017package com.android.server.job.controllers;
Christopher Tate851f3d512014-05-14 17:01:58 -070018
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070019import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
Christopher Tate851f3d512014-05-14 17:01:58 -070020
21import android.app.AlarmManager;
22import android.app.PendingIntent;
23import android.content.BroadcastReceiver;
Christopher Tate851f3d512014-05-14 17:01:58 -070024import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070027import android.os.UserHandle;
Dianne Hackbornf9bac162017-04-20 17:17:48 -070028import android.util.ArraySet;
Christopher Tate851f3d512014-05-14 17:01:58 -070029import android.util.Slog;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080030import android.util.proto.ProtoOutputStream;
Christopher Tate851f3d512014-05-14 17:01:58 -070031
Christopher Tate27d92e42016-05-06 11:25:11 -070032import com.android.server.am.ActivityManagerService;
Christopher Tate7060b042014-06-09 19:50:00 -070033import com.android.server.job.JobSchedulerService;
34import com.android.server.job.StateChangedListener;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080035import com.android.server.job.StateControllerProto;
Christopher Tate851f3d512014-05-14 17:01:58 -070036
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070037import java.io.PrintWriter;
38
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070039public final class IdleController extends StateController {
Christopher Tate851f3d512014-05-14 17:01:58 -070040 private static final String TAG = "IdleController";
Christopher Tate851f3d512014-05-14 17:01:58 -070041
Yao Chenca5edbb2016-01-13 14:44:36 -080042 // Policy: we decide that we're "idle" if the device has been unused /
43 // screen off or dreaming for at least this long
44 private long mInactivityIdleThreshold;
45 private long mIdleWindowSlop;
Dianne Hackbornf9bac162017-04-20 17:17:48 -070046 final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
Christopher Tate851f3d512014-05-14 17:01:58 -070047 IdlenessTracker mIdleTracker;
48
49 // Singleton factory
50 private static Object sCreationLock = new Object();
51 private static volatile IdleController sController;
52
Christopher Tate7060b042014-06-09 19:50:00 -070053 public static IdleController get(JobSchedulerService service) {
Christopher Tate851f3d512014-05-14 17:01:58 -070054 synchronized (sCreationLock) {
55 if (sController == null) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -080056 sController = new IdleController(service, service.getContext(), service.getLock());
Christopher Tate851f3d512014-05-14 17:01:58 -070057 }
58 return sController;
59 }
60 }
61
Dianne Hackborn33d31c52016-02-16 10:30:33 -080062 private IdleController(StateChangedListener stateChangedListener, Context context,
63 Object lock) {
64 super(stateChangedListener, context, lock);
Christopher Tate851f3d512014-05-14 17:01:58 -070065 initIdleStateTracking();
66 }
67
68 /**
69 * StateController interface
70 */
71 @Override
Dianne Hackbornb0001f62016-02-16 10:30:33 -080072 public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
Christopher Tate851f3d512014-05-14 17:01:58 -070073 if (taskStatus.hasIdleConstraint()) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -080074 mTrackedTasks.add(taskStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -070075 taskStatus.setTrackingController(JobStatus.TRACKING_IDLE);
Dianne Hackbornb0001f62016-02-16 10:30:33 -080076 taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle());
Christopher Tate851f3d512014-05-14 17:01:58 -070077 }
78 }
79
80 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -070081 public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
82 boolean forUpdate) {
83 if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) {
84 mTrackedTasks.remove(taskStatus);
85 }
Christopher Tate851f3d512014-05-14 17:01:58 -070086 }
87
88 /**
89 * Interaction with the task manager service
90 */
91 void reportNewIdleState(boolean isIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -080092 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -070093 for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
94 mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
Christopher Tate851f3d512014-05-14 17:01:58 -070095 }
96 }
Matthew Williams9b9244b62014-05-14 11:06:04 -070097 mStateChangedListener.onControllerStateChanged();
Christopher Tate851f3d512014-05-14 17:01:58 -070098 }
99
100 /**
101 * Idle state tracking, and messaging with the task manager when
102 * significant state changes occur
103 */
104 private void initIdleStateTracking() {
Yao Chenca5edbb2016-01-13 14:44:36 -0800105 mInactivityIdleThreshold = mContext.getResources().getInteger(
106 com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
107 mIdleWindowSlop = mContext.getResources().getInteger(
108 com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
Christopher Tate851f3d512014-05-14 17:01:58 -0700109 mIdleTracker = new IdlenessTracker();
110 mIdleTracker.startTracking();
111 }
112
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700113 final class IdlenessTracker extends BroadcastReceiver {
Christopher Tate851f3d512014-05-14 17:01:58 -0700114 private AlarmManager mAlarm;
115 private PendingIntent mIdleTriggerIntent;
116 boolean mIdle;
Kevin Zhuccfe8732015-06-29 16:06:49 -0700117 boolean mScreenOn;
Christopher Tate851f3d512014-05-14 17:01:58 -0700118
119 public IdlenessTracker() {
120 mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
121
Christopher Tate27d92e42016-05-06 11:25:11 -0700122 Intent intent = new Intent(ActivityManagerService.ACTION_TRIGGER_IDLE)
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700123 .setPackage("android")
124 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Christopher Tate851f3d512014-05-14 17:01:58 -0700125 mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
126
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700127 // At boot we presume that the user has just "interacted" with the
128 // device in some meaningful way.
Christopher Tate851f3d512014-05-14 17:01:58 -0700129 mIdle = false;
Kevin Zhuccfe8732015-06-29 16:06:49 -0700130 mScreenOn = true;
Christopher Tate851f3d512014-05-14 17:01:58 -0700131 }
132
133 public boolean isIdle() {
134 return mIdle;
135 }
136
137 public void startTracking() {
138 IntentFilter filter = new IntentFilter();
139
140 // Screen state
141 filter.addAction(Intent.ACTION_SCREEN_ON);
142 filter.addAction(Intent.ACTION_SCREEN_OFF);
143
144 // Dreaming state
145 filter.addAction(Intent.ACTION_DREAMING_STARTED);
146 filter.addAction(Intent.ACTION_DREAMING_STOPPED);
147
Christopher Tateb9583c92014-07-24 17:03:22 -0700148 // Debugging/instrumentation
Christopher Tate27d92e42016-05-06 11:25:11 -0700149 filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
Christopher Tateb9583c92014-07-24 17:03:22 -0700150
Christopher Tate851f3d512014-05-14 17:01:58 -0700151 mContext.registerReceiver(this, filter);
152 }
153
154 @Override
155 public void onReceive(Context context, Intent intent) {
156 final String action = intent.getAction();
Christopher Tate851f3d512014-05-14 17:01:58 -0700157 if (action.equals(Intent.ACTION_SCREEN_ON)
158 || action.equals(Intent.ACTION_DREAMING_STOPPED)) {
Kevin Zhuccfe8732015-06-29 16:06:49 -0700159 if (DEBUG) {
160 Slog.v(TAG,"exiting idle : " + action);
161 }
162 mScreenOn = true;
163 //cancel the alarm
164 mAlarm.cancel(mIdleTriggerIntent);
Christopher Tate851f3d512014-05-14 17:01:58 -0700165 if (mIdle) {
Kevin Zhuccfe8732015-06-29 16:06:49 -0700166 // possible transition to not-idle
Christopher Tate851f3d512014-05-14 17:01:58 -0700167 mIdle = false;
168 reportNewIdleState(mIdle);
169 }
170 } else if (action.equals(Intent.ACTION_SCREEN_OFF)
171 || action.equals(Intent.ACTION_DREAMING_STARTED)) {
172 // when the screen goes off or dreaming starts, we schedule the
173 // alarm that will tell us when we have decided the device is
174 // truly idle.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700175 final long nowElapsed = sElapsedRealtimeClock.millis();
Yao Chenca5edbb2016-01-13 14:44:36 -0800176 final long when = nowElapsed + mInactivityIdleThreshold;
Christopher Tate851f3d512014-05-14 17:01:58 -0700177 if (DEBUG) {
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700178 Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
179 + when);
Christopher Tate851f3d512014-05-14 17:01:58 -0700180 }
Kevin Zhuccfe8732015-06-29 16:06:49 -0700181 mScreenOn = false;
Christopher Tate851f3d512014-05-14 17:01:58 -0700182 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Yao Chenca5edbb2016-01-13 14:44:36 -0800183 when, mIdleWindowSlop, mIdleTriggerIntent);
Christopher Tate27d92e42016-05-06 11:25:11 -0700184 } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
Kevin Zhuccfe8732015-06-29 16:06:49 -0700185 // idle time starts now. Do not set mIdle if screen is on.
186 if (!mIdle && !mScreenOn) {
Christopher Tate851f3d512014-05-14 17:01:58 -0700187 if (DEBUG) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700188 Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
Christopher Tate851f3d512014-05-14 17:01:58 -0700189 }
190 mIdle = true;
191 reportNewIdleState(mIdle);
Christopher Tate7234fc62017-04-03 17:36:07 -0700192 } else {
193 if (DEBUG) {
194 Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
195 + mIdle + " screen=" + mScreenOn);
196 }
Christopher Tate851f3d512014-05-14 17:01:58 -0700197 }
198 }
199 }
200 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700201
202 @Override
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700203 public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800204 pw.print("Idle: ");
Christopher Tate7234fc62017-04-03 17:36:07 -0700205 pw.println(mIdleTracker.isIdle());
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700206 pw.print("Tracking ");
207 pw.print(mTrackedTasks.size());
208 pw.println(":");
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800209 for (int i = 0; i < mTrackedTasks.size(); i++) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700210 final JobStatus js = mTrackedTasks.valueAt(i);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700211 if (!js.shouldDump(filterUid)) {
212 continue;
213 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700214 pw.print(" #");
215 js.printUniqueId(pw);
216 pw.print(" from ");
217 UserHandle.formatUid(pw, js.getSourceUid());
218 pw.println();
Christopher Tate5eeb59c2014-07-22 10:50:22 -0700219 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700220 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800221
222 @Override
223 public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
224 final long token = proto.start(fieldId);
225 final long mToken = proto.start(StateControllerProto.IDLE);
226
227 proto.write(StateControllerProto.IdleController.IS_IDLE, mIdleTracker.isIdle());
228
229 for (int i = 0; i < mTrackedTasks.size(); i++) {
230 final JobStatus js = mTrackedTasks.valueAt(i);
231 if (!js.shouldDump(filterUid)) {
232 continue;
233 }
234 final long jsToken = proto.start(StateControllerProto.IdleController.TRACKED_JOBS);
235 js.writeToShortProto(proto, StateControllerProto.IdleController.TrackedJob.INFO);
236 proto.write(StateControllerProto.IdleController.TrackedJob.SOURCE_UID,
237 js.getSourceUid());
238 proto.end(jsToken);
239 }
240
241 proto.end(mToken);
242 proto.end(token);
243 }
Christopher Tate851f3d512014-05-14 17:01:58 -0700244}