Capture recent motion events used for falsing.

This spits out the last 20 motion events that the FalsingManager
uses into bug reports. It only records the data in eng/userdebug
builds.

This has been cleared with PWG over VC.

A tool to visualize this data in in progress.

Bug: 148084465
Test: manual
Change-Id: I3edf5dc8693a202ff0e31e737e912500a3e4363d
This commit is contained in:
Dave Mankoff
2020-02-11 10:36:57 -05:00
parent 998c6ad176
commit d13251a7fd
5 changed files with 135 additions and 20 deletions

View File

@@ -25,9 +25,12 @@ import android.provider.DeviceConfig;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
import com.android.systemui.classifier.brightline.FalsingDataProvider;
import com.android.systemui.dagger.qualifiers.Main;
@@ -40,6 +43,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -52,7 +56,7 @@ import javax.inject.Singleton;
* {@link FalsingManagerImpl} is used when a Plugin is not loaded.
*/
@Singleton
public class FalsingManagerProxy implements FalsingManager {
public class FalsingManagerProxy implements FalsingManager, Dumpable {
private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
@@ -63,16 +67,20 @@ public class FalsingManagerProxy implements FalsingManager {
private final DeviceConfigProxy mDeviceConfig;
private boolean mBrightlineEnabled;
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private Executor mUiBgExecutor;
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
DisplayMetrics displayMetrics, ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfig, DockManager dockManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpController dumpController,
@UiBackground Executor uiBgExecutor) {
mDisplayMetrics = displayMetrics;
mProximitySensor = proximitySensor;
mDockManager = dockManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mUiBgExecutor = uiBgExecutor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
@@ -101,6 +109,8 @@ public class FalsingManagerProxy implements FalsingManager {
};
pluginManager.addPluginListener(mPluginListener, FalsingPlugin.class);
dumpController.registerDumpable("FalsingManager", this);
}
private void onDeviceConfigPropertiesChanged(Context context, String namespace) {
@@ -130,7 +140,7 @@ public class FalsingManagerProxy implements FalsingManager {
} else {
mInternalFalsingManager = new BrightLineFalsingManager(
new FalsingDataProvider(mDisplayMetrics),
Dependency.get(KeyguardUpdateMonitor.class),
mKeyguardUpdateMonitor,
mProximitySensor,
mDeviceConfig,
mDockManager
@@ -321,6 +331,11 @@ public class FalsingManagerProxy implements FalsingManager {
mInternalFalsingManager.onTouchEvent(ev, width, height);
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
mInternalFalsingManager.dump(pw);
}
@Override
public void dump(PrintWriter pw) {
mInternalFalsingManager.dump(pw);

View File

@@ -21,6 +21,7 @@ import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS
import android.hardware.biometrics.BiometricSourceType;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;
@@ -40,15 +41,19 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
import java.util.StringJoiner;
import java.util.stream.Collectors;
/**
* FalsingManager designed to make clear why a touch was rejected.
*/
public class BrightLineFalsingManager implements FalsingManager {
static final boolean DEBUG = false;
private static final String TAG = "FalsingManager";
private static final int RECENT_INFO_LOG_SIZE = 20;
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int RECENT_INFO_LOG_SIZE = 40;
private static final int RECENT_SWIPE_LOG_SIZE = 20;
private final FalsingDataProvider mDataProvider;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -62,6 +67,8 @@ public class BrightLineFalsingManager implements FalsingManager {
private boolean mJustUnlockedWithFace;
private static final Queue<String> RECENT_INFO_LOG =
new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1);
private static final Queue<DebugSwipeRecord> RECENT_SWIPES =
new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1);
private final List<FalsingClassifier> mClassifiers;
@@ -78,6 +85,7 @@ public class BrightLineFalsingManager implements FalsingManager {
}
}
};
private boolean mPreviousResult = false;
public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor,
@@ -148,7 +156,11 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public boolean isFalseTouch() {
boolean r = !mJustUnlockedWithFace && !mDockManager.isDocked()
if (!mDataProvider.isDirty()) {
return mPreviousResult;
}
mPreviousResult = !mJustUnlockedWithFace && !mDockManager.isDocked()
&& mClassifiers.stream().anyMatch(falsingClassifier -> {
boolean result = falsingClassifier.isFalseTouch();
if (result) {
@@ -167,9 +179,26 @@ public class BrightLineFalsingManager implements FalsingManager {
return result;
});
logDebug("Is false touch? " + r);
logDebug("Is false touch? " + mPreviousResult);
return r;
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(
mPreviousResult,
mDataProvider.getInteractionType(),
mDataProvider.getRecentMotionEvents().stream().map(
motionEvent -> new XYDt(
(int) motionEvent.getX(),
(int) motionEvent.getY(),
(int) (motionEvent.getEventTime() - motionEvent.getDownTime())))
.collect(Collectors.toList())));
while (RECENT_SWIPES.size() > RECENT_INFO_LOG_SIZE) {
DebugSwipeRecord record = RECENT_SWIPES.remove();
}
}
return mPreviousResult;
}
@Override
@@ -212,7 +241,6 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public void onNotificatonStartDraggingDown() {
updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN);
}
@Override
@@ -348,15 +376,35 @@ public class BrightLineFalsingManager implements FalsingManager {
public void dump(PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.println("BRIGHTLINE FALSING MANAGER");
ipw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0);
ipw.print("mJustUnlockedWithFace="); pw.println(mJustUnlockedWithFace ? 1 : 0);
ipw.print("isDocked="); pw.println(mDockManager.isDocked() ? 1 : 0);
ipw.print("classifierEnabled=");
ipw.println(isClassifierEnabled() ? 1 : 0);
ipw.print("mJustUnlockedWithFace=");
ipw.println(mJustUnlockedWithFace ? 1 : 0);
ipw.print("isDocked=");
ipw.println(mDockManager.isDocked() ? 1 : 0);
ipw.print("width=");
ipw.println(mDataProvider.getWidthPixels());
ipw.print("height=");
ipw.println(mDataProvider.getHeightPixels());
ipw.println();
if (RECENT_SWIPES.size() != 0) {
ipw.println("Recent swipes:");
ipw.increaseIndent();
for (DebugSwipeRecord record : RECENT_SWIPES) {
ipw.println(record.getString());
ipw.println();
}
ipw.decreaseIndent();
} else {
ipw.println("No recent swipes");
}
ipw.println();
ipw.println("Recent falsing info:");
ipw.increaseIndent();
for (String msg : RECENT_INFO_LOG) {
ipw.println(msg);
}
ipw.println();
}
@Override
@@ -386,4 +434,46 @@ public class BrightLineFalsingManager implements FalsingManager {
static void logError(String msg) {
Log.e(TAG, msg);
}
private static class DebugSwipeRecord {
private static final byte VERSION = 1; // opaque version number indicating format of data.
private final boolean mIsFalse;
private final int mInteractionType;
private final List<XYDt> mRecentMotionEvents;
DebugSwipeRecord(boolean isFalse, int interactionType,
List<XYDt> recentMotionEvents) {
mIsFalse = isFalse;
mInteractionType = interactionType;
mRecentMotionEvents = recentMotionEvents;
}
String getString() {
StringJoiner sj = new StringJoiner(",");
sj.add(Integer.toString(VERSION))
.add(mIsFalse ? "1" : "0")
.add(Integer.toString(mInteractionType));
for (XYDt event : mRecentMotionEvents) {
sj.add(event.toString());
}
return sj.toString();
}
}
private static class XYDt {
private final int mX;
private final int mY;
private final int mDT;
XYDt(int x, int y, int dT) {
mX = x;
mY = y;
mDT = dT;
}
@Override
public String toString() {
return mX + "," + mY + "," + mDT;
}
}
}

View File

@@ -113,6 +113,10 @@ public class FalsingDataProvider {
this.mInteractionType = interactionType;
}
public boolean isDirty() {
return mDirty;
}
final int getInteractionType() {
return mInteractionType;
}

View File

@@ -28,6 +28,7 @@ import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.WindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.NotificationMessagingUtil;
@@ -98,10 +99,10 @@ public class DependencyProvider {
@Singleton
@Provides
// Single instance of DisplayMetrics, gets updated by StatusBar, but can be used
// anywhere it is needed.
public DisplayMetrics provideDisplayMetrics() {
return new DisplayMetrics();
public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) {
DisplayMetrics displayMetrics = new DisplayMetrics();
context.getDisplay().getMetrics(displayMetrics);
return displayMetrics;
}
/** */

View File

@@ -28,6 +28,7 @@ import android.util.DisplayMetrics;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.DumpController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
import com.android.systemui.dock.DockManager;
@@ -53,6 +54,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
PluginManager mPluginManager;
@Mock(stubOnly = true)
ProximitySensor mProximitySensor;
@Mock(stubOnly = true)
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private FalsingManagerProxy mProxy;
private DeviceConfigProxy mDeviceConfig;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -63,7 +66,6 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDeviceConfig = new DeviceConfigProxyFake();
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
@@ -79,7 +81,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
@Test
public void test_brightLineFalsingManagerDisabled() {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
new DumpController(), mUiBgExecutor);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -89,14 +92,16 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
mExecutor.runAllReady();
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
new DumpController(), mUiBgExecutor);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@Test
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
new DumpController(), mUiBgExecutor);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,