Loading services/core/java/com/android/server/job/controllers/IdleController.java +20 −146 Original line number Diff line number Diff line Loading @@ -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); } /** Loading @@ -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); Loading @@ -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; } private void initIdleStateTracking(Context ctx) { final boolean isCar = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE); if (isCar) { mIdleTracker = new CarIdlenessTracker(); } 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); } } 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++) { Loading services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * 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 services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * 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 services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * 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); } services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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); } Loading
services/core/java/com/android/server/job/controllers/IdleController.java +20 −146 Original line number Diff line number Diff line Loading @@ -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); } /** Loading @@ -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); Loading @@ -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; } private void initIdleStateTracking(Context ctx) { final boolean isCar = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE); if (isCar) { mIdleTracker = new CarIdlenessTracker(); } 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); } } 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++) { Loading
services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java 0 → 100644 +139 −0 Original line number Diff line number Diff line /* * 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
services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * 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
services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java 0 → 100644 +32 −0 Original line number Diff line number Diff line /* * 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); }
services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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); }