IdleController.java revision 5eeb59cceb1f95813c548c1c5937f161c1ed3571
1/* 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 17package com.android.server.job.controllers; 18 19import java.io.PrintWriter; 20import java.util.ArrayList; 21 22import android.app.AlarmManager; 23import android.app.PendingIntent; 24import android.content.BroadcastReceiver; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.os.SystemClock; 30import android.util.Slog; 31 32import com.android.server.job.JobSchedulerService; 33import com.android.server.job.StateChangedListener; 34 35public class IdleController extends StateController { 36 private static final String TAG = "IdleController"; 37 38 // Policy: we decide that we're "idle" if the device has been unused / 39 // screen off or dreaming for at least this long 40 private static final long INACTIVITY_IDLE_THRESHOLD = 71 * 60 * 1000; // millis; 71 min 41 private static final long IDLE_WINDOW_SLOP = 5 * 60 * 1000; // 5 minute window, to be nice 42 43 private static final String ACTION_TRIGGER_IDLE = 44 "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE"; 45 46 final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); 47 IdlenessTracker mIdleTracker; 48 49 // Singleton factory 50 private static Object sCreationLock = new Object(); 51 private static volatile IdleController sController; 52 53 public static IdleController get(JobSchedulerService service) { 54 synchronized (sCreationLock) { 55 if (sController == null) { 56 sController = new IdleController(service, service.getContext()); 57 } 58 return sController; 59 } 60 } 61 62 private IdleController(StateChangedListener stateChangedListener, Context context) { 63 super(stateChangedListener, context); 64 initIdleStateTracking(); 65 } 66 67 /** 68 * StateController interface 69 */ 70 @Override 71 public void maybeStartTrackingJob(JobStatus taskStatus) { 72 if (taskStatus.hasIdleConstraint()) { 73 synchronized (mTrackedTasks) { 74 mTrackedTasks.add(taskStatus); 75 taskStatus.idleConstraintSatisfied.set(mIdleTracker.isIdle()); 76 } 77 } 78 } 79 80 @Override 81 public void maybeStopTrackingJob(JobStatus taskStatus) { 82 synchronized (mTrackedTasks) { 83 mTrackedTasks.remove(taskStatus); 84 } 85 } 86 87 /** 88 * Interaction with the task manager service 89 */ 90 void reportNewIdleState(boolean isIdle) { 91 synchronized (mTrackedTasks) { 92 for (JobStatus task : mTrackedTasks) { 93 task.idleConstraintSatisfied.set(isIdle); 94 } 95 } 96 mStateChangedListener.onControllerStateChanged(); 97 } 98 99 /** 100 * Idle state tracking, and messaging with the task manager when 101 * significant state changes occur 102 */ 103 private void initIdleStateTracking() { 104 mIdleTracker = new IdlenessTracker(); 105 mIdleTracker.startTracking(); 106 } 107 108 class IdlenessTracker extends BroadcastReceiver { 109 private AlarmManager mAlarm; 110 private PendingIntent mIdleTriggerIntent; 111 boolean mIdle; 112 113 public IdlenessTracker() { 114 mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 115 116 Intent intent = new Intent(ACTION_TRIGGER_IDLE); 117 intent.setComponent(new ComponentName(mContext, this.getClass())); 118 mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 119 120 // at boot we presume that the user has just "interacted" with the 121 // device in some meaningful way 122 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 140 // Debugging/instrumentation 141 filter.addAction(ACTION_TRIGGER_IDLE); 142 143 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. 166 long when = SystemClock.elapsedRealtime() + INACTIVITY_IDLE_THRESHOLD; 167 if (DEBUG) { 168 Slog.v(TAG, "Scheduling idle : " + action + " when=" + when); 169 } 170 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, 171 when, IDLE_WINDOW_SLOP, mIdleTriggerIntent); 172 } else if (action.equals(ACTION_TRIGGER_IDLE)) { 173 // idle time starts now 174 if (!mIdle) { 175 if (DEBUG) { 176 Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime()); 177 } 178 mIdle = true; 179 reportNewIdleState(mIdle); 180 } 181 } 182 } 183 } 184 185 @Override 186 public void dumpControllerState(PrintWriter pw) { 187 synchronized (mTrackedTasks) { 188 pw.print("Idle: "); 189 pw.println(mIdleTracker.isIdle() ? "true" : "false"); 190 pw.println(mTrackedTasks.size()); 191 for (int i = 0; i < mTrackedTasks.size(); i++) { 192 final JobStatus js = mTrackedTasks.get(i); 193 pw.print(" "); 194 pw.print(String.valueOf(js.hashCode()).substring(0, 3)); 195 pw.println(".."); 196 } 197 } 198 } 199} 200