Loading services/core/java/com/android/server/DeviceIdleController.java +214 −21 Original line number Diff line number Diff line Loading @@ -87,6 +87,10 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; import com.android.server.deviceidle.ConstraintController; import com.android.server.deviceidle.DeviceIdleConstraintTracker; import com.android.server.deviceidle.IDeviceIdleConstraint; import com.android.server.deviceidle.TvConstraintController; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; Loading @@ -104,6 +108,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.stream.Collectors; /** * Keeps track of device idleness and drives low power mode based on that. Loading Loading @@ -296,6 +301,17 @@ public class DeviceIdleController extends SystemService private Location mLastGpsLocation; // Current locked state of the screen private boolean mScreenLocked; private int mNumBlockingConstraints = 0; /** * Constraints are the "handbrakes" that stop the device from moving into a lower state until * every one is released at the same time. * * @see #registerDeviceIdleConstraintInternal(IDeviceIdleConstraint, String, int) */ private final ArrayMap<IDeviceIdleConstraint, DeviceIdleConstraintTracker> mConstraints = new ArrayMap<>(); private ConstraintController mConstraintController; /** Device is currently active. */ @VisibleForTesting Loading Loading @@ -703,8 +719,7 @@ public class DeviceIdleController extends SystemService * global Settings. Any access to this class or its fields should be done while * holding the DeviceIdleController lock. */ @VisibleForTesting final class Constants extends ContentObserver { public final class Constants extends ContentObserver { // Key names stored in the settings value. private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = "light_after_inactive_to"; Loading Loading @@ -1228,6 +1243,7 @@ public class DeviceIdleController extends SystemService private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7; private static final int MSG_FINISH_IDLE_OP = 8; private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9; private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; final class MyHandler extends Handler { MyHandler(Looper looper) { Loading Loading @@ -1348,6 +1364,15 @@ public class DeviceIdleController extends SystemService final boolean added = (msg.arg2 == 1); mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added); } break; case MSG_SEND_CONSTRAINT_MONITORING: { final IDeviceIdleConstraint constraint = (IDeviceIdleConstraint) msg.obj; final boolean monitoring = (msg.arg1 == 1); if (monitoring) { constraint.startMonitoring(); } else { constraint.stopMonitoring(); } } break; } } } Loading Loading @@ -1512,6 +1537,25 @@ public class DeviceIdleController extends SystemService } public class LocalService { public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { synchronized (DeviceIdleController.this) { onConstraintStateChangedLocked(constraint, active); } } public void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name, @IDeviceIdleConstraint.MinimumState int minState) { registerDeviceIdleConstraintInternal(constraint, name, minState); } public void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint) { unregisterDeviceIdleConstraintInternal(constraint); } public void exitIdle(String reason) { exitIdleInternal(reason); } // duration in milliseconds public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason) { Loading Loading @@ -1612,6 +1656,23 @@ public class DeviceIdleController extends SystemService PowerManager getPowerManager() { return mContext.getSystemService(PowerManager.class); } SensorManager getSensorManager() { return mContext.getSystemService(SensorManager.class); } ConstraintController getConstraintController(Handler handler, LocalService localService) { if (mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) { return new TvConstraintController(mContext, handler); } return null; } boolean useMotionSensor() { return mContext.getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModeUseMotionSensor); } } private final Injector mInjector; Loading @@ -1636,9 +1697,7 @@ public class DeviceIdleController extends SystemService mHandler = mInjector.getHandler(this); mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper()); LocalServices.addService(AppStateTracker.class, mAppStateTracker); mUseMotionSensor = context.getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModeUseMotionSensor); mUseMotionSensor = mInjector.useMotionSensor(); } public DeviceIdleController(Context context) { Loading Loading @@ -1738,7 +1797,7 @@ public class DeviceIdleController extends SystemService mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface( ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class); mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); mSensorManager = mInjector.getSensorManager(); if (mUseMotionSensor) { int sigMotionSensorId = getContext().getResources().getInteger( Loading Loading @@ -1767,6 +1826,12 @@ public class DeviceIdleController extends SystemService .setNumUpdates(1); } mConstraintController = mInjector.getConstraintController( mHandler, getLocalService(LocalService.class)); if (mConstraintController != null) { mConstraintController.start(); } float angleThreshold = getContext().getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f; mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this, Loading Loading @@ -1822,6 +1887,99 @@ public class DeviceIdleController extends SystemService } } @VisibleForTesting boolean hasMotionSensor() { return mUseMotionSensor && mMotionSensor != null; } private void registerDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint, final String name, final int type) { final int minState; switch (type) { case IDeviceIdleConstraint.ACTIVE: minState = STATE_ACTIVE; break; case IDeviceIdleConstraint.SENSING_OR_ABOVE: minState = STATE_SENSING; break; default: Slog.wtf(TAG, "Registering device-idle constraint with invalid type: " + type); return; } synchronized (this) { if (mConstraints.containsKey(constraint)) { Slog.e(TAG, "Re-registering device-idle constraint: " + constraint + "."); return; } DeviceIdleConstraintTracker tracker = new DeviceIdleConstraintTracker(name, minState); mConstraints.put(constraint, tracker); updateActiveConstraintsLocked(); } } private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) { synchronized (this) { // Artifically force the constraint to inactive to unblock anything waiting for it. onConstraintStateChangedLocked(constraint, /* active= */ false); // Let the constraint know that we are not listening to it any more. setConstraintMonitoringLocked(constraint, /* monitoring= */ false); mConstraints.remove(constraint); } } @GuardedBy("this") private void onConstraintStateChangedLocked(IDeviceIdleConstraint constraint, boolean active) { DeviceIdleConstraintTracker tracker = mConstraints.get(constraint); if (tracker == null) { Slog.e(TAG, "device-idle constraint " + constraint + " has not been registered."); return; } if (active != tracker.active && tracker.monitoring) { tracker.active = active; mNumBlockingConstraints += (tracker.active ? +1 : -1); if (mNumBlockingConstraints == 0) { if (mState == STATE_ACTIVE) { becomeInactiveIfAppropriateLocked(); } else if (mNextAlarmTime == 0 || mNextAlarmTime < SystemClock.elapsedRealtime()) { stepIdleStateLocked("s:" + tracker.name); } } } } @GuardedBy("this") private void setConstraintMonitoringLocked(IDeviceIdleConstraint constraint, boolean monitor) { DeviceIdleConstraintTracker tracker = mConstraints.get(constraint); if (tracker.monitoring != monitor) { tracker.monitoring = monitor; updateActiveConstraintsLocked(); // We send the callback on a separate thread instead of just relying on oneway as // the client could be in the system server with us and cause re-entry problems. mHandler.obtainMessage(MSG_SEND_CONSTRAINT_MONITORING, /* monitoring= */ monitor ? 1 : 0, /* <not used>= */ -1, /* constraint= */ constraint).sendToTarget(); } } @GuardedBy("this") private void updateActiveConstraintsLocked() { mNumBlockingConstraints = 0; for (int i = 0; i < mConstraints.size(); i++) { final IDeviceIdleConstraint constraint = mConstraints.keyAt(i); final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i); final boolean monitoring = (tracker.minState == mState); if (monitoring != tracker.monitoring) { setConstraintMonitoringLocked(constraint, monitoring); tracker.active = monitoring; } if (tracker.monitoring && tracker.active) { mNumBlockingConstraints++; } } } public boolean addPowerSaveWhitelistAppInternal(String name) { synchronized (this) { try { Loading Loading @@ -2452,6 +2610,7 @@ public class DeviceIdleController extends SystemService cancelLocatingLocked(); stopMonitoringMotionLocked(); mAnyMotionDetector.stop(); updateActiveConstraintsLocked(); } private void resetLightIdleManagementLocked() { Loading Loading @@ -2587,40 +2746,50 @@ public class DeviceIdleController extends SystemService return; } if (mNumBlockingConstraints != 0 && !mForceIdle) { // We have some constraints from other parts of the system server preventing // us from moving to the next state. if (DEBUG) { Slog.i(TAG, "Cannot step idle state. Blocked by: " + mConstraints.values().stream() .filter(x -> x.active) .map(x -> x.name) .collect(Collectors.joining(","))); } return; } switch (mState) { case STATE_INACTIVE: // We have now been inactive long enough, it is time to start looking // for motion and sleep some more while doing so. startMonitoringMotionLocked(); scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false); mState = STATE_IDLE_PENDING; if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING."); EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_IDLE_PENDING, reason); break; case STATE_IDLE_PENDING: mState = STATE_SENSING; if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING."); EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_SENSING, reason); cancelLocatingLocked(); mLocated = false; mLastGenericLocation = null; mLastGpsLocation = null; updateActiveConstraintsLocked(); // If we have an accelerometer, wait to find out whether we are moving. // Wait for open constraints and an accelerometer reading before moving on. if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) { scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT); mNotMoving = false; mAnyMotionDetector.checkForAnyMotion(); break; } else if (mNumBlockingConstraints != 0) { cancelAlarmLocked(); break; } mNotMoving = true; // Otherwise, fall through and check this off the list of requirements. case STATE_SENSING: cancelSensingTimeoutAlarmLocked(); mState = STATE_LOCATING; if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING."); EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_LOCATING, reason); scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false); LocationManager locationManager = mInjector.getLocationManager(); if (locationManager != null Loading Loading @@ -2669,12 +2838,11 @@ public class DeviceIdleController extends SystemService if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) { mNextIdleDelay = mConstants.IDLE_TIMEOUT; } mState = STATE_IDLE; moveToStateLocked(STATE_IDLE, reason); if (mLightState != LIGHT_STATE_OVERRIDE) { mLightState = LIGHT_STATE_OVERRIDE; cancelLightAlarmLocked(); } EventLogTags.writeDeviceIdle(mState, reason); addEvent(EVENT_DEEP_IDLE, null); mGoingIdleWakeLock.acquire(); mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON); Loading @@ -2692,14 +2860,24 @@ public class DeviceIdleController extends SystemService if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) { mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT; } mState = STATE_IDLE_MAINTENANCE; EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_IDLE_MAINTENANCE, reason); addEvent(EVENT_DEEP_MAINTENANCE, null); mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF); break; } } private void moveToStateLocked(int state, String reason) { final int oldState = mState; mState = state; if (DEBUG) { Slog.d(TAG, String.format("Moved from STATE_%s to STATE_%s.", stateToString(oldState), stateToString(mState))); } EventLogTags.writeDeviceIdle(mState, reason); updateActiveConstraintsLocked(); } void incActiveIdleOps() { synchronized (this) { mActiveIdleOpCount++; Loading Loading @@ -2822,6 +3000,7 @@ public class DeviceIdleController extends SystemService mMaintenanceStartTime = 0; EventLogTags.writeDeviceIdle(mState, type); becomeInactive = true; updateActiveConstraintsLocked(); } if (mLightState == LIGHT_STATE_OVERRIDE) { // We went out of light idle mode because we had started deep idle mode... let's Loading Loading @@ -3798,8 +3977,22 @@ public class DeviceIdleController extends SystemService pw.print(" mScreenLocked="); pw.println(mScreenLocked); pw.print(" mNetworkConnected="); pw.println(mNetworkConnected); pw.print(" mCharging="); pw.println(mCharging); pw.print(" mMotionActive="); pw.println(mMotionListener.active); if (mConstraints.size() != 0) { pw.println(" mConstraints={"); for (int i = 0; i < mConstraints.size(); i++) { final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i); pw.print(" \""); pw.print(tracker.name); pw.print("\"="); if (tracker.minState == mState) { pw.println(tracker.active); } else { pw.print("ignored <mMinState="); pw.print(stateToString(tracker.minState)); pw.println(">"); } } pw.println(" }"); } if (mUseMotionSensor) { pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mNotMoving="); pw.println(mNotMoving); } pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps="); Loading services/core/java/com/android/server/deviceidle/BluetoothConstraint.java 0 → 100644 +135 −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.deviceidle; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.DeviceIdleController; /** * Track whether there are any active Bluetooth devices connected. */ public class BluetoothConstraint implements IDeviceIdleConstraint { private static final String TAG = BluetoothConstraint.class.getSimpleName(); private static final long INACTIVITY_TIMEOUT_MS = 20 * 60 * 1000L; private final Context mContext; private final Handler mHandler; private final DeviceIdleController.LocalService mLocalService; private final BluetoothManager mBluetoothManager; private volatile boolean mConnected = true; private volatile boolean mMonitoring = false; public BluetoothConstraint( Context context, Handler handler, DeviceIdleController.LocalService localService) { mContext = context; mHandler = handler; mLocalService = localService; mBluetoothManager = mContext.getSystemService(BluetoothManager.class); } @Override public synchronized void startMonitoring() { // Start by assuming we have a connected bluetooth device. mConnected = true; mMonitoring = true; // Register a receiver to get updates on bluetooth devices disconnecting or the // adapter state changing. IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(mReceiver, filter); // Some devices will try to stay connected indefinitely. Set a timeout to ignore them. mHandler.sendMessageDelayed( Message.obtain(mHandler, mTimeoutCallback), INACTIVITY_TIMEOUT_MS); // Now we have the receiver registered, make a direct check for connected devices. updateAndReportActiveLocked(); } @Override public synchronized void stopMonitoring() { mContext.unregisterReceiver(mReceiver); mHandler.removeCallbacks(mTimeoutCallback); mMonitoring = false; } private synchronized void cancelMonitoringDueToTimeout() { if (mMonitoring) { mMonitoring = false; mLocalService.onConstraintStateChanged(this, /* active= */ false); } } /** * Check the latest data from BluetoothManager and let DeviceIdleController know whether we * have connected devices (for example TV remotes / gamepads) and thus want to stay awake. */ @GuardedBy("this") private void updateAndReportActiveLocked() { final boolean connected = isBluetoothConnected(mBluetoothManager); if (connected != mConnected) { mConnected = connected; // If we lost all of our connections, we are on track to going into idle state. mLocalService.onConstraintStateChanged(this, /* active= */ mConnected); } } /** * True if the bluetooth adapter exists, is enabled, and has at least one GATT device connected. */ @VisibleForTesting static boolean isBluetoothConnected(BluetoothManager bluetoothManager) { BluetoothAdapter adapter = bluetoothManager.getAdapter(); if (adapter != null && adapter.isEnabled()) { return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT).size() > 0; } return false; } /** * Registered in {@link #startMonitoring()}, unregistered in {@link #stopMonitoring()}. */ @VisibleForTesting final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(intent.getAction())) { mLocalService.exitIdle("bluetooth"); } else { updateAndReportActiveLocked(); } } }; private final Runnable mTimeoutCallback = () -> cancelMonitoringDueToTimeout(); } services/core/java/com/android/server/deviceidle/ConstraintController.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.deviceidle; /** * Device idle constraints for a specific form factor or use-case. */ public interface ConstraintController { /** * Begin any general continuing work and register all constraints. */ void start(); /** * Unregister all constraints and stop any general work. */ void stop(); } services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java 0 → 100644 +57 −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.deviceidle; /** * Current state of an {@link IDeviceIdleConstraint}. * * If the current doze state is between leastActive and mostActive, then startMonitoring() will * be the most recent call. Otherwise, stopMonitoring() is the most recent call. */ public class DeviceIdleConstraintTracker { /** * Appears in "dumpsys deviceidle". */ public final String name; /** * Whenever a constraint is active, it will keep the device at or above * minState (provided the rule is currently in effect). * */ public final int minState; /** * Whether this constraint currently prevents going below {@link #minState}. * * When the state is set to exactly minState, active is automatically * overwritten with {@code true}. */ public boolean active = false; /** * Internal tracking for whether the {@link IDeviceIdleConstraint} on the other * side has been told it needs to send updates. */ public boolean monitoring = false; public DeviceIdleConstraintTracker(final String name, int minState) { this.name = name; this.minState = minState; } } services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java 0 → 100644 +67 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/DeviceIdleController.java +214 −21 Original line number Diff line number Diff line Loading @@ -87,6 +87,10 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; import com.android.server.deviceidle.ConstraintController; import com.android.server.deviceidle.DeviceIdleConstraintTracker; import com.android.server.deviceidle.IDeviceIdleConstraint; import com.android.server.deviceidle.TvConstraintController; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; Loading @@ -104,6 +108,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.stream.Collectors; /** * Keeps track of device idleness and drives low power mode based on that. Loading Loading @@ -296,6 +301,17 @@ public class DeviceIdleController extends SystemService private Location mLastGpsLocation; // Current locked state of the screen private boolean mScreenLocked; private int mNumBlockingConstraints = 0; /** * Constraints are the "handbrakes" that stop the device from moving into a lower state until * every one is released at the same time. * * @see #registerDeviceIdleConstraintInternal(IDeviceIdleConstraint, String, int) */ private final ArrayMap<IDeviceIdleConstraint, DeviceIdleConstraintTracker> mConstraints = new ArrayMap<>(); private ConstraintController mConstraintController; /** Device is currently active. */ @VisibleForTesting Loading Loading @@ -703,8 +719,7 @@ public class DeviceIdleController extends SystemService * global Settings. Any access to this class or its fields should be done while * holding the DeviceIdleController lock. */ @VisibleForTesting final class Constants extends ContentObserver { public final class Constants extends ContentObserver { // Key names stored in the settings value. private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = "light_after_inactive_to"; Loading Loading @@ -1228,6 +1243,7 @@ public class DeviceIdleController extends SystemService private static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7; private static final int MSG_FINISH_IDLE_OP = 8; private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9; private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; final class MyHandler extends Handler { MyHandler(Looper looper) { Loading Loading @@ -1348,6 +1364,15 @@ public class DeviceIdleController extends SystemService final boolean added = (msg.arg2 == 1); mNetworkPolicyManagerInternal.onTempPowerSaveWhitelistChange(appId, added); } break; case MSG_SEND_CONSTRAINT_MONITORING: { final IDeviceIdleConstraint constraint = (IDeviceIdleConstraint) msg.obj; final boolean monitoring = (msg.arg1 == 1); if (monitoring) { constraint.startMonitoring(); } else { constraint.stopMonitoring(); } } break; } } } Loading Loading @@ -1512,6 +1537,25 @@ public class DeviceIdleController extends SystemService } public class LocalService { public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { synchronized (DeviceIdleController.this) { onConstraintStateChangedLocked(constraint, active); } } public void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name, @IDeviceIdleConstraint.MinimumState int minState) { registerDeviceIdleConstraintInternal(constraint, name, minState); } public void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint) { unregisterDeviceIdleConstraintInternal(constraint); } public void exitIdle(String reason) { exitIdleInternal(reason); } // duration in milliseconds public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason) { Loading Loading @@ -1612,6 +1656,23 @@ public class DeviceIdleController extends SystemService PowerManager getPowerManager() { return mContext.getSystemService(PowerManager.class); } SensorManager getSensorManager() { return mContext.getSystemService(SensorManager.class); } ConstraintController getConstraintController(Handler handler, LocalService localService) { if (mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) { return new TvConstraintController(mContext, handler); } return null; } boolean useMotionSensor() { return mContext.getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModeUseMotionSensor); } } private final Injector mInjector; Loading @@ -1636,9 +1697,7 @@ public class DeviceIdleController extends SystemService mHandler = mInjector.getHandler(this); mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper()); LocalServices.addService(AppStateTracker.class, mAppStateTracker); mUseMotionSensor = context.getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModeUseMotionSensor); mUseMotionSensor = mInjector.useMotionSensor(); } public DeviceIdleController(Context context) { Loading Loading @@ -1738,7 +1797,7 @@ public class DeviceIdleController extends SystemService mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface( ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class); mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); mSensorManager = mInjector.getSensorManager(); if (mUseMotionSensor) { int sigMotionSensorId = getContext().getResources().getInteger( Loading Loading @@ -1767,6 +1826,12 @@ public class DeviceIdleController extends SystemService .setNumUpdates(1); } mConstraintController = mInjector.getConstraintController( mHandler, getLocalService(LocalService.class)); if (mConstraintController != null) { mConstraintController.start(); } float angleThreshold = getContext().getResources().getInteger( com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f; mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this, Loading Loading @@ -1822,6 +1887,99 @@ public class DeviceIdleController extends SystemService } } @VisibleForTesting boolean hasMotionSensor() { return mUseMotionSensor && mMotionSensor != null; } private void registerDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint, final String name, final int type) { final int minState; switch (type) { case IDeviceIdleConstraint.ACTIVE: minState = STATE_ACTIVE; break; case IDeviceIdleConstraint.SENSING_OR_ABOVE: minState = STATE_SENSING; break; default: Slog.wtf(TAG, "Registering device-idle constraint with invalid type: " + type); return; } synchronized (this) { if (mConstraints.containsKey(constraint)) { Slog.e(TAG, "Re-registering device-idle constraint: " + constraint + "."); return; } DeviceIdleConstraintTracker tracker = new DeviceIdleConstraintTracker(name, minState); mConstraints.put(constraint, tracker); updateActiveConstraintsLocked(); } } private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) { synchronized (this) { // Artifically force the constraint to inactive to unblock anything waiting for it. onConstraintStateChangedLocked(constraint, /* active= */ false); // Let the constraint know that we are not listening to it any more. setConstraintMonitoringLocked(constraint, /* monitoring= */ false); mConstraints.remove(constraint); } } @GuardedBy("this") private void onConstraintStateChangedLocked(IDeviceIdleConstraint constraint, boolean active) { DeviceIdleConstraintTracker tracker = mConstraints.get(constraint); if (tracker == null) { Slog.e(TAG, "device-idle constraint " + constraint + " has not been registered."); return; } if (active != tracker.active && tracker.monitoring) { tracker.active = active; mNumBlockingConstraints += (tracker.active ? +1 : -1); if (mNumBlockingConstraints == 0) { if (mState == STATE_ACTIVE) { becomeInactiveIfAppropriateLocked(); } else if (mNextAlarmTime == 0 || mNextAlarmTime < SystemClock.elapsedRealtime()) { stepIdleStateLocked("s:" + tracker.name); } } } } @GuardedBy("this") private void setConstraintMonitoringLocked(IDeviceIdleConstraint constraint, boolean monitor) { DeviceIdleConstraintTracker tracker = mConstraints.get(constraint); if (tracker.monitoring != monitor) { tracker.monitoring = monitor; updateActiveConstraintsLocked(); // We send the callback on a separate thread instead of just relying on oneway as // the client could be in the system server with us and cause re-entry problems. mHandler.obtainMessage(MSG_SEND_CONSTRAINT_MONITORING, /* monitoring= */ monitor ? 1 : 0, /* <not used>= */ -1, /* constraint= */ constraint).sendToTarget(); } } @GuardedBy("this") private void updateActiveConstraintsLocked() { mNumBlockingConstraints = 0; for (int i = 0; i < mConstraints.size(); i++) { final IDeviceIdleConstraint constraint = mConstraints.keyAt(i); final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i); final boolean monitoring = (tracker.minState == mState); if (monitoring != tracker.monitoring) { setConstraintMonitoringLocked(constraint, monitoring); tracker.active = monitoring; } if (tracker.monitoring && tracker.active) { mNumBlockingConstraints++; } } } public boolean addPowerSaveWhitelistAppInternal(String name) { synchronized (this) { try { Loading Loading @@ -2452,6 +2610,7 @@ public class DeviceIdleController extends SystemService cancelLocatingLocked(); stopMonitoringMotionLocked(); mAnyMotionDetector.stop(); updateActiveConstraintsLocked(); } private void resetLightIdleManagementLocked() { Loading Loading @@ -2587,40 +2746,50 @@ public class DeviceIdleController extends SystemService return; } if (mNumBlockingConstraints != 0 && !mForceIdle) { // We have some constraints from other parts of the system server preventing // us from moving to the next state. if (DEBUG) { Slog.i(TAG, "Cannot step idle state. Blocked by: " + mConstraints.values().stream() .filter(x -> x.active) .map(x -> x.name) .collect(Collectors.joining(","))); } return; } switch (mState) { case STATE_INACTIVE: // We have now been inactive long enough, it is time to start looking // for motion and sleep some more while doing so. startMonitoringMotionLocked(); scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false); mState = STATE_IDLE_PENDING; if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_IDLE_PENDING."); EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_IDLE_PENDING, reason); break; case STATE_IDLE_PENDING: mState = STATE_SENSING; if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING."); EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_SENSING, reason); cancelLocatingLocked(); mLocated = false; mLastGenericLocation = null; mLastGpsLocation = null; updateActiveConstraintsLocked(); // If we have an accelerometer, wait to find out whether we are moving. // Wait for open constraints and an accelerometer reading before moving on. if (mUseMotionSensor && mAnyMotionDetector.hasSensor()) { scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT); mNotMoving = false; mAnyMotionDetector.checkForAnyMotion(); break; } else if (mNumBlockingConstraints != 0) { cancelAlarmLocked(); break; } mNotMoving = true; // Otherwise, fall through and check this off the list of requirements. case STATE_SENSING: cancelSensingTimeoutAlarmLocked(); mState = STATE_LOCATING; if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING."); EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_LOCATING, reason); scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT, false); LocationManager locationManager = mInjector.getLocationManager(); if (locationManager != null Loading Loading @@ -2669,12 +2838,11 @@ public class DeviceIdleController extends SystemService if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) { mNextIdleDelay = mConstants.IDLE_TIMEOUT; } mState = STATE_IDLE; moveToStateLocked(STATE_IDLE, reason); if (mLightState != LIGHT_STATE_OVERRIDE) { mLightState = LIGHT_STATE_OVERRIDE; cancelLightAlarmLocked(); } EventLogTags.writeDeviceIdle(mState, reason); addEvent(EVENT_DEEP_IDLE, null); mGoingIdleWakeLock.acquire(); mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON); Loading @@ -2692,14 +2860,24 @@ public class DeviceIdleController extends SystemService if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) { mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT; } mState = STATE_IDLE_MAINTENANCE; EventLogTags.writeDeviceIdle(mState, reason); moveToStateLocked(STATE_IDLE_MAINTENANCE, reason); addEvent(EVENT_DEEP_MAINTENANCE, null); mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF); break; } } private void moveToStateLocked(int state, String reason) { final int oldState = mState; mState = state; if (DEBUG) { Slog.d(TAG, String.format("Moved from STATE_%s to STATE_%s.", stateToString(oldState), stateToString(mState))); } EventLogTags.writeDeviceIdle(mState, reason); updateActiveConstraintsLocked(); } void incActiveIdleOps() { synchronized (this) { mActiveIdleOpCount++; Loading Loading @@ -2822,6 +3000,7 @@ public class DeviceIdleController extends SystemService mMaintenanceStartTime = 0; EventLogTags.writeDeviceIdle(mState, type); becomeInactive = true; updateActiveConstraintsLocked(); } if (mLightState == LIGHT_STATE_OVERRIDE) { // We went out of light idle mode because we had started deep idle mode... let's Loading Loading @@ -3798,8 +3977,22 @@ public class DeviceIdleController extends SystemService pw.print(" mScreenLocked="); pw.println(mScreenLocked); pw.print(" mNetworkConnected="); pw.println(mNetworkConnected); pw.print(" mCharging="); pw.println(mCharging); pw.print(" mMotionActive="); pw.println(mMotionListener.active); if (mConstraints.size() != 0) { pw.println(" mConstraints={"); for (int i = 0; i < mConstraints.size(); i++) { final DeviceIdleConstraintTracker tracker = mConstraints.valueAt(i); pw.print(" \""); pw.print(tracker.name); pw.print("\"="); if (tracker.minState == mState) { pw.println(tracker.active); } else { pw.print("ignored <mMinState="); pw.print(stateToString(tracker.minState)); pw.println(">"); } } pw.println(" }"); } if (mUseMotionSensor) { pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mNotMoving="); pw.println(mNotMoving); } pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps="); Loading
services/core/java/com/android/server/deviceidle/BluetoothConstraint.java 0 → 100644 +135 −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.deviceidle; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.DeviceIdleController; /** * Track whether there are any active Bluetooth devices connected. */ public class BluetoothConstraint implements IDeviceIdleConstraint { private static final String TAG = BluetoothConstraint.class.getSimpleName(); private static final long INACTIVITY_TIMEOUT_MS = 20 * 60 * 1000L; private final Context mContext; private final Handler mHandler; private final DeviceIdleController.LocalService mLocalService; private final BluetoothManager mBluetoothManager; private volatile boolean mConnected = true; private volatile boolean mMonitoring = false; public BluetoothConstraint( Context context, Handler handler, DeviceIdleController.LocalService localService) { mContext = context; mHandler = handler; mLocalService = localService; mBluetoothManager = mContext.getSystemService(BluetoothManager.class); } @Override public synchronized void startMonitoring() { // Start by assuming we have a connected bluetooth device. mConnected = true; mMonitoring = true; // Register a receiver to get updates on bluetooth devices disconnecting or the // adapter state changing. IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(mReceiver, filter); // Some devices will try to stay connected indefinitely. Set a timeout to ignore them. mHandler.sendMessageDelayed( Message.obtain(mHandler, mTimeoutCallback), INACTIVITY_TIMEOUT_MS); // Now we have the receiver registered, make a direct check for connected devices. updateAndReportActiveLocked(); } @Override public synchronized void stopMonitoring() { mContext.unregisterReceiver(mReceiver); mHandler.removeCallbacks(mTimeoutCallback); mMonitoring = false; } private synchronized void cancelMonitoringDueToTimeout() { if (mMonitoring) { mMonitoring = false; mLocalService.onConstraintStateChanged(this, /* active= */ false); } } /** * Check the latest data from BluetoothManager and let DeviceIdleController know whether we * have connected devices (for example TV remotes / gamepads) and thus want to stay awake. */ @GuardedBy("this") private void updateAndReportActiveLocked() { final boolean connected = isBluetoothConnected(mBluetoothManager); if (connected != mConnected) { mConnected = connected; // If we lost all of our connections, we are on track to going into idle state. mLocalService.onConstraintStateChanged(this, /* active= */ mConnected); } } /** * True if the bluetooth adapter exists, is enabled, and has at least one GATT device connected. */ @VisibleForTesting static boolean isBluetoothConnected(BluetoothManager bluetoothManager) { BluetoothAdapter adapter = bluetoothManager.getAdapter(); if (adapter != null && adapter.isEnabled()) { return bluetoothManager.getConnectedDevices(BluetoothProfile.GATT).size() > 0; } return false; } /** * Registered in {@link #startMonitoring()}, unregistered in {@link #stopMonitoring()}. */ @VisibleForTesting final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(intent.getAction())) { mLocalService.exitIdle("bluetooth"); } else { updateAndReportActiveLocked(); } } }; private final Runnable mTimeoutCallback = () -> cancelMonitoringDueToTimeout(); }
services/core/java/com/android/server/deviceidle/ConstraintController.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.deviceidle; /** * Device idle constraints for a specific form factor or use-case. */ public interface ConstraintController { /** * Begin any general continuing work and register all constraints. */ void start(); /** * Unregister all constraints and stop any general work. */ void stop(); }
services/core/java/com/android/server/deviceidle/DeviceIdleConstraintTracker.java 0 → 100644 +57 −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.deviceidle; /** * Current state of an {@link IDeviceIdleConstraint}. * * If the current doze state is between leastActive and mostActive, then startMonitoring() will * be the most recent call. Otherwise, stopMonitoring() is the most recent call. */ public class DeviceIdleConstraintTracker { /** * Appears in "dumpsys deviceidle". */ public final String name; /** * Whenever a constraint is active, it will keep the device at or above * minState (provided the rule is currently in effect). * */ public final int minState; /** * Whether this constraint currently prevents going below {@link #minState}. * * When the state is set to exactly minState, active is automatically * overwritten with {@code true}. */ public boolean active = false; /** * Internal tracking for whether the {@link IDeviceIdleConstraint} on the other * side has been told it needs to send updates. */ public boolean monitoring = false; public DeviceIdleConstraintTracker(final String name, int minState) { this.name = name; this.minState = minState; } }
services/core/java/com/android/server/deviceidle/IDeviceIdleConstraint.java 0 → 100644 +67 −0 File added.Preview size limit exceeded, changes collapsed. Show changes