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 Original line Diff line number Diff line
@@ -109,7 +109,7 @@ public class KeyguardPatternViewController
                // Treat single-sized patterns as erroneous taps.
                // Treat single-sized patterns as erroneous taps.
                if (pattern.size() == 1) {
                if (pattern.size() == 1) {
                    mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.falsed(
                    mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.falsed(
                            0.7, "empty pattern input"));
                            0.7, getClass().getSimpleName(), "empty pattern input"));
                }
                }
                mLockPatternView.enableInput();
                mLockPatternView.enableInput();
                onPatternChecked(userId, false, 0, false /* not valid - too short */);
                onPatternChecked(userId, false, 0, false /* not valid - too short */);
+45 −50
Original line number Original line Diff line number Diff line
@@ -42,7 +42,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collection;
import java.util.Collections;
import java.util.Collections;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
import java.util.Queue;
import java.util.Set;
import java.util.Set;
import java.util.StringJoiner;
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) {
            if (belief > FALSE_BELIEF_THRESHOLD) {
                mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse);
                mFalsingBeliefListeners.forEach(FalsingBeliefListener::onFalse);
                logInfo("Triggering False Event (Threshold: " + FALSE_BELIEF_THRESHOLD + ")");
            }
        }
        }
    };
    };


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

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


        boolean result;
        boolean result;


        mDataProvider.setInteractionType(interactionType);

        if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
        if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
            Stream<FalsingClassifier.Result> results =
            Stream<FalsingClassifier.Result> results =
                    mClassifiers.stream().map(falsingClassifier -> {
                    mClassifiers.stream().map(falsingClassifier ->
                        FalsingClassifier.Result classifierResult =
                            falsingClassifier.classifyGesture(
                            falsingClassifier.classifyGesture(
                                    interactionType,
                                    mHistoryTracker.falseBelief(),
                                    mHistoryTracker.falseBelief(),
                                        mHistoryTracker.falseConfidence());
                                    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;
                    });
            mPriorResults = new ArrayList<>();
            mPriorResults = new ArrayList<>();
            final boolean[] localResult = {false};
            final boolean[] localResult = {false};
            results.forEach(classifierResult -> {
            results.forEach(classifierResult -> {
@@ -190,13 +190,13 @@ public class BrightLineFalsingManager implements FalsingManager {
            mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
            mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
        }
        }


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


        if (Build.IS_ENG || Build.IS_USERDEBUG) {
        if (Build.IS_ENG || Build.IS_USERDEBUG) {
            // Copy motion events, as the passed in list gets emptied out elsewhere in the code.
            // Copy motion events, as the passed in list gets emptied out elsewhere in the code.
            RECENT_SWIPES.add(new DebugSwipeRecord(
            RECENT_SWIPES.add(new DebugSwipeRecord(
                    result,
                    result,
                    mDataProvider.getInteractionType(),
                    interactionType,
                    mDataProvider.getRecentMotionEvents().stream().map(
                    mDataProvider.getRecentMotionEvents().stream().map(
                            motionEvent -> new XYDt(
                            motionEvent -> new XYDt(
                                    (int) motionEvent.getX(),
                                    (int) motionEvent.getX(),
@@ -220,37 +220,36 @@ public class BrightLineFalsingManager implements FalsingManager {
        FalsingClassifier.Result singleTapResult =
        FalsingClassifier.Result singleTapResult =
                mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
                mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
        mPriorResults = Collections.singleton(singleTapResult);
        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()) {
            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));
                logDebug("False Single Tap: false (face detected)");
                return false;
                return false;
            } else if (!isFalseDoubleTap()) {
            } else if (!isFalseDoubleTap()) {
                // We must check double tapping before other heuristics. This is because
                // 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
                // the double tap will fail if there's only been one tap. We don't want that
                // failure to be recorded in mPriorResults.
                // failure to be recorded in mPriorResults.
                logDebug("False Single Tap: false (double tapped)");
                return false;
                return false;
            } else if (mHistoryTracker.falseBelief() > TAP_CONFIDENCE_THRESHOLD) {
            } else if (mHistoryTracker.falseBelief() > TAP_CONFIDENCE_THRESHOLD) {
                mPriorResults = Collections.singleton(
                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;
                return true;
            } else {
            } else {
                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1));
                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1));
                logDebug("False Single Tap: false (default)");
                return false;
                return false;
            }
            }

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


        return false;
    }
    }


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


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


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


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


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


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


@@ -86,7 +87,7 @@ class DiagonalClassifier extends FalsingClassifier {
                || angleBetween(angle, minAngle - NINETY_DEG, maxAngle - NINETY_DEG)
                || angleBetween(angle, minAngle - NINETY_DEG, maxAngle - NINETY_DEG)
                || angleBetween(angle, minAngle + ONE_HUNDRED_EIGHTY_DEG,
                || angleBetween(angle, minAngle + ONE_HUNDRED_EIGHTY_DEG,
                maxAngle + 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() {
    private String getReason() {
+5 −6
Original line number Original line Diff line number Diff line
@@ -136,8 +136,6 @@ class DistanceClassifier extends FalsingClassifier {
        float dX = getLastMotionEvent().getX() - getFirstMotionEvent().getX();
        float dX = getLastMotionEvent().getX() - getFirstMotionEvent().getX();
        float dY = getLastMotionEvent().getY() - getFirstMotionEvent().getY();
        float dY = getLastMotionEvent().getY() - getFirstMotionEvent().getY();


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

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


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


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


    String getReason() {
    String getReason() {
@@ -172,7 +171,7 @@ class DistanceClassifier extends FalsingClassifier {
    Result isLongSwipe() {
    Result isLongSwipe() {
        boolean longSwipe = getPassedDistanceThreshold();
        boolean longSwipe = getPassedDistanceThreshold();
        logDebug("Is longSwipe? " + longSwipe);
        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() {
    private boolean getPassedDistanceThreshold() {
+5 −3
Original line number Original line Diff line number Diff line
@@ -46,18 +46,20 @@ public class DoubleTapClassifier extends FalsingClassifier {
    }
    }


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


        StringBuilder reason = new StringBuilder();
        StringBuilder reason = new StringBuilder();


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


        return !isDoubleTap(firstTapEvents, secondTapEvents, reason)
        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. */
    /** Returns true if the two supplied lists of {@link MotionEvent}s look like a double-tap. */
Loading