Fix bug and add logging in FalsingManager

Fixes bug that lead FalsingManager to be incorrect
about the screen on state.

Makes it a fatal error to query whether a touch is
falsing outside of an active session.

Prevents touch collection and logging on the locked
shade which needs no anti-falsing.

Bug: 27227578
Change-Id: I8e330ad28636156a952e61b8fe6a3d201f82e624
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
index 86bea87..bad739fd 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
@@ -36,6 +36,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "ACC";
+    }
+
+    @Override
     public void onTouchEvent(MotionEvent event) {
         int action = event.getActionMasked();
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
index dba731a..526e5fa 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
@@ -54,6 +54,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "ANG";
+    }
+
+    @Override
     public void onTouchEvent(MotionEvent event) {
         int action = event.getActionMasked();
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 89d20de..cb761a9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -48,4 +48,6 @@
      */
     public void onSensorChanged(SensorEvent event) {
     }
+
+    public abstract String getTag();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
index 299d0e3..610e219 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
@@ -25,6 +25,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "DIR";
+    }
+
+    @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
         Point firstPoint = stroke.getPoints().get(0);
         Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
index 8924694..77fda20 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
@@ -25,6 +25,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "DUR";
+    }
+
+    @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
         return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
index 78bc0dd..de8a188 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
@@ -24,6 +24,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "END_LNGTH";
+    }
+
+    @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
         return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
