Merge "RESTRICT AUTOMERGE ProximitySensor now supports dual-sensor approach." into rvc-d1-dev

This commit is contained in:
Dave Mankoff
2020-04-24 00:43:37 +00:00
committed by Android (Google) Code Review
25 changed files with 1411 additions and 254 deletions

View File

@@ -205,6 +205,14 @@
far break points. A sensor value less than this is considered "near". --> far break points. A sensor value less than this is considered "near". -->
<item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen"></item> <item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen"></item>
<!-- Override value to use for proximity sensor as confirmation for proximity_sensor_type. -->
<string name="proximity_sensor_secondary_type" translatable="false"></string>
<!-- If using proximity_sensor_confirmation_type, specifies a threshold value to distinguish
near and far break points. A sensor value less than this is considered "near". -->
<item name="proximity_sensor_secondary_threshold" translatable="false" format="float"
type="dimen"></item>
<!-- Doze: pulse parameter - how long does it take to fade in? --> <!-- Doze: pulse parameter - how long does it take to fade in? -->
<integer name="doze_pulse_duration_in">130</integer> <integer name="doze_pulse_duration_in">130</integer>

View File

@@ -87,7 +87,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
mUiBgExecutor = uiBgExecutor; mUiBgExecutor = uiBgExecutor;
mStatusBarStateController = statusBarStateController; mStatusBarStateController = statusBarStateController;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME); mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig; mDeviceConfig = deviceConfig;
mDeviceConfigListener = mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace()); properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());

View File

@@ -37,6 +37,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@@ -76,7 +77,7 @@ public class BrightLineFalsingManager implements FalsingManager {
private final List<FalsingClassifier> mClassifiers; private final List<FalsingClassifier> mClassifiers;
private ProximitySensor.ProximitySensorListener mSensorEventListener = this::onProximityEvent; private ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() { new KeyguardUpdateMonitorCallback() {
@@ -240,7 +241,7 @@ public class BrightLineFalsingManager implements FalsingManager {
mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent)); 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 // TODO: some of these classifiers might allow us to abort early, meaning we don't have to
// make these calls. // make these calls.
mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent)); mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent));

View File

