Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4124a204 authored by Dave Mankoff's avatar Dave Mankoff
Browse files

Add more logging for falsing.

Makes the FalsingManager slightly less stateful (passing the
interaction type directly to the classifiers as needed). It also
includes more logging than we had before, listing all the failed
classifiers for a gesture, along with the HistoryTracker's
belief and confidence.

Logs are now delayed one gesture. Instead of immediately logging when
a falsing call is made, it waits until the gesture is marked as
complete. This confers two advantages:

1) If multiple calls to #isFalseGesture (and similar) are made, we
   don't log multiple times. We only log the result once.

2) It allows us to log the effects on the HistoryTracker, as that only
   updates itself when the gesture is marked as completed.

Bug: 172655679
Test: atest SystemUITests && manual
Change-Id: I653ac2abb03a91ff000b46075e58137b04226023
parent fa304e1f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@ public class KeyguardPatternViewController
                // Treat single-sized patterns as erroneous taps.
                if (pattern.size() == 1) {
                    mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.falsed(
                            0.7, "empty pattern input"));
                            0.7, getClass().getSimpleName(), "empty pattern input"));
                }
                mLockPatternView.enableInput();
                onPatternChecked(userId, false, 0, false /* not valid - too short */);
+45 −50
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
import java.util.Set;
import java.util.StringJoiner;
@@ -94,9 +93,17 @@ public class BrightLineFalsingManager implements FalsingManager {
        }
    };

    private final BeliefListener mBeliefListener = belief -> {
    private final BeliefListener mBeliefListener = new BeliefListener() {
        @Override
        public void onBeliefChanged(double belief) {
            logInfo(String.format(
                    "{belief=%s confidence=%s}",
                    mHistoryTracker.falseBelief(),
                    mHistoryTracker.falseConfidence()));
            if (belief > FALSE_BELIEF_THRESHOLD) {
                mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse);
                logInfo("Triggering False Event (Threshold: " + FALSE_BELIEF_THRESHOLD + ")");
            }
        }
    };

@@ -105,13 +112,23 @@ public class BrightLineFalsingManager implements FalsingManager {
                @Override
                public void onGestureComplete(long completionTimeMs) {
                    if (mPriorResults != null) {
                        mPriorResults.forEach(result -> {
                            if (result.isFalse()) {
                                String reason = result.getReason();
                                if (reason != null) {
                                    logInfo(reason);
                                }
                            }
                        });

                        mHistoryTracker.addResults(mPriorResults, completionTimeMs);
                        mPriorResults = null;
                    } else {
                        // Gestures that were not classified get treated as a false.
                        mHistoryTracker.addResults(
                                Collections.singleton(
                                        FalsingClassifier.Result.falsed(.8, "unclassified")),
                                        FalsingClassifier.Result.falsed(
                                                .8, getClass().getSimpleName(), "unclassified")),
                                completionTimeMs);
                    }
                }
