From e4a1d0e4228ace08c5fe45bc4c80152846b27739 Mon Sep 17 00:00:00 2001 From: Beverly Date: Tue, 28 Sep 2021 14:09:32 -0400 Subject: [PATCH] Add posture support for prox sensor Some postures don't need to register for a prox sensor Test: manual, atest PostureDependentProximitySensorTest Bug: 192805135 Change-Id: I5c5fbed6d3faff6ba690f67f592bb0b8082062d5 --- .../display/AmbientDisplayConfiguration.java | 7 +- packages/SystemUI/res/values/config.xml | 25 +- .../classifier/FalsingCollectorImpl.java | 7 +- .../android/systemui/doze/DozeTriggers.java | 6 +- .../policy/DevicePostureController.java | 1 + .../PostureDependentProximitySensor.java | 109 +++++ .../systemui/util/sensors/ProximityCheck.java | 90 ++++ .../util/sensors/ProximitySensor.java | 397 +----------------- .../util/sensors/ProximitySensorImpl.java | 360 ++++++++++++++++ .../systemui/util/sensors/SensorModule.java | 172 +++++++- .../util/sensors/ThresholdSensor.java | 42 +- .../util/sensors/ThresholdSensorEvent.java | 49 +++ .../util/sensors/ThresholdSensorImpl.java | 49 ++- .../systemui/doze/DozeTriggersTest.java | 9 +- .../util/sensors/FakeProximitySensor.java | 17 +- .../util/sensors/FakeThresholdSensor.java | 10 + .../PostureDependentProximitySensorTest.java | 114 +++++ .../util/sensors/ProximityCheckTest.java | 10 +- ....java => ProximitySensorImplDualTest.java} | 8 +- ...ava => ProximitySensorImplSingleTest.java} | 10 +- .../util/sensors/ThresholdSensorImplTest.java | 2 +- 21 files changed, 1037 insertions(+), 457 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java create mode 100644 packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java create mode 100644 packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java create mode 100644 packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java rename packages/SystemUI/tests/src/com/android/systemui/util/sensors/{ProximitySensorDualTest.java => ProximitySensorImplDualTest.java} (98%) rename packages/SystemUI/tests/src/com/android/systemui/util/sensors/{ProximitySensorSingleTest.java => ProximitySensorImplSingleTest.java} (95%) 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 c3ac19a46ee63..d0de876a6b27b 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++;