@@ -96,7 +96,7 @@ abstract class FalsingClassifier {
/** /**
* Called when a ProximityEvent occurs (change in near/far). * 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. * The phone screen has turned on and we need to begin falsing detection.

View File

@@ -101,8 +101,8 @@ class ProximityClassifier extends FalsingClassifier {
@Override @Override
public void onProximityEvent( public void onProximityEvent(
ProximitySensor.ProximityEvent proximityEvent) { ProximitySensor.ThresholdSensorEvent proximityEvent) {
boolean near = proximityEvent.getNear(); boolean near = proximityEvent.getBelow();
long timestampNs = proximityEvent.getTimestampNs(); long timestampNs = proximityEvent.getTimestampNs();
logDebug("Sensor is: " + near + " at time " + timestampNs); logDebug("Sensor is: " + near + " at time " + timestampNs);
update(near, timestampNs); update(near, timestampNs);

View File

@@ -36,6 +36,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager; import android.content.pm.ShortcutManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManager;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
@@ -240,6 +241,12 @@ public class SystemServicesModule {
return context.getResources(); return context.getResources();
} }
@Provides
@Singleton
static SensorManager providesSensorManager(Context context) {
return context.getSystemService(SensorManager.class);
}
@Singleton @Singleton
@Provides @Provides
static SensorPrivacyManager provideSensorPrivacyManager(Context context) { static SensorPrivacyManager provideSensorPrivacyManager(Context context) {

View File

@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.ConcurrencyModule; import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager; 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.SystemClock;
import com.android.systemui.util.time.SystemClockImpl; import com.android.systemui.util.time.SystemClockImpl;
@@ -62,7 +63,8 @@ import dagger.Provides;
ConcurrencyModule.class, ConcurrencyModule.class,
LogModule.class, LogModule.class,
PeopleHubModule.class, PeopleHubModule.class,
SettingsModule.class SensorModule.class,
SettingsModule.class,
}, },
subcomponents = {StatusBarComponent.class, subcomponents = {StatusBarComponent.class,
NotificationRowComponent.class, NotificationRowComponent.class,

View File

@@ -81,7 +81,8 @@ public class DozeSensors {
public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager, public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog) { Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
ProximitySensor proximitySensor) {
mContext = context; mContext = context;
mAlarmManager = alarmManager; mAlarmManager = alarmManager;
mSensorManager = sensorManager; mSensorManager = sensorManager;
@@ -153,12 +154,12 @@ public class DozeSensors {
dozeLog), dozeLog),
}; };
mProximitySensor = new ProximitySensor(context.getResources(), sensorManager); mProximitySensor = proximitySensor;
setProxListening(false); // Don't immediately start listening when we register. setProxListening(false); // Don't immediately start listening when we register.
mProximitySensor.register( mProximitySensor.register(
proximityEvent -> { proximityEvent -> {
if (proximityEvent != null) { if (proximityEvent != null) {
mProxCallback.accept(!proximityEvent.getNear()); mProxCallback.accept(!proximityEvent.getBelow());
} }
}); });
} }

View File

@@ -92,8 +92,8 @@ public class DozeTriggers implements DozeMachine.Part {
AlarmManager alarmManager, AmbientDisplayConfiguration config, AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler, DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager, WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
ProximitySensor proximitySensor, ProximitySensor proximitySensor, DozeLog dozeLog,
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) { BroadcastDispatcher broadcastDispatcher) {
mContext = context; mContext = context;
mMachine = machine; mMachine = machine;
mDozeHost = dozeHost; mDozeHost = dozeHost;
@@ -103,7 +103,8 @@ public class DozeTriggers implements DozeMachine.Part {
mWakeLock = wakeLock; mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers; mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters, 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); mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager; mDockManager = dockManager;
mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler); mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);

View File

@@ -60,8 +60,8 @@ public class AsyncSensorManager extends SensorManager
private final List<SensorManagerPlugin> mPlugins; private final List<SensorManagerPlugin> mPlugins;
@Inject @Inject
public AsyncSensorManager(Context context, PluginManager pluginManager) { public AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
this(context.getSystemService(SensorManager.class), pluginManager, null); this(sensorManager, pluginManager, null);
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -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 {
}

View File

@@ -16,92 +16,119 @@
package com.android.systemui.util.sensors; 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.hardware.SensorManager;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.inject.Inject; 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 String TAG = "ProxSensor";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 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 ThresholdSensor mPrimaryThresholdSensor;
private final AsyncSensorManager mSensorManager; private final ThresholdSensor mSecondaryThresholdSensor;
private final float mThreshold; private final DelayableExecutor mDelayableExecutor;
private List<ProximitySensorListener> mListeners = new ArrayList<>(); private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>();
private String mTag = null; private String mTag = null;
@VisibleForTesting ProximityEvent mLastEvent; private ThresholdSensorEvent mLastPrimaryEvent;
private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL; @VisibleForTesting
ThresholdSensorEvent mLastEvent;
private boolean mPaused; private boolean mPaused;
private boolean mRegistered; private boolean mRegistered;
private Runnable mCancelSecondaryRunnable;
private boolean mInitializedListeners = false;
private SensorEventListener mSensorEventListener = new SensorEventListener() { private ThresholdSensor.Listener mPrimaryEventListener = new ThresholdSensor.Listener() {
@Override @Override
public synchronized void onSensorChanged(SensorEvent event) { public void onThresholdCrossed(ThresholdSensorEvent event) {
onSensorEvent(event); onPrimarySensorEvent(event);
} }
};
private ThresholdSensor.Listener mSecondaryEventListener = new ThresholdSensor.Listener() {
@Override @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 @Inject
public ProximitySensor(@Main Resources resources, public ProximitySensor(@PrimaryProxSensor ThresholdSensor primary,
AsyncSensorManager sensorManager) { @SecondaryProxSensor ThresholdSensor secondary,
mSensorManager = sensorManager; @Main DelayableExecutor delayableExecutor) {
mPrimaryThresholdSensor = primary;
Sensor sensor = findCustomProxSensor(resources); mSecondaryThresholdSensor = secondary;
float threshold = 0; mDelayableExecutor = delayableExecutor;
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;
} }
@Override
public void setTag(String tag) { public void setTag(String tag) {
mTag = tag; mTag = tag;
mPrimaryThresholdSensor.setTag(tag + ":primary");
mSecondaryThresholdSensor.setTag(tag + ":secondary");
} }
public void setSensorDelay(int sensorDelay) { @Override
mSensorDelay = sensorDelay; public void setDelay(int delay) {
Assert.isMainThread();
mPrimaryThresholdSensor.setDelay(delay);
mSecondaryThresholdSensor.setDelay(delay);
} }
/** /**
* Unregister with the {@link SensorManager} without unsetting listeners on this object. * Unregister with the {@link SensorManager} without unsetting listeners on this object.
*/ */
@Override
public void pause() { public void pause() {
Assert.isMainThread();
mPaused = true; mPaused = true;
unregisterInternal(); unregisterInternal();
} }
@@ -109,41 +136,13 @@ public class ProximitySensor {
/** /**
* Register with the {@link SensorManager}. No-op if no listeners are registered on this object. * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
*/ */
@Override
public void resume() { public void resume() {
Assert.isMainThread();
mPaused = false; mPaused = false;
registerInternal(); 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<Sensor> 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. * 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. * Returns {@code false} if a Proximity sensor is not available.
*/ */
public boolean getSensorAvailable() { @Override
return mSensor != null; public boolean isLoaded() {
return mPrimaryThresholdSensor.isLoaded();
} }
/** /**
* Add a listener. * Add a listener.
* *
* Registers itself with the {@link SensorManager} if this is the first 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) { @Override
if (!getSensorAvailable()) { public void register(ThresholdSensor.Listener listener) {
return false; Assert.isMainThread();
if (!isLoaded()) {
return;
} }
if (mListeners.contains(listener)) { if (mListeners.contains(listener)) {
Log.d(TAG, "ProxListener registered multiple times: " + listener); logDebug("ProxListener registered multiple times: " + listener);
} else { } else {
mListeners.add(listener); mListeners.add(listener);
} }
registerInternal(); registerInternal();
return true;
} }
protected void registerInternal() { protected void registerInternal() {
Assert.isMainThread();
if (mRegistered || mPaused || mListeners.isEmpty()) { if (mRegistered || mPaused || mListeners.isEmpty()) {
return; return;
} }
if (!mInitializedListeners) {
mPrimaryThresholdSensor.register(mPrimaryEventListener);
mSecondaryThresholdSensor.pause();
mSecondaryThresholdSensor.register(mSecondaryEventListener);
mInitializedListeners = true;
}
logDebug("Registering sensor listener"); logDebug("Registering sensor listener");
mPrimaryThresholdSensor.resume();
mRegistered = true; 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, * If all listeners are removed from an instance of this class,
* it will unregister itself with the SensorManager. * it will unregister itself with the SensorManager.
*/ */
public void unregister(ProximitySensorListener listener) { @Override
public void unregister(ThresholdSensor.Listener listener) {
Assert.isMainThread();
mListeners.remove(listener); mListeners.remove(listener);
if (mListeners.size() == 0) { if (mListeners.size() == 0) {
unregisterInternal(); unregisterInternal();
@@ -203,34 +212,77 @@ public class ProximitySensor {
} }
protected void unregisterInternal() { protected void unregisterInternal() {
Assert.isMainThread();
if (!mRegistered) { if (!mRegistered) {
return; return;
} }
logDebug("unregistering sensor listener"); 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; mRegistered = false;
} }
public Boolean isNear() { 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. */ /** Update all listeners with the last value this class received from the sensor. */
public void alertListeners() { public void alertListeners() {
mListeners.forEach(proximitySensorListener -> Assert.isMainThread();
proximitySensorListener.onSensorEvent(mLastEvent)); if (mLastEvent == null) {
return;
}
List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
listeners.forEach(proximitySensorListener ->
proximitySensorListener.onThresholdCrossed(mLastEvent));
} }
private void onSensorEvent(SensorEvent event) { private void onPrimarySensorEvent(ThresholdSensorEvent event) {
boolean near = event.values[0] < mThreshold; Assert.isMainThread();
mLastEvent = new ProximityEvent(near, event.timestamp); 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(); alertListeners();
} }
@Override @Override
public String toString() { public String toString() {
return String.format("{registered=%s, paused=%s, near=%s, sensor=%s}", return String.format("{registered=%s, paused=%s, near=%s, primarySensor=%s, "
isRegistered(), mPaused, isNear(), mSensor); + "secondarySensor=%s}",
isRegistered(), mPaused, isNear(), mPrimaryThresholdSensor,
mSecondaryThresholdSensor);
} }
/** /**
@@ -248,11 +300,11 @@ public class ProximitySensor {
mSensor.setTag("prox_check"); mSensor.setTag("prox_check");
mHandler = handler; mHandler = handler;
mSensor.pause(); mSensor.pause();
ProximitySensorListener listener = proximityEvent -> { ThresholdSensor.Listener listener = proximityEvent -> {
mCallbacks.forEach( mCallbacks.forEach(
booleanConsumer -> booleanConsumer ->
booleanConsumer.accept( booleanConsumer.accept(
proximityEvent == null ? null : proximityEvent.getNear())); proximityEvent == null ? null : proximityEvent.getBelow()));
mCallbacks.clear(); mCallbacks.clear();
mSensor.pause(); mSensor.pause();
}; };
@@ -274,7 +326,7 @@ public class ProximitySensor {
* Query the proximity sensor, timing out if no result. * Query the proximity sensor, timing out if no result.
*/ */
public void check(long timeoutMs, Consumer<Boolean> callback) { public void check(long timeoutMs, Consumer<Boolean> callback) {
if (!mSensor.getSensorAvailable()) { if (!mSensor.isLoaded()) {
callback.accept(null); callback.accept(null);
} }
mCallbacks.add(callback); 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) { private void logDebug(String msg) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg); Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);

View File

@@ -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 {
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<Listener> 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<Listener> 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<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
Sensor sensor = null;
for (Sensor s : sensorList) {
if (sensorType.equals(s.getStringType())) {
sensor = s;
break;
}
}
return sensor;
}
}
}

View File

@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -77,7 +78,7 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
@Test @Test
public void testRegisterSensor() { public void testRegisterSensor() {
mFalsingManager.onScreenTurningOn(); mFalsingManager.onScreenTurningOn();
verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class)); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
} }
@Test @Test
@@ -85,7 +86,7 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn(); mFalsingManager.onScreenTurningOn();
reset(mProximitySensor); reset(mProximitySensor);
mFalsingManager.onScreenOff(); mFalsingManager.onScreenOff();
verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
} }
@Test @Test
@@ -93,9 +94,9 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn(); mFalsingManager.onScreenTurningOn();
reset(mProximitySensor); reset(mProximitySensor);
mFalsingManager.setQsExpanded(true); mFalsingManager.setQsExpanded(true);
verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
mFalsingManager.setQsExpanded(false); mFalsingManager.setQsExpanded(false);
verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class)); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
} }
@Test @Test
@@ -103,9 +104,9 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn(); mFalsingManager.onScreenTurningOn();
reset(mProximitySensor); reset(mProximitySensor);
mFalsingManager.onBouncerShown(); mFalsingManager.onBouncerShown();
verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
mFalsingManager.onBouncerHidden(); mFalsingManager.onBouncerHidden();
verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class)); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
} }
@Test @Test
@@ -113,6 +114,6 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
mFalsingManager.onScreenTurningOn(); mFalsingManager.onScreenTurningOn();
reset(mProximitySensor); reset(mProximitySensor);
mStatusBarStateController.setState(StatusBarState.SHADE); mStatusBarStateController.setState(StatusBarState.SHADE);
verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class)); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
} }
} }

