diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 518b22bd5e10c..f5b2ac586bd13 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -24,6 +24,8 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import java.util.Arrays;
+
import com.android.internal.R;
/**
@@ -258,10 +260,11 @@ public class AmbientDisplayConfiguration {
String defaultValue,
int posture) {
String sensorType = defaultValue;
- if (posture < postureMapping.length) {
+ if (postureMapping != null && posture < postureMapping.length) {
sensorType = postureMapping[posture];
} else {
- Log.e(TAG, "Unsupported doze posture " + posture);
+ Log.e(TAG, "Unsupported doze posture " + posture
+ + " postureMapping=" + Arrays.toString(postureMapping));
}
return TextUtils.isEmpty(sensorType) ? defaultValue : sensorType;
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 786b371862849..c2901a56ae9b6 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -224,15 +224,23 @@
display brightness, suitable to listen to while the device is asleep (e.g. during
always-on display) -->
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -246,6 +254,15 @@
+
+
+
+
+
+
+
+
- {
int DEVICE_POSTURE_HALF_OPENED = 2;
int DEVICE_POSTURE_OPENED = 3;
int DEVICE_POSTURE_FLIPPED = 4;
+ int SUPPORTED_POSTURES_SIZE = DEVICE_POSTURE_FLIPPED + 1;
/** Return the current device posture. */
@DevicePostureInt int getDevicePosture();
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
new file mode 100644
index 0000000000000..28cba8e16970b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 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.systemui.util.sensors;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.Execution;
+
+import javax.inject.Inject;
+
+/**
+ * Proximity sensor that changes proximity sensor usage based on the current posture.
+ * Posture -> prox sensor mapping can be found in SystemUI config overlays at:
+ * - proximity_sensor_posture_mapping
+ * - proximity_sensor_secondary_posture_mapping.
+ * where the array indices correspond to the following postures:
+ * [UNKNOWN, CLOSED, HALF_OPENED, OPENED]
+ */
+class PostureDependentProximitySensor extends ProximitySensorImpl {
+ private final ThresholdSensor[] mPostureToPrimaryProxSensorMap;
+ private final ThresholdSensor[] mPostureToSecondaryProxSensorMap;
+
+ @Inject
+ PostureDependentProximitySensor(
+ @PrimaryProxSensor ThresholdSensor[] postureToPrimaryProxSensorMap,
+ @SecondaryProxSensor ThresholdSensor[] postureToSecondaryProxSensorMap,
+ @NonNull DelayableExecutor delayableExecutor,
+ @NonNull Execution execution,
+ @NonNull DevicePostureController devicePostureController
+ ) {
+ super(
+ postureToPrimaryProxSensorMap[0],
+ postureToSecondaryProxSensorMap[0],
+ delayableExecutor,
+ execution
+ );
+ mPostureToPrimaryProxSensorMap = postureToPrimaryProxSensorMap;
+ mPostureToSecondaryProxSensorMap = postureToSecondaryProxSensorMap;
+ mDevicePosture = devicePostureController.getDevicePosture();
+ devicePostureController.addCallback(mDevicePostureCallback);
+
+ chooseSensors();
+ }
+ private void chooseSensors() {
+ if (mDevicePosture >= mPostureToPrimaryProxSensorMap.length
+ || mDevicePosture >= mPostureToSecondaryProxSensorMap.length) {
+ Log.e("PostureDependentProxSensor",
+ "unsupported devicePosture=" + mDevicePosture);
+ return;
+ }
+
+ ThresholdSensor newPrimaryProx = mPostureToPrimaryProxSensorMap[mDevicePosture];
+ ThresholdSensor newSecondaryProx = mPostureToSecondaryProxSensorMap[mDevicePosture];
+
+ if (newPrimaryProx != mPrimaryThresholdSensor
+ || newSecondaryProx != mSecondaryThresholdSensor) {
+ logDebug("Register new proximity sensors newPosture="
+ + DevicePostureController.devicePostureToString(mDevicePosture));
+ unregisterInternal();
+
+ if (mPrimaryThresholdSensor != null) {
+ mPrimaryThresholdSensor.unregister(mPrimaryEventListener);
+ }
+ if (mSecondaryThresholdSensor != null) {
+ mSecondaryThresholdSensor.unregister(mSecondaryEventListener);
+ }
+
+ mPrimaryThresholdSensor = newPrimaryProx;
+ mSecondaryThresholdSensor = newSecondaryProx;
+
+ mInitializedListeners = false;
+ registerInternal();
+ }
+ }
+
+ private final DevicePostureController.Callback mDevicePostureCallback =
+ posture -> {
+ if (mDevicePosture == posture) {
+ return;
+ }
+
+ mDevicePosture = posture;
+ chooseSensors();
+ };
+
+ @Override
+ public String toString() {
+ return String.format("{posture=%s, proximitySensor=%s}",
+ DevicePostureController.devicePostureToString(mDevicePosture), super.toString());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java
new file mode 100644
index 0000000000000..a8a6341cc5eeb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 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.systemui.util.sensors;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * Convenience class allowing for briefly checking the proximity sensor.
+ */
+public class ProximityCheck implements Runnable {
+
+ private final ProximitySensor mSensor;
+ private final DelayableExecutor mDelayableExecutor;
+ private List> mCallbacks = new ArrayList<>();
+ private final ThresholdSensor.Listener mListener;
+ private final AtomicBoolean mRegistered = new AtomicBoolean();
+
+ @Inject
+ public ProximityCheck(
+ ProximitySensor sensor,
+ @Main DelayableExecutor delayableExecutor) {
+ mSensor = sensor;
+ mSensor.setTag("prox_check");
+ mDelayableExecutor = delayableExecutor;
+ mListener = this::onProximityEvent;
+ }
+
+ /** Set a descriptive tag for the sensors registration. */
+ public void setTag(String tag) {
+ mSensor.setTag(tag);
+ }
+
+ @Override
+ public void run() {
+ unregister();
+ onProximityEvent(null);
+ }
+
+ /**
+ * Query the proximity sensor, timing out if no result.
+ */
+ public void check(long timeoutMs, Consumer callback) {
+ if (!mSensor.isLoaded()) {
+ callback.accept(null);
+ return;
+ }
+ mCallbacks.add(callback);
+ if (!mRegistered.getAndSet(true)) {
+ mSensor.register(mListener);
+ mDelayableExecutor.executeDelayed(this, timeoutMs);
+ }
+ }
+
+ private void unregister() {
+ mSensor.unregister(mListener);
+ mRegistered.set(false);
+ }
+
+ private void onProximityEvent(ThresholdSensorEvent proximityEvent) {
+ mCallbacks.forEach(
+ booleanConsumer ->
+ booleanConsumer.accept(
+ proximityEvent == null ? null : proximityEvent.getBelow()));
+ mCallbacks.clear();
+ unregister();
+ mRegistered.set(false);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index bd1103982017d..d3f1c93195a15 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -16,149 +16,24 @@
package com.android.systemui.util.sensors;
-import android.hardware.SensorManager;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.concurrency.Execution;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
/**
- * Wrapper around SensorManager customized for the Proximity sensor.
- *
- * The ProximitySensor supports the concept of a primary and a
- * secondary hardware sensor. The primary sensor is used for a first
- * pass check if the phone covered. When triggered, it then checks
- * the secondary sensor for confirmation (if there is one). It does
- * not send a proximity event until the secondary sensor confirms (or
- * rejects) the reading. The secondary sensor is, in fact, the source
- * of truth.
- *
- * This is necessary as sometimes keeping the secondary sensor on for
- * extends periods is undesirable. It may, however, result in increased
- * latency for proximity readings.
- *
- * Phones should configure this via a config.xml overlay. If no
- * proximity sensor is set (primary or secondary) we fall back to the
- * default Sensor.TYPE_PROXIMITY. If proximity_sensor_type is set in
- * config.xml, that will be used as the primary sensor. If
- * proximity_sensor_secondary_type is set, that will function as the
- * secondary sensor. If no secondary is set, only the primary will be
- * used.
+ * Wrapper class for a dual ProximitySensor which supports a secondary sensor gated upon the
+ * primary).
*/
-public class ProximitySensor implements ThresholdSensor {
- private static final String TAG = "ProxSensor";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final long SECONDARY_PING_INTERVAL_MS = 5000;
-
- private final ThresholdSensor mPrimaryThresholdSensor;
- private final ThresholdSensor mSecondaryThresholdSensor;
- private final DelayableExecutor mDelayableExecutor;
- private final Execution mExecution;
- private final List mListeners = new ArrayList<>();
- private String mTag = null;
- @VisibleForTesting protected boolean mPaused;
- private ThresholdSensorEvent mLastPrimaryEvent;
- @VisibleForTesting
- ThresholdSensorEvent mLastEvent;
- private boolean mRegistered;
- private final AtomicBoolean mAlerting = new AtomicBoolean();
- private Runnable mCancelSecondaryRunnable;
- private boolean mInitializedListeners = false;
- private boolean mSecondarySafe = false;
-
- private final ThresholdSensor.Listener mPrimaryEventListener = this::onPrimarySensorEvent;
-
- private final ThresholdSensor.Listener mSecondaryEventListener =
- new ThresholdSensor.Listener() {
- @Override
- public void onThresholdCrossed(ThresholdSensorEvent event) {
- // If we no longer have a "below" signal and the secondary sensor is not
- // considered "safe", then we need to turn it off.
- if (!mSecondarySafe
- && (mLastPrimaryEvent == null
- || !mLastPrimaryEvent.getBelow()
- || !event.getBelow())) {
- chooseSensor();
- if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) {
- // Only check the secondary as long as the primary thinks we're near.
- if (mCancelSecondaryRunnable != null) {
- mCancelSecondaryRunnable.run();
- mCancelSecondaryRunnable = null;
- }
- return;
- } else {
- // Check this sensor again in a moment.
- mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> {
- // This is safe because we know that mSecondaryThresholdSensor
- // is loaded, otherwise we wouldn't be here.
- mPrimaryThresholdSensor.pause();
- mSecondaryThresholdSensor.resume();
- },
- SECONDARY_PING_INTERVAL_MS);
- }
- }
- logDebug("Secondary sensor event: " + event.getBelow() + ".");
-
- if (!mPaused) {
- onSensorEvent(event);
- }
- }
- };
-
- @Inject
- public ProximitySensor(
- @PrimaryProxSensor ThresholdSensor primary,
- @SecondaryProxSensor ThresholdSensor secondary,
- @Main DelayableExecutor delayableExecutor,
- Execution execution) {
- mPrimaryThresholdSensor = primary;
- mSecondaryThresholdSensor = secondary;
- mDelayableExecutor = delayableExecutor;
- mExecution = execution;
- }
-
- @Override
- public void setTag(String tag) {
- mTag = tag;
- mPrimaryThresholdSensor.setTag(tag + ":primary");
- mSecondaryThresholdSensor.setTag(tag + ":secondary");
- }
-
- @Override
- public void setDelay(int delay) {
- mExecution.assertIsMainThread();
- mPrimaryThresholdSensor.setDelay(delay);
- mSecondaryThresholdSensor.setDelay(delay);
- }
+public interface ProximitySensor extends ThresholdSensor {
+ /**
+ * Returns true if we are registered with the SensorManager.
+ */
+ boolean isRegistered();
/**
- * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ * Whether the proximity sensor reports near. Can return null if no information has been
+ * received yet.
*/
- @Override
- public void pause() {
- mExecution.assertIsMainThread();
- mPaused = true;
- unregisterInternal();
- }
+ Boolean isNear();
- /**
- * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
- */
- @Override
- public void resume() {
- mExecution.assertIsMainThread();
- mPaused = false;
- registerInternal();
- }
+ /** Update all listeners with the last value this class received from the sensor. */
+ void alertListeners();
/**
* Sets that it is safe to leave the secondary sensor on indefinitely.
@@ -166,249 +41,5 @@ public class ProximitySensor implements ThresholdSensor {
* The secondary sensor will be turned on if there are any registered listeners, regardless
* of what is reported by the primary sensor.
*/
- public void setSecondarySafe(boolean safe) {
- mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe;
- chooseSensor();
- }
-
- /**
- * Returns true if we are registered with the SensorManager.
- */
- public boolean isRegistered() {
- return mRegistered;
- }
-
- /**
- * Returns {@code false} if a Proximity sensor is not available.
- */
- @Override
- public boolean isLoaded() {
- return mPrimaryThresholdSensor.isLoaded();
- }
-
- /**
- * Add a listener.
- *
- * Registers itself with the {@link SensorManager} if this is the first listener
- * added. If the ProximitySensor is paused, it will be registered when resumed.
- */
- @Override
- public void register(ThresholdSensor.Listener listener) {
- mExecution.assertIsMainThread();
- if (!isLoaded()) {
- return;
- }
-
- if (mListeners.contains(listener)) {
- logDebug("ProxListener registered multiple times: " + listener);
- } else {
- mListeners.add(listener);
- }
- registerInternal();
- }
-
- protected void registerInternal() {
- mExecution.assertIsMainThread();
- if (mRegistered || mPaused || mListeners.isEmpty()) {
- return;
- }
- if (!mInitializedListeners) {
- mPrimaryThresholdSensor.pause();
- mSecondaryThresholdSensor.pause();
- mPrimaryThresholdSensor.register(mPrimaryEventListener);
- mSecondaryThresholdSensor.register(mSecondaryEventListener);
- mInitializedListeners = true;
- }
- logDebug("Registering sensor listener");
-
- mRegistered = true;
- chooseSensor();
- }
-
- private void chooseSensor() {
- mExecution.assertIsMainThread();
- if (!mRegistered || mPaused || mListeners.isEmpty()) {
- return;
- }
- if (mSecondarySafe) {
- mSecondaryThresholdSensor.resume();
- mPrimaryThresholdSensor.pause();
- } else {
- mPrimaryThresholdSensor.resume();
- mSecondaryThresholdSensor.pause();
- }
- }
-
- /**
- * Remove a listener.
- *
- * If all listeners are removed from an instance of this class,
- * it will unregister itself with the SensorManager.
- */
- @Override
- public void unregister(ThresholdSensor.Listener listener) {
- mExecution.assertIsMainThread();
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- unregisterInternal();
- }
- }
-
- protected void unregisterInternal() {
- mExecution.assertIsMainThread();
- if (!mRegistered) {
- return;
- }
- logDebug("unregistering sensor listener");
- mPrimaryThresholdSensor.pause();
- mSecondaryThresholdSensor.pause();
- if (mCancelSecondaryRunnable != null) {
- mCancelSecondaryRunnable.run();
- mCancelSecondaryRunnable = null;
- }
- mLastPrimaryEvent = null; // Forget what we know.
- mLastEvent = null;
- mRegistered = false;
- }
-
- public Boolean isNear() {
- return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null;
- }
-
- /** Update all listeners with the last value this class received from the sensor. */
- public void alertListeners() {
- mExecution.assertIsMainThread();
- if (mAlerting.getAndSet(true)) {
- return;
- }
- if (mLastEvent != null) {
- ThresholdSensorEvent lastEvent = mLastEvent; // Listeners can null out mLastEvent.
- List listeners = new ArrayList<>(mListeners);
- listeners.forEach(proximitySensorListener ->
- proximitySensorListener.onThresholdCrossed(lastEvent));
- }
-
- mAlerting.set(false);
- }
-
- private void onPrimarySensorEvent(ThresholdSensorEvent event) {
- mExecution.assertIsMainThread();
- if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) {
- return;
- }
-
- mLastPrimaryEvent = event;
-
- if (mSecondarySafe && mSecondaryThresholdSensor.isLoaded()) {
- logDebug("Primary sensor reported " + (event.getBelow() ? "near" : "far")
- + ". Checking secondary.");
- if (mCancelSecondaryRunnable == null) {
- mSecondaryThresholdSensor.resume();
- }
- return;
- }
-
-
- if (!mSecondaryThresholdSensor.isLoaded()) { // No secondary
- logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
- onSensorEvent(event);
- } else if (event.getBelow()) { // Covered? Check secondary.
- logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary.");
- if (mCancelSecondaryRunnable != null) {
- mCancelSecondaryRunnable.run();
- }
- mSecondaryThresholdSensor.resume();
- } else { // Uncovered. Report immediately.
- onSensorEvent(event);
- }
- }
-
- private void onSensorEvent(ThresholdSensorEvent event) {
- mExecution.assertIsMainThread();
- if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) {
- return;
- }
-
- if (!mSecondarySafe && !event.getBelow()) {
- chooseSensor();
- }
-
- mLastEvent = event;
- alertListeners();
- }
-
- @Override
- public String toString() {
- return String.format("{registered=%s, paused=%s, near=%s, primarySensor=%s, "
- + "secondarySensor=%s secondarySafe=%s}",
- isRegistered(), mPaused, isNear(), mPrimaryThresholdSensor,
- mSecondaryThresholdSensor, mSecondarySafe);
- }
-
- /**
- * Convenience class allowing for briefly checking the proximity sensor.
- */
- public static class ProximityCheck implements Runnable {
-
- private final ProximitySensor mSensor;
- private final DelayableExecutor mDelayableExecutor;
- private List> mCallbacks = new ArrayList<>();
- private final ThresholdSensor.Listener mListener;
- private final AtomicBoolean mRegistered = new AtomicBoolean();
-
- @Inject
- public ProximityCheck(ProximitySensor sensor, @Main DelayableExecutor delayableExecutor) {
- mSensor = sensor;
- mSensor.setTag("prox_check");
- mDelayableExecutor = delayableExecutor;
- mListener = this::onProximityEvent;
- }
-
- /** Set a descriptive tag for the sensors registration. */
- public void setTag(String tag) {
- mSensor.setTag(tag);
- }
-
- @Override
- public void run() {
- unregister();
- onProximityEvent(null);
- }
-
- /**
- * Query the proximity sensor, timing out if no result.
- */
- public void check(long timeoutMs, Consumer callback) {
- if (!mSensor.isLoaded()) {
- callback.accept(null);
- return;
- }
- mCallbacks.add(callback);
- if (!mRegistered.getAndSet(true)) {
- mSensor.register(mListener);
- mDelayableExecutor.executeDelayed(this, timeoutMs);
- }
- }
-
- private void unregister() {
- mSensor.unregister(mListener);
- mRegistered.set(false);
- }
-
- private void onProximityEvent(ThresholdSensorEvent proximityEvent) {
- mCallbacks.forEach(
- booleanConsumer ->
- booleanConsumer.accept(
- proximityEvent == null ? null : proximityEvent.getBelow()));
- mCallbacks.clear();
- unregister();
- mRegistered.set(false);
- }
- }
-
- private void logDebug(String msg) {
- if (DEBUG) {
- Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
- }
- }
+ void setSecondarySafe(boolean safe);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
new file mode 100644
index 0000000000000..e639313ac6491
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2019 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.systemui.util.sensors;
+
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.Execution;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.inject.Inject;
+
+/**
+ * Wrapper around SensorManager customized for the Proximity sensor.
+ *
+ * The ProximitySensor supports the concept of a primary and a
+ * secondary hardware sensor. The primary sensor is used for a first
+ * pass check if the phone covered. When triggered, it then checks
+ * the secondary sensor for confirmation (if there is one). It does
+ * not send a proximity event until the secondary sensor confirms (or
+ * rejects) the reading. The secondary sensor is, in fact, the source
+ * of truth.
+ *
+ * This is necessary as sometimes keeping the secondary sensor on for
+ * extends periods is undesirable. It may, however, result in increased
+ * latency for proximity readings.
+ *
+ * Phones should configure this via a config.xml overlay. If no
+ * proximity sensor is set (primary or secondary) we fall back to the
+ * default Sensor.TYPE_PROXIMITY. If proximity_sensor_type is set in
+ * config.xml, that will be used as the primary sensor. If
+ * proximity_sensor_secondary_type is set, that will function as the
+ * secondary sensor. If no secondary is set, only the primary will be
+ * used.
+ */
+class ProximitySensorImpl implements ProximitySensor {
+ private static final String TAG = "ProxSensor";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final long SECONDARY_PING_INTERVAL_MS = 5000;
+
+ ThresholdSensor mPrimaryThresholdSensor;
+ ThresholdSensor mSecondaryThresholdSensor;
+ private final DelayableExecutor mDelayableExecutor;
+ private final Execution mExecution;
+ private final List mListeners = new ArrayList<>();
+ private String mTag = null;
+ @VisibleForTesting protected boolean mPaused;
+ private ThresholdSensorEvent mLastPrimaryEvent;
+ @VisibleForTesting
+ ThresholdSensorEvent mLastEvent;
+ private boolean mRegistered;
+ private final AtomicBoolean mAlerting = new AtomicBoolean();
+ private Runnable mCancelSecondaryRunnable;
+ boolean mInitializedListeners = false;
+ private boolean mSecondarySafe = false; // safe to skip primary sensor check and use secondary
+ protected @DevicePostureController.DevicePostureInt int mDevicePosture;
+
+ final ThresholdSensor.Listener mPrimaryEventListener = this::onPrimarySensorEvent;
+
+ final ThresholdSensor.Listener mSecondaryEventListener =
+ new ThresholdSensor.Listener() {
+ @Override
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
+ // If we no longer have a "below" signal and the secondary sensor is not
+ // considered "safe", then we need to turn it off.
+ if (!mSecondarySafe
+ && (mLastPrimaryEvent == null
+ || !mLastPrimaryEvent.getBelow()
+ || !event.getBelow())) {
+ chooseSensor();
+ if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) {
+ // Only check the secondary as long as the primary thinks we're near.
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ mCancelSecondaryRunnable = null;
+ }
+ return;
+ } else {
+ // Check this sensor again in a moment.
+ mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> {
+ // This is safe because we know that mSecondaryThresholdSensor
+ // is loaded, otherwise we wouldn't be here.
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.resume();
+ },
+ SECONDARY_PING_INTERVAL_MS);
+ }
+ }
+ logDebug("Secondary sensor event: " + event.getBelow() + ".");
+
+ if (!mPaused) {
+ onSensorEvent(event);
+ }
+ }
+ };
+
+ @Inject
+ ProximitySensorImpl(
+ @PrimaryProxSensor ThresholdSensor primary,
+ @SecondaryProxSensor ThresholdSensor secondary,
+ @Main DelayableExecutor delayableExecutor,
+ Execution execution) {
+ mPrimaryThresholdSensor = primary;
+ mSecondaryThresholdSensor = secondary;
+ mDelayableExecutor = delayableExecutor;
+ mExecution = execution;
+ }
+
+ @Override
+ public void setTag(String tag) {
+ mTag = tag;
+ mPrimaryThresholdSensor.setTag(tag + ":primary");
+ mSecondaryThresholdSensor.setTag(tag + ":secondary");
+ }
+
+ @Override
+ public void setDelay(int delay) {
+ mExecution.assertIsMainThread();
+ mPrimaryThresholdSensor.setDelay(delay);
+ mSecondaryThresholdSensor.setDelay(delay);
+ }
+
+ /**
+ * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ */
+ @Override
+ public void pause() {
+ mExecution.assertIsMainThread();
+ mPaused = true;
+ unregisterInternal();
+ }
+
+ /**
+ * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
+ */
+ @Override
+ public void resume() {
+ mExecution.assertIsMainThread();
+ mPaused = false;
+ registerInternal();
+ }
+
+ @Override
+ public void setSecondarySafe(boolean safe) {
+ mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe;
+ chooseSensor();
+ }
+
+ /**
+ * Returns true if we are registered with the SensorManager.
+ */
+ @Override
+ public boolean isRegistered() {
+ return mRegistered;
+ }
+
+ /**
+ * Returns {@code false} if a Proximity sensor is not available.
+ */
+ @Override
+ public boolean isLoaded() {
+ return mPrimaryThresholdSensor.isLoaded();
+ }
+
+ /**
+ * Add a listener.
+ *
+ * Registers itself with the {@link SensorManager} if this is the first listener
+ * added. If the ProximitySensor is paused, it will be registered when resumed.
+ */
+ @Override
+ public void register(ThresholdSensor.Listener listener) {
+ mExecution.assertIsMainThread();
+ if (!isLoaded()) {
+ return;
+ }
+
+ if (mListeners.contains(listener)) {
+ logDebug("ProxListener registered multiple times: " + listener);
+ } else {
+ mListeners.add(listener);
+ }
+ registerInternal();
+ }
+
+ protected void registerInternal() {
+ mExecution.assertIsMainThread();
+ if (mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ if (!mInitializedListeners) {
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.pause();
+ mPrimaryThresholdSensor.register(mPrimaryEventListener);
+ mSecondaryThresholdSensor.register(mSecondaryEventListener);
+ mInitializedListeners = true;
+ }
+
+ mRegistered = true;
+ chooseSensor();
+ }
+
+ private void chooseSensor() {
+ mExecution.assertIsMainThread();
+ if (!mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ if (mSecondarySafe) {
+ mSecondaryThresholdSensor.resume();
+ mPrimaryThresholdSensor.pause();
+ } else {
+ mPrimaryThresholdSensor.resume();
+ mSecondaryThresholdSensor.pause();
+ }
+ }
+
+ /**
+ * Remove a listener.
+ *
+ * If all listeners are removed from an instance of this class,
+ * it will unregister itself with the SensorManager.
+ */
+ @Override
+ public void unregister(ThresholdSensor.Listener listener) {
+ mExecution.assertIsMainThread();
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ unregisterInternal();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return mPrimaryThresholdSensor.getName();
+ }
+
+ @Override
+ public String getType() {
+ return mPrimaryThresholdSensor.getType();
+ }
+
+ protected void unregisterInternal() {
+ mExecution.assertIsMainThread();
+ if (!mRegistered) {
+ return;
+ }
+ logDebug("unregistering sensor listener");
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.pause();
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ mCancelSecondaryRunnable = null;
+ }
+ mLastPrimaryEvent = null; // Forget what we know.
+ mLastEvent = null;
+ mRegistered = false;
+ }
+
+ @Override
+ public Boolean isNear() {
+ return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null;
+ }
+
+ @Override
+ public void alertListeners() {
+ mExecution.assertIsMainThread();
+ if (mAlerting.getAndSet(true)) {
+ return;
+ }
+ if (mLastEvent != null) {
+ ThresholdSensorEvent lastEvent = mLastEvent; // Listeners can null out mLastEvent.
+ List listeners = new ArrayList<>(mListeners);
+ listeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onThresholdCrossed(lastEvent));
+ }
+
+ mAlerting.set(false);
+ }
+
+ private void onPrimarySensorEvent(ThresholdSensorEvent event) {
+ mExecution.assertIsMainThread();
+ if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) {
+ return;
+ }
+
+ mLastPrimaryEvent = event;
+
+ if (mSecondarySafe && mSecondaryThresholdSensor.isLoaded()) {
+ logDebug("Primary sensor reported " + (event.getBelow() ? "near" : "far")
+ + ". Checking secondary.");
+ if (mCancelSecondaryRunnable == null) {
+ mSecondaryThresholdSensor.resume();
+ }
+ return;
+ }
+
+
+ if (!mSecondaryThresholdSensor.isLoaded()) { // No secondary
+ logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
+ onSensorEvent(event);
+ } else if (event.getBelow()) { // Covered? Check secondary.
+ logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary.");
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ }
+ mSecondaryThresholdSensor.resume();
+ } else { // Uncovered. Report immediately.
+ onSensorEvent(event);
+ }
+ }
+
+ private void onSensorEvent(ThresholdSensorEvent event) {
+ mExecution.assertIsMainThread();
+ if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) {
+ return;
+ }
+
+ if (!mSecondarySafe && !event.getBelow()) {
+ chooseSensor();
+ }
+
+ mLastEvent = event;
+ alertListeners();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{registered=%s, paused=%s, near=%s, posture=%s, primarySensor=%s, "
+ + "secondarySensor=%s secondarySafe=%s}",
+ isRegistered(), mPaused, isNear(), mDevicePosture, mPrimaryThresholdSensor,
+ mSecondaryThresholdSensor, mSecondarySafe);
+ }
+
+ void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
index 11e7df8bd85f8..0be6068c22a40 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
@@ -16,11 +16,23 @@
package com.android.systemui.util.sensors;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.util.HashMap;
+import java.util.Map;
+
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -31,8 +43,10 @@ import dagger.Provides;
public class SensorModule {
@Provides
@PrimaryProxSensor
- static ThresholdSensor providePrimaryProxSensor(SensorManager sensorManager,
- ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ static ThresholdSensor providePrimaryProximitySensor(
+ SensorManager sensorManager,
+ ThresholdSensorImpl.Builder thresholdSensorBuilder
+ ) {
try {
return thresholdSensorBuilder
.setSensorDelay(SensorManager.SENSOR_DELAY_NORMAL)
@@ -52,8 +66,9 @@ public class SensorModule {
@Provides
@SecondaryProxSensor
- static ThresholdSensor provideSecondaryProxSensor(
- ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ static ThresholdSensor provideSecondaryProximitySensor(
+ ThresholdSensorImpl.Builder thresholdSensorBuilder
+ ) {
try {
return thresholdSensorBuilder
.setSensorResourceId(R.string.proximity_sensor_secondary_type, true)
@@ -64,4 +79,153 @@ public class SensorModule {
return thresholdSensorBuilder.setSensor(null).setThresholdValue(0).build();
}
}
+
+ /**
+ * If postures are supported on the device, returns a posture dependent proximity sensor
+ * which switches proximity sensors based on the current posture.
+ *
+ * If postures are not supported the regular {@link ProximitySensorImpl} will be returned.
+ */
+ @Provides
+ static ProximitySensor provideProximitySensor(
+ @Main Resources resources,
+ Lazy postureDependentProximitySensorProvider,
+ Lazy proximitySensorProvider
+ ) {
+ if (hasPostureSupport(
+ resources.getStringArray(R.array.proximity_sensor_posture_mapping))) {
+ return postureDependentProximitySensorProvider.get();
+ } else {
+ return proximitySensorProvider.get();
+ }
+ }
+
+ @Provides
+ static ProximityCheck provideProximityCheck(
+ ProximitySensor proximitySensor,
+ @Main DelayableExecutor delayableExecutor
+ ) {
+ return new ProximityCheck(
+ proximitySensor,
+ delayableExecutor
+ );
+ }
+
+ @Provides
+ @PrimaryProxSensor
+ @NonNull
+ static ThresholdSensor[] providePostureToProximitySensorMapping(
+ ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
+ @Main Resources resources
+ ) {
+ return createPostureToSensorMapping(
+ thresholdSensorImplBuilderFactory,
+ resources.getStringArray(R.array.proximity_sensor_posture_mapping),
+ R.dimen.proximity_sensor_threshold,
+ R.dimen.proximity_sensor_threshold_latch
+ );
+ }
+
+ @Provides
+ @SecondaryProxSensor
+ @NonNull
+ static ThresholdSensor[] providePostureToSecondaryProximitySensorMapping(
+ ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
+ @Main Resources resources
+ ) {
+ return createPostureToSensorMapping(
+ thresholdSensorImplBuilderFactory,
+ resources.getStringArray(R.array.proximity_sensor_secondary_posture_mapping),
+ R.dimen.proximity_sensor_secondary_threshold,
+ R.dimen.proximity_sensor_secondary_threshold_latch
+ );
+ }
+
+ /**
+ * Builds sensors to use per posture.
+ *
+ * @param sensorTypes an array where the index represents
+ * {@link DevicePostureController.DevicePostureInt} and the value
+ * at the given index is the sensorType. Empty values represent
+ * no sensor desired.
+ * @param proximitySensorThresholdResourceId resource id for the threshold for all sensor
+ * postures. This currently only supports one value.
+ * This needs to be updated in the future if postures
+ * use different sensors with differing thresholds.
+ * @param proximitySensorThresholdLatchResourceId resource id for the latch for all sensor
+ * postures. This currently only supports one
+ * value. This needs to be updated in the future
+ * if postures use different sensors with
+ * differing latches.
+ * @return an array where the index represents the device posture
+ * {@link DevicePostureController.DevicePostureInt} and the value at the index is the sensor to
+ * use when the device is in that posture.
+ */
+ @NonNull
+ private static ThresholdSensor[] createPostureToSensorMapping(
+ ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
+ String[] sensorTypes,
+ int proximitySensorThresholdResourceId,
+ int proximitySensorThresholdLatchResourceId
+
+ ) {
+ ThresholdSensor noProxSensor = thresholdSensorImplBuilderFactory
+ .createBuilder()
+ .setSensor(null).setThresholdValue(0).build();
+
+
+ // length and index of sensorMap correspond to DevicePostureController.DevicePostureInt:
+ final ThresholdSensor[] sensorMap =
+ new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE];
+ for (int i = 0; i < DevicePostureController.SUPPORTED_POSTURES_SIZE; i++) {
+ sensorMap[i] = noProxSensor;
+ }
+
+ if (!hasPostureSupport(sensorTypes)) {
+ Log.e("SensorModule", "config doesn't support postures,"
+ + " but attempting to retrieve proxSensorMapping");
+ return sensorMap;
+ }
+
+ // Map of sensorType => Sensor, so we reuse the same sensor if it's the same between
+ // postures
+ Map typeToSensorMap = new HashMap<>();
+ for (int i = 0; i < sensorTypes.length; i++) {
+ try {
+ final String sensorType = sensorTypes[i];
+ if (typeToSensorMap.containsKey(sensorType)) {
+ sensorMap[i] = typeToSensorMap.get(sensorType);
+ } else {
+ sensorMap[i] = thresholdSensorImplBuilderFactory
+ .createBuilder()
+ .setSensorType(sensorTypes[i], true)
+ .setThresholdResourceId(proximitySensorThresholdResourceId)
+ .setThresholdLatchResourceId(proximitySensorThresholdLatchResourceId)
+ .build();
+ typeToSensorMap.put(sensorType, sensorMap[i]);
+ }
+ } catch (IllegalStateException e) {
+ // do nothing, sensor at this posture is already set to noProxSensor
+ }
+ }
+
+ return sensorMap;
+ }
+
+ /**
+ * Returns true if there's at least one non-empty sensor type in the given array.
+ */
+ private static boolean hasPostureSupport(String[] postureToSensorTypeMapping) {
+ if (postureToSensorTypeMapping == null || postureToSensorTypeMapping.length == 0) {
+ return false;
+ }
+
+ for (String sensorType : postureToSensorTypeMapping) {
+ if (!TextUtils.isEmpty(sensorType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
index 363a734a6ae51..d81a8d5991d18 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
@@ -16,8 +16,6 @@
package com.android.systemui.util.sensors;
-import java.util.Locale;
-
/**
* A wrapper class for sensors that have a boolean state - above/below.
*/
@@ -76,6 +74,16 @@ public interface ThresholdSensor {
*/
void unregister(Listener listener);
+ /**
+ * Name of the sensor.
+ */
+ String getName();
+
+ /**
+ * Type of the sensor.
+ */
+ String getType();
+
/**
* Interface for listening to events on {@link ThresholdSensor}
*/
@@ -85,34 +93,4 @@ public interface ThresholdSensor {
*/
void onThresholdCrossed(ThresholdSensorEvent event);
}
-
- /**
- * Returned when the below/above state of a {@link ThresholdSensor} changes.
- */
- class ThresholdSensorEvent {
- private final boolean mBelow;
- private final long mTimestampNs;
-
- public ThresholdSensorEvent(boolean below, long timestampNs) {
- mBelow = below;
- mTimestampNs = timestampNs;
- }
-
- public boolean getBelow() {
- return mBelow;
- }
-
- public long getTimestampNs() {
- return mTimestampNs;
- }
-
- public long getTimestampMs() {
- return mTimestampNs / 1000000;
- }
-
- @Override
- public String toString() {
- return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mBelow, mTimestampNs);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java
new file mode 100644
index 0000000000000..afce09fbe6d04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.systemui.util.sensors;
+
+import java.util.Locale;
+
+/**
+ * Returned when the below/above state of a {@link ThresholdSensor} changes.
+ */
+public class ThresholdSensorEvent {
+ private final boolean mBelow;
+ private final long mTimestampNs;
+
+ public ThresholdSensorEvent(boolean below, long timestampNs) {
+ mBelow = below;
+ mTimestampNs = timestampNs;
+ }
+
+ public boolean getBelow() {
+ return mBelow;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+
+ public long getTimestampMs() {
+ return mTimestampNs / 1000000;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mBelow, mTimestampNs);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
index d10cf9b180c31..a9086b140a3c5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
@@ -21,6 +21,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -32,7 +33,10 @@ import java.util.List;
import javax.inject.Inject;
-class ThresholdSensorImpl implements ThresholdSensor {
+/**
+ * Sensor that will only trigger beyond some lower and upper threshold.
+ */
+public class ThresholdSensorImpl implements ThresholdSensor {
private static final String TAG = "ThresholdSensor";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -198,6 +202,15 @@ class ThresholdSensorImpl implements ThresholdSensor {
alertListenersInternal(belowThreshold, timestampNs);
}
+ @Override
+ public String getName() {
+ return mSensor != null ? mSensor.getName() : null;
+ }
+
+ @Override
+ public String getType() {
+ return mSensor != null ? mSensor.getStringType() : null;
+ }
@Override
public String toString() {
@@ -211,7 +224,12 @@ class ThresholdSensorImpl implements ThresholdSensor {
}
}
- static class Builder {
+ /**
+ * Use to build a ThresholdSensor. Should only be used once per sensor built, since
+ * parameters are not reset after calls to build(). For ease of retrievingnew Builders, use
+ * {@link BuilderFactory}.
+ */
+ public static class Builder {
private final Resources mResources;
private final AsyncSensorManager mSensorManager;
private final Execution mExecution;
@@ -318,7 +336,7 @@ class ThresholdSensorImpl implements ThresholdSensor {
@VisibleForTesting
Sensor findSensorByType(String sensorType, boolean requireWakeUp) {
- if (sensorType.isEmpty()) {
+ if (TextUtils.isEmpty(sensorType)) {
return null;
}
@@ -336,4 +354,29 @@ class ThresholdSensorImpl implements ThresholdSensor {
return sensor;
}
}
+
+ /**
+ * Factory that creates a new ThresholdSensorImpl.Builder. In general, Builders should not be
+ * reused after creating a ThresholdSensor or else there may be default threshold and sensor
+ * values set from the previous built sensor.
+ */
+ public static class BuilderFactory {
+ private final Resources mResources;
+ private final AsyncSensorManager mSensorManager;
+ private final Execution mExecution;
+
+ @Inject
+ BuilderFactory(
+ @Main Resources resources,
+ AsyncSensorManager sensorManager,
+ Execution execution) {
+ mResources = resources;
+ mSensorManager = sensorManager;
+ mExecution = execution;
+ }
+
+ ThresholdSensorImpl.Builder createBuilder() {
+ return new Builder(mResources, mSensorManager, mExecution);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index b688fcc50373b..31fa3f841b197 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -53,7 +53,8 @@ import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager;
import com.android.systemui.util.sensors.FakeThresholdSensor;
-import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.sensors.ProximityCheck;
+import com.android.systemui.util.sensors.ThresholdSensorEvent;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -80,7 +81,7 @@ public class DozeTriggersTest extends SysuiTestCase {
@Mock
private DockManager mDockManager;
@Mock
- private ProximitySensor.ProximityCheck mProximityCheck;
+ private ProximityCheck mProximityCheck;
@Mock
private AuthController mAuthController;
@Mock
@@ -136,14 +137,14 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
- mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 1));
+ mProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 1));
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(false, 2));
+ mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2));
mProximitySensor.alertListeners();
waitForSensorManager();
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
index 50947ab0ee86d..22cf744c726b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -19,14 +19,21 @@ package com.android.systemui.util.sensors;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.FakeExecution;
-public class FakeProximitySensor extends ProximitySensor {
+public class FakeProximitySensor extends ProximitySensorImpl {
private boolean mAvailable;
private boolean mRegistered;
- public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary,
- DelayableExecutor delayableExecutor) {
- super(primary, secondary == null ? new FakeThresholdSensor() : secondary,
- delayableExecutor, new FakeExecution());
+ public FakeProximitySensor(
+ ThresholdSensor primary,
+ ThresholdSensor secondary,
+ DelayableExecutor delayableExecutor
+ ) {
+ super(
+ primary,
+ secondary == null ? new FakeThresholdSensor() : secondary,
+ delayableExecutor,
+ new FakeExecution()
+ );
mAvailable = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java
index d9f978944cdef..0d4a6c7023fbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java
@@ -59,6 +59,16 @@ public class FakeThresholdSensor implements ThresholdSensor {
mListeners.remove(listener);
}
+ @Override
+ public String getName() {
+ return "FakeThresholdSensorName";
+ }
+
+ @Override
+ public String getType() {
+ return "FakeThresholdSensorType";
+ }
+
public void setLoaded(boolean loaded) {
mIsLoaded = loaded;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
new file mode 100644
index 0000000000000..075f393df15a9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 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.systemui.util.sensors;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.FakeExecution;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PostureDependentProximitySensorTest extends SysuiTestCase {
+ @Mock private Resources mResources;
+ @Mock private DevicePostureController mDevicePostureController;
+ @Mock private AsyncSensorManager mSensorManager;
+
+ @Captor private ArgumentCaptor mPostureListenerCaptor =
+ ArgumentCaptor.forClass(DevicePostureController.Callback.class);
+ private DevicePostureController.Callback mPostureListener;
+
+ private PostureDependentProximitySensor mProximitySensor;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ allowTestableLooperAsMainThread();
+
+ mProximitySensor = new PostureDependentProximitySensor(
+ new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE],
+ new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE],
+ mFakeExecutor,
+ new FakeExecution(),
+ mDevicePostureController
+ );
+ }
+
+ @Test
+ public void testPostureChangeListenerAdded() {
+ capturePostureListener();
+ }
+
+ @Test
+ public void testPostureChangeListenerUpdatesPosture() {
+ // GIVEN posture listener is registered
+ capturePostureListener();
+
+ // WHEN the posture changes to DEVICE_POSTURE_OPENED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_OPENED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_OPENED,
+ mProximitySensor.mDevicePosture);
+
+ // WHEN the posture changes to DEVICE_POSTURE_CLOSED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_CLOSED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_CLOSED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_CLOSED,
+ mProximitySensor.mDevicePosture);
+
+ // WHEN the posture changes to DEVICE_POSTURE_FLIPPED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_FLIPPED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_FLIPPED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_FLIPPED,
+ mProximitySensor.mDevicePosture);
+
+ // WHEN the posture changes to DEVICE_POSTURE_HALF_OPENED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_HALF_OPENED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_HALF_OPENED,
+ mProximitySensor.mDevicePosture);
+ }
+
+ private void capturePostureListener() {
+ verify(mDevicePostureController).addCallback(mPostureListenerCaptor.capture());
+ mPostureListener = mPostureListenerCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
index 242fe9f5fffe3..19dbf9aa3c13f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
@@ -49,7 +49,7 @@ public class ProximityCheckTest extends SysuiTestCase {
private TestableCallback mTestableCallback = new TestableCallback();
- private ProximitySensor.ProximityCheck mProximityCheck;
+ private ProximityCheck mProximityCheck;
@Before
public void setUp() throws Exception {
@@ -58,7 +58,7 @@ public class ProximityCheckTest extends SysuiTestCase {
thresholdSensor.setLoaded(true);
mFakeProximitySensor = new FakeProximitySensor(thresholdSensor, null, mFakeExecutor);
- mProximityCheck = new ProximitySensor.ProximityCheck(mFakeProximitySensor, mFakeExecutor);
+ mProximityCheck = new ProximityCheck(mFakeProximitySensor, mFakeExecutor);
}
@Test
@@ -67,7 +67,7 @@ public class ProximityCheckTest extends SysuiTestCase {
assertNull(mTestableCallback.mLastResult);
- mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
+ mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0));
mFakeProximitySensor.alertListeners();
assertTrue(mTestableCallback.mLastResult);
@@ -103,7 +103,7 @@ public class ProximityCheckTest extends SysuiTestCase {
mProximityCheck.check(100, mTestableCallback);
- mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
+ mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0));
mFakeProximitySensor.alertListeners();
assertThat(mTestableCallback.mLastResult).isNotNull();
@@ -123,7 +123,7 @@ public class ProximityCheckTest extends SysuiTestCase {
assertNull(mTestableCallback.mLastResult);
- mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
+ mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0));
mFakeProximitySensor.alertListeners();
assertTrue(mTestableCallback.mLastResult);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
index 0e9d96c61e540..5e75578961450 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
@@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ProximitySensorDualTest extends SysuiTestCase {
+public class ProximitySensorImplDualTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThresholdSensor mThresholdSensorPrimary;
@@ -57,7 +57,7 @@ public class ProximitySensorDualTest extends SysuiTestCase {
mThresholdSensorSecondary = new FakeThresholdSensor();
mThresholdSensorSecondary.setLoaded(true);
- mProximitySensor = new ProximitySensor(
+ mProximitySensor = new ProximitySensorImpl(
mThresholdSensorPrimary, mThresholdSensorSecondary, mFakeExecutor,
new FakeExecution());
}
@@ -430,11 +430,11 @@ public class ProximitySensorDualTest extends SysuiTestCase {
}
private static class TestableListener implements ThresholdSensor.Listener {
- ThresholdSensor.ThresholdSensorEvent mLastEvent;
+ ThresholdSensorEvent mLastEvent;
int mCallCount = 0;
@Override
- public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
+ public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) {
mLastEvent = proximityEvent;
mCallCount++;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
index 6c6d355d78663..752cd32111617 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
@@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ProximitySensorSingleTest extends SysuiTestCase {
+public class ProximitySensorImplSingleTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThresholdSensor mThresholdSensor;
@@ -54,7 +54,7 @@ public class ProximitySensorSingleTest extends SysuiTestCase {
mThresholdSensor = new FakeThresholdSensor();
mThresholdSensor.setLoaded(true);
- mProximitySensor = new ProximitySensor(
+ mProximitySensor = new ProximitySensorImpl(
mThresholdSensor, new FakeThresholdSensor(), mFakeExecutor, new FakeExecution());
}
@@ -215,7 +215,7 @@ public class ProximitySensorSingleTest extends SysuiTestCase {
public void testPreventRecursiveAlert() {
TestableListener listenerA = new TestableListener() {
@Override
- public void onThresholdCrossed(ProximitySensor.ThresholdSensorEvent proximityEvent) {
+ public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) {
super.onThresholdCrossed(proximityEvent);
if (mCallCount < 2) {
mProximitySensor.alertListeners();
@@ -231,11 +231,11 @@ public class ProximitySensorSingleTest extends SysuiTestCase {
}
private static class TestableListener implements ThresholdSensor.Listener {
- ThresholdSensor.ThresholdSensorEvent mLastEvent;
+ ThresholdSensorEvent mLastEvent;
int mCallCount = 0;
@Override
- public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
+ public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) {
mLastEvent = proximityEvent;
mCallCount++;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
index 125063a7adc44..b10f16c963edd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
@@ -380,7 +380,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase {
int mCallCount;
@Override
- public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) {
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
mBelow = event.getBelow();
mTimestampNs = event.getTimestampNs();
mCallCount++;