IdleController.java revision 0529dac587b1361eac359737a5f10972bce5fd0d
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.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.os.SystemClock; 29import android.util.Slog; 30 31import com.android.server.job.JobSchedulerService; 32import com.android.server.job.StateChangedListener; 33 34public class IdleController extends StateController { 35 private static final String TAG = "IdleController"; 36 37 private static final String ACTION_TRIGGER_IDLE = 38 "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE"; 39 40 // Policy: we decide that we're "idle" if the device has been unused / 41 // screen off or dreaming for at least this long 42 private long mInactivityIdleThreshold; 43 private long mIdleWindowSlop; 44 final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); 45 IdlenessTracker mIdleTracker; 46 47 // Singleton factory 48 private static Object sCreationLock = new Object(); 49 private static volatile IdleController sController; 50 51 public static IdleController get(JobSchedulerService service) { 52 synchronized (sCreationLock) { 53 if (sController == null) { 54 sController = new IdleController(service, service.getContext(), service.getLock()); 55 } 56 return sController; 57 } 58 } 59 60 private IdleController(StateChangedListener stateChangedListener, Context context, 61 Object lock) { 62 super(stateChangedListener, context, lock); 63 initIdleStateTracking(); 64 } 65 66 /** 67 * StateController interface 68 */ 69 @Override 70 public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { 71 if (taskStatus.hasIdleConstraint()) { 72 mTrackedTasks.add(taskStatus); 73 taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle()); 74 } 75 } 76 77 @Override 78 public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) { 79 mTrackedTasks.remove(taskStatus); 80 } 81 82 /** 83 * Interaction with the task manager service 84 */ 85 void reportNewIdleState(boolean isIdle) { 86 synchronized (mLock) { 87 for (JobStatus task : mTrackedTasks) { 88 task.setIdleConstraintSatisfied(isIdle); 89 } 90 } 91 mStateChangedListener.onControllerStateChanged(); 92 } 93 94 /** 95 * Idle state tracking, and messaging with the task manager when 96 * significant state changes occur 97 */ 98 private void initIdleStateTracking() { 99 mInactivityIdleThreshold = mContext.getResources().getInteger( 100 com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold); 101 mIdleWindowSlop = mContext.getResources().getInteger( 102 com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop); 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 boolean mScreenOn; 112 113 public IdlenessTracker() { 114 mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 115 116 Intent intent = new Intent(ACTION_TRIGGER_IDLE) 117 .setPackage("android") 118 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 119 mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 120 121 // At boot we presume that the user has just "interacted" with the 122 // device in some meaningful way. 123 mIdle = false; 124 mScreenOn = true; 125 } 126 127 public boolean isIdle() { 128 return mIdle; 129 } 130 131 public void startTracking() { 132 IntentFilter filter = new IntentFilter(); 133 134 // Screen state 135 filter.addAction(Intent.ACTION_SCREEN_ON); 136 filter.addAction(Intent.ACTION_SCREEN_OFF); 137 138 // Dreaming state 139 filter.addAction(Intent.ACTION_DREAMING_STARTED); 140 filter.addAction(Intent.ACTION_DREAMING_STOPPED); 141 142 // Debugging/instrumentation 143 filter.addAction(ACTION_TRIGGER_IDLE); 144 145 mContext.registerReceiver(this, filter); 146 } 147 148 @Override 149 public void onReceive(Context context, Intent intent) { 150 final String action = intent.getAction(); 151 152 if (action.equals(Intent.ACTION_SCREEN_ON) 153 || action.equals(Intent.ACTION_DREAMING_STOPPED)) { 154 if (DEBUG) { 155 Slog.v(TAG,"exiting idle : " + action); 156 } 157 mScreenOn = true; 158 //cancel the alarm 159 mAlarm.cancel(mIdleTriggerIntent); 160 if (mIdle) { 161 // possible transition to not-idle 162 mIdle = false; 163 reportNewIdleState(mIdle); 164 } 165 } else if (action.equals(Intent.ACTION_SCREEN_OFF) 166 || action.equals(Intent.ACTION_DREAMING_STARTED)) { 167 // when the screen goes off or dreaming starts, we schedule the 168 // alarm that will tell us when we have decided the device is 169 // truly idle. 170 final long nowElapsed = SystemClock.elapsedRealtime(); 171 final long when = nowElapsed + mInactivityIdleThreshold; 172 if (DEBUG) { 173 Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when=" 174 + when); 175 } 176 mScreenOn = false; 177 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, 178 when, mIdleWindowSlop, mIdleTriggerIntent); 179 } else if (action.equals(ACTION_TRIGGER_IDLE)) { 180 // idle time starts now. Do not set mIdle if screen is on. 181 if (!mIdle && !mScreenOn) { 182 if (DEBUG) { 183 Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime()); 184 } 185 mIdle = true; 186 reportNewIdleState(mIdle); 187 } 188 } 189 } 190 } 191 192 @Override 193 public void dumpControllerStateLocked(PrintWriter pw) { 194 pw.print("Idle: "); 195 pw.println(mIdleTracker.isIdle() ? "true" : "false"); 196 pw.println(mTrackedTasks.size()); 197 for (int i = 0; i < mTrackedTasks.size(); i++) { 198 final JobStatus js = mTrackedTasks.get(i); 199 pw.print(" "); 200 pw.print(String.valueOf(js.getJobId() + "," + js.getUid())); 201 pw.println(".."); 202 } 203 } 204} 205