blob: 8e2ca182e44876d114d3a7cdd4f3cd59c6aa7f1f [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
Matthew Williamseffacfa2014-06-05 20:56:40 -070019import java.io.PrintWriter;
Christopher Tate851f3d512014-05-14 17:01:58 -070020import java.util.ArrayList;
21
22import android.app.AlarmManager;
23import android.app.PendingIntent;
24import android.content.BroadcastReceiver;
Christopher Tate851f3d512014-05-14 17:01:58 -070025import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.os.SystemClock;
29import android.util.Slog;
30
Christopher Tate7060b042014-06-09 19:50:00 -070031import com.android.server.job.JobSchedulerService;
32import com.android.server.job.StateChangedListener;
Christopher Tate851f3d512014-05-14 17:01:58 -070033
34public class IdleController extends StateController {
35 private static final String TAG = "IdleController";
Christopher Tate851f3d512014-05-14 17:01:58 -070036
37 // Policy: we decide that we're "idle" if the device has been unused /
38 // screen off or dreaming for at least this long
39 private static final long INACTIVITY_IDLE_THRESHOLD = 71 * 60 * 1000; // millis; 71 min
40 private static final long IDLE_WINDOW_SLOP = 5 * 60 * 1000; // 5 minute window, to be nice
41
42 private static final String ACTION_TRIGGER_IDLE =
43 "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE";
44
Christopher Tate7060b042014-06-09 19:50:00 -070045 final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
Christopher Tate851f3d512014-05-14 17:01:58 -070046 IdlenessTracker mIdleTracker;
47
48 // Singleton factory
49 private static Object sCreationLock = new Object();
50 private static volatile IdleController sController;
51
Christopher Tate7060b042014-06-09 19:50:00 -070052 public static IdleController get(JobSchedulerService service) {
Christopher Tate851f3d512014-05-14 17:01:58 -070053 synchronized (sCreationLock) {
54 if (sController == null) {
Matthew Williams3d86fd22014-05-16 18:02:17 -070055 sController = new IdleController(service, service.getContext());
Christopher Tate851f3d512014-05-14 17:01:58 -070056 }
57 return sController;
58 }
59 }
60
Matthew Williams3d86fd22014-05-16 18:02:17 -070061 private IdleController(StateChangedListener stateChangedListener, Context context) {
62 super(stateChangedListener, context);
Christopher Tate851f3d512014-05-14 17:01:58 -070063 initIdleStateTracking();
64 }
65
66 /**
67 * StateController interface
68 */
69 @Override
Christopher Tate7060b042014-06-09 19:50:00 -070070 public void maybeStartTrackingJob(JobStatus taskStatus) {
Christopher Tate851f3d512014-05-14 17:01:58 -070071 if (taskStatus.hasIdleConstraint()) {
72 synchronized (mTrackedTasks) {
73 mTrackedTasks.add(taskStatus);
74 taskStatus.idleConstraintSatisfied.set(mIdleTracker.isIdle());
75 }
76 }
77 }
78
79 @Override
Christopher Tate7060b042014-06-09 19:50:00 -070080 public void maybeStopTrackingJob(JobStatus taskStatus) {
Christopher Tate851f3d512014-05-14 17:01:58 -070081 synchronized (mTrackedTasks) {
82 mTrackedTasks.remove(taskStatus);
83 }
84 }
85
86 /**
87 * Interaction with the task manager service
88 */
89 void reportNewIdleState(boolean isIdle) {
90 synchronized (mTrackedTasks) {
Christopher Tate7060b042014-06-09 19:50:00 -070091 for (JobStatus task : mTrackedTasks) {
Christopher Tate851f3d512014-05-14 17:01:58 -070092 task.idleConstraintSatisfied.set(isIdle);
Christopher Tate851f3d512014-05-14 17:01:58 -070093 }
94 }
Matthew Williams9b9244b62014-05-14 11:06:04 -070095 mStateChangedListener.onControllerStateChanged();
Christopher Tate851f3d512014-05-14 17:01:58 -070096 }
97
98 /**
99 * Idle state tracking, and messaging with the task manager when
100 * significant state changes occur
101 */
102 private void initIdleStateTracking() {
103 mIdleTracker = new IdlenessTracker();
104 mIdleTracker.startTracking();
105 }
106
107 class IdlenessTracker extends BroadcastReceiver {
108 private AlarmManager mAlarm;
109 private PendingIntent mIdleTriggerIntent;
110 boolean mIdle;
111
112 public IdlenessTracker() {
113 mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
114
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700115 Intent intent = new Intent(ACTION_TRIGGER_IDLE)
116 .setPackage("android")
117 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Christopher Tate851f3d512014-05-14 17:01:58 -0700118 mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
119
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700120 // At boot we presume that the user has just "interacted" with the
121 // device in some meaningful way.
Christopher Tate851f3d512014-05-14 17:01:58 -0700122 mIdle = false;
123 }
124
125 public boolean isIdle() {
126 return mIdle;
127 }
128
129 public void startTracking() {
130 IntentFilter filter = new IntentFilter();
131
132 // Screen state
133 filter.addAction(Intent.ACTION_SCREEN_ON);
134 filter.addAction(Intent.ACTION_SCREEN_OFF);
135
136 // Dreaming state
137 filter.addAction(Intent.ACTION_DREAMING_STARTED);
138 filter.addAction(Intent.ACTION_DREAMING_STOPPED);
139
Christopher Tateb9583c92014-07-24 17:03:22 -0700140 // Debugging/instrumentation
141 filter.addAction(ACTION_TRIGGER_IDLE);
142
Christopher Tate851f3d512014-05-14 17:01:58 -0700143 mContext.registerReceiver(this, filter);
144 }
145
146 @Override
147 public void onReceive(Context context, Intent intent) {
148 final String action = intent.getAction();
149
150 if (action.equals(Intent.ACTION_SCREEN_ON)
151 || action.equals(Intent.ACTION_DREAMING_STOPPED)) {
152 // possible transition to not-idle
153 if (mIdle) {
154 if (DEBUG) {
155 Slog.v(TAG, "exiting idle : " + action);
156 }
157 mAlarm.cancel(mIdleTriggerIntent);
158 mIdle = false;
159 reportNewIdleState(mIdle);
160 }
161 } else if (action.equals(Intent.ACTION_SCREEN_OFF)
162 || action.equals(Intent.ACTION_DREAMING_STARTED)) {
163 // when the screen goes off or dreaming starts, we schedule the
164 // alarm that will tell us when we have decided the device is
165 // truly idle.
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700166 final long nowElapsed = SystemClock.elapsedRealtime();
167 final long when = nowElapsed + INACTIVITY_IDLE_THRESHOLD;
Christopher Tate851f3d512014-05-14 17:01:58 -0700168 if (DEBUG) {
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700169 Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
170 + when);
Christopher Tate851f3d512014-05-14 17:01:58 -0700171 }
172 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
173 when, IDLE_WINDOW_SLOP, mIdleTriggerIntent);
174 } else if (action.equals(ACTION_TRIGGER_IDLE)) {
175 // idle time starts now
176 if (!mIdle) {
177 if (DEBUG) {
178 Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime());
179 }
180 mIdle = true;
181 reportNewIdleState(mIdle);
182 }
183 }
184 }
185 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700186
187 @Override
188 public void dumpControllerState(PrintWriter pw) {
Christopher Tate5eeb59c2014-07-22 10:50:22 -0700189 synchronized (mTrackedTasks) {
190 pw.print("Idle: ");
191 pw.println(mIdleTracker.isIdle() ? "true" : "false");
192 pw.println(mTrackedTasks.size());
193 for (int i = 0; i < mTrackedTasks.size(); i++) {
194 final JobStatus js = mTrackedTasks.get(i);
195 pw.print(" ");
196 pw.print(String.valueOf(js.hashCode()).substring(0, 3));
197 pw.println("..");
198 }
199 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700200 }
Christopher Tate851f3d512014-05-14 17:01:58 -0700201}