summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/job/controllers/IdleController.java166
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java139
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java175
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java32
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java46
5 files changed, 412 insertions, 146 deletions
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 644f2c4d6864..e3c311f9e327 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -16,41 +16,33 @@
package com.android.server.job.controllers;
-import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-
-import android.app.AlarmManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.am.ActivityManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
+import com.android.server.job.controllers.idle.CarIdlenessTracker;
+import com.android.server.job.controllers.idle.DeviceIdlenessTracker;
+import com.android.server.job.controllers.idle.IdlenessListener;
+import com.android.server.job.controllers.idle.IdlenessTracker;
import java.util.function.Predicate;
-public final class IdleController extends StateController {
- private static final String TAG = "JobScheduler.Idle";
- private static final boolean DEBUG = JobSchedulerService.DEBUG
- || Log.isLoggable(TAG, Log.DEBUG);
-
+public final class IdleController extends StateController implements IdlenessListener {
+ private static final String TAG = "JobScheduler.IdleController";
// Policy: we decide that we're "idle" if the device has been unused /
// screen off or dreaming or wireless charging dock idle for at least this long
- private long mInactivityIdleThreshold;
- private long mIdleWindowSlop;
final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
IdlenessTracker mIdleTracker;
public IdleController(JobSchedulerService service) {
super(service);
- initIdleStateTracking();
+ initIdleStateTracking(mContext);
}
/**
@@ -74,9 +66,10 @@ public final class IdleController extends StateController {
}
/**
- * Interaction with the task manager service
+ * State-change notifications from the idleness tracker
*/
- void reportNewIdleState(boolean isIdle) {
+ @Override
+ public void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
@@ -89,141 +82,22 @@ public final class IdleController extends StateController {
* Idle state tracking, and messaging with the task manager when
* significant state changes occur
*/
- private void initIdleStateTracking() {
- mInactivityIdleThreshold = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
- mIdleWindowSlop = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
- mIdleTracker = new IdlenessTracker();
- mIdleTracker.startTracking();
- }
-
- final class IdlenessTracker extends BroadcastReceiver {
- private AlarmManager mAlarm;
-
- // After construction, mutations of idle/screen-on state will only happen
- // on the main looper thread, either in onReceive() or in an alarm callback.
- private boolean mIdle;
- private boolean mScreenOn;
- private boolean mDockIdle;
-
- private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
- handleIdleTrigger();
- };
-
- public IdlenessTracker() {
- mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
- // At boot we presume that the user has just "interacted" with the
- // device in some meaningful way.
- mIdle = false;
- mScreenOn = true;
- mDockIdle = false;
- }
-
- public boolean isIdle() {
- return mIdle;
- }
-
- public void startTracking() {
- IntentFilter filter = new IntentFilter();
-
- // Screen state
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
-
- // Dreaming state
- filter.addAction(Intent.ACTION_DREAMING_STARTED);
- filter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
- // Debugging/instrumentation
- filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
-
- // Wireless charging dock state
- filter.addAction(Intent.ACTION_DOCK_IDLE);
- filter.addAction(Intent.ACTION_DOCK_ACTIVE);
-
- mContext.registerReceiver(this, filter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_ON)
- || action.equals(Intent.ACTION_DREAMING_STOPPED)
- || action.equals(Intent.ACTION_DOCK_ACTIVE)) {
- if (action.equals(Intent.ACTION_DOCK_ACTIVE)) {
- if (!mScreenOn) {
- // Ignore this intent during screen off
- return;
- } else {
- mDockIdle = false;
- }
- } else {
- mScreenOn = true;
- mDockIdle = false;
- }
- if (DEBUG) {
- Slog.v(TAG,"exiting idle : " + action);
- }
- //cancel the alarm
- mAlarm.cancel(mIdleAlarmListener);
- if (mIdle) {
- // possible transition to not-idle
- mIdle = false;
- reportNewIdleState(mIdle);
- }
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)
- || action.equals(Intent.ACTION_DREAMING_STARTED)
- || action.equals(Intent.ACTION_DOCK_IDLE)) {
- // when the screen goes off or dreaming starts or wireless charging dock in idle,
- // we schedule the alarm that will tell us when we have decided the device is
- // truly idle.
- if (action.equals(Intent.ACTION_DOCK_IDLE)) {
- if (!mScreenOn) {
- // Ignore this intent during screen off
- return;
- } else {
- mDockIdle = true;
- }
- } else {
- mScreenOn = false;
- mDockIdle = false;
- }
- final long nowElapsed = sElapsedRealtimeClock.millis();
- final long when = nowElapsed + mInactivityIdleThreshold;
- if (DEBUG) {
- Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
- + when);
- }
- mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
- } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
- handleIdleTrigger();
- }
- }
-
- private void handleIdleTrigger() {
- // idle time starts now. Do not set mIdle if screen is on.
- if (!mIdle && (!mScreenOn || mDockIdle)) {
- if (DEBUG) {
- Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
- }
- mIdle = true;
- reportNewIdleState(mIdle);
- } else {
- if (DEBUG) {
- Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
- + mIdle + " screen=" + mScreenOn);
- }
- }
+ private void initIdleStateTracking(Context ctx) {
+ final boolean isCar = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ if (isCar) {
+ mIdleTracker = new CarIdlenessTracker();
+ } else {
+ mIdleTracker = new DeviceIdlenessTracker();
}
+ mIdleTracker.startTracking(ctx, this);
}
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
pw.println("Currently idle: " + mIdleTracker.isIdle());
+ pw.println("Idleness tracker:"); mIdleTracker.dump(pw);
pw.println();
for (int i = 0; i < mTrackedTasks.size(); i++) {
diff --git a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
new file mode 100644
index 000000000000..a3949a4521c2
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 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.idle;
+
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.job.JobSchedulerService;
+
+import java.io.PrintWriter;
+
+public final class CarIdlenessTracker extends BroadcastReceiver implements IdlenessTracker {
+ private static final String TAG = "JobScheduler.CarIdlenessTracker";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final String ACTION_FORCE_IDLE = "com.android.server.ACTION_FORCE_IDLE";
+ public static final String ACTION_UNFORCE_IDLE = "com.android.server.ACTION_UNFORCE_IDLE";
+
+ // After construction, mutations of idle/screen-on state will only happen
+ // on the main looper thread, either in onReceive() or in an alarm callback.
+ private boolean mIdle;
+ private boolean mScreenOn;
+ private IdlenessListener mIdleListener;
+
+ public CarIdlenessTracker() {
+ // At boot we presume that the user has just "interacted" with the
+ // device in some meaningful way.
+ mIdle = false;
+ mScreenOn = true;
+ }
+
+ @Override
+ public boolean isIdle() {
+ return mIdle;
+ }
+
+ @Override
+ public void startTracking(Context context, IdlenessListener listener) {
+ mIdleListener = listener;
+
+ IntentFilter filter = new IntentFilter();
+
+ // Screen state
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+
+ // Debugging/instrumentation
+ filter.addAction(ACTION_FORCE_IDLE);
+ filter.addAction(ACTION_UNFORCE_IDLE);
+ filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
+
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print(" mIdle: "); pw.println(mIdle);
+ pw.print(" mScreenOn: "); pw.println(mScreenOn);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ logIfDebug("Received action: " + action);
+
+ // Check for forced actions
+ if (action.equals(ACTION_FORCE_IDLE)) {
+ logIfDebug("Forcing idle...");
+ enterIdleState(true);
+ } else if (action.equals(ACTION_UNFORCE_IDLE)) {
+ logIfDebug("Unforcing idle...");
+ exitIdleState(true);
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ logIfDebug("Going idle...");
+ mScreenOn = false;
+ enterIdleState(false);
+ } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ logIfDebug("exiting idle...");
+ mScreenOn = true;
+ exitIdleState(true);
+ } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
+ if (!mScreenOn) {
+ logIfDebug("Idle trigger fired...");
+ enterIdleState(false);
+ } else {
+ logIfDebug("TRIGGER_IDLE received but not changing state; idle="
+ + mIdle + " screen=" + mScreenOn);
+ }
+ }
+ }
+
+ private void enterIdleState(boolean forced) {
+ if (!forced && mIdle) {
+ // Already idle and don't need to trigger callbacks since not forced
+ logIfDebug("Device is already considered idle");
+ return;
+ }
+ mIdle = true;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+
+ private void exitIdleState(boolean forced) {
+ if (!forced && !mIdle) {
+ // Already out of idle and don't need to trigger callbacks since not forced
+ logIfDebug("Device is already considered not idle");
+ return;
+ }
+ mIdle = false;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+
+ private void logIfDebug(String msg) {
+ if (DEBUG) {
+ Slog.v(TAG, msg);
+ }
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
new file mode 100644
index 000000000000..a85bd4066ad3
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2018 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.idle;
+
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.job.JobSchedulerService;
+
+import java.io.PrintWriter;
+
+public final class DeviceIdlenessTracker extends BroadcastReceiver implements IdlenessTracker {
+ private static final String TAG = "JobScheduler.DeviceIdlenessTracker";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private AlarmManager mAlarm;
+
+ // After construction, mutations of idle/screen-on state will only happen
+ // on the main looper thread, either in onReceive() or in an alarm callback.
+ private long mInactivityIdleThreshold;
+ private long mIdleWindowSlop;
+ private boolean mIdle;
+ private boolean mScreenOn;
+ private boolean mDockIdle;
+ private IdlenessListener mIdleListener;
+
+ private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
+ handleIdleTrigger();
+ };
+
+ public DeviceIdlenessTracker() {
+ // At boot we presume that the user has just "interacted" with the
+ // device in some meaningful way.
+ mIdle = false;
+ mScreenOn = true;
+ mDockIdle = false;
+ }
+
+ @Override
+ public boolean isIdle() {
+ return mIdle;
+ }
+
+ @Override
+ public void startTracking(Context context, IdlenessListener listener) {
+ mIdleListener = listener;
+ mInactivityIdleThreshold = context.getResources().getInteger(
+ com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+ mIdleWindowSlop = context.getResources().getInteger(
+ com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
+ mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+ IntentFilter filter = new IntentFilter();
+
+ // Screen state
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+
+ // Dreaming state
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+
+ // Debugging/instrumentation
+ filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
+
+ // Wireless charging dock state
+ filter.addAction(Intent.ACTION_DOCK_IDLE);
+ filter.addAction(Intent.ACTION_DOCK_ACTIVE);
+
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print(" mIdle: "); pw.println(mIdle);
+ pw.print(" mScreenOn: "); pw.println(mScreenOn);
+ pw.print(" mDockIdle: "); pw.println(mDockIdle);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(Intent.ACTION_SCREEN_ON)
+ || action.equals(Intent.ACTION_DREAMING_STOPPED)
+ || action.equals(Intent.ACTION_DOCK_ACTIVE)) {
+ if (action.equals(Intent.ACTION_DOCK_ACTIVE)) {
+ if (!mScreenOn) {
+ // Ignore this intent during screen off
+ return;
+ } else {
+ mDockIdle = false;
+ }
+ } else {
+ mScreenOn = true;
+ mDockIdle = false;
+ }
+ if (DEBUG) {
+ Slog.v(TAG,"exiting idle : " + action);
+ }
+ //cancel the alarm
+ mAlarm.cancel(mIdleAlarmListener);
+ if (mIdle) {
+ // possible transition to not-idle
+ mIdle = false;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)
+ || action.equals(Intent.ACTION_DREAMING_STARTED)
+ || action.equals(Intent.ACTION_DOCK_IDLE)) {
+ // when the screen goes off or dreaming starts or wireless charging dock in idle,
+ // we schedule the alarm that will tell us when we have decided the device is
+ // truly idle.
+ if (action.equals(Intent.ACTION_DOCK_IDLE)) {
+ if (!mScreenOn) {
+ // Ignore this intent during screen off
+ return;
+ } else {
+ mDockIdle = true;
+ }
+ } else {
+ mScreenOn = false;
+ mDockIdle = false;
+ }
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final long when = nowElapsed + mInactivityIdleThreshold;
+ if (DEBUG) {
+ Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
+ + when);
+ }
+ mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
+ } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
+ handleIdleTrigger();
+ }
+ }
+
+ private void handleIdleTrigger() {
+ // idle time starts now. Do not set mIdle if screen is on.
+ if (!mIdle && (!mScreenOn || mDockIdle)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
+ }
+ mIdle = true;
+ mIdleListener.reportNewIdleState(mIdle);
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
+ + mIdle + " screen=" + mScreenOn);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java b/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java
new file mode 100644
index 000000000000..7ffd7cd3e2e0
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.idle;
+
+/**
+ * Interface through which an IdlenessTracker informs the job scheduler of
+ * changes in the device's inactivity state.
+ */
+public interface IdlenessListener {
+ /**
+ * Tell the job scheduler that the device's idle state has changed.
+ *
+ * @param deviceIsIdle {@code true} to indicate that the device is now considered
+ * to be idle; {@code false} to indicate that the device is now being interacted with,
+ * so jobs with idle constraints should not be run.
+ */
+ void reportNewIdleState(boolean deviceIsIdle);
+}
diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java
new file mode 100644
index 000000000000..09f01c2e4113
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.idle;
+
+import android.content.Context;
+
+import java.io.PrintWriter;
+
+public interface IdlenessTracker {
+ /**
+ * One-time initialization: this method is called once, after construction of
+ * the IdlenessTracker instance. This is when the tracker should actually begin
+ * monitoring whatever signals it consumes in deciding when the device is in a
+ * non-interacting state. When the idle state changes thereafter, the given
+ * listener must be called to report the new state.
+ */
+ void startTracking(Context context, IdlenessListener listener);
+
+ /**
+ * Report whether the device is currently considered "idle" for purposes of
+ * running scheduled jobs with idleness constraints.
+ *
+ * @return {@code true} if the job scheduler should consider idleness
+ * constraints to be currently satisfied; {@code false} otherwise.
+ */
+ boolean isIdle();
+
+ /**
+ * Dump useful information about tracked idleness-related state in plaintext.
+ */
+ void dump(PrintWriter pw);
+}