blob: 02d4f403424437017996043d7bf8a3d2b63cff4c [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.job.controllers;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.util.Slog;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Controls when apps are considered idle and if jobs pertaining to those apps should
* be executed. Apps that haven't been actively launched or accessed from a foreground app
* for a certain amount of time (maybe hours or days) are considered idle. When the app comes
* out of idle state, it will be allowed to run scheduled jobs.
*/
public class AppIdleController extends StateController {
private static final String LOG_TAG = "AppIdleController";
private static final boolean DEBUG = false;
// Singleton factory
private static Object sCreationLock = new Object();
private static volatile AppIdleController sController;
final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
private final UsageStatsManagerInternal mUsageStatsInternal;
boolean mAppIdleParoleOn;
public static AppIdleController get(JobSchedulerService service) {
synchronized (sCreationLock) {
if (sController == null) {
sController = new AppIdleController(service, service.getContext());
}
return sController;
}
}
private AppIdleController(StateChangedListener stateChangedListener, Context context) {
super(stateChangedListener, context);
mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
}
@Override
public void maybeStartTrackingJob(JobStatus jobStatus) {
synchronized (mTrackedTasks) {
mTrackedTasks.add(jobStatus);
String packageName = jobStatus.job.getService().getPackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
jobStatus.getUserId());
if (DEBUG) {
Slog.d(LOG_TAG, "Start tracking, setting idle state of "
+ packageName + " to " + appIdle);
}
jobStatus.appNotIdleConstraintSatisfied.set(!appIdle);
}
}
@Override
public void maybeStopTrackingJob(JobStatus jobStatus) {
synchronized (mTrackedTasks) {
mTrackedTasks.remove(jobStatus);
}
}
@Override
public void dumpControllerState(PrintWriter pw) {
pw.println("AppIdle");
pw.println("Parole On: " + mAppIdleParoleOn);
synchronized (mTrackedTasks) {
for (JobStatus task : mTrackedTasks) {
pw.print(task.job.getService().getPackageName());
pw.print(":idle=" + !task.appNotIdleConstraintSatisfied.get());
pw.print(", ");
}
pw.println();
}
}
void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
// Flag if any app's idle state has changed
boolean changed = false;
synchronized (mTrackedTasks) {
if (mAppIdleParoleOn == isAppIdleParoleOn) {
return;
}
mAppIdleParoleOn = isAppIdleParoleOn;
for (JobStatus task : mTrackedTasks) {
String packageName = task.job.getService().getPackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
task.getUserId());
if (DEBUG) {
Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
}
if (task.appNotIdleConstraintSatisfied.get() == appIdle) {
task.appNotIdleConstraintSatisfied.set(!appIdle);
changed = true;
}
}
}
if (changed) {
mStateChangedListener.onControllerStateChanged();
}
}
private class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
boolean changed = false;
synchronized (mTrackedTasks) {
if (mAppIdleParoleOn) {
return;
}
for (JobStatus task : mTrackedTasks) {
if (task.job.getService().getPackageName().equals(packageName)
&& task.getUserId() == userId) {
if (task.appNotIdleConstraintSatisfied.get() != !idle) {
if (DEBUG) {
Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
+ packageName + " to " + idle);
}
task.appNotIdleConstraintSatisfied.set(!idle);
changed = true;
}
}
}
}
if (changed) {
mStateChangedListener.onControllerStateChanged();
}
}
@Override
public void onParoleStateChanged(boolean isParoleOn) {
if (DEBUG) {
Slog.d(LOG_TAG, "Parole on: " + isParoleOn);
}
setAppIdleParoleOn(isParoleOn);
}
}
}