Merge "Add falsing protection to the pulsing state" into tm-qpr-dev

This commit is contained in:
Beverly Tai
2022-08-26 18:18:32 +00:00
committed by Android (Google) Code Review
11 changed files with 404 additions and 61 deletions

View File

@@ -102,6 +102,12 @@ public interface FalsingManager {
*/ */
boolean isFalseDoubleTap(); boolean isFalseDoubleTap();
/**
* Whether the last proximity event reported NEAR. May be used to short circuit motion events
* that require the proximity sensor is not covered.
*/
boolean isProximityNear();
boolean isClassifierEnabled(); boolean isClassifierEnabled();
boolean shouldEnforceBouncer(); boolean shouldEnforceBouncer();

View File

@@ -80,12 +80,14 @@ public class BrightLineFalsingManager implements FalsingManager {
private final Collection<FalsingClassifier> mClassifiers; private final Collection<FalsingClassifier> mClassifiers;
private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
private List<FalsingTapListener> mFalsingTapListeners = new ArrayList<>(); private List<FalsingTapListener> mFalsingTapListeners = new ArrayList<>();
private ProximityEvent mLastProximityEvent;
private boolean mDestroyed; private boolean mDestroyed;
private final SessionListener mSessionListener = new SessionListener() { private final SessionListener mSessionListener = new SessionListener() {
@Override @Override
public void onSessionEnded() { public void onSessionEnded() {
mLastProximityEvent = null;
mClassifiers.forEach(FalsingClassifier::onSessionEnded); mClassifiers.forEach(FalsingClassifier::onSessionEnded);
} }
@@ -336,6 +338,7 @@ public class BrightLineFalsingManager implements FalsingManager {
public void onProximityEvent(ProximityEvent proximityEvent) { public void onProximityEvent(ProximityEvent 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.
mLastProximityEvent = proximityEvent;
mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent)); mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent));
} }
@@ -347,6 +350,11 @@ public class BrightLineFalsingManager implements FalsingManager {
} }
} }
@Override
public boolean isProximityNear() {
return mLastProximityEvent != null && mLastProximityEvent.getCovered();
}
@Override @Override
public boolean isUnlockingDisabled() { public boolean isUnlockingDisabled() {
return false; return false;

View File

@@ -47,9 +47,9 @@ import javax.inject.Inject;
@SysUISingleton @SysUISingleton
class FalsingCollectorImpl implements FalsingCollector { class FalsingCollectorImpl implements FalsingCollector {
private static final boolean DEBUG = false; private static final String TAG = "FalsingCollector";
private static final String TAG = "FalsingManager"; private static final String PROXIMITY_SENSOR_TAG = "FalsingCollector";
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final long GESTURE_PROCESSING_DELAY_MS = 100; private static final long GESTURE_PROCESSING_DELAY_MS = 100;
private final FalsingDataProvider mFalsingDataProvider; private final FalsingDataProvider mFalsingDataProvider;

View File

@@ -143,6 +143,11 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
return mInternalFalsingManager.isFalseDoubleTap(); return mInternalFalsingManager.isFalseDoubleTap();
} }
@Override
public boolean isProximityNear() {
return mInternalFalsingManager.isProximityNear();
}
@Override @Override
public boolean isClassifierEnabled() { public boolean isClassifierEnabled() {
return mInternalFalsingManager.isClassifierEnabled(); return mInternalFalsingManager.isClassifierEnabled();

View File

@@ -17,12 +17,9 @@
package com.android.systemui.shade; package com.android.systemui.shade;
import android.app.StatusBarManager; import android.app.StatusBarManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.session.MediaSessionLegacyHelper; import android.media.session.MediaSessionLegacyHelper;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.InputDevice; import android.view.InputDevice;
@@ -52,7 +49,6 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Optional; import java.util.Optional;
@@ -66,7 +62,6 @@ import javax.inject.Inject;
public class NotificationShadeWindowViewController { public class NotificationShadeWindowViewController {
private static final String TAG = "NotifShadeWindowVC"; private static final String TAG = "NotifShadeWindowVC";
private final FalsingCollector mFalsingCollector; private final FalsingCollector mFalsingCollector;
private final TunerService mTunerService;
private final SysuiStatusBarStateController mStatusBarStateController; private final SysuiStatusBarStateController mStatusBarStateController;
private final NotificationShadeWindowView mView; private final NotificationShadeWindowView mView;
private final NotificationShadeDepthController mDepthController; private final NotificationShadeDepthController mDepthController;
@@ -77,6 +72,7 @@ public class NotificationShadeWindowViewController {
private final StatusBarWindowStateController mStatusBarWindowStateController; private final StatusBarWindowStateController mStatusBarWindowStateController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final AmbientState mAmbientState; private final AmbientState mAmbientState;
private final PulsingGestureListener mPulsingGestureListener;
private GestureDetector mGestureDetector; private GestureDetector mGestureDetector;
private View mBrightnessMirror; private View mBrightnessMirror;
@@ -88,8 +84,6 @@ public class NotificationShadeWindowViewController {
private final CentralSurfaces mService; private final CentralSurfaces mService;
private final NotificationShadeWindowController mNotificationShadeWindowController; private final NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper; private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
private boolean mSingleTapEnabled;
private boolean mExpandingBelowNotch; private boolean mExpandingBelowNotch;
private final DockManager mDockManager; private final DockManager mDockManager;
private final NotificationPanelViewController mNotificationPanelViewController; private final NotificationPanelViewController mNotificationPanelViewController;
@@ -102,7 +96,6 @@ public class NotificationShadeWindowViewController {
public NotificationShadeWindowViewController( public NotificationShadeWindowViewController(
LockscreenShadeTransitionController transitionController, LockscreenShadeTransitionController transitionController,
FalsingCollector falsingCollector, FalsingCollector falsingCollector,
TunerService tunerService,
SysuiStatusBarStateController statusBarStateController, SysuiStatusBarStateController statusBarStateController,
DockManager dockManager, DockManager dockManager,
NotificationShadeDepthController depthController, NotificationShadeDepthController depthController,
@@ -117,10 +110,11 @@ public class NotificationShadeWindowViewController {
CentralSurfaces centralSurfaces, CentralSurfaces centralSurfaces,
NotificationShadeWindowController controller, NotificationShadeWindowController controller,
KeyguardUnlockAnimationController keyguardUnlockAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController,
AmbientState ambientState) { AmbientState ambientState,
PulsingGestureListener pulsingGestureListener
) {
mLockscreenShadeTransitionController = transitionController; mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector; mFalsingCollector = falsingCollector;
mTunerService = tunerService;
mStatusBarStateController = statusBarStateController; mStatusBarStateController = statusBarStateController;
mView = notificationShadeWindowView; mView = notificationShadeWindowView;
mDockManager = dockManager; mDockManager = dockManager;
@@ -136,6 +130,7 @@ public class NotificationShadeWindowViewController {
mNotificationShadeWindowController = controller; mNotificationShadeWindowController = controller;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mAmbientState = ambientState; mAmbientState = ambientState;
mPulsingGestureListener = pulsingGestureListener;
// This view is not part of the newly inflated expanded status bar. // This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -151,46 +146,7 @@ public class NotificationShadeWindowViewController {
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */ /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() { public void setupExpandedStatusBar() {
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller); mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
mGestureDetector = new GestureDetector(mView.getContext(), mPulsingGestureListener);
TunerService.Tunable tunable = (key, newValue) -> {
AmbientDisplayConfiguration configuration =
new AmbientDisplayConfiguration(mView.getContext());
switch (key) {
case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE:
mDoubleTapEnabled = configuration.doubleTapGestureEnabled(
UserHandle.USER_CURRENT);
break;
case Settings.Secure.DOZE_TAP_SCREEN_GESTURE:
mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT);
}
};
mTunerService.addTunable(tunable,
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
Settings.Secure.DOZE_TAP_SCREEN_GESTURE);
GestureDetector.SimpleOnGestureListener gestureListener =
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mSingleTapEnabled && !mDockManager.isDocked()) {
mService.wakeUpIfDozing(
SystemClock.uptimeMillis(), mView, "SINGLE_TAP");
return true;
}
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
if (mDoubleTapEnabled || mSingleTapEnabled) {
mService.wakeUpIfDozing(
SystemClock.uptimeMillis(), mView, "DOUBLE_TAP");
return true;
}
return false;
}
};
mGestureDetector = new GestureDetector(mView.getContext(), gestureListener);
mLowLightClockController.ifPresent(controller -> controller.attachLowLightClockView(mView)); mLowLightClockController.ifPresent(controller -> controller.attachLowLightClockView(mView));

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2022 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.shade
import android.hardware.display.AmbientDisplayConfiguration
import android.os.SystemClock
import android.os.UserHandle
import android.provider.Settings
import android.view.GestureDetector
import android.view.MotionEvent
import com.android.systemui.Dumpable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
import java.io.PrintWriter
import javax.inject.Inject
/**
* If tap and/or double tap to wake is enabled, this gestureListener will wake the display on
* tap/double tap when the device is pulsing (AoD 2). Taps are gated by the proximity sensor and
* falsing manager.
*
* Touches go through the [NotificationShadeWindowViewController] when the device is pulsing.
* Otherwise, if the device is dozing and NOT pulsing, wake-ups are handled by
* [com.android.systemui.doze.DozeSensors].
*/
@CentralSurfacesComponent.CentralSurfacesScope
class PulsingGestureListener @Inject constructor(
private val notificationShadeWindowView: NotificationShadeWindowView,
private val falsingManager: FalsingManager,
private val dockManager: DockManager,
private val centralSurfaces: CentralSurfaces,
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
tunerService: TunerService,
dumpManager: DumpManager
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
private var doubleTapEnabled = false
private var singleTapEnabled = false
init {
val tunable = Tunable { key: String?, _: String? ->
when (key) {
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE ->
doubleTapEnabled = ambientDisplayConfiguration.doubleTapGestureEnabled(
UserHandle.USER_CURRENT)
Settings.Secure.DOZE_TAP_SCREEN_GESTURE ->
singleTapEnabled = ambientDisplayConfiguration.tapGestureEnabled(
UserHandle.USER_CURRENT)
}
}
tunerService.addTunable(tunable,
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
Settings.Secure.DOZE_TAP_SCREEN_GESTURE)
dumpManager.registerDumpable(this)
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
if (singleTapEnabled &&
!dockManager.isDocked &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)
) {
centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(),
notificationShadeWindowView,
"PULSING_SINGLE_TAP")
return true
}
return false
}
override fun onDoubleTap(e: MotionEvent): Boolean {
if ((doubleTapEnabled || singleTapEnabled) &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseDoubleTap
) {
centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(),
notificationShadeWindowView,
"PULSING_DOUBLE_TAP")
return true
}
return false
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("singleTapEnabled=$singleTapEnabled")
pw.println("doubleTapEnabled=$doubleTapEnabled")
pw.println("isDocked=${dockManager.isDocked}")
pw.println("isProxCovered=${falsingManager.isProximityNear}")
}
}

View File

@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before; import org.junit.Before;
@@ -111,5 +112,51 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
} }
@Test
public void testIsProxNear_noProxEvents_defaultsToFalse() {
assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
}
@Test
public void testIsProxNear_receivesNearEvent() {
mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
@Override
public boolean getCovered() {
return true;
}
@Override
public long getTimestampNs() {
return 0;
}
});
assertThat(mBrightLineFalsingManager.isProximityNear()).isTrue();
}
@Test
public void testIsProxNear_receivesNearAndThenFarEvent() {
mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
@Override
public boolean getCovered() {
return true;
}
@Override
public long getTimestampNs() {
return 0;
}
});
mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
@Override
public boolean getCovered() {
return false;
}
@Override
public long getTimestampNs() {
return 5;
}
});
assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
}
} }