@@ -154,30 +171,13 @@ public class BrightLineFalsingManager implements FalsingManager {

        boolean result;

        mDataProvider.setInteractionType(interactionType);

        if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
            Stream<FalsingClassifier.Result> results =
                    mClassifiers.stream().map(falsingClassifier -> {
                        FalsingClassifier.Result classifierResult =
                    mClassifiers.stream().map(falsingClassifier ->
                            falsingClassifier.classifyGesture(
                                    interactionType,
                                    mHistoryTracker.falseBelief(),
                                        mHistoryTracker.falseConfidence());
                        if (classifierResult.isFalse()) {
                            logInfo(String.format(
                                    (Locale) null,
                                    "{classifier=%s, interactionType=%d}",
                                    falsingClassifier.getClass().getName(),
                                    mDataProvider.getInteractionType()));
                            String reason = classifierResult.getReason();
                            if (reason != null) {
                                logInfo(reason);
                            }
                        } else {
                            logDebug(falsingClassifier.getClass().getName() + ": false");
                        }
                        return classifierResult;
                    });
                                    mHistoryTracker.falseConfidence()));
            mPriorResults = new ArrayList<>();
            final boolean[] localResult = {false};
            results.forEach(classifierResult -> {
@@ -190,13 +190,13 @@ public class BrightLineFalsingManager implements FalsingManager {
            mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
        }

        logDebug("Is false touch? " + result);
        logDebug("False Gesture: " + result);

        if (Build.IS_ENG || Build.IS_USERDEBUG) {
            // Copy motion events, as the passed in list gets emptied out elsewhere in the code.
            RECENT_SWIPES.add(new DebugSwipeRecord(
                    result,
                    mDataProvider.getInteractionType(),
                    interactionType,
                    mDataProvider.getRecentMotionEvents().stream().map(
                            motionEvent -> new XYDt(
                                    (int) motionEvent.getX(),
@@ -220,37 +220,36 @@ public class BrightLineFalsingManager implements FalsingManager {
        FalsingClassifier.Result singleTapResult =
                mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
        mPriorResults = Collections.singleton(singleTapResult);
        if (singleTapResult.isFalse()) {
            logInfo(String.format(
                    (Locale) null, "{classifier=%s}", mSingleTapClassifier.getClass().getName()));
            String reason = singleTapResult.getReason();
            if (reason != null) {
                logInfo(reason);
            }
            return true;
        }

        if (robustCheck) {
        if (!singleTapResult.isFalse() && robustCheck) {
            if (mDataProvider.isJustUnlockedWithFace()) {
                // Immediately pass if a face is detected.
                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
                logDebug("False Single Tap: false (face detected)");
                return false;
            } else if (!isFalseDoubleTap()) {
                // We must check double tapping before other heuristics. This is because
                // the double tap will fail if there's only been one tap. We don't want that
                // failure to be recorded in mPriorResults.
                logDebug("False Single Tap: false (double tapped)");
                return false;
            } else if (mHistoryTracker.falseBelief() > TAP_CONFIDENCE_THRESHOLD) {
                mPriorResults = Collections.singleton(
                        FalsingClassifier.Result.falsed(0, "bad history"));
                        FalsingClassifier.Result.falsed(
                                0, getClass().getSimpleName(), "bad history"));
                logDebug("False Single Tap: true (bad history)");
                return true;
            } else {
                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1));
                logDebug("False Single Tap: false (default)");
                return false;
            }

        } else {
            logDebug("False Single Tap: " + singleTapResult.isFalse() + " (simple)");
            return singleTapResult.isFalse();
        }

        return false;
    }

    @Override
@@ -259,16 +258,12 @@ public class BrightLineFalsingManager implements FalsingManager {
            return false;
        }

        FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture();
        FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture(
                Classifier.GENERIC,
                mHistoryTracker.falseBelief(),
                mHistoryTracker.falseConfidence());
        mPriorResults = Collections.singleton(result);
        if (result.isFalse()) {
            logInfo(String.format(
                    (Locale) null, "{classifier=%s}", mDoubleTapClassifier.getClass().getName()));
            String reason = result.getReason();
            if (reason != null) {
                logInfo(reason);
            }
        }
        logDebug("False Double Tap: " + result.isFalse());
        return result.isFalse();
    }

+5 −4
Original line number Diff line number Diff line
@@ -62,15 +62,16 @@ class DiagonalClassifier extends FalsingClassifier {
                VERTICAL_ANGLE_RANGE);
    }

    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
    Result calculateFalsingResult(
            @Classifier.InteractionType int interactionType,
            double historyBelief, double historyConfidence) {
        float angle = getAngle();

        if (angle == Float.MAX_VALUE) {  // Unknown angle
            return Result.passed(0);
        }

        if (getInteractionType() == LEFT_AFFORDANCE
                || getInteractionType() == RIGHT_AFFORDANCE) {
        if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE) {
            return Result.passed(0);
        }

@@ -86,7 +87,7 @@ class DiagonalClassifier extends FalsingClassifier {
                || angleBetween(angle, minAngle - NINETY_DEG, maxAngle - NINETY_DEG)
                || angleBetween(angle, minAngle + ONE_HUNDRED_EIGHTY_DEG,
                maxAngle + ONE_HUNDRED_EIGHTY_DEG);
        return falsed ? Result.falsed(0.5f, getReason()) : Result.passed(0.5);
        return falsed ? falsed(0.5f, getReason()) : Result.passed(0.5);
    }

    private String getReason() {
+5 −6
Original line number Diff line number Diff line
@@ -136,8 +136,6 @@ class DistanceClassifier extends FalsingClassifier {
        float dX = getLastMotionEvent().getX() - getFirstMotionEvent().getX();
        float dY = getLastMotionEvent().getY() - getFirstMotionEvent().getY();

        logInfo("dX: " + dX + " dY: " + dY + " xV: " + vX + " yV: " + vY);

        return new DistanceVectors(dX, dY, vX, vY);
    }

@@ -147,9 +145,10 @@ class DistanceClassifier extends FalsingClassifier {
    }

    @Override
    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
        return !getPassedFlingThreshold()
                ? Result.falsed(0.5, getReason()) : Result.passed(0.5);
    Result calculateFalsingResult(
            @Classifier.InteractionType int interactionType,
            double historyBelief, double historyConfidence) {
        return !getPassedFlingThreshold() ? falsed(0.5, getReason()) : Result.passed(0.5);
    }

    String getReason() {
@@ -172,7 +171,7 @@ class DistanceClassifier extends FalsingClassifier {
    Result isLongSwipe() {
        boolean longSwipe = getPassedDistanceThreshold();
        logDebug("Is longSwipe? " + longSwipe);
        return longSwipe ? Result.passed(0.5) : Result.falsed(0.5, getReason());
        return longSwipe ? Result.passed(0.5) : falsed(0.5, getReason());
    }

    private boolean getPassedDistanceThreshold() {
+5 −3
Original line number Diff line number Diff line
@@ -46,18 +46,20 @@ public class DoubleTapClassifier extends FalsingClassifier {
    }

    @Override
    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
    Result calculateFalsingResult(
            @Classifier.InteractionType int interactionType,
            double historyBelief, double historyConfidence) {
        List<MotionEvent> secondTapEvents = getRecentMotionEvents();
        List<MotionEvent> firstTapEvents = getPriorMotionEvents();

        StringBuilder reason = new StringBuilder();

        if (firstTapEvents == null) {
            return Result.falsed(0, "Only one gesture recorded");
            return falsed(0, "Only one gesture recorded");
        }

        return !isDoubleTap(firstTapEvents, secondTapEvents, reason)
                ? Result.falsed(0.5, reason.toString()) : Result.passed(0.5);
                ? falsed(0.5, reason.toString()) : Result.passed(0.5);
    }

    /** Returns true if the two supplied lists of {@link MotionEvent}s look like a double-tap. */
Loading