1 /* 2 * Copyright (C) 2019 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 17 package com.android.server.job.restrictions; 18 19 import android.app.job.JobInfo; 20 import android.app.job.JobParameters; 21 import android.app.job.JobScheduler; 22 import android.os.PowerManager; 23 import android.os.PowerManager.OnThermalStatusChangedListener; 24 import android.util.IndentingPrintWriter; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.server.job.JobSchedulerService; 28 import com.android.server.job.controllers.JobStatus; 29 30 public class ThermalStatusRestriction extends JobRestriction { 31 private static final String TAG = "ThermalStatusRestriction"; 32 33 /** The threshold at which we start restricting low and min priority jobs. */ 34 private static final int LOW_PRIORITY_THRESHOLD = PowerManager.THERMAL_STATUS_LIGHT; 35 /** The threshold at which we start restricting higher priority jobs. */ 36 private static final int HIGHER_PRIORITY_THRESHOLD = PowerManager.THERMAL_STATUS_MODERATE; 37 /** The lowest threshold at which we start restricting jobs. */ 38 private static final int LOWER_THRESHOLD = LOW_PRIORITY_THRESHOLD; 39 /** The threshold at which we start restricting ALL jobs. */ 40 private static final int UPPER_THRESHOLD = PowerManager.THERMAL_STATUS_SEVERE; 41 42 private volatile int mThermalStatus = PowerManager.THERMAL_STATUS_NONE; 43 ThermalStatusRestriction(JobSchedulerService service)44 public ThermalStatusRestriction(JobSchedulerService service) { 45 super(service, JobParameters.STOP_REASON_DEVICE_STATE, 46 JobScheduler.PENDING_JOB_REASON_DEVICE_STATE, 47 JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); 48 } 49 50 @Override onSystemServicesReady()51 public void onSystemServicesReady() { 52 final PowerManager powerManager = 53 mService.getTestableContext().getSystemService(PowerManager.class); 54 // Use MainExecutor 55 powerManager.addThermalStatusListener(new OnThermalStatusChangedListener() { 56 @Override 57 public void onThermalStatusChanged(int status) { 58 // This is called on the main thread. Do not do any slow operations in it. 59 // mService.onControllerStateChanged() will just post a message, which is okay. 60 61 // There are three buckets: 62 // 1. Below the lower threshold (we don't care about changes within this bucket) 63 // 2. Between the lower and upper thresholds. 64 // -> We care about transitions across buckets 65 // -> We care about transitions within the middle bucket 66 // 3. Upper the upper threshold (we don't care about changes within this bucket) 67 final boolean significantChange = 68 // Handle transitions within and into the bucket we care about (thus 69 // causing us to change our restrictions). 70 (status >= LOWER_THRESHOLD && status <= UPPER_THRESHOLD) 71 // Take care of transitions from the 2nd or 3rd bucket to the 1st 72 // bucket (thus exiting any restrictions we started enforcing). 73 || (mThermalStatus >= LOWER_THRESHOLD && status < LOWER_THRESHOLD) 74 // Take care of transitions from the 1st or 2nd bucket to the 3rd 75 // bucket (thus resulting in us beginning to enforce the tightest 76 // restrictions). 77 || (mThermalStatus < UPPER_THRESHOLD && status > UPPER_THRESHOLD); 78 final boolean increased = mThermalStatus < status; 79 mThermalStatus = status; 80 if (significantChange) { 81 mService.onRestrictionStateChanged(ThermalStatusRestriction.this, increased); 82 } 83 } 84 }); 85 } 86 87 @Override isJobRestricted(JobStatus job)88 public boolean isJobRestricted(JobStatus job) { 89 if (mThermalStatus >= UPPER_THRESHOLD) { 90 return true; 91 } 92 final int priority = job.getEffectivePriority(); 93 if (mThermalStatus >= HIGHER_PRIORITY_THRESHOLD) { 94 // For moderate throttling: 95 // Let all user-initiated jobs run. 96 // Only let expedited jobs run if: 97 // 1. They haven't previously run 98 // 2. They're already running and aren't yet in overtime 99 // Only let high priority jobs run if: 100 // They are already running and aren't yet in overtime 101 // Don't let any other job run. 102 if (job.shouldTreatAsUserInitiatedJob()) { 103 return false; 104 } 105 if (job.shouldTreatAsExpeditedJob()) { 106 return job.getNumPreviousAttempts() > 0 107 || (mService.isCurrentlyRunningLocked(job) 108 && mService.isJobInOvertimeLocked(job)); 109 } 110 if (priority == JobInfo.PRIORITY_HIGH) { 111 return !mService.isCurrentlyRunningLocked(job) 112 || mService.isJobInOvertimeLocked(job); 113 } 114 return true; 115 } 116 if (mThermalStatus >= LOW_PRIORITY_THRESHOLD) { 117 // For light throttling, throttle all min priority jobs and all low priority jobs that 118 // aren't already running or have been running for long enough. 119 return priority == JobInfo.PRIORITY_MIN 120 || (priority == JobInfo.PRIORITY_LOW 121 && (!mService.isCurrentlyRunningLocked(job) 122 || mService.isJobInOvertimeLocked(job))); 123 } 124 return false; 125 } 126 127 @VisibleForTesting getThermalStatus()128 int getThermalStatus() { 129 return mThermalStatus; 130 } 131 132 @Override dumpConstants(IndentingPrintWriter pw)133 public void dumpConstants(IndentingPrintWriter pw) { 134 pw.print("Thermal status: "); 135 pw.println(mThermalStatus); 136 } 137 } 138