diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 62335abd4329e..3395c71bd5a31 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -205,6 +205,14 @@
far break points. A sensor value less than this is considered "near". -->
+
+
+
+
+
+
130
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 79b691bb3e37c..cab805848e7bc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -87,7 +87,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
mUiBgExecutor = uiBgExecutor;
mStatusBarStateController = statusBarStateController;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
- mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
+ mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index caab18712b0b3..fad6aaf06ab3a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -37,6 +37,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.sensors.ThresholdSensor;
import java.io.PrintWriter;
import java.util.ArrayDeque;
@@ -76,7 +77,7 @@ public class BrightLineFalsingManager implements FalsingManager {
private final List mClassifiers;
- private ProximitySensor.ProximitySensorListener mSensorEventListener = this::onProximityEvent;
+ private ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -240,7 +241,7 @@ public class BrightLineFalsingManager implements FalsingManager {
mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent));
}
- private void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {
+ private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
// TODO: some of these classifiers might allow us to abort early, meaning we don't have to
// make these calls.
mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index cf088213644e9..85e95a66bfe3c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -96,7 +96,7 @@ abstract class FalsingClassifier {
/**
* Called when a ProximityEvent occurs (change in near/far).
*/
- void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {};
+ void onProximityEvent(ProximitySensor.ThresholdSensorEvent proximityEvent) {};
/**
* The phone screen has turned on and we need to begin falsing detection.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
index 749914e1b6251..b128678af5db2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
@@ -101,8 +101,8 @@ class ProximityClassifier extends FalsingClassifier {
@Override
public void onProximityEvent(
- ProximitySensor.ProximityEvent proximityEvent) {
- boolean near = proximityEvent.getNear();
+ ProximitySensor.ThresholdSensorEvent proximityEvent) {
+ boolean near = proximityEvent.getBelow();
long timestampNs = proximityEvent.getTimestampNs();
logDebug("Sensor is: " + near + " at time " + timestampNs);
update(near, timestampNs);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 8f3dc224384bc..250fb2b678bc0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -36,6 +36,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.content.res.Resources;
+import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
import android.media.AudioManager;
import android.net.ConnectivityManager;
@@ -240,6 +241,12 @@ public class SystemServicesModule {
return context.getResources();
}
+ @Provides
+ @Singleton
+ static SensorManager providesSensorManager(Context context) {
+ return context.getSystemService(SensorManager.class);
+ }
+
@Singleton
@Provides
static SensorPrivacyManager provideSensorPrivacyManager(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 90cd13fd1330f..cb45926d3f240 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.SensorModule;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
@@ -62,7 +63,8 @@ import dagger.Provides;
ConcurrencyModule.class,
LogModule.class,
PeopleHubModule.class,
- SettingsModule.class
+ SensorModule.class,
+ SettingsModule.class,
},
subcomponents = {StatusBarComponent.class,
NotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 700a8611c8bd7..8a8f0450550d0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -81,7 +81,8 @@ public class DozeSensors {
public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
- Callback callback, Consumer proxCallback, DozeLog dozeLog) {
+ Callback callback, Consumer proxCallback, DozeLog dozeLog,
+ ProximitySensor proximitySensor) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -153,12 +154,12 @@ public class DozeSensors {
dozeLog),
};
- mProximitySensor = new ProximitySensor(context.getResources(), sensorManager);
+ mProximitySensor = proximitySensor;
setProxListening(false); // Don't immediately start listening when we register.
mProximitySensor.register(
proximityEvent -> {
if (proximityEvent != null) {
- mProxCallback.accept(!proximityEvent.getNear());
+ mProxCallback.accept(!proximityEvent.getBelow());
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index b3299916356cc..4d40986003ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -92,8 +92,8 @@ public class DozeTriggers implements DozeMachine.Part {
AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
- ProximitySensor proximitySensor,
- DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
+ ProximitySensor proximitySensor, DozeLog dozeLog,
+ BroadcastDispatcher broadcastDispatcher) {
mContext = context;
mMachine = machine;
mDozeHost = dozeHost;
@@ -103,7 +103,8 @@ public class DozeTriggers implements DozeMachine.Part {
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar, dozeLog);
+ config, wakeLock, this::onSensor, this::onProximityFar, dozeLog,
+ proximitySensor);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index 2224c9cce40ae..450336a6b73bb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -60,8 +60,8 @@ public class AsyncSensorManager extends SensorManager
private final List mPlugins;
@Inject
- public AsyncSensorManager(Context context, PluginManager pluginManager) {
- this(context.getSystemService(SensorManager.class), pluginManager, null);
+ public AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
+ this(sensorManager, pluginManager, null);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PrimaryProxSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PrimaryProxSensor.java
new file mode 100644
index 0000000000000..96c76c1a15d0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PrimaryProxSensor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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 java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface PrimaryProxSensor {
+}
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 378dde284747a..9be42f3fc5f48 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -16,92 +16,119 @@
package com.android.systemui.util.sensors;
-import android.content.res.Resources;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import java.util.function.Consumer;
import javax.inject.Inject;
/**
- * Simple wrapper around SensorManager customized for the Proximity sensor.
+ * 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.
*/
-public class ProximitySensor {
+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 Sensor mSensor;
- private final AsyncSensorManager mSensorManager;
- private final float mThreshold;
- private List mListeners = new ArrayList<>();
+ private final ThresholdSensor mPrimaryThresholdSensor;
+ private final ThresholdSensor mSecondaryThresholdSensor;
+ private final DelayableExecutor mDelayableExecutor;
+ private final List mListeners = new ArrayList<>();
private String mTag = null;
- @VisibleForTesting ProximityEvent mLastEvent;
- private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
+ private ThresholdSensorEvent mLastPrimaryEvent;
+ @VisibleForTesting
+ ThresholdSensorEvent mLastEvent;
private boolean mPaused;
private boolean mRegistered;
+ private Runnable mCancelSecondaryRunnable;
+ private boolean mInitializedListeners = false;
- private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ private ThresholdSensor.Listener mPrimaryEventListener = new ThresholdSensor.Listener() {
@Override
- public synchronized void onSensorChanged(SensorEvent event) {
- onSensorEvent(event);
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
+ onPrimarySensorEvent(event);
}
+ };
+ private ThresholdSensor.Listener mSecondaryEventListener = new ThresholdSensor.Listener() {
@Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
+ // This sensor should only be used briefly. Turn it off as soon as we get a reading.
+ mSecondaryThresholdSensor.pause();
+
+ // Only check the secondary as long as the primary thinks we're near.
+ if (!mLastPrimaryEvent.getBelow()) {
+ mCancelSecondaryRunnable = null;
+ return;
+ }
+ logDebug("Secondary sensor event: " + event.getBelow() + ".");
+
+ onSensorEvent(event);
+
+ // Check this sensor again in a moment.
+ mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(
+ mSecondaryThresholdSensor::resume, SECONDARY_PING_INTERVAL_MS);
}
};
@Inject
- public ProximitySensor(@Main Resources resources,
- AsyncSensorManager sensorManager) {
- mSensorManager = sensorManager;
-
- Sensor sensor = findCustomProxSensor(resources);
- float threshold = 0;
- if (sensor != null) {
- try {
- threshold = getCustomProxThreshold(resources);
- } catch (IllegalStateException e) {
- Log.e(TAG, "Can not load custom proximity sensor.", e);
- sensor = null;
- }
- }
- if (sensor == null) {
- sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- if (sensor != null) {
- threshold = sensor.getMaximumRange();
- }
- }
-
- mThreshold = threshold;
-
- mSensor = sensor;
+ public ProximitySensor(@PrimaryProxSensor ThresholdSensor primary,
+ @SecondaryProxSensor ThresholdSensor secondary,
+ @Main DelayableExecutor delayableExecutor) {
+ mPrimaryThresholdSensor = primary;
+ mSecondaryThresholdSensor = secondary;
+ mDelayableExecutor = delayableExecutor;
}
+ @Override
public void setTag(String tag) {
mTag = tag;
+ mPrimaryThresholdSensor.setTag(tag + ":primary");
+ mSecondaryThresholdSensor.setTag(tag + ":secondary");
}
- public void setSensorDelay(int sensorDelay) {
- mSensorDelay = sensorDelay;
+ @Override
+ public void setDelay(int delay) {
+ Assert.isMainThread();
+ mPrimaryThresholdSensor.setDelay(delay);
+ mSecondaryThresholdSensor.setDelay(delay);
}
/**
* Unregister with the {@link SensorManager} without unsetting listeners on this object.
*/
+ @Override
public void pause() {
+ Assert.isMainThread();
mPaused = true;
unregisterInternal();
}
@@ -109,41 +136,13 @@ public class ProximitySensor {
/**
* Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
*/
+ @Override
public void resume() {
+ Assert.isMainThread();
mPaused = false;
registerInternal();
}
- /**
- * Returns a brightness sensor that can be used for proximity purposes.
- */
- private Sensor findCustomProxSensor(Resources resources) {
- String sensorType = resources.getString(R.string.proximity_sensor_type);
- if (sensorType.isEmpty()) {
- return null;
- }
- List sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- Sensor sensor = null;
- for (Sensor s : sensorList) {
- if (sensorType.equals(s.getStringType())) {
- sensor = s;
- break;
- }
- }
-
- return sensor;
- }
-
- /**
- * Returns a threshold value that can be used along with {@link #findCustomProxSensor}
- */
- private float getCustomProxThreshold(Resources resources) {
- try {
- return resources.getFloat(R.dimen.proximity_sensor_threshold);
- } catch (Resources.NotFoundException e) {
- throw new IllegalStateException("R.dimen.proximity_sensor_threshold must be set.");
- }
- }
/**
* Returns true if we are registered with the SensorManager.
@@ -155,38 +154,46 @@ public class ProximitySensor {
/**
* Returns {@code false} if a Proximity sensor is not available.
*/
- public boolean getSensorAvailable() {
- return mSensor != null;
+ @Override
+ public boolean isLoaded() {
+ return mPrimaryThresholdSensor.isLoaded();
}
/**
* Add a listener.
*
* Registers itself with the {@link SensorManager} if this is the first listener
- * added. If a cool down is currently running, the sensor will be registered when it is over.
+ * added. If the ProximitySensor is paused, it will be registered when resumed.
*/
- public boolean register(ProximitySensorListener listener) {
- if (!getSensorAvailable()) {
- return false;
+ @Override
+ public void register(ThresholdSensor.Listener listener) {
+ Assert.isMainThread();
+ if (!isLoaded()) {
+ return;
}
if (mListeners.contains(listener)) {
- Log.d(TAG, "ProxListener registered multiple times: " + listener);
+ logDebug("ProxListener registered multiple times: " + listener);
} else {
mListeners.add(listener);
}
registerInternal();
-
- return true;
}
protected void registerInternal() {
+ Assert.isMainThread();
if (mRegistered || mPaused || mListeners.isEmpty()) {
return;
}
+ if (!mInitializedListeners) {
+ mPrimaryThresholdSensor.register(mPrimaryEventListener);
+ mSecondaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.register(mSecondaryEventListener);
+ mInitializedListeners = true;
+ }
logDebug("Registering sensor listener");
+ mPrimaryThresholdSensor.resume();
mRegistered = true;
- mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
}
/**
@@ -195,7 +202,9 @@ public class ProximitySensor {
* If all listeners are removed from an instance of this class,
* it will unregister itself with the SensorManager.
*/
- public void unregister(ProximitySensorListener listener) {
+ @Override
+ public void unregister(ThresholdSensor.Listener listener) {
+ Assert.isMainThread();
mListeners.remove(listener);
if (mListeners.size() == 0) {
unregisterInternal();
@@ -203,34 +212,77 @@ public class ProximitySensor {
}
protected void unregisterInternal() {
+ Assert.isMainThread();
if (!mRegistered) {
return;
}
logDebug("unregistering sensor listener");
- mSensorManager.unregisterListener(mSensorEventListener);
+ 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 getSensorAvailable() && mLastEvent != null ? mLastEvent.getNear() : null;
+ return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null;
}
/** Update all listeners with the last value this class received from the sensor. */
public void alertListeners() {
- mListeners.forEach(proximitySensorListener ->
- proximitySensorListener.onSensorEvent(mLastEvent));
+ Assert.isMainThread();
+ if (mLastEvent == null) {
+ return;
+ }
+
+ List listeners = new ArrayList<>(mListeners);
+ listeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onThresholdCrossed(mLastEvent));
}
- private void onSensorEvent(SensorEvent event) {
- boolean near = event.values[0] < mThreshold;
- mLastEvent = new ProximityEvent(near, event.timestamp);
+ private void onPrimarySensorEvent(ThresholdSensorEvent event) {
+ Assert.isMainThread();
+ if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) {
+ return;
+ }
+
+ mLastPrimaryEvent = event;
+
+ if (event.getBelow() && mSecondaryThresholdSensor.isLoaded()) {
+ logDebug("Primary sensor is near. Checking secondary.");
+ if (mCancelSecondaryRunnable == null) {
+ mSecondaryThresholdSensor.resume();
+ }
+ } else {
+ if (!mSecondaryThresholdSensor.isLoaded()) {
+ logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
+ } else {
+ logDebug("Primary sensor event: " + event.getBelow() + ".");
+ }
+ onSensorEvent(event);
+ }
+ }
+
+ private void onSensorEvent(ThresholdSensorEvent event) {
+ Assert.isMainThread();
+ if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) {
+ return;
+ }
+
+ mLastEvent = event;
alertListeners();
}
@Override
public String toString() {
- return String.format("{registered=%s, paused=%s, near=%s, sensor=%s}",
- isRegistered(), mPaused, isNear(), mSensor);
+ return String.format("{registered=%s, paused=%s, near=%s, primarySensor=%s, "
+ + "secondarySensor=%s}",
+ isRegistered(), mPaused, isNear(), mPrimaryThresholdSensor,
+ mSecondaryThresholdSensor);
}
/**
@@ -248,11 +300,11 @@ public class ProximitySensor {
mSensor.setTag("prox_check");
mHandler = handler;
mSensor.pause();
- ProximitySensorListener listener = proximityEvent -> {
+ ThresholdSensor.Listener listener = proximityEvent -> {
mCallbacks.forEach(
booleanConsumer ->
booleanConsumer.accept(
- proximityEvent == null ? null : proximityEvent.getNear()));
+ proximityEvent == null ? null : proximityEvent.getBelow()));
mCallbacks.clear();
mSensor.pause();
};
@@ -274,7 +326,7 @@ public class ProximitySensor {
* Query the proximity sensor, timing out if no result.
*/
public void check(long timeoutMs, Consumer callback) {
- if (!mSensor.getSensorAvailable()) {
+ if (!mSensor.isLoaded()) {
callback.accept(null);
}
mCallbacks.add(callback);
@@ -285,43 +337,6 @@ public class ProximitySensor {
}
}
- /** Implement to be notified of ProximityEvents. */
- public interface ProximitySensorListener {
- /** Called when the ProximitySensor changes. */
- void onSensorEvent(ProximityEvent proximityEvent);
- }
-
- /**
- * Returned when the near/far state of a {@link ProximitySensor} changes.
- */
- public static class ProximityEvent {
- private final boolean mNear;
- private final long mTimestampNs;
-
- public ProximityEvent(boolean near, long timestampNs) {
- mNear = near;
- mTimestampNs = timestampNs;
- }
-
- public boolean getNear() {
- return mNear;
- }
-
- 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}", mNear, mTimestampNs);
- }
-
- }
-
private void logDebug(String msg) {
if (DEBUG) {
Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SecondaryProxSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SecondaryProxSensor.java
new file mode 100644
index 0000000000000..89fc0eabf607f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SecondaryProxSensor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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 java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+@interface SecondaryProxSensor {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
new file mode 100644
index 0000000000000..5a93e6c9f77a9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 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.Sensor;
+import android.hardware.SensorManager;
+
+import com.android.systemui.R;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger module for Sensor related classes.
+ */
+@Module
+public class SensorModule {
+ @Provides
+ @PrimaryProxSensor
+ static ThresholdSensor providePrimaryProxSensor(SensorManager sensorManager,
+ ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ try {
+ return thresholdSensorBuilder
+ .setSensorDelay(SensorManager.SENSOR_DELAY_NORMAL)
+ .setSensorResourceId(R.string.proximity_sensor_type)
+ .setThresholdResourceId(R.dimen.proximity_sensor_threshold)
+ .build();
+ } catch (IllegalStateException e) {
+ Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ return thresholdSensorBuilder
+ .setSensor(defaultSensor)
+ .setThresholdValue(defaultSensor.getMaximumRange())
+ .build();
+ }
+ }
+
+ @Provides
+ @SecondaryProxSensor
+ static ThresholdSensor provideSecondaryProxSensor(
+ ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ try {
+ return thresholdSensorBuilder
+ .setSensorResourceId(R.string.proximity_sensor_secondary_type)
+ .setThresholdResourceId(R.dimen.proximity_sensor_secondary_threshold)
+ .build();
+ } catch (IllegalStateException e) {
+ return thresholdSensorBuilder.setSensor(null).setThresholdValue(0).build();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
new file mode 100644
index 0000000000000..363a734a6ae51
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 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;
+
+/**
+ * A wrapper class for sensors that have a boolean state - above/below.
+ */
+public interface ThresholdSensor {
+ /**
+ * Optional label to use for logging.
+ *
+ * This should be set to something meaningful by owner of the instance.
+ */
+ void setTag(String tag);
+
+ /**
+ * Change the delay used when registering the sensor.
+ *
+ * If the sensor is already registered, this should cause it to re-register with the new
+ * delay.
+ */
+ void setDelay(int delay);
+
+ /**
+ * True if this sensor successfully loads and can be listened to.
+ */
+ boolean isLoaded();
+
+ /**
+ * Registers with the sensor and calls the supplied callback on value change.
+ *
+ * If this instance is paused, the listener will be recorded, but no registration with
+ * the underlying physical sensor will occur until {@link #resume()} is called.
+ *
+ * @see #unregister(Listener)
+ */
+ void register(Listener listener);
+
+ /**
+ * Unregisters from the physical sensor without removing any supplied listeners.
+ *
+ * No events will be sent to listeners as long as this sensor is paused.
+ *
+ * @see #resume()
+ * @see #unregister(Listener)
+ */
+ void pause();
+
+ /**
+ * Resumes listening to the physical sensor after previously pausing.
+ *
+ * @see #pause()
+ */
+ void resume();
+
+ /**
+ * Unregister a listener with the sensor.
+ *
+ * @see #register(Listener)
+ */
+ void unregister(Listener listener);
+
+ /**
+ * Interface for listening to events on {@link ThresholdSensor}
+ */
+ interface Listener {
+ /**
+ * Called whenever the threshold for the registered sensor is crossed.
+ */
+ 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/ThresholdSensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
new file mode 100644
index 0000000000000..546333b3a073b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2020 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.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+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.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+class ThresholdSensorImpl implements ThresholdSensor {
+ private static final String TAG = "ThresholdSensor";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final AsyncSensorManager mSensorManager;
+ private final Sensor mSensor;
+ private final float mThreshold;
+ private boolean mRegistered;
+ private boolean mPaused;
+ private List mListeners = new ArrayList<>();
+ private Boolean mLastBelow;
+ private String mTag;
+ private int mSensorDelay;
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ boolean below = event.values[0] < mThreshold;
+ logDebug("Sensor value: " + event.values[0]);
+ onSensorEvent(below, event.timestamp);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ private ThresholdSensorImpl(AsyncSensorManager sensorManager,
+ Sensor sensor, float threshold, int sensorDelay) {
+ mSensorManager = sensorManager;
+ mSensor = sensor;
+ mThreshold = threshold;
+ mSensorDelay = sensorDelay;
+ }
+
+ @Override
+ public void setTag(String tag) {
+ mTag = tag;
+ }
+
+ @Override
+ public void setDelay(int delay) {
+ if (delay == mSensorDelay) {
+ return;
+ }
+
+ mSensorDelay = delay;
+ if (isLoaded()) {
+ unregisterInternal();
+ registerInternal();
+ }
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return mSensor != null;
+ }
+
+ @VisibleForTesting
+ boolean isRegistered() {
+ return mRegistered;
+ }
+
+ /**
+ * Registers the listener with the sensor.
+ *
+ * Multiple listeners are not supported at this time.
+ *
+ * Returns true if the listener was successfully registered. False otherwise.
+ */
+ @Override
+ public void register(Listener listener) {
+ Assert.isMainThread();
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
+ registerInternal();
+ }
+
+ @Override
+ public void unregister(Listener listener) {
+ Assert.isMainThread();
+ mListeners.remove(listener);
+ unregisterInternal();
+ }
+
+ /**
+ * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ */
+ @Override
+ public void pause() {
+ Assert.isMainThread();
+ mPaused = true;
+ unregisterInternal();
+ }
+
+ /**
+ * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
+ */
+ @Override
+ public void resume() {
+ Assert.isMainThread();
+ mPaused = false;
+ registerInternal();
+ }
+
+ private void alertListenersInternal(boolean below, long timestampNs) {
+ List listeners = new ArrayList<>(mListeners);
+ listeners.forEach(listener ->
+ listener.onThresholdCrossed(new ThresholdSensorEvent(below, timestampNs)));
+ }
+
+ private void registerInternal() {
+ Assert.isMainThread();
+ if (mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ logDebug("Registering sensor listener");
+ mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
+ mRegistered = true;
+ }
+
+ private void unregisterInternal() {
+ Assert.isMainThread();
+ if (!mRegistered) {
+ return;
+ }
+ logDebug("Unregister sensor listener");
+ mSensorManager.unregisterListener(mSensorEventListener);
+ mRegistered = false;
+ mLastBelow = null; // Forget what we know.
+ }
+
+ private void onSensorEvent(boolean below, long timestampNs) {
+ Assert.isMainThread();
+ if (mLastBelow != null && mLastBelow == below) {
+ return;
+ }
+ mLastBelow = below;
+ alertListenersInternal(below, timestampNs);
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("{registered=%s, paused=%s, threshold=%s, sensor=%s}",
+ isLoaded(), mRegistered, mPaused, mThreshold, mSensor);
+ }
+
+ private void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+
+ static class Builder {
+ private final Resources mResources;
+ private final AsyncSensorManager mSensorManager;
+ private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;;
+ private float mThresholdValue;
+ private Sensor mSensor;
+ private boolean mSensorSet;
+ private boolean mThresholdSet;
+
+ @Inject
+ Builder(@Main Resources resources, AsyncSensorManager sensorManager) {
+ mResources = resources;
+ mSensorManager = sensorManager;
+ }
+
+
+ Builder setSensorDelay(int sensorDelay) {
+ mSensorDelay = sensorDelay;
+ return this;
+ }
+
+ Builder setSensorResourceId(int sensorResourceId) {
+ setSensorType(mResources.getString(sensorResourceId));
+ return this;
+ }
+
+ Builder setThresholdResourceId(int thresholdResourceId) {
+ try {
+ setThresholdValue(mResources.getFloat(thresholdResourceId));
+ } catch (Resources.NotFoundException e) {
+ // no-op
+ }
+ return this;
+ }
+
+ Builder setSensorType(String sensorType) {
+ Sensor sensor = findSensorByType(sensorType);
+ if (sensor != null) {
+ setSensor(sensor);
+ }
+ return this;
+ }
+
+ Builder setThresholdValue(float thresholdValue) {
+ mThresholdValue = thresholdValue;
+ mThresholdSet = true;
+ return this;
+ }
+
+ Builder setSensor(Sensor sensor) {
+ mSensor = sensor;
+ mSensorSet = true;
+ return this;
+ }
+
+ /**
+ * Creates a {@link ThresholdSensor} backed by a {@link ThresholdSensorImpl}.
+ */
+ public ThresholdSensor build() {
+ if (!mSensorSet) {
+ throw new IllegalStateException("A sensor was not successfully set.");
+ }
+
+ if (!mThresholdSet) {
+ throw new IllegalStateException("A threshold was not successfully set.");
+ }
+
+ return new ThresholdSensorImpl(
+ mSensorManager, mSensor, mThresholdValue, mSensorDelay);
+ }
+
+ private Sensor findSensorByType(String sensorType) {
+ if (sensorType.isEmpty()) {
+ return null;
+ }
+
+ List sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ Sensor sensor = null;
+ for (Sensor s : sensorList) {
+ if (sensorType.equals(s.getStringType())) {
+ sensor = s;
+ break;
+ }
+ }
+
+ return sensor;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
index 8b5cc9abb777d..ffe062598102d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.sensors.ThresholdSensor;
import org.junit.Before;
import org.junit.Test;
@@ -77,7 +78,7 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
@Test
public void testRegisterSensor() {
mFalsingManager.onScreenTurningOn();
- verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+ verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
}
@Test
@@ -85,7 +86,7 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn();
reset(mProximitySensor);
mFalsingManager.onScreenOff();
- verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+ verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
}
@Test
@@ -93,9 +94,9 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn();
reset(mProximitySensor);
mFalsingManager.setQsExpanded(true);
- verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+ verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
mFalsingManager.setQsExpanded(false);
- verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+ verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
}
@Test
@@ -103,9 +104,9 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn();
reset(mProximitySensor);
mFalsingManager.onBouncerShown();
- verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+ verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
mFalsingManager.onBouncerHidden();
- verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+ verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
}
@Test
@@ -113,6 +114,6 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn();
reset(mProximitySensor);
mStatusBarStateController.setState(StatusBarState.SHADE);
- verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+ verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
index 5b32a39403cd3..3cebf0d6af577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -136,7 +136,8 @@ public class ProximityClassifierTest extends ClassifierTest {
motionEvent.recycle();
}
- private ProximitySensor.ProximityEvent createSensorEvent(boolean covered, long timestampMs) {
- return new ProximitySensor.ProximityEvent(covered, timestampMs * NS_PER_MS);
+ private ProximitySensor.ThresholdSensorEvent createSensorEvent(
+ boolean covered, long timestampMs) {
+ return new ProximitySensor.ThresholdSensorEvent(covered, timestampMs * NS_PER_MS);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index ff03fbae3f3a3..55fc239338367 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -46,6 +46,7 @@ import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before;
@@ -83,6 +84,9 @@ public class DozeSensorsTest extends SysuiTestCase {
private DozeLog mDozeLog;
@Mock
private Sensor mProximitySensor;
+ @Mock
+ private ProximitySensor mMockProxSensor;
+
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper;
private DozeSensors mDozeSensors;
@@ -104,9 +108,9 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test
public void testRegisterProx() {
// We should not register with the sensor manager initially.
- verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
+ verify(mMockProxSensor).pause();
mDozeSensors.setProxListening(true);
- verify(mSensorManager).registerListener(any(), any(Sensor.class), anyInt());
+ verify(mMockProxSensor).resume();
}
@Test
@@ -162,7 +166,8 @@ public class DozeSensorsTest extends SysuiTestCase {
TestableDozeSensors() {
super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
- mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog);
+ mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
+ mMockProxSensor);
for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
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 debc9d6430e07..1d2021721634d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -42,10 +42,13 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.concurrency.FakeExecutor;
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.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -71,10 +74,12 @@ public class DozeTriggersTest extends SysuiTestCase {
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
private DockManager mDockManager;
+
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
private Sensor mTapSensor;
private FakeProximitySensor mProximitySensor;
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setUp() throws Exception {
@@ -86,7 +91,10 @@ public class DozeTriggersTest extends SysuiTestCase {
WakeLock wakeLock = new WakeLockFake();
AsyncSensorManager asyncSensorManager =
new AsyncSensorManager(mSensors, null, new Handler());
- mProximitySensor = new FakeProximitySensor(getContext().getResources(), asyncSensorManager);
+
+ FakeThresholdSensor thresholdSensor = new FakeThresholdSensor();
+ thresholdSensor.setLoaded(true);
+ mProximitySensor = new FakeProximitySensor(thresholdSensor, null, mExecutor);
mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
@@ -104,17 +112,17 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
- mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1));
+ mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 1));
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
- waitForSensorManager();
- mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
+ mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(false, 2));
mProximitySensor.alertListeners();
+ waitForSensorManager();
+ captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
verify(mMachine).requestPulse(anyInt());
}
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 31d884c38f582..16edc9d9cf82b 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
@@ -16,13 +16,15 @@
package com.android.systemui.util.sensors;
-import android.content.res.Resources;
+import com.android.systemui.util.concurrency.DelayableExecutor;
public class FakeProximitySensor extends ProximitySensor {
private boolean mAvailable;
- public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) {
- super(resources, sensorManager);
+ public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary,
+ DelayableExecutor delayableExecutor) {
+ super(primary, secondary == null ? new FakeThresholdSensor() : secondary,
+ delayableExecutor);
mAvailable = true;
}
@@ -30,12 +32,12 @@ public class FakeProximitySensor extends ProximitySensor {
mAvailable = available;
}
- public void setLastEvent(ProximityEvent event) {
+ public void setLastEvent(ThresholdSensorEvent event) {
mLastEvent = event;
}
@Override
- public boolean getSensorAvailable() {
+ public boolean isLoaded() {
return mAvailable;
}
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
new file mode 100644
index 0000000000000..d9f978944cdef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.ArrayList;
+import java.util.List;
+
+public class FakeThresholdSensor implements ThresholdSensor {
+ private boolean mIsLoaded;
+ private boolean mPaused;
+ private List mListeners = new ArrayList<>();
+
+ public FakeThresholdSensor() {
+ }
+
+ public void setTag(String tag) {
+ }
+
+ @Override
+ public void setDelay(int delay) {
+ }
+
+ @Override
+ public boolean isLoaded() {
+ return mIsLoaded;
+ }
+
+ @Override
+ public void pause() {
+ mPaused = true;
+ }
+
+ @Override
+ public void resume() {
+ mPaused = false;
+ }
+
+ @Override
+ public void register(ThresholdSensor.Listener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void unregister(ThresholdSensor.Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ public void setLoaded(boolean loaded) {
+ mIsLoaded = loaded;
+ }
+
+ void triggerEvent(boolean below, long timestampNs) {
+ if (!mPaused) {
+ for (Listener listener : mListeners) {
+ listener.onThresholdCrossed(new ThresholdSensorEvent(below, timestampNs));
+ }
+ }
+ }
+
+ boolean isPaused() {
+ return mPaused;
+ }
+
+ int getNumListeners() {
+ return mListeners.size();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
new file mode 100644
index 0000000000000..a39c201695183
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2020 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.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+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.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ProximitySensorDualTest extends SysuiTestCase {
+ private ProximitySensor mProximitySensor;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ private FakeThresholdSensor mThresholdSensorPrimary;
+ private FakeThresholdSensor mThresholdSensorSecondary;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ allowTestableLooperAsMainThread();
+ mThresholdSensorPrimary = new FakeThresholdSensor();
+ mThresholdSensorPrimary.setLoaded(true);
+ mThresholdSensorSecondary = new FakeThresholdSensor();
+ mThresholdSensorSecondary.setLoaded(true);
+
+ mProximitySensor = new ProximitySensor(
+ mThresholdSensorPrimary, mThresholdSensorSecondary, mFakeExecutor);
+ }
+
+ @Test
+ public void testSingleListener() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ assertTrue(mProximitySensor.isRegistered());
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+
+ // Trigger second sensor. Nothing should happen yet.
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+
+ // Trigger first sensor. Our second sensor is now registered.
+ mThresholdSensorPrimary.triggerEvent(true, 0);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertFalse(mThresholdSensorSecondary.isPaused());
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+
+ // Trigger second sensor.
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+ assertTrue(mThresholdSensorSecondary.isPaused());
+
+ mProximitySensor.unregister(listener);
+ }
+
+ @Test
+ public void testSecondaryPausing() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+
+ // Trigger first sensor. Our second sensor is now registered.
+ mThresholdSensorPrimary.triggerEvent(true, 0);
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+
+ // Trigger second sensor.
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+ assertTrue(mThresholdSensorSecondary.isPaused());
+
+ // Advance time. Second sensor should resume.
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runNextReady();
+ assertFalse(mThresholdSensorSecondary.isPaused());
+
+ // Triggering should pause again.
+ mThresholdSensorSecondary.triggerEvent(false, 0);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(2, listener.mCallCount);
+ assertTrue(mThresholdSensorSecondary.isPaused());
+
+ mProximitySensor.unregister(listener);
+ }
+
+ @Test
+ public void testUnregister() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ assertTrue(mProximitySensor.isRegistered());
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertNull(listener.mLastEvent);
+
+ mThresholdSensorPrimary.triggerEvent(true, 0);
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+
+ mProximitySensor.unregister(listener);
+ assertTrue(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testPauseAndResume() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mThresholdSensorPrimary.triggerEvent(true, 0);
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+
+ mProximitySensor.pause();
+ assertFalse(mProximitySensor.isRegistered());
+ assertTrue(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+
+ // More events do nothing when paused.
+ mThresholdSensorSecondary.triggerEvent(false, 1);
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+
+ mProximitySensor.resume();
+ assertTrue(mProximitySensor.isRegistered());
+ // Still matches our previous call
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+
+ // Need to trigger the primary sensor before the secondary re-registers itself.
+ mThresholdSensorPrimary.triggerEvent(true, 3);
+ mThresholdSensorSecondary.triggerEvent(false, 3);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(2, listener.mCallCount);
+
+ mProximitySensor.unregister(listener);
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testPrimarySecondaryDisagreement() {
+ TestableListener listener = new TestableListener();
+
+ mProximitySensor.register(listener);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+
+ // Trigger our sensors with different values. Secondary overrides primary.
+ mThresholdSensorPrimary.triggerEvent(true, 0);
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+ mThresholdSensorSecondary.triggerEvent(false, 0);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+
+ mThresholdSensorSecondary.resume();
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(2, listener.mCallCount);
+
+ mThresholdSensorSecondary.resume();
+ mThresholdSensorSecondary.triggerEvent(false, 0);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(3, listener.mCallCount);
+
+ mProximitySensor.unregister(listener);
+ }
+
+ @Test
+ public void testPrimaryCancelsSecondary() {
+ TestableListener listener = new TestableListener();
+
+ mProximitySensor.register(listener);
+ assertFalse(mThresholdSensorPrimary.isPaused());
+ assertTrue(mThresholdSensorSecondary.isPaused());
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+
+ mThresholdSensorPrimary.triggerEvent(true, 0);
+ assertNull(listener.mLastEvent);
+ assertEquals(0, listener.mCallCount);
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+
+ // When the primary reports false, the secondary is no longer needed. We get an immediate
+ // report.
+ mThresholdSensorPrimary.triggerEvent(false, 1);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(2, listener.mCallCount);
+
+ // The secondary is now ignored. No more work is scheduled.
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runNextReady();
+ mThresholdSensorSecondary.triggerEvent(true, 0);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(2, listener.mCallCount);
+ assertEquals(0, mFakeExecutor.numPending());
+
+ mProximitySensor.unregister(listener);
+ }
+
+ private static class TestableListener implements ThresholdSensor.Listener {
+ ThresholdSensor.ThresholdSensorEvent mLastEvent;
+ int mCallCount = 0;
+
+ @Override
+ public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
+ mLastEvent = proximityEvent;
+ mCallCount++;
+ }
+ };
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java
similarity index 54%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java
index 526fba726e9d9..f1cee5061695f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java
@@ -21,33 +21,40 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+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.MockitoAnnotations;
+/**
+ * Tests for ProximitySensor that rely on a single hardware sensor.
+ */
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ProximitySensorTest extends SysuiTestCase {
-
+public class ProximitySensorSingleTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
- private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ private FakeThresholdSensor mThresholdSensor;
@Before
public void setUp() throws Exception {
- FakeSensorManager sensorManager = new FakeSensorManager(getContext());
- AsyncSensorManager asyncSensorManager = new AsyncSensorManager(
- sensorManager, null, new Handler());
- mFakeProximitySensor = sensorManager.getFakeProximitySensor();
- mProximitySensor = new ProximitySensor(getContext().getResources(), asyncSensorManager);
+ MockitoAnnotations.initMocks(this);
+ allowTestableLooperAsMainThread();
+ mThresholdSensor = new FakeThresholdSensor();
+ mThresholdSensor.setLoaded(true);
+
+ mProximitySensor = new ProximitySensor(
+ mThresholdSensor, new FakeThresholdSensor(), mFakeExecutor);
}
@Test
@@ -56,19 +63,17 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listener);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
assertNull(listener.mLastEvent);
- mFakeProximitySensor.sendProximityResult(true);
- assertFalse(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 1);
- mFakeProximitySensor.sendProximityResult(false);
- assertTrue(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 2);
+ mThresholdSensor.triggerEvent(false, 0);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+ mThresholdSensor.triggerEvent(true, 0);
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(2, listener.mCallCount);
mProximitySensor.unregister(listener);
- waitForSensorManager();
}
@Test
@@ -79,28 +84,25 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listenerA);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
mProximitySensor.register(listenerB);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
assertNull(listenerA.mLastEvent);
assertNull(listenerB.mLastEvent);
- mFakeProximitySensor.sendProximityResult(true);
- assertFalse(listenerA.mLastEvent.getNear());
- assertFalse(listenerB.mLastEvent.getNear());
- assertEquals(listenerA.mCallCount, 1);
- assertEquals(listenerB.mCallCount, 1);
- mFakeProximitySensor.sendProximityResult(false);
- assertTrue(listenerA.mLastEvent.getNear());
- assertTrue(listenerB.mLastEvent.getNear());
- assertEquals(listenerA.mCallCount, 2);
- assertEquals(listenerB.mCallCount, 2);
+ mThresholdSensor.triggerEvent(false, 0);
+ assertFalse(listenerA.mLastEvent.getBelow());
+ assertFalse(listenerB.mLastEvent.getBelow());
+ assertEquals(1, listenerA.mCallCount);
+ assertEquals(1, listenerB.mCallCount);
+ mThresholdSensor.triggerEvent(true, 1);
+ assertTrue(listenerA.mLastEvent.getBelow());
+ assertTrue(listenerB.mLastEvent.getBelow());
+ assertEquals(2, listenerA.mCallCount);
+ assertEquals(2, listenerB.mCallCount);
mProximitySensor.unregister(listenerA);
mProximitySensor.unregister(listenerB);
- waitForSensorManager();
}
@Test
@@ -110,22 +112,19 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listenerA);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
mProximitySensor.register(listenerA);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
assertNull(listenerA.mLastEvent);
- mFakeProximitySensor.sendProximityResult(true);
- assertFalse(listenerA.mLastEvent.getNear());
- assertEquals(listenerA.mCallCount, 1);
- mFakeProximitySensor.sendProximityResult(false);
- assertTrue(listenerA.mLastEvent.getNear());
- assertEquals(listenerA.mCallCount, 2);
+ mThresholdSensor.triggerEvent(false, 0);
+ assertFalse(listenerA.mLastEvent.getBelow());
+ assertEquals(1, listenerA.mCallCount);
+ mThresholdSensor.triggerEvent(true, 1);
+ assertTrue(listenerA.mLastEvent.getBelow());
+ assertEquals(2, listenerA.mCallCount);
mProximitySensor.unregister(listenerA);
- waitForSensorManager();
}
@Test
public void testUnregister() {
@@ -133,16 +132,14 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listener);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
assertNull(listener.mLastEvent);
- mFakeProximitySensor.sendProximityResult(true);
- assertFalse(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 1);
+ mThresholdSensor.triggerEvent(false, 0);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
mProximitySensor.unregister(listener);
- waitForSensorManager();
assertFalse(mProximitySensor.isRegistered());
}
@@ -152,39 +149,35 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listener);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
assertNull(listener.mLastEvent);
- mFakeProximitySensor.sendProximityResult(true);
- assertFalse(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 1);
+ mThresholdSensor.triggerEvent(false, 0);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
mProximitySensor.pause();
- waitForSensorManager();
assertFalse(mProximitySensor.isRegistered());
// More events do nothing when paused.
- mFakeProximitySensor.sendProximityResult(true);
- assertFalse(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 1);
- mFakeProximitySensor.sendProximityResult(false);
- assertFalse(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 1);
+ mThresholdSensor.triggerEvent(false, 1);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
+ mThresholdSensor.triggerEvent(true, 2);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
mProximitySensor.resume();
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
// Still matches our previous call
- assertFalse(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 1);
+ assertFalse(listener.mLastEvent.getBelow());
+ assertEquals(1, listener.mCallCount);
- mFakeProximitySensor.sendProximityResult(true);
- assertFalse(listener.mLastEvent.getNear());
- assertEquals(listener.mCallCount, 2);
+ mThresholdSensor.triggerEvent(true, 3);
+ assertTrue(listener.mLastEvent.getBelow());
+ assertEquals(2, listener.mCallCount);
mProximitySensor.unregister(listener);
- waitForSensorManager();
assertFalse(mProximitySensor.isRegistered());
}
@@ -197,46 +190,35 @@ public class ProximitySensorTest extends SysuiTestCase {
mProximitySensor.register(listenerA);
mProximitySensor.register(listenerB);
- waitForSensorManager();
assertTrue(mProximitySensor.isRegistered());
assertNull(listenerA.mLastEvent);
assertNull(listenerB.mLastEvent);
mProximitySensor.alertListeners();
assertNull(listenerA.mLastEvent);
- assertEquals(listenerA.mCallCount, 1);
+ assertEquals(0, listenerA.mCallCount);
assertNull(listenerB.mLastEvent);
- assertEquals(listenerB.mCallCount, 1);
+ assertEquals(0, listenerB.mCallCount);
- mFakeProximitySensor.sendProximityResult(false);
- assertTrue(listenerA.mLastEvent.getNear());
- assertEquals(listenerA.mCallCount, 2);
- assertTrue(listenerB.mLastEvent.getNear());
- assertEquals(listenerB.mCallCount, 2);
+ mThresholdSensor.triggerEvent(true, 0);
+ assertTrue(listenerA.mLastEvent.getBelow());
+ assertEquals(1, listenerA.mCallCount);
+ assertTrue(listenerB.mLastEvent.getBelow());
+ assertEquals(1, listenerB.mCallCount);
mProximitySensor.unregister(listenerA);
mProximitySensor.unregister(listenerB);
- waitForSensorManager();
}
- class TestableListener implements ProximitySensor.ProximitySensorListener {
- ProximitySensor.ProximityEvent mLastEvent;
+ private static class TestableListener implements ThresholdSensor.Listener {
+ ThresholdSensor.ThresholdSensorEvent mLastEvent;
int mCallCount = 0;
@Override
- public void onSensorEvent(ProximitySensor.ProximityEvent proximityEvent) {
+ public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
mLastEvent = proximityEvent;
mCallCount++;
}
-
- void reset() {
- mLastEvent = null;
- mCallCount = 0;
- }
};
- private void waitForSensorManager() {
- TestableLooper.get(this).processAllMessages();
- }
-
}
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
new file mode 100644
index 0000000000000..09ec8e55efd62
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2020 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.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Handler;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ThresholdSensorImplTest extends SysuiTestCase {
+
+ private ThresholdSensorImpl mThresholdSensor;
+ private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
+
+ @Before
+ public void setUp() throws Exception {
+ allowTestableLooperAsMainThread();
+ FakeSensorManager sensorManager = new FakeSensorManager(getContext());
+
+ AsyncSensorManager asyncSensorManager = new AsyncSensorManager(
+ sensorManager, null, new Handler());
+
+ mFakeProximitySensor = sensorManager.getFakeProximitySensor();
+ ThresholdSensorImpl.Builder thresholdSensorBuilder = new ThresholdSensorImpl.Builder(
+ null, asyncSensorManager);
+ mThresholdSensor = (ThresholdSensorImpl) thresholdSensorBuilder
+ .setSensor(mFakeProximitySensor.getSensor())
+ .setThresholdValue(mFakeProximitySensor.getSensor().getMaximumRange())
+ .build();
+ }
+
+ @Test
+ public void testSingleListener() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mThresholdSensor.isRegistered());
+ mThresholdSensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ assertEquals(0, listener.mCallCount);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mBelow);
+ assertEquals(1, listener.mCallCount);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listener.mBelow);
+ assertEquals(2, listener.mCallCount);
+
+ mThresholdSensor.unregister(listener);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testMultiListener() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mThresholdSensor.isRegistered());
+
+ mThresholdSensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ mThresholdSensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ assertEquals(0, listenerA.mCallCount);
+ assertEquals(0, listenerB.mCallCount);
+
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listenerA.mBelow);
+ assertFalse(listenerB.mBelow);
+ assertEquals(1, listenerA.mCallCount);
+ assertEquals(1, listenerB.mCallCount);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mBelow);
+ assertTrue(listenerB.mBelow);
+ assertEquals(2, listenerA.mCallCount);
+ assertEquals(2, listenerB.mCallCount);
+
+ mThresholdSensor.unregister(listenerA);
+ mThresholdSensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testDuplicateListener() {
+ TestableListener listenerA = new TestableListener();
+
+ assertFalse(mThresholdSensor.isRegistered());
+
+ mThresholdSensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ mThresholdSensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ assertEquals(0, listenerA.mCallCount);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listenerA.mBelow);
+ assertEquals(1, listenerA.mCallCount);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mBelow);
+ assertEquals(2, listenerA.mCallCount);
+
+ mThresholdSensor.unregister(listenerA);
+ waitForSensorManager();
+ }
+ @Test
+ public void testUnregister() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mThresholdSensor.isRegistered());
+ mThresholdSensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ assertEquals(0, listener.mCallCount);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mBelow);
+ assertEquals(1, listener.mCallCount);
+
+ mThresholdSensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mThresholdSensor.isRegistered());
+ }
+
+ @Test
+ public void testPauseAndResume() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mThresholdSensor.isRegistered());
+ mThresholdSensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ assertEquals(0, listener.mCallCount);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mBelow);
+ assertEquals(1, listener.mCallCount);
+
+ mThresholdSensor.pause();
+ waitForSensorManager();
+ assertFalse(mThresholdSensor.isRegistered());
+
+ // More events do nothing when paused.
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mBelow);
+ assertEquals(1, listener.mCallCount);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertFalse(listener.mBelow);
+ assertEquals(1, listener.mCallCount);
+
+ mThresholdSensor.resume();
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ // Still matches our previous call
+ assertFalse(listener.mBelow);
+ assertEquals(1, listener.mCallCount);
+
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listener.mBelow);
+ assertEquals(2, listener.mCallCount);
+
+ mThresholdSensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mThresholdSensor.isRegistered());
+ }
+
+ @Test
+ public void testAlertListeners() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mThresholdSensor.isRegistered());
+
+ mThresholdSensor.register(listenerA);
+ mThresholdSensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mThresholdSensor.isRegistered());
+ assertEquals(0, listenerA.mCallCount);
+ assertEquals(0, listenerB.mCallCount);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listenerA.mBelow);
+ assertEquals(1, listenerA.mCallCount);
+ assertFalse(listenerB.mBelow);
+ assertEquals(1, listenerB.mCallCount);
+
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mBelow);
+ assertEquals(2, listenerA.mCallCount);
+ assertTrue(listenerB.mBelow);
+ assertEquals(2, listenerB.mCallCount);
+
+ mThresholdSensor.unregister(listenerA);
+ mThresholdSensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ static class TestableListener implements ThresholdSensor.Listener {
+ boolean mBelow;
+ long mTimestampNs;
+ int mCallCount;
+
+ @Override
+ public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) {
+ mBelow = event.getBelow();
+ mTimestampNs = event.getTimestampNs();
+ mCallCount++;
+ }
+ }
+
+ private void waitForSensorManager() {
+ TestableLooper.get(this).processAllMessages();
+ }
+
+}