diff options
7 files changed, 244 insertions, 99 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index ee79e6b973de..ff3c5d46ed92 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -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 ); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index 9e0b7025ddf8..b68a56dcc750 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java index 685e7c534b66..bf397518de46 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java @@ -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. diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java index 2644bf9f26ce..5045095a475f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java new file mode 100644 index 000000000000..a905eba1f0ed --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java @@ -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); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java index 7ea6493da83d..32cf8429cf62 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java @@ -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, diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java index a6cabbf49458..49ba30272c70 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java @@ -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); } } |