diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 425e797a9e0e97dadfd256d0758cde2ada9f9ef3..4676a0a000edf5a090a907d6a09cbd0c0cd06875 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -44,6 +44,7 @@ interface IPowerManager void userActivity(int displayId, long time, int event, int flags); void wakeUp(long time, int reason, String details, String opPackageName); + void wakeUpWithProximityCheck(long time, int reason, String details, String opPackageName); @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) void goToSleep(long time, int reason, int flags); @UnsupportedAppUsage(maxTargetSdk = 28) diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 4dae7c7c96d5b3d79f574e39a099b664b084944a..7a2b840ae9a5420164b9d4773a51fda6d6744fb4 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1498,6 +1498,22 @@ public final class PowerManager { } } + /** + * Forces the device to wake up from sleep only if + * nothing is blocking the proximity sensor + * + * @see #wakeUp + * + * @hide + */ + public void wakeUpWithProximityCheck(long time, @WakeReason int reason, String details) { + try { + mService.wakeUpWithProximityCheck(time, reason, details, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Forces the device to start napping. * <p> diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d14cee87e3af3bfc1e32e175f6db06ea3f65e799..e0a282c614e243e306e1416b066b82e4e7719f44 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10257,6 +10257,13 @@ public final class Settings { */ public static final String LOCKSCREEN_MEDIA_METADATA = "lockscreen_media_metadata"; + /** + * Check the proximity sensor during wakeup + * + * @hide + */ + public static final String PROXIMITY_ON_WAKE = "proximity_on_wake"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. diff --git a/core/res/res/values/lmodroid_config.xml b/core/res/res/values/lmodroid_config.xml index eeb7b8452c59f96e146937ebad0622827bb6bbbe..74777ec5e143046b0680def790a8628b79614148 100644 --- a/core/res/res/values/lmodroid_config.xml +++ b/core/res/res/values/lmodroid_config.xml @@ -31,4 +31,10 @@ <string-array name="config_vendorPlatformSignatures"> </string-array> + <!-- Default value for proximity check on screen wake + NOTE ! - Enable for devices that have a fast response proximity sensor (ideally < 300ms)--> + <bool name="config_proximityCheckOnWake">false</bool> + <integer name="config_proximityCheckTimeout">250</integer> + <bool name="config_proximityCheckOnWakeEnabledByDefault">false</bool> + </resources> diff --git a/core/res/res/values/lmodroid_symbols.xml b/core/res/res/values/lmodroid_symbols.xml index 97606d7253418f2274552e57357bc2ce38625cf2..d203a5eee004af7b74703eac6f22dcd67113d15c 100644 --- a/core/res/res/values/lmodroid_symbols.xml +++ b/core/res/res/values/lmodroid_symbols.xml @@ -23,4 +23,13 @@ <!-- Vendor signatures --> <java-symbol type="array" name="config_vendorPlatformSignatures" /> + <!-- Proximity check on screen on --> + <java-symbol type="bool" name="config_proximityCheckOnWake" /> + + <!-- Proximity check timeout --> + <java-symbol type="integer" name="config_proximityCheckTimeout" /> + + <!-- Proximity check on screen on default --> + <java-symbol type="bool" name="config_proximityCheckOnWakeEnabledByDefault" /> + </resources> diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index df96b4eedade2711e97d87da12678ab682c2fcb5..86fb4c4b7a0de4d5eca2c30a8031a721ad38dd8a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3489,7 +3489,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { wakeUpFromPowerKey(event.getDownTime()); } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP) && isWakeKeyWhenScreenOff(keyCode)) { - wakeUpFromWakeKey(event); + wakeUpFromWakeKey(event, false); } return 0; } @@ -3564,7 +3564,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (isWakeKey) { - wakeUpFromWakeKey(event); + wakeUpFromWakeKey(event, false); } return result; } @@ -3893,7 +3893,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (isWakeKey) { - wakeUpFromWakeKey(event); + // Check proximity only on wake key + wakeUpFromWakeKey(event, event.getKeyCode() == KeyEvent.KEYCODE_WAKEUP); } if ((result & ACTION_PASS_TO_USER) != 0) { @@ -4402,7 +4403,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void wakeUpFromPowerKey(long eventTime) { if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, - PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) { + PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER", true)) { // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout if (shouldWakeUpWithHomeIntent()) { startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true, @@ -4411,9 +4412,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void wakeUpFromWakeKey(KeyEvent event) { + private void wakeUpFromWakeKey(KeyEvent event, boolean withProximityCheck) { if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, - PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) { + PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY", withProximityCheck)) { // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) { startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true, @@ -4424,6 +4425,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, String details) { + return wakeUp(wakeTime, wakeInTheaterMode, reason, details, false); + } + + private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, + String details, boolean withProximityCheck) { final boolean theaterModeEnabled = isTheaterModeEnabled(); if (!wakeInTheaterMode && theaterModeEnabled) { return false; @@ -4434,7 +4440,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.THEATER_MODE_ON, 0); } - mPowerManager.wakeUp(wakeTime, reason, details); + if (withProximityCheck) { + mPowerManager.wakeUpWithProximityCheck(wakeTime, reason, details); + } else { + mPowerManager.wakeUp(wakeTime, reason, details); + } return true; } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 962f97ba915964cd9784681385a4e5973a71533c..44273ab320a1da3508480517ac9328670b0c213e 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -47,6 +47,9 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; import android.hardware.display.AmbientDisplayConfiguration; @@ -88,6 +91,7 @@ import android.service.dreams.DreamManagerInternal; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.sysprop.InitProperties; +import android.telephony.TelephonyManager; import android.util.KeyValueListParser; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -156,6 +160,8 @@ public final class PowerManagerService extends SystemService private static final int MSG_CHECK_FOR_LONG_WAKELOCKS = 4; // Message: Sent when an attentive timeout occurs to update the power state. private static final int MSG_ATTENTIVE_TIMEOUT = 5; + // Message: Sent when waking up with proximity check. + private static final int MSG_WAKE_UP = 6; // Dirty bit: mWakeLocks changed private static final int DIRTY_WAKE_LOCKS = 1 << 0; @@ -267,6 +273,8 @@ public final class PowerManagerService extends SystemService */ private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L; + private static final float PROXIMITY_NEAR_THRESHOLD = 5.0f; + private final Context mContext; private final ServiceThread mHandlerThread; private final Handler mHandler; @@ -938,6 +946,17 @@ public final class PowerManagerService extends SystemService private static native boolean nativeSetPowerMode(int mode, boolean enabled); private static native boolean nativeForceSuspend(); + // Whether proximity check on wake is enabled by default + private boolean mProximityWakeEnabledByDefaultConfig; + + private boolean mProximityWakeSupported; + private boolean mProximityWakeEnabled; + private int mProximityTimeOut; + private SensorManager mSensorManager; + private Sensor mProximitySensor; + private SensorEventListener mProximityListener; + private PowerManager.WakeLock mProximityWakeLock; + public PowerManagerService(Context context) { this(context, new Injector()); } @@ -1174,6 +1193,10 @@ public final class PowerManagerService extends SystemService // Shouldn't happen since in-process. } + // Initialize proximity sensor + mSensorManager = mContext.getSystemService(SensorManager.class); + mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + // Go. readConfigurationLocked(); updateSettingsLocked(); @@ -1229,6 +1252,9 @@ public final class PowerManagerService extends SystemService resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.DEVICE_DEMO_MODE), false, mSettingsObserver, UserHandle.USER_SYSTEM); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.PROXIMITY_ON_WAKE), + false, mSettingsObserver, UserHandle.USER_ALL); IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE)); if (vrManager != null) { try { @@ -1302,6 +1328,16 @@ public final class PowerManagerService extends SystemService com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1); mSupportsDoubleTapWakeConfig = resources.getBoolean( com.android.internal.R.bool.config_supportDoubleTapWake); + mProximityWakeSupported = resources.getBoolean( + com.android.internal.R.bool.config_proximityCheckOnWake); + mProximityWakeEnabledByDefaultConfig = resources.getBoolean( + com.android.internal.R.bool.config_proximityCheckOnWakeEnabledByDefault); + mProximityTimeOut = resources.getInteger( + com.android.internal.R.integer.config_proximityCheckTimeout); + if (mProximityWakeSupported) { + mProximityWakeLock = mContext.getSystemService(PowerManager.class) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ProximityWakeLock"); + } } private void updateSettingsLocked() { @@ -1354,6 +1390,11 @@ public final class PowerManagerService extends SystemService Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); + mProximityWakeEnabled = Settings.Secure.getIntForUser(resolver, + Settings.Secure.PROXIMITY_ON_WAKE, + mProximityWakeEnabledByDefaultConfig ? 1 : 0, + UserHandle.USER_CURRENT) == 1; + mDirty |= DIRTY_SETTINGS; } @@ -4827,6 +4868,10 @@ public final class PowerManagerService extends SystemService case MSG_ATTENTIVE_TIMEOUT: handleAttentiveTimeout(); break; + case MSG_WAKE_UP: + cleanupProximity(); + ((Runnable) msg.obj).run(); + break; } return true; @@ -5304,6 +5349,20 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void wakeUp(long eventTime, @WakeReason int reason, String details, String opPackageName) { + wakeUp(eventTime, reason, details, opPackageName, false); + } + + @Override // Binder call + public void wakeUpWithProximityCheck(long eventTime, @WakeReason int reason, + String details, String opPackageName) { + wakeUp(eventTime, reason, details, opPackageName, true); + } + + /** + * @hide + */ + public void wakeUp(long eventTime, @WakeReason int reason, String details, + String opPackageName, boolean checkProximity) { if (eventTime > mClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); } @@ -5312,12 +5371,19 @@ public final class PowerManagerService extends SystemService android.Manifest.permission.DEVICE_POWER, null); final int uid = Binder.getCallingUid(); - final long ident = Binder.clearCallingIdentity(); - try { - wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid, - opPackageName, uid); - } finally { - Binder.restoreCallingIdentity(ident); + final Runnable r = () -> { + final long ident = Binder.clearCallingIdentity(); + try { + wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, + uid, opPackageName, uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + }; + if (checkProximity) { + runWithProximityCheck(r); + } else { + r.run(); } } @@ -6163,4 +6229,76 @@ public final class PowerManagerService extends SystemService return interceptPowerKeyDownInternal(event); } } + + private void cleanupProximity() { + synchronized (mProximityWakeLock) { + cleanupProximityLocked(); + } + } + + private void cleanupProximityLocked() { + if (mProximityWakeLock.isHeld()) { + mProximityWakeLock.release(); + } + if (mProximityListener != null) { + mSensorManager.unregisterListener(mProximityListener); + mProximityListener = null; + } + } + + private void runWithProximityCheck(final Runnable r) { + if (mHandler.hasMessages(MSG_WAKE_UP)) { + // A message is already queued + return; + } + + final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + final boolean hasIncomingCall = tm.getCallState() == TelephonyManager.CALL_STATE_RINGING; + + if (mProximityWakeSupported && mProximityWakeEnabled + && mProximitySensor != null && !hasIncomingCall) { + final Message msg = mHandler.obtainMessage(MSG_WAKE_UP); + msg.obj = r; + mHandler.sendMessageDelayed(msg, mProximityTimeOut); + runPostProximityCheck(r); + } else { + r.run(); + } + } + + private void runPostProximityCheck(final Runnable r) { + if (mSensorManager == null) { + r.run(); + return; + } + synchronized (mProximityWakeLock) { + mProximityWakeLock.acquire(); + mProximityListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + cleanupProximityLocked(); + if (!mHandler.hasMessages(MSG_WAKE_UP)) { + Slog.w(TAG, "Proximity sensor took too long, " + + "wake event already triggered!"); + return; + } + mHandler.removeMessages(MSG_WAKE_UP); + final float distance = event.values[0]; + if (distance >= PROXIMITY_NEAR_THRESHOLD || + distance >= mProximitySensor.getMaximumRange()) { + r.run(); + } else { + Slog.w(TAG, "Not waking up. Proximity sensor is blocked."); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Do nothing + } + }; + mSensorManager.registerListener(mProximityListener, + mProximitySensor, SensorManager.SENSOR_DELAY_FASTEST); + } + } }