DeviceIdleJobsController.java revision e9a988caca733d2f292991a52a0047685a69812f
1/* 2 * Copyright (C) 2016 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 android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.os.PowerManager; 24import android.os.UserHandle; 25import android.util.Slog; 26 27import com.android.internal.util.ArrayUtils; 28import com.android.server.DeviceIdleController; 29import com.android.server.LocalServices; 30import com.android.server.job.JobSchedulerService; 31import com.android.server.job.JobStore; 32 33import java.io.PrintWriter; 34import java.util.Arrays; 35 36/** 37 * When device is dozing, set constraint for all jobs, except whitelisted apps, as not satisfied. 38 * When device is not dozing, set constraint for all jobs as satisfied. 39 */ 40public class DeviceIdleJobsController extends StateController { 41 42 private static final String LOG_TAG = "DeviceIdleJobsController"; 43 private static final boolean LOG_DEBUG = false; 44 45 // Singleton factory 46 private static Object sCreationLock = new Object(); 47 private static DeviceIdleJobsController sController; 48 49 private final JobSchedulerService mJobSchedulerService; 50 private final PowerManager mPowerManager; 51 private final DeviceIdleController.LocalService mLocalDeviceIdleController; 52 53 /** 54 * True when in device idle mode, so we don't want to schedule any jobs. 55 */ 56 private boolean mDeviceIdleMode; 57 private int[] mDeviceIdleWhitelistAppIds; 58 59 final JobStore.JobStatusFunctor mUpdateFunctor = new JobStore.JobStatusFunctor() { 60 @Override public void process(JobStatus jobStatus) { 61 updateTaskStateLocked(jobStatus); 62 } 63 }; 64 65 /** 66 * Returns a singleton for the DeviceIdleJobsController 67 */ 68 public static DeviceIdleJobsController get(JobSchedulerService service) { 69 synchronized (sCreationLock) { 70 if (sController == null) { 71 sController = new DeviceIdleJobsController(service, service.getContext(), 72 service.getLock()); 73 } 74 return sController; 75 } 76 } 77 78 // onReceive 79 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 80 @Override 81 public void onReceive(Context context, Intent intent) { 82 final String action = intent.getAction(); 83 if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action) 84 || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { 85 updateIdleMode(mPowerManager != null 86 ? (mPowerManager.isDeviceIdleMode() 87 || mPowerManager.isLightDeviceIdleMode()) 88 : false); 89 } else if (PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED.equals(action)) { 90 updateWhitelist(); 91 } 92 } 93 }; 94 95 private DeviceIdleJobsController(JobSchedulerService jobSchedulerService, Context context, 96 Object lock) { 97 super(jobSchedulerService, context, lock); 98 99 mJobSchedulerService = jobSchedulerService; 100 // Register for device idle mode changes 101 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 102 mLocalDeviceIdleController = 103 LocalServices.getService(DeviceIdleController.LocalService.class); 104 final IntentFilter filter = new IntentFilter(); 105 filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); 106 filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); 107 filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); 108 mContext.registerReceiverAsUser( 109 mBroadcastReceiver, UserHandle.ALL, filter, null, null); 110 } 111 112 void updateIdleMode(boolean enabled) { 113 boolean changed = false; 114 // Need the whitelist to be ready when going into idle 115 if (mDeviceIdleWhitelistAppIds == null) { 116 updateWhitelist(); 117 } 118 synchronized (mLock) { 119 if (mDeviceIdleMode != enabled) { 120 changed = true; 121 } 122 mDeviceIdleMode = enabled; 123 if (LOG_DEBUG) Slog.d(LOG_TAG, "mDeviceIdleMode=" + mDeviceIdleMode); 124 mJobSchedulerService.getJobStore().forEachJob(mUpdateFunctor); 125 } 126 // Inform the job scheduler service about idle mode changes 127 if (changed) { 128 mStateChangedListener.onDeviceIdleStateChanged(enabled); 129 } 130 } 131 132 /** 133 * Fetches the latest whitelist from the device idle controller. 134 */ 135 void updateWhitelist() { 136 synchronized (mLock) { 137 if (mLocalDeviceIdleController != null) { 138 mDeviceIdleWhitelistAppIds = 139 mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds(); 140 if (LOG_DEBUG) { 141 Slog.d(LOG_TAG, "Got whitelist " + Arrays.toString(mDeviceIdleWhitelistAppIds)); 142 } 143 } 144 } 145 } 146 147 /** 148 * Checks if the given job's scheduling app id exists in the device idle user whitelist. 149 */ 150 boolean isWhitelistedLocked(JobStatus job) { 151 if (mDeviceIdleWhitelistAppIds != null 152 && ArrayUtils.contains(mDeviceIdleWhitelistAppIds, 153 UserHandle.getAppId(job.getSourceUid()))) { 154 return true; 155 } 156 return false; 157 } 158 159 private void updateTaskStateLocked(JobStatus task) { 160 boolean enableTask = !mDeviceIdleMode || isWhitelistedLocked(task); 161 task.setDeviceNotDozingConstraintSatisfied(enableTask); 162 } 163 164 @Override 165 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { 166 synchronized (mLock) { 167 updateTaskStateLocked(jobStatus); 168 } 169 } 170 171 @Override 172 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { 173 } 174 175 @Override 176 public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) { 177 pw.println("DeviceIdleJobsController"); 178 mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() { 179 @Override public void process(JobStatus jobStatus) { 180 if (!jobStatus.shouldDump(filterUid)) { 181 return; 182 } 183 pw.print(" #"); 184 jobStatus.printUniqueId(pw); 185 pw.print(" from "); 186 UserHandle.formatUid(pw, jobStatus.getSourceUid()); 187 pw.print(": "); 188 pw.print(jobStatus.getSourcePackageName()); 189 pw.print(", runnable="); 190 pw.println((jobStatus.satisfiedConstraints 191 & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0); 192 } 193 }); 194 } 195}