Merge changes from topic "b172655679-bayes-falsing" into sc-dev

* changes:
  Add Falsing to quick settings bottom buttons.
  Collapse QS when falsing happens.
  Separate out FalsingManager#isFalseTap
This commit is contained in:
Dave Mankoff
2021-04-07 21:14:12 +00:00
committed by Android (Google) Code Review
19 changed files with 167 additions and 91 deletions

View File

@@ -70,17 +70,17 @@ target. Match the methods with the gesture you expect the device owner to use.
### Single Tap ### Single Tap
`FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`. This `FalsingManager#isSimpleTape()`. This method
method tells the `FalsingManager` that you want to validate a single tap. It performs a only very basic checking, checking that observed `MotionEvent`s are
all within some small x & y region ("touch slop"). Useful for only the most simple of scenarios,
you probably want `FalsingManager#isFalseTap` method for most cases.
`FalsingManager#isFalseTap(@Penalty int penalty)`. This
method tells the `FalsingManager` that you want to thoroughly validate a single tap. It
returns true if it thinks the tap should be rejected (i.e. the tap looks more returns true if it thinks the tap should be rejected (i.e. the tap looks more
like a swipe) and false otherwise. like a swipe) and false otherwise.
`robustCheck` determines what heuristics are used. If set to false, the method It runs through the following heuristics to validate a tap:
performs a only very basic checking, checking that observed `MotionEvent`s are
all within some small x & y region ("touch slop").
When `robustCheck` is set to true, several more advanced rules are additionally
applied:
1. If the device recognizes a face (i.e. face-auth) the tap is **accepted**. 1. If the device recognizes a face (i.e. face-auth) the tap is **accepted**.
2. If the tap is the _second_ tap in recent history and looks like a valid Double Tap 2. If the tap is the _second_ tap in recent history and looks like a valid Double Tap
@@ -90,19 +90,18 @@ applied:
4. Otherwise the tap is **accepted**. 4. Otherwise the tap is **accepted**.
All the above rules are applied only after first confirming the gesture does All the above rules are applied only after first confirming the gesture does
in fact look like a basic tap. in fact look like a simple tap.
`falsePenalty` is a measure of how much the `HistoryTracker`'s belief should be `penalty` is a measure of how much the `HistoryTracker`'s belief should be
penalized in the event that the tap is rejected. This value is only used if penalized in the event that the tap is rejected. This value is only used if
`robustCheck` is set to true. the gesture fails to validate as a simple tap.
A value of `0` means no change in belief. A value of `1` means a _very_ strong The `@FalsingManager.Penalty` values are fairly straightforward, but note that you
confidence in a false tap. In general, as a single tap on the screen is not should generally be choosing `LOW_PENALTY`. It is inherently difficult to know if a
verifiable, a small value should be supplied - on the order of `0.1`. Pass `0` tap is truly false or not, so a single mis-tap should apply only a small penalty.
if you don't want to penalize belief at all. Pass a higher value If the owner is further along in a UX flow, and is still mis-tapping, it may make more
the earlier in the UX flow your interaction occurs. Once an owner is farther sense to increase the penalty as mis-taps should be less likely to occur after
along in a UX flow (multiple taps or swipes), its safer to assume that a single several successful gestures.
accidental tap should cause less of a penalty.
### Double Tap ### Double Tap

View File