View File

@@ -26,19 +26,18 @@ import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager import com.android.systemui.dock.DockManager
import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.lowlightclock.LowLightClockController import com.android.systemui.lowlightclock.LowLightClockController
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationShadeDepthController import com.android.systemui.statusbar.NotificationShadeDepthController
import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.tuner.TunerService
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import java.util.Optional import java.util.Optional
import org.junit.Before import org.junit.Before
@@ -60,8 +59,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock @Mock
private lateinit var view: NotificationShadeWindowView private lateinit var view: NotificationShadeWindowView
@Mock @Mock
private lateinit var tunserService: TunerService
@Mock
private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock @Mock
private lateinit var centralSurfaces: CentralSurfaces private lateinit var centralSurfaces: CentralSurfaces
@@ -91,6 +88,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock @Mock
private lateinit var lowLightClockController: LowLightClockController private lateinit var lowLightClockController: LowLightClockController
@Mock
private lateinit var pulsingGestureListener: PulsingGestureListener
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler private lateinit var interactionEventHandler: InteractionEventHandler
@@ -105,7 +104,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
underTest = NotificationShadeWindowViewController( underTest = NotificationShadeWindowViewController(
lockscreenShadeTransitionController, lockscreenShadeTransitionController,
FalsingCollectorFake(), FalsingCollectorFake(),
tunserService,
sysuiStatusBarStateController, sysuiStatusBarStateController,
dockManager, dockManager,
notificationShadeDepthController, notificationShadeDepthController,
@@ -120,7 +118,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
centralSurfaces, centralSurfaces,
notificationShadeWindowController, notificationShadeWindowController,
keyguardUnlockAnimationController, keyguardUnlockAnimationController,
ambientState ambientState,
pulsingGestureListener
) )
underTest.setupExpandedStatusBar() underTest.setupExpandedStatusBar()

