Add new ProximitySensor wrapper that automatically uses appropriate sensor.

Bug: 140270231
Test: manual && atest SystemUITests
Change-Id: Id151ce8f702f7deadab73ee3904a0a883870b237
Merged-In: Id151ce8f702f7deadab73ee3904a0a883870b237
This commit is contained in:
Dave Mankoff
2019-09-12 13:39:42 -04:00
parent d65f8c6704
commit 46b9d68f6c
7 changed files with 244 additions and 99 deletions

View File

@@ -34,7 +34,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.ProximitySensor;
import java.io.PrintWriter;
@@ -50,18 +50,23 @@ import javax.inject.Singleton;
@Singleton
public class FalsingManagerProxy implements FalsingManager {
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
private final ProximitySensor mProximitySensor;
private FalsingManager mInternalFalsingManager;
private final Handler mMainHandler;
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager,
@Named(MAIN_HANDLER_NAME) Handler handler) {
@Named(MAIN_HANDLER_NAME) Handler handler,
ProximitySensor proximitySensor) {
mMainHandler = handler;
mProximitySensor = proximitySensor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
command -> mMainHandler.post(command),
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace())
);
setupFalsingManager(context);
); setupFalsingManager(context);
final PluginListener<FalsingPlugin> mPluginListener = new PluginListener<FalsingPlugin>() {
public void onPluginConnected(FalsingPlugin plugin, Context context) {
FalsingManager pluginFalsingManager = plugin.getFalsingManager(context);
@@ -103,8 +108,8 @@ public class FalsingManagerProxy implements FalsingManager {
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
new FalsingDataProvider(context.getResources().getDisplayMetrics()),
Dependency.get(AsyncSensorManager.class),
KeyguardUpdateMonitor.getInstance(context)
Dependency.get(KeyguardUpdateMonitor.class),
mProximitySensor
);
}

View File

@@ -19,10 +19,6 @@ package com.android.systemui.classifier.brightline;
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_REMAIN_LOCKED;
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricSourceType;
import android.net.Uri;
import android.util.Log;
@@ -33,12 +29,11 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.util.ProximitySensor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* FalsingManager designed to make clear why a touch was rejected.
@@ -48,9 +43,9 @@ public class BrightLineFalsingManager implements FalsingManager {
static final boolean DEBUG = false;
private static final String TAG = "FalsingManagerPlugin";
private final SensorManager mSensorManager;
private final FalsingDataProvider mDataProvider;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ProximitySensor mProximitySensor;
private boolean mSessionStarted;
private MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
@@ -58,20 +53,9 @@ public class BrightLineFalsingManager implements FalsingManager {
private boolean mScreenOn;
private boolean mJustUnlockedWithFace;
private final ExecutorService mBackgroundExecutor = Executors.newSingleThreadExecutor();
private final List<FalsingClassifier> mClassifiers;
private SensorEventListener mSensorEventListener = new SensorEventListener() {
@Override
public synchronized void onSensorChanged(SensorEvent event) {
onSensorEvent(event);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
private ProximitySensor.ProximitySensorListener mSensorEventListener = this::onProximityEvent;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@@ -87,11 +71,11 @@ public class BrightLineFalsingManager implements FalsingManager {
public BrightLineFalsingManager(
FalsingDataProvider falsingDataProvider,
SensorManager sensorManager,
KeyguardUpdateMonitor keyguardUpdateMonitor) {
KeyguardUpdateMonitor keyguardUpdateMonitor,
ProximitySensor proximitySensor) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDataProvider = falsingDataProvider;
mSensorManager = sensorManager;
mProximitySensor = proximitySensor;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
mMetricsLogger = new MetricsLogger();
@@ -108,24 +92,12 @@ public class BrightLineFalsingManager implements FalsingManager {
}
private void registerSensors() {
Sensor s = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (s != null) {
// This can be expensive, and doesn't need to happen on the main thread.
mBackgroundExecutor.submit(() -> {
logDebug("registering sensor listener");
mSensorManager.registerListener(
mSensorEventListener, s, SensorManager.SENSOR_DELAY_GAME);
});
}
mProximitySensor.register(mSensorEventListener);
}
private void unregisterSensors() {
// This can be expensive, and doesn't need to happen on the main thread.
mBackgroundExecutor.submit(() -> {
logDebug("unregistering sensor listener");
mSensorManager.unregisterListener(mSensorEventListener);
});
mProximitySensor.unregister(mSensorEventListener);
}
private void sessionStart() {
@@ -187,10 +159,10 @@ public class BrightLineFalsingManager implements FalsingManager {
mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent));
}
private void onSensorEvent(SensorEvent sensorEvent) {
private void onProximityEvent(ProximitySensor.ProximityEvent 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.onSensorEvent(sensorEvent));
mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent));
}
@Override

View File

@@ -16,10 +16,10 @@
package com.android.systemui.classifier.brightline;
import android.hardware.SensorEvent;
import android.view.MotionEvent;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.util.ProximitySensor;
import java.util.List;
@@ -98,9 +98,9 @@ abstract class FalsingClassifier {
void onTouchEvent(MotionEvent motionEvent) {};
/**
* Called whenever a SensorEvent occurs, specifically the ProximitySensor.
* Called when a ProximityEvent occurs (change in near/far).
*/
void onSensorEvent(SensorEvent sensorEvent) {};
void onProximityEvent(ProximitySensor.ProximityEvent proximityEvent) {};
/**
* The phone screen has turned on and we need to begin falsing detection.

View File

@@ -19,11 +19,11 @@ package com.android.systemui.classifier.brightline;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.provider.DeviceConfig;
import android.view.MotionEvent;
import com.android.systemui.util.ProximitySensor;
/**
* False touch if proximity sensor is covered for more than a certain percentage of the gesture.
@@ -97,14 +97,12 @@ class ProximityClassifier extends FalsingClassifier {
}
@Override
public void onSensorEvent(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_PROXIMITY) {
logDebug("Sensor is: " + (sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange())
+ " at time " + sensorEvent.timestamp);
update(
sensorEvent.values[0] < sensorEvent.sensor.getMaximumRange(),
sensorEvent.timestamp);
}
public void onProximityEvent(
ProximitySensor.ProximityEvent proximityEvent) {
boolean near = proximityEvent.getNear();
long timestampNs = proximityEvent.getTimestampNs();
logDebug("Sensor is: " + near + " at time " + timestampNs);
update(near, timestampNs);
}
@Override

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.util;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
import com.android.systemui.R;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
/**
* Simple wrapper around SensorManager customized for the Proximity sensor.
*/
public class ProximitySensor {
private static final String TAG = "ProxSensor";
private static final boolean DEBUG = false;
private final Sensor mSensor;
private final AsyncSensorManager mSensorManager;
private final boolean mUsingBrightnessSensor;
private final float mMaxRange;
private SensorEventListener mSensorEventListener = new SensorEventListener() {
@Override
public synchronized void onSensorChanged(SensorEvent event) {
onSensorEvent(event);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
private boolean mNear;
private List<ProximitySensorListener> mListeners = new ArrayList<>();
private String mTag = null;
@Inject
public ProximitySensor(Context context, AsyncSensorManager sensorManager) {
mSensorManager = sensorManager;
Sensor sensor = findBrightnessSensor(context, sensorManager);
if (sensor == null) {
mUsingBrightnessSensor = false;
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
} else {
mUsingBrightnessSensor = true;
}
mSensor = sensor;
if (mSensor != null) {
mMaxRange = mSensor.getMaximumRange();
} else {
mMaxRange = 0;
}
}
public void setTag(String tag) {
mTag = tag;
}
private Sensor findBrightnessSensor(Context context, SensorManager sensorManager) {
String sensorType = context.getString(R.string.doze_brightness_sensor_type);
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
Sensor sensor = null;
for (Sensor s : sensorList) {
if (sensorType.equals(s.getStringType())) {
sensor = s;
break;
}
}
return sensor;
}
/**
* Returns {@code false} if a Proximity sensor is not available.
*/
public boolean getSensorAvailable() {
return mSensor != null;
}
/**
* Add a listener.
*
* Registers itself with the {@link SensorManager} if this is the first listener
* added.
*/
public boolean register(ProximitySensorListener listener) {
if (!getSensorAvailable()) {
return false;
}
logDebug("using brightness sensor? " + mUsingBrightnessSensor);
mListeners.add(listener);
if (mListeners.size() == 1) {
logDebug("registering sensor listener");
mSensorManager.registerListener(
mSensorEventListener, mSensor, SensorManager.SENSOR_DELAY_GAME);
}
return true;
}
/**
* Remove a listener.
*
* If all listeners are removed from an instance of this class,
* it will unregister itself with the SensorManager.
*/
public void unregister(ProximitySensorListener listener) {
mListeners.remove(listener);
if (mListeners.size() == 0) {
logDebug("unregistering sensor listener");
mSensorManager.unregisterListener(mSensorEventListener);
}
}
public boolean isNear() {
return getSensorAvailable() && mNear;
}
private void onSensorEvent(SensorEvent event) {
boolean near = event.values[0] < mMaxRange;
if (mUsingBrightnessSensor) {
near = event.values[0] == 0;
}
mNear = near;
mListeners.forEach(proximitySensorListener ->
proximitySensorListener.onProximitySensorEvent(
new ProximityEvent(mNear, event.timestamp)));
}
/** Implement to be notified of ProximityEvents. */
public interface ProximitySensorListener {
/** Called when the ProximitySensor changes. */
void onProximitySensorEvent(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;
}
}
private void logDebug(String msg) {
if (DEBUG) {
Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
}
}
}

View File

@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.ProximitySensor;
import org.junit.After;
import org.junit.Before;
@@ -43,8 +44,10 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class FalsingManagerProxyTest extends SysuiTestCase {
@Mock
@Mock(stubOnly = true)
PluginManager mPluginManager;
@Mock(stubOnly = true)
ProximitySensor mProximitySensor;
private boolean mDefaultConfigValue;
private Handler mHandler;
private TestableLooper mTestableLooper;
@@ -69,7 +72,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
@Test
public void test_brightLineFalsingManagerDisabled() {
FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
FalsingManagerProxy proxy = new FalsingManagerProxy(
getContext(), mPluginManager, mHandler, mProximitySensor);
assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -78,14 +82,16 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
public void test_brightLineFalsingManagerEnabled() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
FalsingManagerProxy proxy = new FalsingManagerProxy(
getContext(), mPluginManager, mHandler, mProximitySensor);
assertThat(proxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@Test
public void test_brightLineFalsingManagerToggled() {
FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler);
FalsingManagerProxy proxy = new FalsingManagerProxy(
getContext(), mPluginManager, mHandler, mProximitySensor);
assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,

View File

@@ -23,24 +23,21 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
import com.android.systemui.util.ProximitySensor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.lang.reflect.Field;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -78,8 +75,8 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testPass_mostlyUncovered() {
touchDown();
mClassifier.onSensorEvent(createSensorEvent(true, 1));
mClassifier.onSensorEvent(createSensorEvent(false, 2));
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 2));
touchUp(20);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@@ -88,8 +85,8 @@ public class ProximityClassifierTest extends ClassifierTest {
public void testPass_quickSettings() {
touchDown();
when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
mClassifier.onSensorEvent(createSensorEvent(true, 1));
mClassifier.onSensorEvent(createSensorEvent(false, 11));
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@@ -97,8 +94,8 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testFail_covered() {
touchDown();
mClassifier.onSensorEvent(createSensorEvent(true, 1));
mClassifier.onSensorEvent(createSensorEvent(false, 11));
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@@ -106,10 +103,10 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testFail_mostlyCovered() {
touchDown();
mClassifier.onSensorEvent(createSensorEvent(true, 1));
mClassifier.onSensorEvent(createSensorEvent(true, 95));
mClassifier.onSensorEvent(createSensorEvent(true, 96));
mClassifier.onSensorEvent(createSensorEvent(false, 100));
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(true, 95));
mClassifier.onProximityEvent(createSensorEvent(true, 96));
mClassifier.onProximityEvent(createSensorEvent(false, 100));
touchUp(100);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@@ -117,8 +114,8 @@ public class ProximityClassifierTest extends ClassifierTest {
@Test
public void testPass_coveredWithLongSwipe() {
touchDown();
mClassifier.onSensorEvent(createSensorEvent(true, 1));
mClassifier.onSensorEvent(createSensorEvent(false, 11));
mClassifier.onProximityEvent(createSensorEvent(true, 1));
mClassifier.onProximityEvent(createSensorEvent(false, 11));
touchUp(10);
when(mDistanceClassifier.isLongSwipe()).thenReturn(true);
assertThat(mClassifier.isFalseTouch(), is(false));
@@ -139,26 +136,7 @@ public class ProximityClassifierTest extends ClassifierTest {
motionEvent.recycle();
}
private SensorEvent createSensorEvent(boolean covered, long timestampMs) {
SensorEvent sensorEvent = Mockito.mock(SensorEvent.class);
Sensor sensor = Mockito.mock(Sensor.class);
when(sensor.getType()).thenReturn(Sensor.TYPE_PROXIMITY);
when(sensor.getMaximumRange()).thenReturn(1f);
sensorEvent.sensor = sensor;
sensorEvent.timestamp = timestampMs * NS_PER_MS;
try {
Field valuesField = SensorEvent.class.getField("values");
valuesField.setAccessible(true);
float[] sensorValue = {covered ? 0 : 1};
try {
valuesField.set(sensorEvent, sensorValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return sensorEvent;
private ProximitySensor.ProximityEvent createSensorEvent(boolean covered, long timestampMs) {
return new ProximitySensor.ProximityEvent(covered, timestampMs * NS_PER_MS);
}
}