@@ -16,6 +16,7 @@
package com.android.systemui.plugins; package com.android.systemui.plugins;
import android.annotation.IntDef;
import android.net.Uri; import android.net.Uri;
import android.view.MotionEvent; import android.view.MotionEvent;
@@ -24,6 +25,8 @@ import com.android.systemui.util.sensors.ThresholdSensor;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** /**
* Interface that decides whether a touch on the phone was accidental. i.e. Pocket Dialing. * Interface that decides whether a touch on the phone was accidental. i.e. Pocket Dialing.
@@ -34,6 +37,20 @@ import java.io.PrintWriter;
public interface FalsingManager { public interface FalsingManager {
int VERSION = 6; int VERSION = 6;
int NO_PENALTY = 0;
int LOW_PENALTY = 1;
int MODERATE_PENALTY = 2;
int HIGH_PENALTY = 3;
@IntDef({
NO_PENALTY,
LOW_PENALTY,
MODERATE_PENALTY,
HIGH_PENALTY
})
@Retention(RetentionPolicy.SOURCE)
public @interface Penalty {}
void onSuccessfulUnlock(); void onSuccessfulUnlock();
boolean isUnlockingDisabled(); boolean isUnlockingDisabled();
@@ -41,23 +58,31 @@ public interface FalsingManager {
/** Returns true if the gesture should be rejected. */ /** Returns true if the gesture should be rejected. */
boolean isFalseTouch(int interactionType); boolean isFalseTouch(int interactionType);
/** /**
* Returns true if the FalsingManager thinks the last gesure was not a valid tap. * Does basic checking to see if gesture looks like a tap.
* *
* The first parameter, robustCheck, distinctly changes behavior. When set to false, * Only does the most basic of checks. No penalty is applied if this method returns false.
* this method simply looks at the last gesture and returns whether it is a tap or not, (as *
* opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis * For more robust analysis, use {@link #isFalseTap(int)}.
* is performed that can include historical interactions and other contextual cues to see */
boolean isSimpleTap();
/**
* Returns true if the FalsingManager thinks the last gesture was not a valid tap.
*
* This method runs a more thorough analysis than the similar {@link #isSimpleTap()},
* that can include historical interactions and other contextual cues to see
* if the tap looks accidental. * if the tap looks accidental.
* *
* Set robustCheck to true if you want to validate a tap for launching an action, like opening * Use this method to validate a tap for launching an action, like opening
* a notification. Set to false if you simply want to know if the last gesture looked like a * a notification.
* tap.
* *
* The second parameter, falsePenalty, indicates how much this should affect future gesture * The only parameter, penalty, indicates how much this should affect future gesture
* classifications if this tap looks like a false. * classifications if this tap looks like a false. As single taps are hard to confirm as false
* or otherwise, a low penalty value is encouraged unless context indicates otherwise.
*/ */
boolean isFalseTap(boolean robustCheck, double falsePenalty); boolean isFalseTap(@Penalty int penalty);
/** /**
* Returns true if the last two gestures do not look like a double tap. * Returns true if the last two gestures do not look like a double tap.

View File

@@ -218,18 +218,43 @@ public class BrightLineFalsingManager implements FalsingManager {
} }
@Override @Override
public boolean isFalseTap(boolean robustCheck, double falsePenalty) { public boolean isSimpleTap() {
FalsingClassifier.Result result = mSingleTapClassifier.isTap(
mDataProvider.getRecentMotionEvents(), 0);
mPriorResults = Collections.singleton(result);
return !result.isFalse();
}
@Override
public boolean isFalseTap(@Penalty int penalty) {
if (skipFalsing()) { if (skipFalsing()) {
return false; return false;
} }
double falsePenalty = 0;
switch(penalty) {
case NO_PENALTY:
falsePenalty = 0;
break;
case LOW_PENALTY:
falsePenalty = 0.1;
break;
case MODERATE_PENALTY:
falsePenalty = 0.3;
break;
case HIGH_PENALTY:
falsePenalty = 0.6;
break;
}
FalsingClassifier.Result singleTapResult = FalsingClassifier.Result singleTapResult =
mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents().isEmpty() mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents().isEmpty()
? mDataProvider.getPriorMotionEvents() ? mDataProvider.getPriorMotionEvents()
: mDataProvider.getRecentMotionEvents()); : mDataProvider.getRecentMotionEvents(), falsePenalty);
mPriorResults = Collections.singleton(singleTapResult); mPriorResults = Collections.singleton(singleTapResult);
if (!singleTapResult.isFalse() && robustCheck) { if (!singleTapResult.isFalse()) {
if (mDataProvider.isJustUnlockedWithFace()) { if (mDataProvider.isJustUnlockedWithFace()) {
// Immediately pass if a face is detected. // Immediately pass if a face is detected.
mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1)); mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));

View File

@@ -66,13 +66,13 @@ public class DoubleTapClassifier extends FalsingClassifier {
public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents, public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents,
StringBuilder reason) { StringBuilder reason) {
Result firstTap = mSingleTapClassifier.isTap(firstEvents); Result firstTap = mSingleTapClassifier.isTap(firstEvents, 0.5);
if (firstTap.isFalse()) { if (firstTap.isFalse()) {
reason.append("First gesture is not a tap. ").append(firstTap.getReason()); reason.append("First gesture is not a tap. ").append(firstTap.getReason());
return false; return false;
} }
Result secondTap = mSingleTapClassifier.isTap(secondEvents); Result secondTap = mSingleTapClassifier.isTap(secondEvents, 0.5);
if (secondTap.isFalse()) { if (secondTap.isFalse()) {
reason.append("Second gesture is not a tap. ").append(secondTap.getReason()); reason.append("Second gesture is not a tap. ").append(secondTap.getReason());
return false; return false;

View File

@@ -32,7 +32,7 @@ import java.util.List;
*/ */
public class FalsingManagerFake implements FalsingManager { public class FalsingManagerFake implements FalsingManager {
private boolean mIsFalseTouch; private boolean mIsFalseTouch;
private boolean mIsFalseTap; private boolean mIsSimpleTap;
private boolean mIsFalseDoubleTap; private boolean mIsFalseDoubleTap;
private boolean mIsUnlockingDisabled; private boolean mIsUnlockingDisabled;
private boolean mIsClassiferEnabled; private boolean mIsClassiferEnabled;
@@ -67,12 +67,12 @@ public class FalsingManagerFake implements FalsingManager {
return mIsFalseTouch; return mIsFalseTouch;
} }
public void setFalseRobustTap(boolean falseRobustTap) { public void setFalseTap(boolean falseRobustTap) {
mIsFalseRobustTap = falseRobustTap; mIsFalseRobustTap = falseRobustTap;
} }
public void setFalseTap(boolean falseTap) { public void setSimpleTap(boolean isSimpleTape) {
mIsFalseTap = falseTap; mIsSimpleTap = isSimpleTape;
} }
public void setFalseDoubleTap(boolean falseDoubleTap) { public void setFalseDoubleTap(boolean falseDoubleTap) {
@@ -80,8 +80,13 @@ public class FalsingManagerFake implements FalsingManager {
} }
@Override @Override
public boolean isFalseTap(boolean robustCheck, double falsePenalty) { public boolean isSimpleTap() {
return robustCheck ? mIsFalseRobustTap : mIsFalseTap; return mIsSimpleTap;
}
@Override
public boolean isFalseTap(@Penalty int penalty) {
return mIsFalseRobustTap;
} }
@Override @Override

View File

@@ -131,8 +131,13 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable {
} }
@Override @Override
public boolean isFalseTap(boolean robustCheck, double falsePenalty) { public boolean isSimpleTap() {
return mInternalFalsingManager.isFalseTap(robustCheck, falsePenalty); return mInternalFalsingManager.isSimpleTap();
}
@Override
public boolean isFalseTap(@Penalty int penalty) {
return mInternalFalsingManager.isFalseTap(penalty);
} }
@Override @Override

View File

@@ -42,11 +42,11 @@ public class SingleTapClassifier extends FalsingClassifier {
Result calculateFalsingResult( Result calculateFalsingResult(
@Classifier.InteractionType int interactionType, @Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) { double historyBelief, double historyConfidence) {
return isTap(getRecentMotionEvents()); return isTap(getRecentMotionEvents(), 0.5);
} }
/** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */ /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
public Result isTap(List<MotionEvent> motionEvents) { public Result isTap(List<MotionEvent> motionEvents, double falsePenalty) {
if (motionEvents.isEmpty()) { if (motionEvents.isEmpty()) {
return falsed(0, "no motion events"); return falsed(0, "no motion events");
} }
@@ -60,13 +60,13 @@ public class SingleTapClassifier extends FalsingClassifier {
+ Math.abs(event.getX() - downX) + Math.abs(event.getX() - downX)
+ "vs " + "vs "
+ mTouchSlop; + mTouchSlop;
return falsed(0.5, reason); return falsed(falsePenalty, reason);
} else if (Math.abs(event.getY() - downY) >= mTouchSlop) { } else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
reason = "dY too big for a tap: " reason = "dY too big for a tap: "
+ Math.abs(event.getY() - downY) + Math.abs(event.getY() - downY)
+ " vs " + " vs "
+ mTouchSlop; + mTouchSlop;
return falsed(0.5, reason); return falsed(falsePenalty, reason);
} }
} }
return Result.passed(0); return Result.passed(0);

