Add FalsingManager and Classifier to SystemUI
Adds the possibility to analyze and classify touch and sensor events as
human or false touches.
Change-Id: I5079c02406d532fea38ca2d302e8606effae0696
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
new file mode 100644
index 0000000..347273a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2015 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.content.Context;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.MotionEvent;
+
+import com.android.systemui.analytics.DataCollector;
+import com.android.systemui.statusbar.StatusBarState;
+
+/**
+ * When the phone is locked, listens to touch, sensor and phone events and sends them to
+ * DataCollector and HumanInteractionClassifier.
+ *
+ * It does not collect touch events when the bouncer shows up.
+ */
+public class FalsingManager implements SensorEventListener {
+ private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
+
+ private static final int[] SENSORS = new int[] {
+ Sensor.TYPE_ACCELEROMETER,
+ Sensor.TYPE_GYROSCOPE,
+ Sensor.TYPE_PROXIMITY,
+ Sensor.TYPE_LIGHT,
+ Sensor.TYPE_ROTATION_VECTOR,
+ };
+
+ private final Handler mHandler = new Handler();
+ private final Context mContext;
+
+ private final SensorManager mSensorManager;
+ private final DataCollector mDataCollector;
+ private final HumanInteractionClassifier mHumanInteractionClassifier;
+
+ private static FalsingManager sInstance = null;
+
+ private boolean mEnforceBouncer = false;
+ private boolean mBouncerOn = false;
+ private boolean mSessionActive = false;
+ private int mState = StatusBarState.SHADE;
+
+ protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateConfiguration();
+ }
+ };
+
+ private FalsingManager(Context context) {
+ mContext = context;
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ mDataCollector = DataCollector.getInstance(mContext);
+ mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
+ mSettingsObserver,
+ UserHandle.USER_ALL);
+
+ updateConfiguration();
+ }
+
+ public static FalsingManager getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new FalsingManager(context);
+ }
+ return sInstance;
+ }
+
+ private void updateConfiguration() {
+ mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
+ ENFORCE_BOUNCER, 0);
+ }
+
+ private boolean sessionEntrypoint() {
+ if (!mSessionActive && isEnabled() &&
+ (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
+ onSessionStart();
+ return true;
+ }
+ return false;
+ }
+
+ private void sessionExitpoint() {
+ if (mSessionActive) {
+ mSessionActive = false;
+ mSensorManager.unregisterListener(this);
+ }
+ }
+
+ private void onSessionStart() {
+ mBouncerOn = false;
+ mSessionActive = true;
+ for (int sensorType : SENSORS) {
+ Sensor s = mSensorManager.getDefaultSensor(sensorType);
+ if (s != null) {
+ mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
+ }
+ }
+ }
+
+ private boolean isEnabled() {
+ return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
+ }
+
+ /**
+ * @param type the type of action for which this method is called
+ * @return true if the classifier determined that this is not a human interacting with the phone
+ */
+ public boolean isFalseTouch(int type) {
+ return mHumanInteractionClassifier.getFalseTouchEvaluation(type) > 0.5;
+ }
+
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ mDataCollector.onSensorChanged(event);
+ mHumanInteractionClassifier.onSensorChanged(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ mDataCollector.onAccuracyChanged(sensor, accuracy);
+ }
+
+ public boolean shouldEnforceBouncer() {
+ return mEnforceBouncer;
+ }
+
+ public void setStatusBarState(int state) {
+ mState = state;
+ }
+
+ public void onScreenTurningOn() {
+ if (sessionEntrypoint()) {
+ mDataCollector.onScreenTurningOn();
+ }
+ }
+
+ public void onScreenOnFromTouch() {
+ if (sessionEntrypoint()) {
+ mDataCollector.onScreenOnFromTouch();
+ }
+ }
+
+ public void onScreenOff() {
+ mDataCollector.onScreenOff();
+ sessionExitpoint();
+ }
+
+ public void onSucccessfulUnlock() {
+ mDataCollector.onSucccessfulUnlock();
+ sessionExitpoint();
+ }
+
+ public void onBouncerShown() {
+ if (!mBouncerOn) {
+ mBouncerOn = true;
+ mDataCollector.onBouncerShown();
+ }
+ }
+
+ public void onBouncerHidden() {
+ if (mBouncerOn) {
+ mBouncerOn = false;
+ mDataCollector.onBouncerHidden();
+ }
+ }
+
+ public void onQsDown() {
+ mDataCollector.onQsDown();
+ }
+
+ public void setQsExpanded(boolean expanded) {
+ mDataCollector.setQsExpanded(expanded);
+ }
+
+ public void onTrackingStarted() {
+ mDataCollector.onTrackingStarted();
+ }
+
+ public void onTrackingStopped() {
+ mDataCollector.onTrackingStopped();
+ }
+
+ public void onNotificationActive() {
+ mDataCollector.onNotificationActive();
+ }
+
+ public void onNotificationDoubleTap() {
+ mDataCollector.onNotificationDoubleTap();
+ }
+
+ public void setNotificationExpanded() {
+ mDataCollector.setNotificationExpanded();
+ }
+
+ public void onNotificatonStartDraggingDown() {
+ mDataCollector.onNotificatonStartDraggingDown();
+ }
+
+ public void onNotificatonStopDraggingDown() {
+ mDataCollector.onNotificatonStopDraggingDown();
+ }
+
+ public void onNotificationDismissed() {
+ mDataCollector.onNotificationDismissed();
+ }
+
+ public void onNotificatonStartDismissing() {
+ mDataCollector.onNotificatonStartDismissing();
+ }
+
+ public void onNotificatonStopDismissing() {
+ mDataCollector.onNotificatonStopDismissing();
+ }
+
+ public void onCameraOn() {
+ mDataCollector.onCameraOn();
+ }
+
+ public void onLeftAffordanceOn() {
+ mDataCollector.onLeftAffordanceOn();
+ }
+
+ public void onAffordanceSwipingStarted(boolean rightCorner) {
+ mDataCollector.onAffordanceSwipingStarted(rightCorner);
+ }
+
+ public void onAffordanceSwipingAborted() {
+ mDataCollector.onAffordanceSwipingAborted();
+ }
+
+ public void onUnlockHintStarted() {
+ mDataCollector.onUnlockHintStarted();
+ }
+
+ public void onCameraHintStarted() {
+ mDataCollector.onCameraHintStarted();
+ }
+
+ public void onLeftAffordanceHintStarted() {
+ mDataCollector.onLeftAffordanceHintStarted();
+ }
+
+ public void onTouchEvent(MotionEvent event, int width, int height) {
+ if (mSessionActive && !mBouncerOn) {
+ mDataCollector.onTouchEvent(event, width, height);
+ mHumanInteractionClassifier.onTouchEvent(event);
+ }
+ }
+}