summaryrefslogtreecommitdiffstats
path: root/services/core
diff options
context:
space:
mode:
Diffstat (limited to 'services/core')
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java30
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java7
-rw-r--r--services/core/java/com/android/server/job/controllers/AppIdleController.java174
-rw-r--r--services/core/java/com/android/server/job/controllers/JobStatus.java9
-rw-r--r--services/core/java/com/android/server/job/controllers/StateController.java2
6 files changed, 217 insertions, 6 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 7a74e45..c2b0a4d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -73,6 +73,7 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
+ static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || true;
static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : "";
static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b0b410b..cdaa5a3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -43,6 +43,7 @@ import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.res.Resources;
@@ -61,8 +62,8 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.SparseIntArray;
-
import android.view.Display;
+
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.DumpHeapActivity;
@@ -96,7 +97,6 @@ import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
-
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -17854,6 +17854,10 @@ public final class ActivityManagerService extends ActivityManagerNative
app.lastCpuTime = app.curCpuTime;
}
+ // Inform UsageStats of important process state change
+ // Must be called before updating setProcState
+ maybeUpdateUsageStats(app);
+
app.setProcState = app.curProcState;
if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) {
app.notCachedSinceIdle = false;
@@ -17916,6 +17920,28 @@ public final class ActivityManagerService extends ActivityManagerNative
return success;
}
+ private void maybeUpdateUsageStats(ProcessRecord app) {
+ if (DEBUG_USAGE_STATS) {
+ Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
+ + "] state changes: old = " + app.setProcState + ", new = "
+ + app.curProcState);
+ }
+ if (mUsageStatsService == null) {
+ return;
+ }
+ if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ && (app.setProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ || app.setProcState < 0)) {
+ String[] packages = app.getPackageList();
+ if (packages != null) {
+ for (int i = 0; i < packages.length; i++) {
+ mUsageStatsService.reportEvent(packages[i], app.userId,
+ UsageEvents.Event.INTERACTION);
+ }
+ }
+ }
+ }
+
private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
if (proc.thread != null) {
if (proc.baseProcessTracker != null) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index d79b5fd..ecda36a 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -51,6 +51,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.app.IBatteryStats;
+import com.android.server.job.controllers.AppIdleController;
import com.android.server.job.controllers.BatteryController;
import com.android.server.job.controllers.ConnectivityController;
import com.android.server.job.controllers.IdleController;
@@ -317,6 +318,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mControllers.add(TimeController.get(this));
mControllers.add(IdleController.get(this));
mControllers.add(BatteryController.get(this));
+ mControllers.add(AppIdleController.get(this));
mHandler = new JobHandler(context.getMainLooper());
mJobSchedulerStub = new JobSchedulerStub();
@@ -688,7 +690,6 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
final boolean userRunning = mStartedUsers.contains(job.getUserId());
-
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ " ready=" + jobReady + " pending=" + jobPending
@@ -738,6 +739,10 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
if (availableContext != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "About to run job "
+ + nextPending.getJob().getService().toString());
+ }
if (!availableContext.executeRunnableJob(nextPending)) {
if (DEBUG) {
Slog.d(TAG, "Error executing " + nextPending);
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
new file mode 100644
index 0000000..03e9ad5
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -0,0 +1,174 @@
+/*
+ * 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+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
+ implements UsageStatsManagerInternal.AppIdleStateChangeListener {
+
+ private static final String LOG_TAG = "AppIdleController";
+ private static final boolean DEBUG = true;
+
+ // Singleton factory
+ private static Object sCreationLock = new Object();
+ private static volatile AppIdleController sController;
+ final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
+ private final UsageStatsManagerInternal mUsageStatsInternal;
+ private final BatteryManagerInternal mBatteryManagerInternal;
+ private boolean mPluggedIn;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+ int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+ // TODO: Allow any charger type
+ onPluggedIn((plugged & BatteryManager.BATTERY_PLUGGED_AC) != 0);
+ }
+ }
+ };
+
+ 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);
+ mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ mPluggedIn = isPowered();
+ mUsageStatsInternal.addAppIdleStateChangeListener(this);
+ registerReceivers();
+ }
+
+ private void registerReceivers() {
+ // Monitor battery charging state
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private boolean isPowered() {
+ // TODO: Allow any charger type
+ return mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_AC);
+ }
+
+ @Override
+ public void maybeStartTrackingJob(JobStatus jobStatus) {
+ synchronized (mTrackedTasks) {
+ mTrackedTasks.add(jobStatus);
+ String packageName = jobStatus.job.getService().getPackageName();
+ final boolean appIdle = !mPluggedIn && 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) {
+ // TODO:
+ }
+
+ @Override
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+ boolean changed = false;
+ synchronized (mTrackedTasks) {
+ // If currently plugged in, we don't care about app idle state
+ if (mPluggedIn) {
+ 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();
+ }
+ }
+
+ void onPluggedIn(boolean pluggedIn) {
+ // Flag if any app's idle state has changed
+ boolean changed = false;
+ synchronized (mTrackedTasks) {
+ if (mPluggedIn == pluggedIn) {
+ return;
+ }
+ mPluggedIn = pluggedIn;
+ for (JobStatus task : mTrackedTasks) {
+ String packageName = task.job.getService().getPackageName();
+ final boolean appIdle = !mPluggedIn && mUsageStatsInternal.isAppIdle(packageName,
+ task.getUserId());
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Plugged in " + pluggedIn + ", setting idle state of "
+ + packageName + " to " + appIdle);
+ }
+ if (task.appNotIdleConstraintSatisfied.get() == appIdle) {
+ task.appNotIdleConstraintSatisfied.set(!appIdle);
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e3c55b6..69c63f3 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -54,6 +54,7 @@ public class JobStatus {
final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean unmeteredConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean appNotIdleConstraintSatisfied = new AtomicBoolean();
/**
* Earliest point in the future at which this job will be eligible to run. A value of 0
@@ -199,8 +200,11 @@ public class JobStatus {
* the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
*/
public synchronized boolean isReady() {
- return isConstraintsSatisfied()
- || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get());
+ // Deadline constraint trumps other constraints
+ // AppNotIdle implicit constraint trumps all!
+ return (isConstraintsSatisfied()
+ || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get()))
+ && appNotIdleConstraintSatisfied.get();
}
/**
@@ -229,6 +233,7 @@ public class JobStatus {
+ ",N=" + job.getNetworkType() + ",C=" + job.isRequireCharging()
+ ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
+ ",P=" + job.isPersisted()
+ + ",ANI=" + appNotIdleConstraintSatisfied.get()
+ (isReady() ? "(READY)" : "")
+ "]";
}
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index efd1928..cda7c32 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -44,7 +44,7 @@ public abstract class StateController {
/**
* Implement the logic here to decide whether a job should be tracked by this controller.
- * This logic is put here so the JobManger can be completely agnostic of Controller logic.
+ * This logic is put here so the JobManager can be completely agnostic of Controller logic.
* Also called when updating a task, so implementing controllers have to be aware of
* preexisting tasks.
*/