View File

@@ -34,6 +34,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.globalactions.GlobalActionsDialogLite; import com.android.systemui.globalactions.GlobalActionsDialogLite;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.MultiUserSwitch; import com.android.systemui.statusbar.phone.MultiUserSwitch;
@@ -62,6 +63,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
private final QuickQSPanelController mQuickQSPanelController; private final QuickQSPanelController mQuickQSPanelController;
private final TunerService mTunerService; private final TunerService mTunerService;
private final MetricsLogger mMetricsLogger; private final MetricsLogger mMetricsLogger;
private final FalsingManager mFalsingManager;
private final SettingsButton mSettingsButton; private final SettingsButton mSettingsButton;
private final TextView mBuildText; private final TextView mBuildText;
private final View mEdit; private final View mEdit;
@@ -83,8 +85,9 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() { private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
// Don't do anything until view are unhidden // Don't do anything until views are unhidden. Don't do anything if the tap looks
if (!mExpanded) { // suspicious.
if (!mExpanded || mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return; return;
} }
@@ -132,7 +135,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer, QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer,
QuickQSPanelController quickQSPanelController, QuickQSPanelController quickQSPanelController,
TunerService tunerService, MetricsLogger metricsLogger, TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
@Named(PM_LITE_ENABLED) boolean showPMLiteButton, @Named(PM_LITE_ENABLED) boolean showPMLiteButton,
GlobalActionsDialogLite globalActionsDialog) { GlobalActionsDialogLite globalActionsDialog) {
super(view); super(view);
@@ -146,6 +149,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
mQuickQSPanelController = quickQSPanelController; mQuickQSPanelController = quickQSPanelController;
mTunerService = tunerService; mTunerService = tunerService;
mMetricsLogger = metricsLogger; mMetricsLogger = metricsLogger;
mFalsingManager = falsingManager;
mSettingsButton = mView.findViewById(R.id.settings_button); mSettingsButton = mView.findViewById(R.id.settings_button);
mBuildText = mView.findViewById(R.id.build); mBuildText = mView.findViewById(R.id.build);
@@ -184,9 +188,13 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
return false; return false;
}); });
mEdit.setOnClickListener(view -> mEdit.setOnClickListener(view -> {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
mQsPanelController.showEdit(view))); return;
}
mActivityStarter.postQSRunnableDismissingKeyguard(() ->
mQsPanelController.showEdit(view));
});
mMultiUserSwitch.setQSDetailDisplayer(mQsDetailDisplayer); mMultiUserSwitch.setQSDetailDisplayer(mQsDetailDisplayer);
mQsPanelController.setFooterPageIndicator(mPageIndicator); mQsPanelController.setFooterPageIndicator(mPageIndicator);