View File

@@ -136,7 +136,8 @@ public class ProximityClassifierTest extends ClassifierTest {
motionEvent.recycle(); motionEvent.recycle();
} }
private ProximitySensor.ProximityEvent createSensorEvent(boolean covered, long timestampMs) { private ProximitySensor.ThresholdSensorEvent createSensorEvent(
return new ProximitySensor.ProximityEvent(covered, timestampMs * NS_PER_MS); boolean covered, long timestampMs) {
return new ProximitySensor.ThresholdSensorEvent(covered, timestampMs * NS_PER_MS);
} }
} }

View File

@@ -46,6 +46,7 @@ import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before; import org.junit.Before;
@@ -83,6 +84,9 @@ public class DozeSensorsTest extends SysuiTestCase {
private DozeLog mDozeLog; private DozeLog mDozeLog;
@Mock @Mock
private Sensor mProximitySensor; private Sensor mProximitySensor;
@Mock
private ProximitySensor mMockProxSensor;
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener; private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper; private TestableLooper mTestableLooper;
private DozeSensors mDozeSensors; private DozeSensors mDozeSensors;
@@ -104,9 +108,9 @@ public class DozeSensorsTest extends SysuiTestCase {
@Test @Test
public void testRegisterProx() { public void testRegisterProx() {
// We should not register with the sensor manager initially. // We should not register with the sensor manager initially.
verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt()); verify(mMockProxSensor).pause();
mDozeSensors.setProxListening(true); mDozeSensors.setProxListening(true);
verify(mSensorManager).registerListener(any(), any(Sensor.class), anyInt()); verify(mMockProxSensor).resume();
} }
@Test @Test
@@ -162,7 +166,8 @@ public class DozeSensorsTest extends SysuiTestCase {
TestableDozeSensors() { TestableDozeSensors() {
super(getContext(), mAlarmManager, mSensorManager, mDozeParameters, super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog); mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
mMockProxSensor);
for (TriggerSensor sensor : mSensors) { for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType() && ((PluginSensor) sensor).mPluginSensor.getType()

View File

@@ -42,10 +42,13 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters; 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.AsyncSensorManager;
import com.android.systemui.util.sensors.FakeProximitySensor; import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager; 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.ProximitySensor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake; import com.android.systemui.util.wakelock.WakeLockFake;
@@ -71,10 +74,12 @@ public class DozeTriggersTest extends SysuiTestCase {
private BroadcastDispatcher mBroadcastDispatcher; private BroadcastDispatcher mBroadcastDispatcher;
@Mock @Mock
private DockManager mDockManager; private DockManager mDockManager;
private DozeTriggers mTriggers; private DozeTriggers mTriggers;
private FakeSensorManager mSensors; private FakeSensorManager mSensors;
private Sensor mTapSensor; private Sensor mTapSensor;
private FakeProximitySensor mProximitySensor; private FakeProximitySensor mProximitySensor;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@@ -86,7 +91,10 @@ public class DozeTriggersTest extends SysuiTestCase {
WakeLock wakeLock = new WakeLockFake(); WakeLock wakeLock = new WakeLockFake();
AsyncSensorManager asyncSensorManager = AsyncSensorManager asyncSensorManager =
new AsyncSensorManager(mSensors, null, new Handler()); 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, mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true, asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
@@ -104,17 +112,17 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine); clearInvocations(mMachine);
mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1)); mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 1));
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
mProximitySensor.alertListeners(); mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt()); verify(mMachine, never()).requestPulse(anyInt());
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(false, 2));
waitForSensorManager();
mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
mProximitySensor.alertListeners(); mProximitySensor.alertListeners();
waitForSensorManager();
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
verify(mMachine).requestPulse(anyInt()); verify(mMachine).requestPulse(anyInt());
} }

