blob: 9eda046fa1e43b2748bd6e9f091ec04b5c321997 [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 -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;
27import android.os.SystemClock;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070028import android.os.UserHandle;
Dianne Hackbornf9bac162017-04-20 17:17:48 -070029import android.util.ArraySet;
Christopher Tate851f3d512014-05-14 17:01:58 -070030import android.util.Slog;
31
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;
Christopher Tate851f3d512014-05-14 17:01:58 -070035
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070036public final class IdleController extends StateController {
Christopher Tate851f3d512014-05-14 17:01:58 -070037 private static final String TAG = "IdleController";
Christopher Tate851f3d512014-05-14 17:01:58 -070038
Yao Chenca5edbb2016-01-13 14:44:36 -080039 // Policy: we decide that we're "idle" if the device has been unused /
40 // screen off or dreaming for at least this long
41 private long mInactivityIdleThreshold;
42 private long mIdleWindowSlop;
Dianne Hackbornf9bac162017-04-20 17:17:48 -070043 final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
Christopher Tate851f3d512014-05-14 17:01:58 -070044 IdlenessTracker mIdleTracker;
45
46 // Singleton factory
47 private static Object sCreationLock = new Object();
48 private static volatile IdleController sController;
49
Christopher Tate7060b042014-06-09 19:50:00 -070050 public static IdleController get(JobSchedulerService service) {
Christopher Tate851f3d512014-05-14 17:01:58 -070051 synchronized (sCreationLock) {
52 if (sController == null) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -080053 sController = new IdleController(service, service.getContext(), service.getLock());
Christopher Tate851f3d512014-05-14 17:01:58 -070054 }
55 return sController;
56 }
57 }
58
Dianne Hackborn33d31c52016-02-16 10:30:33 -080059 private IdleController(StateChangedListener stateChangedListener, Context context,
60 Object lock) {
61 super(stateChangedListener, context, lock);
Christopher Tate851f3d512014-05-14 17:01:58 -070062 initIdleStateTracking();
63 }
64
65 /**
66 * StateController interface
67 */
68 @Override
Dianne Hackbornb0001f62016-02-16 10:30:33 -080069 public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
Christopher Tate851f3d512014-05-14 17:01:58 -070070 if (taskStatus.hasIdleConstraint()) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -080071 mTrackedTasks.add(taskStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -070072 taskStatus.setTrackingController(JobStatus.TRACKING_IDLE);
Dianne Hackbornb0001f62016-02-16 10:30:33 -080073 taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle());
Christopher Tate851f3d512014-05-14 17:01:58 -070074 }
75 }
76
77 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -070078 public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
79 boolean forUpdate) {
80 if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) {
81 mTrackedTasks.remove(taskStatus);
82 }
Christopher Tate851f3d512014-05-14 17:01:58 -070083 }
84
85 /**
86 * Interaction with the task manager service
87 */
88 void reportNewIdleState(boolean isIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -080089 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -070090 for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
91 mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
Christopher Tate851f3d512014-05-14 17:01:58 -070092 }
93 }
Matthew Williams9b9244b62014-05-14 11:06:04 -070094 mStateChangedListener.onControllerStateChanged();
Christopher Tate851f3d512014-05-14 17:01:58 -070095 }
96
97 /**
98 * Idle state tracking, and messaging with the task manager when
99 * significant state changes occur
100 */
101 private void initIdleStateTracking() {
Yao Chenca5edbb2016-01-13 14:44:36 -0800102 mInactivityIdleThreshold = mContext.getResources().getInteger(
103 com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
104 mIdleWindowSlop = mContext.getResources().getInteger(
105 com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
Christopher Tate851f3d512014-05-14 17:01:58 -0700106 mIdleTracker = new IdlenessTracker();
107 mIdleTracker.startTracking();
108 }
109
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700110 final class IdlenessTracker extends BroadcastReceiver {
Christopher Tate851f3d512014-05-14 17:01:58 -0700111 private AlarmManager mAlarm;
112 private PendingIntent mIdleTriggerIntent;
113 boolean mIdle;
Kevin Zhuccfe8732015-06-29 16:06:49 -0700114 boolean mScreenOn;
Christopher Tate851f3d512014-05-14 17:01:58 -0700115
116 public IdlenessTracker() {
117 mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
118
Christopher Tate27d92e42016-05-06 11:25:11 -0700119 Intent intent = new Intent(ActivityManagerService.ACTION_TRIGGER_IDLE)
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700120 .setPackage("android")
121 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
Christopher Tate851f3d512014-05-14 17:01:58 -0700122 mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
123
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700124 // At boot we presume that the user has just "interacted" with the
125 // device in some meaningful way.
Christopher Tate851f3d512014-05-14 17:01:58 -0700126 mIdle = false;
Kevin Zhuccfe8732015-06-29 16:06:49 -0700127 mScreenOn = true;
Christopher Tate851f3d512014-05-14 17:01:58 -0700128 }
129
130 public boolean isIdle() {
131 return mIdle;
132 }
133
134 public void startTracking() {
135 IntentFilter filter = new IntentFilter();
136
137 // Screen state
138 filter.addAction(Intent.ACTION_SCREEN_ON);
139 filter.addAction(Intent.ACTION_SCREEN_OFF);
140
141 // Dreaming state
142 filter.addAction(Intent.ACTION_DREAMING_STARTED);
143 filter.addAction(Intent.ACTION_DREAMING_STOPPED);
144
Christopher Tateb9583c92014-07-24 17:03:22 -0700145 // Debugging/instrumentation
Christopher Tate27d92e42016-05-06 11:25:11 -0700146 filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
Christopher Tateb9583c92014-07-24 17:03:22 -0700147
Christopher Tate851f3d512014-05-14 17:01:58 -0700148 mContext.registerReceiver(this, filter);
149 }
150
151 @Override
152 public void onReceive(Context context, Intent intent) {
153 final String action = intent.getAction();
Christopher Tate851f3d512014-05-14 17:01:58 -0700154 if (action.equals(Intent.ACTION_SCREEN_ON)
155 || action.equals(Intent.ACTION_DREAMING_STOPPED)) {
Kevin Zhuccfe8732015-06-29 16:06:49 -0700156 if (DEBUG) {
157 Slog.v(TAG,"exiting idle : " + action);
158 }
159 mScreenOn = true;
160 //cancel the alarm
161 mAlarm.cancel(mIdleTriggerIntent);
Christopher Tate851f3d512014-05-14 17:01:58 -0700162 if (mIdle) {
Kevin Zhuccfe8732015-06-29 16:06:49 -0700163 // possible transition to not-idle
Christopher Tate851f3d512014-05-14 17:01:58 -0700164 mIdle = false;
165 reportNewIdleState(mIdle);
166 }
167 } else if (action.equals(Intent.ACTION_SCREEN_OFF)
168 || action.equals(Intent.ACTION_DREAMING_STARTED)) {
169 // when the screen goes off or dreaming starts, we schedule the
170 // alarm that will tell us when we have decided the device is
171 // truly idle.
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700172 final long nowElapsed = SystemClock.elapsedRealtime();
Yao Chenca5edbb2016-01-13 14:44:36 -0800173 final long when = nowElapsed + mInactivityIdleThreshold;
Christopher Tate851f3d512014-05-14 17:01:58 -0700174 if (DEBUG) {
Matthew Williamsbe0c4172014-08-06 18:14:16 -0700175 Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
176 + when);
Christopher Tate851f3d512014-05-14 17:01:58 -0700177 }
Kevin Zhuccfe8732015-06-29 16:06:49 -0700178 mScreenOn = false;
Christopher Tate851f3d512014-05-14 17:01:58 -0700179 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Yao Chenca5edbb2016-01-13 14:44:36 -0800180 when, mIdleWindowSlop, mIdleTriggerIntent);
Christopher Tate27d92e42016-05-06 11:25:11 -0700181 } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
Kevin Zhuccfe8732015-06-29 16:06:49 -0700182 // idle time starts now. Do not set mIdle if screen is on.
183 if (!mIdle && !mScreenOn) {
Christopher Tate851f3d512014-05-14 17:01:58 -0700184 if (DEBUG) {
185 Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime());
186 }
187 mIdle = true;
188 reportNewIdleState(mIdle);
Christopher Tate7234fc62017-04-03 17:36:07 -0700189 } else {
190 if (DEBUG) {
191 Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
192 + mIdle + " screen=" + mScreenOn);
193 }
Christopher Tate851f3d512014-05-14 17:01:58 -0700194 }
195 }
196 }
197 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700198
199 @Override
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700200 public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800201 pw.print("Idle: ");
Christopher Tate7234fc62017-04-03 17:36:07 -0700202 pw.println(mIdleTracker.isIdle());
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700203 pw.print("Tracking ");
204 pw.print(mTrackedTasks.size());
205 pw.println(":");
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800206 for (int i = 0; i < mTrackedTasks.size(); i++) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700207 final JobStatus js = mTrackedTasks.valueAt(i);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -0700208 if (!js.shouldDump(filterUid)) {
209 continue;
210 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700211 pw.print(" #");
212 js.printUniqueId(pw);
213 pw.print(" from ");
214 UserHandle.formatUid(pw, js.getSourceUid());
215 pw.println();
Christopher Tate5eeb59c2014-07-22 10:50:22 -0700216 }
Matthew Williamseffacfa2014-06-05 20:56:40 -0700217 }
Christopher Tate851f3d512014-05-14 17:01:58 -0700218}