index 652d969..9b6ddc8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
@@ -26,6 +26,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "END_RTIO";
+    }
+
+    @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
         float ratio;
         if (stroke.getTotalLength() == 0.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
new file mode 100644
index 0000000..1338d9d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 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.classifier;
+
+import android.app.ActivityThread;
+import android.app.Application;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Keeps track of interesting falsing data.
+ *
+ * By default the log only gets collected on userdebug builds. To turn it on on user:
+ *  adb shell setprop debug.falsing_log true
+ *
+ * The log gets dumped as part of the SystemUI services. To dump on demand:
+ *  adb shell dumpsys activity service com.android.systemui SystemBars | grep -A 999 FALSING | less
+ *
+ * To dump into logcat:
+ *  adb shell setprop debug.falsing_logcat true
+ *
+ * To adjust the log buffer size:
+ *  adb shell setprop debug.falsing_log_size 200
+ */
+public class FalsingLog {
+    public static final boolean ENABLED = SystemProperties.getBoolean("debug.falsing_log",
+            Build.IS_DEBUGGABLE);
+    private static final boolean LOGCAT = SystemProperties.getBoolean("debug.falsing_logcat",
+            false);
+
+    public static final boolean VERBOSE = false;
+
+    private static final int MAX_SIZE = SystemProperties.getInt("debug.falsing_log_size", 100);
+
+    private static final String TAG = "FalsingLog";
+
+    private final ArrayDeque<String> mLog = new ArrayDeque<>(MAX_SIZE);
+    private final SimpleDateFormat mFormat = new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
+
+    private static FalsingLog sInstance;
+
+    private FalsingLog() {
+    }
+
+    public static void v(String tag, String s) {
+        if (!VERBOSE) {
+            return;
+        }
+        if (LOGCAT) {
+            Log.v(TAG, tag + "\t" + s);
+        }
+        log("V", tag, s);
+    }
+
+    public static void i(String tag, String s) {
+        if (LOGCAT) {
+            Log.i(TAG, tag + "\t" + s);
+        }
+        log("I", tag, s);
+    }
+
+    public static void w(String tag, String s) {
+        if (LOGCAT) {
+            Log.w(TAG, tag + "\t" + s);
+        }
+        log("W", tag, s);
+    }
+
+    public static void e(String tag, String s) {
+        if (LOGCAT) {
+            Log.e(TAG, tag + "\t" + s);
+        }
+        log("E", tag, s);
+    }
+
+    public static synchronized void log(String level, String tag, String s) {
+        if (!ENABLED) {
+            return;
+        }
+        if (sInstance == null) {
+            sInstance = new FalsingLog();
+        }
+
+        if (sInstance.mLog.size() >= MAX_SIZE) {
+            sInstance.mLog.removeFirst();
+        }
+        String entry = new StringBuilder().append(sInstance.mFormat.format(new Date()))
+            .append(" ").append(level).append(" ")
+            .append(tag).append(" ").append(s).toString();
+        sInstance.mLog.add(entry);
+    }
+
+    public static synchronized void dump(PrintWriter pw) {
+        pw.println("FALSING LOG:");
+        if (!ENABLED) {
+            pw.println("Disabled, to enable: setprop debug.falsing_log 1");
+            pw.println();
+            return;
+        }
+        if (sInstance == null || sInstance.mLog.isEmpty()) {
+            pw.println("<empty>");
+            pw.println();
+            return;
+        }
+        for (String s : sInstance.mLog) {
+            pw.println(s);
+        }
+        pw.println();
+    }
+
+    public static synchronized void wtf(String tag, String s) {
+        if (!ENABLED) {
+            return;
+        }
+        e(tag, s);
+
+        Application application = ActivityThread.currentApplication();
+        String fileMessage = "";
+        if (Build.IS_DEBUGGABLE && application != null) {
+            File f = new File(application.getDataDir(), "falsing-"
+                    + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".txt");
+            PrintWriter pw = null;
+            try {
+                pw = new PrintWriter(f);
+                dump(pw);
+                pw.close();
+                fileMessage = "Log written to " + f.getAbsolutePath();
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to write falsing log", e);
+            } finally {
+                if (pw != null) {
+                    pw.close();
+                }
+            }
+        } else {
+            Log.e(TAG, "Unable to write log, build must be debuggable.");
+        }
+
+        Log.wtf(TAG, tag + " " + s + "; " + fileMessage);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index c09376b..937f7d3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -31,6 +31,8 @@
 import com.android.systemui.analytics.DataCollector;
 import com.android.systemui.statusbar.StatusBarState;
 
+import java.io.PrintWriter;
+
 /**
  * When the phone is locked, listens to touch, sensor and phone events and sends them to
  * DataCollector and HumanInteractionClassifier.
@@ -102,8 +104,14 @@
     }
 
     private boolean shouldSessionBeActive() {
-        return isEnabled() && mScreenOn &&
-                (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED);
+        if (FalsingLog.ENABLED && FalsingLog.VERBOSE)
+            FalsingLog.v("shouldBeActive", new StringBuilder()
+                    .append("enabled=").append(isEnabled() ? 1 : 0)
+                    .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
+                    .append(" mState=").append(StatusBarState.toShortString(mState))
+                    .toString()
+            );
+        return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD);
     }
 
     private boolean sessionEntrypoint() {
@@ -122,6 +130,9 @@
     }
 
     private void onSessionStart() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
+        }
         mBouncerOn = false;
         mSessionActive = true;
 
@@ -154,6 +165,16 @@
      * @return true if the classifier determined that this is not a human interacting with the phone
      */
     public boolean isFalseTouch() {
+        if (FalsingLog.ENABLED) {
+            if (!mSessionActive) {
+                FalsingLog.wtf("isFalseTouch", new StringBuilder()
+                        .append("Session is not active, yet there's a query for a false touch.")
+                        .append(" enabled=").append(isEnabled() ? 1 : 0)
+                        .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
+                        .append(" mState=").append(StatusBarState.toShortString(mState))
+                        .toString());
+            }
+        }
         return mHumanInteractionClassifier.isFalseTouch();
     }
 
@@ -173,6 +194,12 @@
     }
 
     public void setStatusBarState(int state) {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("setStatusBarState", new StringBuilder()
+                    .append("from=").append(StatusBarState.toShortString(mState))
+                    .append(" to=").append(StatusBarState.toShortString(state))
+                    .toString());
+        }
         mState = state;
         if (shouldSessionBeActive()) {
             sessionEntrypoint();
@@ -182,6 +209,11 @@
     }
 
     public void onScreenTurningOn() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onScreenTurningOn", new StringBuilder()
+                    .append("from=").append(mScreenOn ? 1 : 0)
+                    .toString());
+        }
         mScreenOn = true;
         if (sessionEntrypoint()) {
             mDataCollector.onScreenTurningOn();
@@ -189,6 +221,11 @@
     }
 
     public void onScreenOnFromTouch() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