View File

@@ -16,13 +16,15 @@
package com.android.systemui.util.sensors; package com.android.systemui.util.sensors;
import android.content.res.Resources; import com.android.systemui.util.concurrency.DelayableExecutor;
public class FakeProximitySensor extends ProximitySensor { public class FakeProximitySensor extends ProximitySensor {
private boolean mAvailable; private boolean mAvailable;
public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) { public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary,
super(resources, sensorManager); DelayableExecutor delayableExecutor) {
super(primary, secondary == null ? new FakeThresholdSensor() : secondary,
delayableExecutor);
mAvailable = true; mAvailable = true;
} }
@@ -30,12 +32,12 @@ public class FakeProximitySensor extends ProximitySensor {
mAvailable = available; mAvailable = available;
} }
public void setLastEvent(ProximityEvent event) { public void setLastEvent(ThresholdSensorEvent event) {
mLastEvent = event; mLastEvent = event;
} }
@Override @Override
public boolean getSensorAvailable() { public boolean isLoaded() {
return mAvailable; return mAvailable;
} }

View File

@@ -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<Listener> 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();
}
}

View File

@@ -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++;
}
};
}

View File

@@ -21,33 +21,40 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import android.os.Handler;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper; import android.testing.TestableLooper;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; 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.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
/**
* Tests for ProximitySensor that rely on a single hardware sensor.
*/
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper @TestableLooper.RunWithLooper
public class ProximitySensorTest extends SysuiTestCase { public class ProximitySensorSingleTest extends SysuiTestCase {
private ProximitySensor mProximitySensor; private ProximitySensor mProximitySensor;
private FakeSensorManager.FakeProximitySensor mFakeProximitySensor; private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThresholdSensor mThresholdSensor;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
FakeSensorManager sensorManager = new FakeSensorManager(getContext()); MockitoAnnotations.initMocks(this);
AsyncSensorManager asyncSensorManager = new AsyncSensorManager( allowTestableLooperAsMainThread();
sensorManager, null, new Handler()); mThresholdSensor = new FakeThresholdSensor();
mFakeProximitySensor = sensorManager.getFakeProximitySensor(); mThresholdSensor.setLoaded(true);
mProximitySensor = new ProximitySensor(getContext().getResources(), asyncSensorManager);
mProximitySensor = new ProximitySensor(
mThresholdSensor, new FakeThresholdSensor(), mFakeExecutor);
} }
@Test @Test
@@ -56,19 +63,17 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listener); mProximitySensor.register(listener);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
assertNull(listener.mLastEvent); assertNull(listener.mLastEvent);
mFakeProximitySensor.sendProximityResult(true); mThresholdSensor.triggerEvent(false, 0);
assertFalse(listener.mLastEvent.getNear()); assertFalse(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 1); assertEquals(1, listener.mCallCount);
mFakeProximitySensor.sendProximityResult(false); mThresholdSensor.triggerEvent(true, 0);
assertTrue(listener.mLastEvent.getNear()); assertTrue(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 2); assertEquals(2, listener.mCallCount);
mProximitySensor.unregister(listener); mProximitySensor.unregister(listener);
waitForSensorManager();
} }
@Test @Test
@@ -79,28 +84,25 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listenerA); mProximitySensor.register(listenerA);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
mProximitySensor.register(listenerB); mProximitySensor.register(listenerB);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
assertNull(listenerA.mLastEvent); assertNull(listenerA.mLastEvent);
assertNull(listenerB.mLastEvent); assertNull(listenerB.mLastEvent);
mFakeProximitySensor.sendProximityResult(true); mThresholdSensor.triggerEvent(false, 0);
assertFalse(listenerA.mLastEvent.getNear()); assertFalse(listenerA.mLastEvent.getBelow());
assertFalse(listenerB.mLastEvent.getNear()); assertFalse(listenerB.mLastEvent.getBelow());
assertEquals(listenerA.mCallCount, 1); assertEquals(1, listenerA.mCallCount);
assertEquals(listenerB.mCallCount, 1); assertEquals(1, listenerB.mCallCount);
mFakeProximitySensor.sendProximityResult(false); mThresholdSensor.triggerEvent(true, 1);
assertTrue(listenerA.mLastEvent.getNear()); assertTrue(listenerA.mLastEvent.getBelow());
assertTrue(listenerB.mLastEvent.getNear()); assertTrue(listenerB.mLastEvent.getBelow());
assertEquals(listenerA.mCallCount, 2); assertEquals(2, listenerA.mCallCount);
assertEquals(listenerB.mCallCount, 2); assertEquals(2, listenerB.mCallCount);
mProximitySensor.unregister(listenerA); mProximitySensor.unregister(listenerA);
mProximitySensor.unregister(listenerB); mProximitySensor.unregister(listenerB);
waitForSensorManager();
} }
@Test @Test
@@ -110,22 +112,19 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listenerA); mProximitySensor.register(listenerA);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
mProximitySensor.register(listenerA); mProximitySensor.register(listenerA);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
assertNull(listenerA.mLastEvent); assertNull(listenerA.mLastEvent);
mFakeProximitySensor.sendProximityResult(true); mThresholdSensor.triggerEvent(false, 0);
assertFalse(listenerA.mLastEvent.getNear()); assertFalse(listenerA.mLastEvent.getBelow());
assertEquals(listenerA.mCallCount, 1); assertEquals(1, listenerA.mCallCount);
mFakeProximitySensor.sendProximityResult(false); mThresholdSensor.triggerEvent(true, 1);
assertTrue(listenerA.mLastEvent.getNear()); assertTrue(listenerA.mLastEvent.getBelow());
assertEquals(listenerA.mCallCount, 2); assertEquals(2, listenerA.mCallCount);
mProximitySensor.unregister(listenerA); mProximitySensor.unregister(listenerA);
waitForSensorManager();
} }
@Test @Test
public void testUnregister() { public void testUnregister() {
@@ -133,16 +132,14 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listener); mProximitySensor.register(listener);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
assertNull(listener.mLastEvent); assertNull(listener.mLastEvent);
mFakeProximitySensor.sendProximityResult(true); mThresholdSensor.triggerEvent(false, 0);
assertFalse(listener.mLastEvent.getNear()); assertFalse(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 1); assertEquals(1, listener.mCallCount);
mProximitySensor.unregister(listener); mProximitySensor.unregister(listener);
waitForSensorManager();
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
} }
@@ -152,39 +149,35 @@ public class ProximitySensorTest extends SysuiTestCase {
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
mProximitySensor.register(listener); mProximitySensor.register(listener);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
assertNull(listener.mLastEvent); assertNull(listener.mLastEvent);
mFakeProximitySensor.sendProximityResult(true); mThresholdSensor.triggerEvent(false, 0);
assertFalse(listener.mLastEvent.getNear()); assertFalse(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 1); assertEquals(1, listener.mCallCount);
mProximitySensor.pause(); mProximitySensor.pause();
waitForSensorManager();
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
// More events do nothing when paused. // More events do nothing when paused.
mFakeProximitySensor.sendProximityResult(true); mThresholdSensor.triggerEvent(false, 1);
assertFalse(listener.mLastEvent.getNear()); assertFalse(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 1); assertEquals(1, listener.mCallCount);
mFakeProximitySensor.sendProximityResult(false); mThresholdSensor.triggerEvent(true, 2);
assertFalse(listener.mLastEvent.getNear()); assertFalse(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 1); assertEquals(1, listener.mCallCount);
mProximitySensor.resume(); mProximitySensor.resume();
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
// Still matches our previous call // Still matches our previous call
assertFalse(listener.mLastEvent.getNear()); assertFalse(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 1); assertEquals(1, listener.mCallCount);
mFakeProximitySensor.sendProximityResult(true); mThresholdSensor.triggerEvent(true, 3);
assertFalse(listener.mLastEvent.getNear()); assertTrue(listener.mLastEvent.getBelow());
assertEquals(listener.mCallCount, 2); assertEquals(2, listener.mCallCount);
mProximitySensor.unregister(listener); mProximitySensor.unregister(listener);
waitForSensorManager();
assertFalse(mProximitySensor.isRegistered()); assertFalse(mProximitySensor.isRegistered());
} }
@@ -197,46 +190,35 @@ public class ProximitySensorTest extends SysuiTestCase {
mProximitySensor.register(listenerA); mProximitySensor.register(listenerA);
mProximitySensor.register(listenerB); mProximitySensor.register(listenerB);
waitForSensorManager();
assertTrue(mProximitySensor.isRegistered()); assertTrue(mProximitySensor.isRegistered());
assertNull(listenerA.mLastEvent); assertNull(listenerA.mLastEvent);
assertNull(listenerB.mLastEvent); assertNull(listenerB.mLastEvent);
mProximitySensor.alertListeners(); mProximitySensor.alertListeners();
assertNull(listenerA.mLastEvent); assertNull(listenerA.mLastEvent);
assertEquals(listenerA.mCallCount, 1); assertEquals(0, listenerA.mCallCount);
assertNull(listenerB.mLastEvent); assertNull(listenerB.mLastEvent);
assertEquals(listenerB.mCallCount, 1); assertEquals(0, listenerB.mCallCount);
mFakeProximitySensor.sendProximityResult(false); mThresholdSensor.triggerEvent(true, 0);
assertTrue(listenerA.mLastEvent.getNear()); assertTrue(listenerA.mLastEvent.getBelow());
assertEquals(listenerA.mCallCount, 2); assertEquals(1, listenerA.mCallCount);
assertTrue(listenerB.mLastEvent.getNear()); assertTrue(listenerB.mLastEvent.getBelow());
assertEquals(listenerB.mCallCount, 2); assertEquals(1, listenerB.mCallCount);
mProximitySensor.unregister(listenerA); mProximitySensor.unregister(listenerA);
mProximitySensor.unregister(listenerB); mProximitySensor.unregister(listenerB);
waitForSensorManager();
} }
class TestableListener implements ProximitySensor.ProximitySensorListener { private static class TestableListener implements ThresholdSensor.Listener {
ProximitySensor.ProximityEvent mLastEvent; ThresholdSensor.ThresholdSensorEvent mLastEvent;
int mCallCount = 0; int mCallCount = 0;
@Override @Override
public void onSensorEvent(ProximitySensor.ProximityEvent proximityEvent) { public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
mLastEvent = proximityEvent; mLastEvent = proximityEvent;
mCallCount++; mCallCount++;
} }
void reset() {
mLastEvent = null;
mCallCount = 0;
}
}; };
private void waitForSensorManager() {
TestableLooper.get(this).processAllMessages();
}
} }

View File

@@ -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();
}
}