IdleController.java revision 27d92e4e397728d56f4f951dd4ce99668c7c447b
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.am.ActivityManagerService; 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 long mInactivityIdleThreshold; 41 private long mIdleWindowSlop; 42 final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); 43 IdlenessTracker mIdleTracker; 44 45 // Singleton factory 46 private static Object sCreationLock = new Object(); 47 private static volatile IdleController sController; 48 49 public static IdleController get(JobSchedulerService service) { 50 synchronized (sCreationLock) { 51 if (sController == null) { 52 sController = new IdleController(service, service.getContext(), service.getLock()); 53 } 54 return sController; 55 } 56 } 57 58 private IdleController(StateChangedListener stateChangedListener, Context context, 59 Object lock) { 60 super(stateChangedListener, context, lock); 61 initIdleStateTracking(); 62 } 63 64 /** 65 * StateController interface 66 */ 67 @Override 68 public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { 69 if (taskStatus.hasIdleConstraint()) { 70 mTrackedTasks.add(taskStatus); 71 taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle()); 72 } 73 } 74 75 @Override 76 public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) { 77 mTrackedTasks.remove(taskStatus); 78 } 79 80 /** 81 * Interaction with the task manager service 82 */ 83 void reportNewIdleState(boolean isIdle) { 84 synchronized (mLock) { 85 for (JobStatus task : mTrackedTasks) { 86 task.setIdleConstraintSatisfied(isIdle); 87 } 88 } 89 mStateChangedListener.onControllerStateChanged(); 90 } 91 92 /** 93 * Idle state tracking, and messaging with the task manager when 94 * significant state changes occur 95 */ 96 private void initIdleStateTracking() { 97 mInactivityIdleThreshold = mContext.getResources().getInteger( 98 com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold); 99 mIdleWindowSlop = mContext.getResources().getInteger( 100 com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop); 101 mIdleTracker = new IdlenessTracker(); 102 mIdleTracker.startTracking(); 103 } 104 105 class IdlenessTracker extends BroadcastReceiver { 106 private AlarmManager mAlarm; 107 private PendingIntent mIdleTriggerIntent; 108 boolean mIdle; 109 boolean mScreenOn; 110 111 public IdlenessTracker() { 112 mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); 113 114 Intent intent = new Intent(ActivityManagerService.ACTION_TRIGGER_IDLE) 115 .setPackage("android") 116 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 117 mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 118 119 // At boot we presume that the user has just "interacted" with the 120 // device in some meaningful way. 121 mIdle = false; 122 mScreenOn = true; 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(ActivityManagerService.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 if (DEBUG) { 153 Slog.v(TAG,"exiting idle : " + action); 154 } 155 mScreenOn = true; 156 //cancel the alarm 157 mAlarm.cancel(mIdleTriggerIntent); 158 if (mIdle) { 159 // possible transition to not-idle 160 mIdle = false; 161 reportNewIdleState(mIdle); 162 } 163 } else if (action.equals(Intent.ACTION_SCREEN_OFF) 164 || action.equals(Intent.ACTION_DREAMING_STARTED)) { 165 // when the screen goes off or dreaming starts, we schedule the 166 // alarm that will tell us when we have decided the device is 167 // truly idle. 168 final long nowElapsed = SystemClock.elapsedRealtime(); 169 final long when = nowElapsed + mInactivityIdleThreshold; 170 if (DEBUG) { 171 Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when=" 172 + when); 173 } 174 mScreenOn = false; 175 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, 176 when, mIdleWindowSlop, mIdleTriggerIntent); 177 } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) { 178 // idle time starts now. Do not set mIdle if screen is on. 179 if (!mIdle && !mScreenOn) { 180 if (DEBUG) { 181 Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime()); 182 } 183 mIdle = true; 184 reportNewIdleState(mIdle); 185 } 186 } 187 } 188 } 189 190 @Override 191 public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { 192 pw.print("Idle: "); 193 pw.println(mIdleTracker.isIdle() ? "true" : "false"); 194 pw.println(mTrackedTasks.size()); 195 for (int i = 0; i < mTrackedTasks.size(); i++) { 196 final JobStatus js = mTrackedTasks.get(i); 197 if (!js.shouldDump(filterUid)) { 198 continue; 199 } 200 pw.print(" "); 201 pw.print(String.valueOf(js.getJobId() + "," + js.getUid())); 202 pw.println(".."); 203 } 204 } 205} 206