View File

@@ -279,7 +279,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(), mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
getInstanceId()); getInstanceId());
mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state); mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
if (!mFalsingManager.isFalseTap(true, 0.1)) { if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
mHandler.sendEmptyMessage(H.CLICK); mHandler.sendEmptyMessage(H.CLICK);
} }
} }

View File

@@ -125,7 +125,7 @@ public class ActivatableNotificationViewController
result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight()); result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight());
} else if (ev.getAction() == MotionEvent.ACTION_UP) { } else if (ev.getAction() == MotionEvent.ACTION_UP) {
// If this is a false tap, capture the even so it doesn't result in a click. // If this is a false tap, capture the even so it doesn't result in a click.
return mFalsingManager.isFalseTap(true, 0.1); return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
} }
return result; return result;
} }

View File

@@ -881,7 +881,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// Other parts of the system may intercept and handle all the falsing. // Other parts of the system may intercept and handle all the falsing.
// Otherwise, if we see motion and follow-on events, try to classify them as a tap. // Otherwise, if we see motion and follow-on events, try to classify them as a tap.
if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) { if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
mFalsingManager.isFalseTap(true, 0.3); mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY);
} }
return super.onInterceptTouchEvent(ev); return super.onInterceptTouchEvent(ev);
} }

View File

