PowerManager: Add proximity check on wake
Co-authored-by: Adnan Begovic <adnan@cyngn.com>
Co-authored-by: Christopher R. Palmer <crpalmer@gmail.com>
Co-authored-by: Danesh M <daneshm90@gmail.com>
Co-authored-by: dankoman <dankoman30@gmail.com>
Co-authored-by: Erica Chang <echang@cyngn.com>
Co-authored-by: Han Wang <416810799@qq.com>
Co-authored-by: LuK1337 <priv.luk@gmail.com>
Co-authored-by: Michael Bestas <mkbestas@lineageos.org>
Co-authored-by: Oliver Scott <olivercscott@gmail.com>
Co-authored-by: Pat Erley <perley@cyngn.com>
Co-authored-by: Roman Birg <roman@cyngn.com>
Co-authored-by: Schubi <schubi@erlangen.ccc.de>
Co-authored-by: Scott Mertz <scott@cyngn.com>
Co-authored-by: Steve Kondik <steve@cyngn.com>
Co-authored-by: vm03 <vasy@vasy.ru>
Change-Id: Ia5ff446d88435773acccc7467e66009000dc20cb
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index c95b278..69aecb1 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -182,4 +182,6 @@
const int GO_TO_SLEEP_REASON_MAX = 10;
const int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0;
+ // Lineage custom API
+ void wakeUpWithProximityCheck(long time, int reason, String details, String opPackageName, int displayId);
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 0b5e734..1845deb3 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1763,6 +1763,24 @@
}
/**
+ * Forces the display with the supplied displayId to turn on only if nothing is blocking the
+ * proximity sensor.
+ *
+ * @see #wakeUp
+ *
+ * @hide
+ */
+ public void wakeUpWithProximityCheck(long time, @WakeReason int reason, String details,
+ int displayId) {
+ try {
+ mService.wakeUpWithProximityCheck(time, reason, details, mContext.getOpPackageName(),
+ displayId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Forces the device to start napping.
* <p>
* If the device is currently awake, starts dreaming, otherwise does nothing.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b1007466..93088ff 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4505,7 +4505,7 @@
}
if (isWakeKey) {
- wakeUpFromWakeKey(event);
+ wakeUpFromWakeKey(event, true);
}
return result;
}
@@ -4930,7 +4930,8 @@
}
if (isWakeKey) {
- wakeUpFromWakeKey(event);
+ // Check proximity only on wake key
+ wakeUpFromWakeKey(event, event.getKeyCode() == KeyEvent.KEYCODE_WAKEUP);
}
// If the key event is targeted to a specific display, then the user is interacting with
@@ -5459,11 +5460,26 @@
wakeUpFromWakeKey(
event.getEventTime(),
event.getKeyCode(),
- event.getAction() == KeyEvent.ACTION_DOWN);
+ event.getAction() == KeyEvent.ACTION_DOWN,
+ false);
+ }
+
+ private void wakeUpFromWakeKey(KeyEvent event, boolean withProximityCheck) {
+ wakeUpFromWakeKey(
+ event.getEventTime(),
+ event.getKeyCode(),
+ event.getAction() == KeyEvent.ACTION_DOWN,
+ withProximityCheck);
}
private void wakeUpFromWakeKey(long eventTime, int keyCode, boolean isDown) {
- if (mWindowWakeUpPolicy.wakeUpFromKey(DEFAULT_DISPLAY, eventTime, keyCode, isDown)) {
+ wakeUpFromWakeKey(eventTime, keyCode, isDown, false);
+ }
+
+ private void wakeUpFromWakeKey(long eventTime, int keyCode, boolean isDown,
+ boolean withProximityCheck) {
+ if (mWindowWakeUpPolicy.wakeUpFromKey(DEFAULT_DISPLAY, eventTime, keyCode, isDown,
+ withProximityCheck)) {
final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER;
// Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
if (shouldWakeUpWithHomeIntent() && keyCanLaunchHome) {
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
index 30325b9..0a77038 100644
--- a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -117,6 +117,22 @@
* executed; {@code false} otherwise.
*/
boolean wakeUpFromKey(int displayId, long eventTime, int keyCode, boolean isDown) {
+ return wakeUpFromKey(displayId, eventTime, keyCode, isDown, false);
+ }
+
+ /**
+ * Wakes up from a key event.
+ *
+ * @param displayId the id of the display to wake.
+ * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+ * @param keyCode the {@link android.view.KeyEvent} key code of the key event.
+ * @param isDown {@code true} if the event's action is {@link KeyEvent#ACTION_DOWN}.
+ * @param withProximityCheck {@code true} if we should check proximity sensor before wakeup.
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromKey(int displayId, long eventTime, int keyCode, boolean isDown,
+ boolean withProximityCheck) {
final boolean wakeAllowedDuringTheaterMode =
keyCode == KEYCODE_POWER
? mAllowTheaterModeWakeFromPowerKey
@@ -134,12 +150,14 @@
displayId,
eventTime,
keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
- keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+ keyCode == KEYCODE_POWER ? "POWER" : "KEY",
+ withProximityCheck);
} else {
wakeUp(
eventTime,
keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
- keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+ keyCode == KEYCODE_POWER ? "POWER" : "KEY",
+ withProximityCheck);
}
return true;
}
@@ -276,14 +294,34 @@
/** Wakes up {@link PowerManager}. */
private void wakeUp(long wakeTime, @WakeReason int reason, String details) {
- mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
+ wakeUp(wakeTime, reason, details, false);
+ }
+
+ private void wakeUp(long wakeTime, @WakeReason int reason, String details,
+ boolean withProximityCheck) {
+ if (withProximityCheck) {
+ mPowerManager.wakeUpWithProximityCheck(wakeTime, reason, "android.policy:" + details,
+ Display.DEFAULT_DISPLAY);
+ } else {
+ mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
+ }
}
/** Wakes up given display. */
private void wakeUp(int displayId, long wakeTime, @WakeReason int reason, String details) {
+ wakeUp(displayId, wakeTime, reason, details, false);
+ }
+
+ private void wakeUp(int displayId, long wakeTime, @WakeReason int reason, String details,
+ boolean withProximityCheck) {
// If we're given an invalid display id to wake, fall back to waking default display
final int displayIdToWake =
displayId == Display.INVALID_DISPLAY ? Display.DEFAULT_DISPLAY : displayId;
- mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details, displayIdToWake);
+ if (withProximityCheck) {
+ mPowerManager.wakeUpWithProximityCheck(wakeTime, reason, "android.policy:" + details,
+ displayIdToWake);
+ } else {
+ mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details, displayIdToWake);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index fd98557..5c92a3a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -60,6 +60,9 @@
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.devicestate.DeviceState;
@@ -108,6 +111,7 @@
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
import android.sysprop.PowerProperties;
+import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.IntArray;
import android.util.KeyValueListParser;
@@ -152,6 +156,8 @@
import dalvik.annotation.optimization.NeverCompile;
+import lineageos.providers.LineageSettings;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -197,6 +203,9 @@
// Message: Sent when the policy wants to force disable wakelocks.
private static final int MSG_FORCE_DISABLE_WAKELOCKS = 8;
+ // Message: Sent when waking up with proximity check.
+ private static final int MSG_WAKE_UP = 9;
+
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
// Dirty bit: mWakefulness changed
@@ -251,6 +260,9 @@
// This should perhaps be a setting.
private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000;
+ // Threshold to consider proximity sensor covered
+ private static final float PROXIMITY_NEAR_THRESHOLD = 5.0f;
+
// How long a partial wake lock must be held until we consider it a long wake lock.
static final long MIN_LONG_WAKE_CHECK_INTERVAL = 60*1000;
@@ -1187,6 +1199,17 @@
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());
}
@@ -1442,6 +1465,10 @@
// Shouldn't happen since in-process.
}
+ // Initialize proximity sensor
+ mSensorManager = mContext.getSystemService(SensorManager.class);
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+
mLowPowerStandbyController.systemReady();
// Go.
@@ -1516,6 +1543,11 @@
Settings.Global.DEVICE_DEMO_MODE),
false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ // Register for Lineage settings changes.
+ resolver.registerContentObserver(LineageSettings.System.getUriFor(
+ LineageSettings.System.PROXIMITY_ON_WAKE),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+
// Register for broadcasts from other components of the system.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -1582,6 +1614,17 @@
com.android.internal.R.bool.config_brightWhenDozing);
mSupportsDoubleTapWakeConfig = resources.getBoolean(
com.android.internal.R.bool.config_supportDoubleTapWake);
+
+ mProximityWakeSupported = resources.getBoolean(
+ org.lineageos.platform.internal.R.bool.config_proximityCheckOnWake);
+ mProximityWakeEnabledByDefaultConfig = resources.getBoolean(
+ org.lineageos.platform.internal.R.bool.config_proximityCheckOnWakeEnabledByDefault);
+ mProximityTimeOut = resources.getInteger(
+ org.lineageos.platform.internal.R.integer.config_proximityCheckTimeout);
+ if (mProximityWakeSupported) {
+ mProximityWakeLock = mContext.getSystemService(PowerManager.class)
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ProximityWakeLock");
+ }
}
@GuardedBy("mLock")
@@ -1631,6 +1674,10 @@
mSystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue);
}
+ mProximityWakeEnabled = LineageSettings.System.getIntForUser(resolver,
+ LineageSettings.System.PROXIMITY_ON_WAKE,
+ mProximityWakeEnabledByDefaultConfig ? 1 : 0, UserHandle.USER_CURRENT) == 1;
+
mDirty |= DIRTY_SETTINGS;
}
@@ -5543,6 +5590,10 @@
setForceDisableWakelocksInternal(false);
}
break;
+ case MSG_WAKE_UP:
+ cleanupProximity();
+ ((Runnable) msg.obj).run();
+ break;
}
return true;
@@ -6298,12 +6349,24 @@
@Override // Binder call
public void wakeUp(long eventTime, @WakeReason int reason, String details,
String opPackageName) {
- wakeUpWithDisplayId(eventTime, reason, details, opPackageName, Display.DEFAULT_DISPLAY);
+ wakeUpWithDisplayId(eventTime, reason, details, opPackageName, Display.DEFAULT_DISPLAY,
+ false);
+ }
+
+ @Override // Binder call
+ public void wakeUpWithProximityCheck(long eventTime, @WakeReason int reason,
+ String details, String opPackageName, int displayId) {
+ wakeUpWithDisplayId(eventTime, reason, details, opPackageName, displayId, true);
}
@Override // Binder call
public void wakeUpWithDisplayId(long eventTime, @WakeReason int reason, String details,
String opPackageName, int displayId) {
+ wakeUpWithDisplayId(eventTime, reason, details, opPackageName, displayId, false);
+ }
+
+ public void wakeUpWithDisplayId(long eventTime, @WakeReason int reason, String details,
+ String opPackageName, int displayId, boolean checkProximity) {
final long now = mClock.uptimeMillis();
if (eventTime > now) {
Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now);
@@ -6314,20 +6377,27 @@
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
- final long ident = Binder.clearCallingIdentity();
- try {
- int displayGroupId = getDisplayGroupId(displayId);
- synchronized (mLock) {
- if (!mBootCompleted && sQuiescent) {
- mDirty |= DIRTY_QUIESCENT;
- updatePowerStateLocked();
- return;
+ final Runnable r = () -> {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ int displayGroupId = getDisplayGroupId(displayId);
+ synchronized (mLock) {
+ if (!mBootCompleted && sQuiescent) {
+ mDirty |= DIRTY_QUIESCENT;
+ updatePowerStateLocked();
+ return;
+ }
+ wakePowerGroupLocked(mPowerGroups.get(displayGroupId), eventTime,
+ reason, details, uid, opPackageName, uid);
}
- wakePowerGroupLocked(mPowerGroups.get(displayGroupId), eventTime,
- reason, details, uid, opPackageName, uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
+ };
+ if (checkProximity) {
+ runWithProximityCheck(r);
+ } else {
+ r.run();
}
}
@@ -7692,4 +7762,76 @@
}
return displayInfo.displayGroupId;
}
+
+ 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);
+ }
+ }
}