+                    .append("from=").append(mScreenOn ? 1 : 0)
+                    .toString());
+        }
         mScreenOn = true;
         if (sessionEntrypoint()) {
             mDataCollector.onScreenOnFromTouch();
@@ -196,17 +233,30 @@
     }
 
     public void onScreenOff() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onScreenOff", new StringBuilder()
+                    .append("from=").append(mScreenOn ? 1 : 0)
+                    .toString());
+        }
         mDataCollector.onScreenOff();
         mScreenOn = false;
         sessionExitpoint(false /* force */);
     }
 
     public void onSucccessfulUnlock() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onSucccessfulUnlock", "");
+        }
         mDataCollector.onSucccessfulUnlock();
         sessionExitpoint(true /* force */);
     }
 
     public void onBouncerShown() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onBouncerShown", new StringBuilder()
+                    .append("from=").append(mBouncerOn ? 1 : 0)
+                    .toString());
+        }
         if (!mBouncerOn) {
             mBouncerOn = true;
             mDataCollector.onBouncerShown();
@@ -214,6 +264,11 @@
     }
 
     public void onBouncerHidden() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onBouncerHidden", new StringBuilder()
+                    .append("from=").append(mBouncerOn ? 1 : 0)
+                    .toString());
+        }
         if (mBouncerOn) {
             mBouncerOn = false;
             mDataCollector.onBouncerHidden();
@@ -221,6 +276,9 @@
     }
 
     public void onQsDown() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onQsDown", "");
+        }
         mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
         mDataCollector.onQsDown();
     }
@@ -230,6 +288,9 @@
     }
 
     public void onTrackingStarted() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onTrackingStarted", "");
+        }
         mHumanInteractionClassifier.setType(Classifier.UNLOCK);
         mDataCollector.onTrackingStarted();
     }
@@ -251,6 +312,9 @@
     }
 
     public void onNotificatonStartDraggingDown() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onNotificatonStartDraggingDown", "");
+        }
         mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
         mDataCollector.onNotificatonStartDraggingDown();
     }
@@ -264,6 +328,9 @@
     }
 
     public void onNotificatonStartDismissing() {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onNotificatonStartDismissing", "");
+        }
         mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
         mDataCollector.onNotificatonStartDismissing();
     }
@@ -281,6 +348,9 @@
     }
 
     public void onAffordanceSwipingStarted(boolean rightCorner) {
+        if (FalsingLog.ENABLED) {
+            FalsingLog.i("onAffordanceSwipingStarted", "");
+        }
         if (rightCorner) {
             mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
         } else {
@@ -311,4 +381,14 @@
             mHumanInteractionClassifier.onTouchEvent(event);
         }
     }
+
+    public void dump(PrintWriter pw) {
+        pw.println("FALSING MANAGER");
+        pw.print("classifierEnabled="); pw.println(isClassiferEnabled() ? 1 : 0);
+        pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
+        pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
+        pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
+        pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
+        pw.println();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index 45eb9ad..5e35d76 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -23,6 +23,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import java.util.ArrayDeque;
@@ -43,14 +44,12 @@
     private final Handler mHandler = new Handler();
     private final Context mContext;
 
-    private ArrayList<StrokeClassifier> mStrokeClassifiers = new ArrayList<>();
-    private ArrayList<GestureClassifier> mGestureClassifiers = new ArrayList<>();
-    private ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
-    private final int mStrokeClassifiersSize;
-    private final int mGestureClassifiersSize;
+    private final StrokeClassifier[] mStrokeClassifiers;
+    private final GestureClassifier[] mGestureClassifiers;
+    private final ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
+    private final HistoryEvaluator mHistoryEvaluator;
     private final float mDpi;
 
-    private HistoryEvaluator mHistoryEvaluator;
     private boolean mEnableClassifier = false;
     private int mCurrentType = Classifier.GENERIC;
 
@@ -68,25 +67,27 @@
         // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
         // were to be used separately. Due negligible differences in xdpi and ydpi we can just
         // take the average.
+        // TODO: make this respect DPI changes.
         mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
         mClassifierData = new ClassifierData(mDpi);
         mHistoryEvaluator = new HistoryEvaluator();
 
-        mStrokeClassifiers.add(new AnglesClassifier(mClassifierData));
-        mStrokeClassifiers.add(new SpeedClassifier(mClassifierData));
-        mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData));
-        mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData));
-        mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData));
-        mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData));
-        mStrokeClassifiers.add(new SpeedAnglesClassifier(mClassifierData));
-        mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData));
-        mStrokeClassifiers.add(new DirectionClassifier(mClassifierData));
+        mStrokeClassifiers = new StrokeClassifier[]{
+                new AnglesClassifier(mClassifierData),
+                new SpeedClassifier(mClassifierData),
+                new DurationCountClassifier(mClassifierData),
+                new EndPointRatioClassifier(mClassifierData),
+                new EndPointLengthClassifier(mClassifierData),
+                new AccelerationClassifier(mClassifierData),
+                new SpeedAnglesClassifier(mClassifierData),
+                new LengthCountClassifier(mClassifierData),
+                new DirectionClassifier(mClassifierData),
+        };
 