@@ -64,7 +64,7 @@ public class NotificationTapHelper {
mTrackTouch = event.getY() <= maxTouchableHeight; mTrackTouch = event.getY() <= maxTouchableHeight;
break; break;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
if (mTrackTouch && mFalsingManager.isFalseTap(false, 0)) { if (mTrackTouch && !mFalsingManager.isSimpleTap()) {
makeInactive(); makeInactive();
mTrackTouch = false; mTrackTouch = false;
} }
@@ -78,10 +78,10 @@ public class NotificationTapHelper {
// 1) See if we have confidence that we can activate after a single tap. // 1) See if we have confidence that we can activate after a single tap.
// 2) Else, see if it looks like a tap at all and check for a double-tap. // 2) Else, see if it looks like a tap at all and check for a double-tap.
if (!mFalsingManager.isFalseTap(true, 0)) { if (!mFalsingManager.isFalseTap(FalsingManager.NO_PENALTY)) {
makeInactive(); makeInactive();
return mDoubleTapListener.onDoubleTap(); return mDoubleTapListener.onDoubleTap();
} else if (!mFalsingManager.isFalseTap(false, 0)) { } else if (mFalsingManager.isSimpleTap()) {
if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) { if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
return true; return true;
} }

View File

@@ -681,6 +681,9 @@ public class StatusBar extends SystemUI implements DemoMode,
new FalsingManager.FalsingBeliefListener() { new FalsingManager.FalsingBeliefListener() {
@Override @Override
public void onFalse() { public void onFalse() {
// Hides quick settings.
mNotificationPanelViewController.resetViews(true);
// Hides bouncer and quick-quick settings.
mStatusBarKeyguardViewManager.reset(true); mStatusBarKeyguardViewManager.reset(true);
} }
}; };

View File

@@ -16,6 +16,9 @@
package com.android.systemui.classifier; package com.android.systemui.classifier;
import static com.android.systemui.plugins.FalsingManager.HIGH_PENALTY;
import static com.android.systemui.plugins.FalsingManager.NO_PENALTY;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@@ -55,8 +58,6 @@ import java.util.Set;
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
public class BrightLineClassifierTest extends SysuiTestCase { public class BrightLineClassifierTest extends SysuiTestCase {
private static final long DOUBLE_TAP_TIMEOUT_MS = 1000;
private BrightLineFalsingManager mBrightLineFalsingManager; private BrightLineFalsingManager mBrightLineFalsingManager;
@Mock @Mock
private FalsingDataProvider mFalsingDataProvider; private FalsingDataProvider mFalsingDataProvider;
@@ -91,7 +92,7 @@ public class BrightLineClassifierTest extends SysuiTestCase {
.thenReturn(mPassedResult); .thenReturn(mPassedResult);
when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble())) when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mPassedResult); .thenReturn(mPassedResult);
when(mSingleTapClassfier.isTap(any(List.class))).thenReturn(mPassedResult); when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mPassedResult);
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mPassedResult); .thenReturn(mPassedResult);
mClassifiers.add(mClassifierA); mClassifiers.add(mClassifierA);
@@ -170,13 +171,13 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test @Test
public void testIsFalseTap_BasicCheck() { public void testIsFalseTap_BasicCheck() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult); when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isTrue(); assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse();
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult); when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse(); assertThat(mBrightLineFalsingManager.isSimpleTap()).isTrue();
} }
@Test @Test
@@ -185,26 +186,26 @@ public class BrightLineClassifierTest extends SysuiTestCase {
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>()); when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>());
when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList); when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList);
mBrightLineFalsingManager.isFalseTap(false, 0); mBrightLineFalsingManager.isFalseTap(0);
verify(mSingleTapClassfier).isTap(mMotionEventList); verify(mSingleTapClassfier).isTap(mMotionEventList, 0);
} }
@Test @Test
public void testIsFalseTap_RobustCheck_NoFaceAuth() { public void testIsFalseTap_RobustCheck_NoFaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult); when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mFalsedResult); .thenReturn(mFalsedResult);
when(mHistoryTracker.falseBelief()).thenReturn(1.0); when(mHistoryTracker.falseBelief()).thenReturn(1.0);
mFalsingDataProvider.setJustUnlockedWithFace(false); mFalsingDataProvider.setJustUnlockedWithFace(false);
assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isTrue(); assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isTrue();
} }
@Test @Test
public void testIsFalseTap_RobustCheck_FaceAuth() { public void testIsFalseTap_RobustCheck_FaceAuth() {
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult); when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isFalse(); assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isFalse();
} }
@Test @Test
@@ -230,7 +231,7 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test @Test
public void testHistory_singleTap() { public void testHistory_singleTap() {
// When trying to classify single taps, we don't immediately add results to history. // When trying to classify single taps, we don't immediately add results to history.
mBrightLineFalsingManager.isFalseTap(false, 0); mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(1000); mGestureFinalizedListener.onGestureFinalized(1000);
verify(mHistoryTracker).addResults(anyCollection(), eq(1000L)); verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
} }
@@ -238,9 +239,9 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test @Test
public void testHistory_multipleSingleTaps() { public void testHistory_multipleSingleTaps() {
// When trying to classify single taps, we don't immediately add results to history. // When trying to classify single taps, we don't immediately add results to history.
mBrightLineFalsingManager.isFalseTap(false, 0); mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(1000); mGestureFinalizedListener.onGestureFinalized(1000);
mBrightLineFalsingManager.isFalseTap(false, 0); mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(2000); mGestureFinalizedListener.onGestureFinalized(2000);
verify(mHistoryTracker).addResults(anyCollection(), eq(1000L)); verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
verify(mHistoryTracker).addResults(anyCollection(), eq(2000L)); verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
@@ -249,10 +250,10 @@ public class BrightLineClassifierTest extends SysuiTestCase {
@Test @Test
public void testHistory_doubleTap() { public void testHistory_doubleTap() {
// When trying to classify single taps, we don't immediately add results to history. // When trying to classify single taps, we don't immediately add results to history.
mBrightLineFalsingManager.isFalseTap(false, 0); mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(1000); mGestureFinalizedListener.onGestureFinalized(1000);
// Before checking for double tap, we may check for single-tap on the second gesture. // Before checking for double tap, we may check for single-tap on the second gesture.
mBrightLineFalsingManager.isFalseTap(false, 0); mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mBrightLineFalsingManager.isFalseDoubleTap(); mBrightLineFalsingManager.isFalseDoubleTap();
mGestureFinalizedListener.onGestureFinalized(2000); mGestureFinalizedListener.onGestureFinalized(2000);
@@ -270,8 +271,8 @@ public class BrightLineClassifierTest extends SysuiTestCase {
.thenReturn(mFalsedResult); .thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult); when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse(); assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse();
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble())) when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mFalsedResult); .thenReturn(mFalsedResult);

View File

@@ -18,6 +18,7 @@ package com.android.systemui.classifier;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -78,7 +79,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test @Test
public void testSingleTap() { public void testSingleTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult); when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1);
@@ -88,7 +89,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test @Test
public void testDoubleTap() { public void testDoubleTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult); when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mPassedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -104,7 +105,8 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test @Test
public void testBadFirstTap() { public void testBadFirstTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult, mFalsedResult); when(mSingleTapClassifier.isTap(anyList(), anyDouble()))
.thenReturn(mPassedResult, mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -120,7 +122,8 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test @Test
public void testBadSecondTap() { public void testBadSecondTap() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult, mPassedResult); when(mSingleTapClassifier.isTap(anyList(), anyDouble()))
.thenReturn(mFalsedResult, mPassedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -136,7 +139,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test @Test
public void testBadTouchSlop() { public void testBadTouchSlop() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult); when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -152,7 +155,7 @@ public class DoubleTapClassifierTest extends ClassifierTest {
@Test @Test
public void testBadTouchSlow() { public void testBadTouchSlow() {
when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult); when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);

View File

@@ -142,12 +142,12 @@ public class SingleTapClassifierTest extends ClassifierTest {
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isFalse(); assertThat(mClassifier.isTap(mMotionEvents, 0.5).isFalse()).isFalse();
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1); addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1); addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1);
assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isTrue(); assertThat(mClassifier.isTap(mMotionEvents, 0.5).isFalse()).isTrue();
} }

