blob: bd8fe289ddada112677579cdf4fd8818aa6e868f [file] [log] [blame]
Amith Yamasanib0ff3222015-03-04 09:56:14 -08001/*
2 * Copyright (C) 2015 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.app.usage.UsageStatsManagerInternal;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070020import android.os.UserHandle;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070021import android.util.Log;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080022import android.util.Slog;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080023import android.util.proto.ProtoOutputStream;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080024
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070025import com.android.internal.util.IndentingPrintWriter;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080026import com.android.server.LocalServices;
27import com.android.server.job.JobSchedulerService;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080028import com.android.server.job.StateControllerProto;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080029
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070030import java.util.function.Consumer;
31import java.util.function.Predicate;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080032
33/**
34 * Controls when apps are considered idle and if jobs pertaining to those apps should
35 * be executed. Apps that haven't been actively launched or accessed from a foreground app
36 * for a certain amount of time (maybe hours or days) are considered idle. When the app comes
37 * out of idle state, it will be allowed to run scheduled jobs.
38 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070039public final class AppIdleController extends StateController {
Jeff Sharkey01bb5302018-02-21 20:12:40 -070040 private static final String TAG = "JobScheduler.AppIdle";
41 private static final boolean DEBUG = JobSchedulerService.DEBUG
42 || Log.isLoggable(TAG, Log.DEBUG);
Amith Yamasanib0ff3222015-03-04 09:56:14 -080043
Amith Yamasanib0ff3222015-03-04 09:56:14 -080044 private final UsageStatsManagerInternal mUsageStatsInternal;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070045 private boolean mInitializedParoleOn;
Xiaohui Chen8dca36d2015-06-19 12:44:59 -070046 boolean mAppIdleParoleOn;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080047
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070048 final class GlobalUpdateFunc implements Consumer<JobStatus> {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070049 boolean mChanged;
50
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070051 @Override
52 public void accept(JobStatus jobStatus) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070053 String packageName = jobStatus.getSourcePackageName();
54 final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
55 jobStatus.getSourceUid(), jobStatus.getSourceUserId());
56 if (DEBUG) {
Jeff Sharkey01bb5302018-02-21 20:12:40 -070057 Slog.d(TAG, "Setting idle state of " + packageName + " to " + appIdle);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070058 }
59 if (jobStatus.setAppNotIdleConstraintSatisfied(!appIdle)) {
60 mChanged = true;
61 }
62 }
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070063 }
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070064
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070065 final static class PackageUpdateFunc implements Consumer<JobStatus> {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070066 final int mUserId;
67 final String mPackage;
68 final boolean mIdle;
69 boolean mChanged;
70
71 PackageUpdateFunc(int userId, String pkg, boolean idle) {
72 mUserId = userId;
73 mPackage = pkg;
74 mIdle = idle;
75 }
76
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070077 @Override
78 public void accept(JobStatus jobStatus) {
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070079 if (jobStatus.getSourcePackageName().equals(mPackage)
80 && jobStatus.getSourceUserId() == mUserId) {
81 if (jobStatus.setAppNotIdleConstraintSatisfied(!mIdle)) {
82 if (DEBUG) {
Jeff Sharkey01bb5302018-02-21 20:12:40 -070083 Slog.d(TAG, "App Idle state changed, setting idle state of "
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070084 + mPackage + " to " + mIdle);
85 }
86 mChanged = true;
87 }
88 }
89 }
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070090 }
Dianne Hackborn8db0fc12016-04-12 13:48:25 -070091
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -070092 public AppIdleController(JobSchedulerService service) {
93 super(service);
Amith Yamasanib0ff3222015-03-04 09:56:14 -080094 mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
Dianne Hackborne9a988c2016-05-27 17:59:40 -070095 mAppIdleParoleOn = true;
Xiaohui Chen8dca36d2015-06-19 12:44:59 -070096 mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
Amith Yamasanib0ff3222015-03-04 09:56:14 -080097 }
98
Amith Yamasanib0ff3222015-03-04 09:56:14 -080099 @Override
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800100 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700101 if (!mInitializedParoleOn) {
102 mInitializedParoleOn = true;
103 mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
104 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800105 String packageName = jobStatus.getSourcePackageName();
106 final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
107 jobStatus.getSourceUid(), jobStatus.getSourceUserId());
108 if (DEBUG) {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700109 Slog.d(TAG, "Start tracking, setting idle state of "
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800110 + packageName + " to " + appIdle);
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800111 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800112 jobStatus.setAppNotIdleConstraintSatisfied(!appIdle);
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800113 }
114
115 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700116 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
117 boolean forUpdate) {
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800118 }
119
120 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700121 public void dumpControllerStateLocked(final IndentingPrintWriter pw,
122 final Predicate<JobStatus> predicate) {
123 pw.println("Parole on: " + mAppIdleParoleOn);
124 pw.println();
125
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700126 mService.getJobStore().forEachJob(predicate, (jobStatus) -> {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700127 pw.print("#");
128 jobStatus.printUniqueId(pw);
129 pw.print(" from ");
130 UserHandle.formatUid(pw, jobStatus.getSourceUid());
131 pw.print(": ");
132 pw.print(jobStatus.getSourcePackageName());
133 if ((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0) {
134 pw.println(" RUNNABLE");
135 } else {
136 pw.println(" WAITING");
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700137 }
138 });
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800139 }
140
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800141 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700142 public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
143 Predicate<JobStatus> predicate) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800144 final long token = proto.start(fieldId);
145 final long mToken = proto.start(StateControllerProto.APP_IDLE);
146
147 proto.write(StateControllerProto.AppIdleController.IS_PAROLE_ON, mAppIdleParoleOn);
148
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700149 mService.getJobStore().forEachJob(predicate, (js) -> {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700150 final long jsToken =
151 proto.start(StateControllerProto.AppIdleController.TRACKED_JOBS);
152 js.writeToShortProto(proto, StateControllerProto.AppIdleController.TrackedJob.INFO);
153 proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_UID,
154 js.getSourceUid());
155 proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_PACKAGE_NAME,
156 js.getSourcePackageName());
157 proto.write(
158 StateControllerProto.AppIdleController.TrackedJob.ARE_CONSTRAINTS_SATISFIED,
159 (js.satisfiedConstraints & JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
160 proto.end(jsToken);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800161 });
162
163 proto.end(mToken);
164 proto.end(token);
165 }
166
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700167 void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800168 // Flag if any app's idle state has changed
169 boolean changed = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800170 synchronized (mLock) {
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700171 if (mAppIdleParoleOn == isAppIdleParoleOn) {
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800172 return;
173 }
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700174 mAppIdleParoleOn = isAppIdleParoleOn;
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700175 GlobalUpdateFunc update = new GlobalUpdateFunc();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700176 mService.getJobStore().forEachJob(update);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700177 if (update.mChanged) {
178 changed = true;
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800179 }
180 }
181 if (changed) {
182 mStateChangedListener.onControllerStateChanged();
183 }
184 }
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700185
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700186 private final class AppIdleStateChangeListener
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700187 extends UsageStatsManagerInternal.AppIdleStateChangeListener {
188 @Override
Amith Yamasani84cd7b72017-11-07 13:59:37 -0800189 public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700190 boolean changed = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800191 synchronized (mLock) {
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700192 if (mAppIdleParoleOn) {
193 return;
194 }
Christopher Tatea732f012017-10-26 17:26:53 -0700195
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700196 PackageUpdateFunc update = new PackageUpdateFunc(userId, packageName, idle);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700197 mService.getJobStore().forEachJob(update);
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700198 if (update.mChanged) {
199 changed = true;
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700200 }
201 }
202 if (changed) {
203 mStateChangedListener.onControllerStateChanged();
204 }
205 }
206
207 @Override
208 public void onParoleStateChanged(boolean isParoleOn) {
209 if (DEBUG) {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700210 Slog.d(TAG, "Parole on: " + isParoleOn);
Xiaohui Chen8dca36d2015-06-19 12:44:59 -0700211 }
212 setAppIdleParoleOn(isParoleOn);
213 }
214 }
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800215}