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