AppIdleController.java revision 8db0fc15b85c6501a0418b17edee2d9c447b408a
1/* 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; 20import android.content.Context; 21import android.util.Slog; 22 23import com.android.server.LocalServices; 24import com.android.server.job.JobSchedulerService; 25import com.android.server.job.JobStore; 26import com.android.server.job.StateChangedListener; 27 28import java.io.PrintWriter; 29import java.util.ArrayList; 30 31/** 32 * Controls when apps are considered idle and if jobs pertaining to those apps should 33 * be executed. Apps that haven't been actively launched or accessed from a foreground app 34 * for a certain amount of time (maybe hours or days) are considered idle. When the app comes 35 * out of idle state, it will be allowed to run scheduled jobs. 36 */ 37public class AppIdleController extends StateController { 38 39 private static final String LOG_TAG = "AppIdleController"; 40 private static final boolean DEBUG = false; 41 42 // Singleton factory 43 private static Object sCreationLock = new Object(); 44 private static volatile AppIdleController sController; 45 private final JobSchedulerService mJobSchedulerService; 46 private final UsageStatsManagerInternal mUsageStatsInternal; 47 boolean mAppIdleParoleOn; 48 49 final class GlobalUpdateFunc implements JobStore.JobStatusFunctor { 50 boolean mChanged; 51 52 @Override public void process(JobStatus jobStatus) { 53 String packageName = jobStatus.getSourcePackageName(); 54 final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, 55 jobStatus.getSourceUid(), jobStatus.getSourceUserId()); 56 if (DEBUG) { 57 Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle); 58 } 59 if (jobStatus.setAppNotIdleConstraintSatisfied(!appIdle)) { 60 mChanged = true; 61 } 62 } 63 }; 64 65 final static class PackageUpdateFunc implements JobStore.JobStatusFunctor { 66 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 77 @Override public void process(JobStatus jobStatus) { 78 if (jobStatus.getSourcePackageName().equals(mPackage) 79 && jobStatus.getSourceUserId() == mUserId) { 80 if (jobStatus.setAppNotIdleConstraintSatisfied(!mIdle)) { 81 if (DEBUG) { 82 Slog.d(LOG_TAG, "App Idle state changed, setting idle state of " 83 + mPackage + " to " + mIdle); 84 } 85 mChanged = true; 86 } 87 } 88 } 89 }; 90 91 public static AppIdleController get(JobSchedulerService service) { 92 synchronized (sCreationLock) { 93 if (sController == null) { 94 sController = new AppIdleController(service, service.getContext(), 95 service.getLock()); 96 } 97 return sController; 98 } 99 } 100 101 private AppIdleController(JobSchedulerService service, Context context, Object lock) { 102 super(service, context, lock); 103 mJobSchedulerService = service; 104 mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class); 105 mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn(); 106 mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener()); 107 } 108 109 @Override 110 public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { 111 String packageName = jobStatus.getSourcePackageName(); 112 final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, 113 jobStatus.getSourceUid(), jobStatus.getSourceUserId()); 114 if (DEBUG) { 115 Slog.d(LOG_TAG, "Start tracking, setting idle state of " 116 + packageName + " to " + appIdle); 117 } 118 jobStatus.setAppNotIdleConstraintSatisfied(!appIdle); 119 } 120 121 @Override 122 public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { 123 } 124 125 @Override 126 public void dumpControllerStateLocked(final PrintWriter pw) { 127 pw.println("AppIdle"); 128 pw.println("Parole On: " + mAppIdleParoleOn); 129 mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() { 130 @Override public void process(JobStatus jobStatus) { 131 pw.print(" "); 132 pw.print(jobStatus.getSourcePackageName()); 133 pw.print(": runnable="); 134 pw.println((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0); 135 } 136 }); 137 pw.println(); 138 } 139 140 void setAppIdleParoleOn(boolean isAppIdleParoleOn) { 141 // Flag if any app's idle state has changed 142 boolean changed = false; 143 synchronized (mLock) { 144 if (mAppIdleParoleOn == isAppIdleParoleOn) { 145 return; 146 } 147 mAppIdleParoleOn = isAppIdleParoleOn; 148 GlobalUpdateFunc update = new GlobalUpdateFunc(); 149 mJobSchedulerService.getJobStore().forEachJob(update); 150 if (update.mChanged) { 151 changed = true; 152 } 153 } 154 if (changed) { 155 mStateChangedListener.onControllerStateChanged(); 156 } 157 } 158 159 private class AppIdleStateChangeListener 160 extends UsageStatsManagerInternal.AppIdleStateChangeListener { 161 @Override 162 public void onAppIdleStateChanged(String packageName, int userId, boolean idle) { 163 boolean changed = false; 164 synchronized (mLock) { 165 if (mAppIdleParoleOn) { 166 return; 167 } 168 PackageUpdateFunc update = new PackageUpdateFunc(userId, packageName, idle); 169 mJobSchedulerService.getJobStore().forEachJob(update); 170 if (update.mChanged) { 171 changed = true; 172 } 173 } 174 if (changed) { 175 mStateChangedListener.onControllerStateChanged(); 176 } 177 } 178 179 @Override 180 public void onParoleStateChanged(boolean isParoleOn) { 181 if (DEBUG) { 182 Slog.d(LOG_TAG, "Parole on: " + isParoleOn); 183 } 184 setAppIdleParoleOn(isParoleOn); 185 } 186 } 187} 188