View File

@@ -89,6 +89,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private LowLightClockController mLowLightClockController; @Mock private LowLightClockController mLowLightClockController;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private AmbientState mAmbientState; @Mock private AmbientState mAmbientState;
@Mock private PulsingGestureListener mPulsingGestureListener;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor; mInteractionEventHandlerCaptor;
@@ -110,7 +111,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mController = new NotificationShadeWindowViewController( mController = new NotificationShadeWindowViewController(
mLockscreenShadeTransitionController, mLockscreenShadeTransitionController,
new FalsingCollectorFake(), new FalsingCollectorFake(),
mTunerService,
mStatusBarStateController, mStatusBarStateController,
mDockManager, mDockManager,
mNotificationShadeDepthController, mNotificationShadeDepthController,
@@ -125,7 +125,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mCentralSurfaces, mCentralSurfaces,
mNotificationShadeWindowController, mNotificationShadeWindowController,
mKeyguardUnlockAnimationController, mKeyguardUnlockAnimationController,
mAmbientState); mAmbientState,
mPulsingGestureListener
);
mController.setupExpandedStatusBar(); mController.setupExpandedStatusBar();
mController.setDragDownHelper(mDragDownHelper); mController.setDragDownHelper(mDragDownHelper);
} }

View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2022 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.shade
import android.hardware.display.AmbientDisplayConfiguration
import android.provider.Settings.Secure.DOZE_DOUBLE_TAP_GESTURE
import android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
import com.android.systemui.util.mockito.eq
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyObject
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class PulsingGestureListenerTest : SysuiTestCase() {
@Mock
private lateinit var view: NotificationShadeWindowView
@Mock
private lateinit var centralSurfaces: CentralSurfaces
@Mock
private lateinit var dockManager: DockManager
@Mock
private lateinit var falsingManager: FalsingManager
@Mock
private lateinit var ambientDisplayConfiguration: AmbientDisplayConfiguration
@Mock
private lateinit var tunerService: TunerService
@Mock
private lateinit var dumpManager: DumpManager
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
private lateinit var underTest: PulsingGestureListener
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
underTest = PulsingGestureListener(
view,
falsingManager,
dockManager,
centralSurfaces,
ambientDisplayConfiguration,
tunerService,
dumpManager
)
whenever(dockManager.isDocked).thenReturn(false)
}
@Test
fun testGestureDetector_singleTapEnabled() {
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
whenever(falsingManager.isProximityNear).thenReturn(false)
// GIVEN the falsing manager does NOT think the tap is a false tap
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
// WHEN there's a tap
underTest.onSingleTapConfirmed(downEv)
// THEN wake up device if dozing
verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
@Test
fun testGestureDetector_doubleTapEnabled() {
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
whenever(falsingManager.isProximityNear).thenReturn(false)
// GIVEN the falsing manager does NOT think the tap is a false tap
whenever(falsingManager.isFalseDoubleTap).thenReturn(false)
// WHEN there's a double tap
underTest.onDoubleTap(downEv)
// THEN wake up device if dozing
verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
@Test
fun testGestureDetector_singleTapEnabled_falsing() {
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
whenever(falsingManager.isProximityNear).thenReturn(false)
// GIVEN the falsing manager thinks the tap is a false tap
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
// WHEN there's a tap
underTest.onSingleTapConfirmed(downEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
@Test
fun testGestureDetector_doubleTapEnabled_falsing() {
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
whenever(falsingManager.isProximityNear).thenReturn(false)
// GIVEN the falsing manager thinks the tap is a false tap
whenever(falsingManager.isFalseDoubleTap).thenReturn(true)
// WHEN there's a tap
underTest.onDoubleTap(downEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
@Test
fun testGestureDetector_singleTapEnabled_proxCovered() {
// GIVEN tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
// GIVEN prox is covered
whenever(falsingManager.isProximityNear()).thenReturn(true)
// WHEN there's a tap
underTest.onSingleTapConfirmed(downEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
@Test
fun testGestureDetector_doubleTapEnabled_proxCovered() {
// GIVEN double tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
whenever(falsingManager.isFalseDoubleTap).thenReturn(false)
// GIVEN prox is covered
whenever(falsingManager.isProximityNear()).thenReturn(true)
// WHEN there's a tap
underTest.onDoubleTap(downEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
fun updateSettings() {
tunableCaptor = ArgumentCaptor.forClass(Tunable::class.java)
verify(tunerService).addTunable(
tunableCaptor.capture(),
eq(DOZE_DOUBLE_TAP_GESTURE),
eq(DOZE_TAP_SCREEN_GESTURE))
tunableCaptor.value.onTuningChanged(DOZE_DOUBLE_TAP_GESTURE, "")
tunableCaptor.value.onTuningChanged(DOZE_TAP_SCREEN_GESTURE, "")
}
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)

View File

@@ -40,6 +40,7 @@ public class FalsingManagerFake implements FalsingManager {
private boolean mIsReportingEnabled; private boolean mIsReportingEnabled;
private boolean mIsFalseRobustTap; private boolean mIsFalseRobustTap;
private boolean mDestroyed; private boolean mDestroyed;
private boolean mIsProximityNear;
private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>(); private final List<FalsingBeliefListener> mFalsingBeliefListeners = new ArrayList<>();
private final List<FalsingTapListener> mTapListeners = new ArrayList<>(); private final List<FalsingTapListener> mTapListeners = new ArrayList<>();
@@ -82,6 +83,10 @@ public class FalsingManagerFake implements FalsingManager {
mIsFalseDoubleTap = falseDoubleTap; mIsFalseDoubleTap = falseDoubleTap;
} }
public void setIsProximityNear(boolean proxNear) {
mIsProximityNear = proxNear;
}
@Override @Override
public boolean isSimpleTap() { public boolean isSimpleTap() {
checkDestroyed(); checkDestroyed();
@@ -100,6 +105,11 @@ public class FalsingManagerFake implements FalsingManager {
return mIsFalseDoubleTap; return mIsFalseDoubleTap;
} }
@Override
public boolean isProximityNear() {
return mIsProximityNear;
}
@VisibleForTesting @VisibleForTesting
public void setIsClassifierEnabled(boolean isClassifierEnabled) { public void setIsClassifierEnabled(boolean isClassifierEnabled) {
mIsClassifierEnabled = isClassifierEnabled; mIsClassifierEnabled = isClassifierEnabled;