-        mGestureClassifiers.add(new PointerCountClassifier(mClassifierData));
-        mGestureClassifiers.add(new ProximityClassifier(mClassifierData));
-
-        mStrokeClassifiersSize = mStrokeClassifiers.size();
-        mGestureClassifiersSize = mGestureClassifiers.size();
+        mGestureClassifiers = new GestureClassifier[] {
+                new PointerCountClassifier(mClassifierData),
+                new ProximityClassifier(mClassifierData)
+        };
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(HIC_ENABLE), false,
@@ -150,21 +151,30 @@
     private void addTouchEvent(MotionEvent event) {
         mClassifierData.update(event);
 
-        for (int i = 0; i < mStrokeClassifiersSize; i++) {
-            mStrokeClassifiers.get(i).onTouchEvent(event);
+        for (StrokeClassifier c : mStrokeClassifiers) {
+            c.onTouchEvent(event);
         }
 
-        for (int i = 0; i < mGestureClassifiersSize; i++) {
-            mGestureClassifiers.get(i).onTouchEvent(event);
+        for (GestureClassifier c : mGestureClassifiers) {
+            c.onTouchEvent(event);
         }
 
         int size = mClassifierData.getEndingStrokes().size();
         for (int i = 0; i < size; i++) {
             Stroke stroke = mClassifierData.getEndingStrokes().get(i);
             float evaluation = 0.0f;
-            for (int j = 0; j < mStrokeClassifiersSize; j++) {
-                evaluation += mStrokeClassifiers.get(j).getFalseTouchEvaluation(
-                        mCurrentType, stroke);
+            StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("stroke") : null;
+            for (StrokeClassifier c : mStrokeClassifiers) {
+                float e = c.getFalseTouchEvaluation(mCurrentType, stroke);
+                if (FalsingLog.ENABLED) {
+                    String tag = c.getTag();
+                    sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
+                }
+                evaluation += e;
+            }
+
+            if (FalsingLog.ENABLED) {
+                FalsingLog.i(" addTouchEvent", sb.toString());
             }
             mHistoryEvaluator.addStroke(evaluation);
         }
@@ -172,8 +182,17 @@
         int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
             float evaluation = 0.0f;
-            for (int i = 0; i < mGestureClassifiersSize; i++) {
-                evaluation += mGestureClassifiers.get(i).getFalseTouchEvaluation(mCurrentType);
+            StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("gesture") : null;
+            for (GestureClassifier c : mGestureClassifiers) {
+                float e = c.getFalseTouchEvaluation(mCurrentType);
+                if (FalsingLog.ENABLED) {
+                    String tag = c.getTag();
+                    sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
+                }
+                evaluation += e;
+            }
+            if (FalsingLog.ENABLED) {
+                FalsingLog.i(" addTouchEvent", sb.toString());
             }
             mHistoryEvaluator.addGesture(evaluation);
             setType(Classifier.GENERIC);
@@ -184,18 +203,25 @@
 
     @Override
     public void onSensorChanged(SensorEvent event) {
-        for (int i = 0; i < mStrokeClassifiers.size(); i++) {
-            mStrokeClassifiers.get(i).onSensorChanged(event);
+        for (Classifier c : mStrokeClassifiers) {
+            c.onSensorChanged(event);
         }
 
-        for (int i = 0; i < mGestureClassifiers.size(); i++) {
-            mGestureClassifiers.get(i).onSensorChanged(event);
+        for (Classifier c : mGestureClassifiers) {
+            c.onSensorChanged(event);
         }
     }
 
     public boolean isFalseTouch() {
         if (mEnableClassifier) {
-            return mHistoryEvaluator.getEvaluation() >= 5.0f;
+            float evaluation = mHistoryEvaluator.getEvaluation();
+            boolean result = evaluation >= 5.0f;
+            if (FalsingLog.ENABLED) {
+                FalsingLog.i("isFalseTouch", new StringBuilder()
+                        .append("eval=").append(evaluation).append(" result=")
+                        .append(result ? 1 : 0).toString());
+            }
+            return result;
         }
         return false;
     }
@@ -203,4 +229,9 @@
     public boolean isEnabled() {
         return mEnableClassifier;
     }
+
+    @Override
+    public String getTag() {
+        return "HIC";
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
index cedf467..53678a6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
@@ -28,6 +28,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "LEN_CNT";
+    }
+
+    @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
         return LengthCountEvaluator.evaluate(stroke.getTotalLength()
                 / Math.max(1.0f, stroke.getCount() - 2));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
index 5097b63..136c433 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -29,6 +29,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "PTR_CNT";
+    }
+
+    @Override
     public void onTouchEvent(MotionEvent event) {
         int action = event.getActionMasked();
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 6995064..62adfc8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -35,6 +35,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "PROX";
+    }
+
+    @Override
     public void onSensorChanged(SensorEvent event) {
         if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
             update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
index d58274d..6df72b1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
@@ -41,6 +41,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "SPD_ANG";
+    }
+
+    @Override
     public void onTouchEvent(MotionEvent event) {
         int action = event.getActionMasked();
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
index 81b78c7..01fcc37 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
@@ -27,6 +27,11 @@
     }
 
     @Override
+    public String getTag() {
+        return "SPD";
+    }
+
+    @Override
     public float getFalseTouchEvaluation(int type, Stroke stroke) {
         float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS;
         if (duration == 0.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
index 7e7fc3a..c0148c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
@@ -41,4 +41,20 @@
      * Status bar is locked and shows the full screen user switcher.
      */
     public static final int FULLSCREEN_USER_SWITCHER = 3;
+
+
+    public static String toShortString(int x) {
+        switch (x) {
+            case SHADE:
+                return "SHD";
+            case SHADE_LOCKED:
+                return "SHD_LCK";
+            case KEYGUARD:
+                return "KGRD";
+            case FULLSCREEN_USER_SWITCHER:
+                return "FS_USRSW";
+            default:
+                return "bad_value_" + x;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e739944..c5d5866 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -112,6 +112,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
@@ -2246,6 +2247,10 @@
         mStatusBarWindowManager.setPanelExpanded(isExpanded);
     }
 
+    public void onScreenTurnedOff() {
+        mFalsingManager.onScreenOff();
+    }
+
     /**
      * All changes to the status bar and notifications funnel through here and are batched.
      */
@@ -2949,18 +2954,15 @@
             KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
         }
 
+        FalsingManager.getInstance(mContext).dump(pw);
+        FalsingLog.dump(pw);
+
         pw.println("SharedPreferences:");
         for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
             pw.print("  "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
         }
     }
 
-    private String hunStateToString(Entry entry) {
-        if (entry == null) return "null";
-        if (entry.notification == null) return "corrupt";
-        return entry.notification.getPackageName();
-    }
-
     private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
         pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
         pw.println(BarTransitions.modeToString(transitions.getMode()));
@@ -4169,7 +4171,6 @@
         mDeviceInteractive = false;
         mWakeUpComingFromTouch = false;
         mWakeUpTouchLocation = null;
-        mFalsingManager.onScreenOff();
         mStackScroller.setAnimationsEnabled(false);
         updateVisibleToUser();
         if (mLaunchCameraOnFinishedGoingToSleep) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0e84f733..2ba1562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -212,6 +212,7 @@
 
     public void onScreenTurnedOff() {
         mScreenTurnedOn = false;
+        mPhoneStatusBar.onScreenTurnedOff();
     }
 
     public void notifyDeviceWakeUpRequested() {