View File

@@ -38,6 +38,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.Dependency; import com.android.systemui.Dependency;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.globalactions.GlobalActionsDialogLite; import com.android.systemui.globalactions.GlobalActionsDialogLite;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
@@ -121,7 +122,7 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
mController = new QSFooterViewController(mView, mUserManager, mUserInfoController, mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController, mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService, new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService,
mMetricsLogger, false, mGlobalActionsDialog); mMetricsLogger, new FalsingManagerFake(), false, mGlobalActionsDialog);
mController.init(); mController.init();
} }

View File

@@ -150,12 +150,12 @@ public class QSTileImplTest extends SysuiTestCase {
@Test @Test
public void testClick_falsing() { public void testClick_falsing() {
mFalsingManager.setFalseRobustTap(true); mFalsingManager.setFalseTap(true);
mTile.click(); mTile.click();
mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages();
assertThat(mTile.mClicked).isFalse(); assertThat(mTile.mClicked).isFalse();
mFalsingManager.setFalseRobustTap(false); mFalsingManager.setFalseTap(false);
mTile.click(); mTile.click();
mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages();
assertThat(mTile.mClicked).isTrue(); assertThat(mTile.mClicked).isTrue();

View File

@@ -63,7 +63,8 @@ public class NotificationTapHelperTest extends SysuiTestCase {
when(mResources.getDimension(R.dimen.double_tap_slop)) when(mResources.getDimension(R.dimen.double_tap_slop))
.thenReturn((float) ViewConfiguration.get(mContext).getScaledTouchSlop() - 1); .thenReturn((float) ViewConfiguration.get(mContext).getScaledTouchSlop() - 1);
mFalsingManager.setFalseRobustTap(true); // Test double tapping most of the time. mFalsingManager.setSimpleTap(true);
mFalsingManager.setFalseTap(true); // Test double tapping most of the time.
mNotificationTapHelper = new NotificationTapHelper.Factory(mFalsingManager, mFakeExecutor) mNotificationTapHelper = new NotificationTapHelper.Factory(mFalsingManager, mFakeExecutor)
.create(mActivationListener, mDoubleTapListener, mSlideBackListener); .create(mActivationListener, mDoubleTapListener, mSlideBackListener);
@@ -158,7 +159,7 @@ public class NotificationTapHelperTest extends SysuiTestCase {
1, 1,
0); 0);
mFalsingManager.setFalseTap(true); mFalsingManager.setSimpleTap(false);
mNotificationTapHelper.onTouchEvent(evDownA); mNotificationTapHelper.onTouchEvent(evDownA);
mNotificationTapHelper.onTouchEvent(evUpA); mNotificationTapHelper.onTouchEvent(evUpA);
verify(mActivationListener, never()).onActiveChanged(true); verify(mActivationListener, never()).onActiveChanged(true);