Home | History | Annotate | Download | only in classifier
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.systemui.classifier;
     18 
     19 import android.content.Context;
     20 import android.database.ContentObserver;
     21 import android.hardware.Sensor;
     22 import android.hardware.SensorEvent;
     23 import android.hardware.SensorEventListener;
     24 import android.hardware.SensorManager;
     25 import android.net.Uri;
     26 import android.os.Handler;
     27 import android.os.PowerManager;
     28 import android.os.UserHandle;
     29 import android.provider.Settings;
     30 import android.view.MotionEvent;
     31 import android.view.accessibility.AccessibilityManager;
     32 
     33 import com.android.systemui.analytics.DataCollector;
     34 import com.android.systemui.statusbar.StatusBarState;
     35 
     36 import java.io.PrintWriter;
     37 
     38 /**
     39  * When the phone is locked, listens to touch, sensor and phone events and sends them to
     40  * DataCollector and HumanInteractionClassifier.
     41  *
     42  * It does not collect touch events when the bouncer shows up.
     43  */
     44 public class FalsingManager implements SensorEventListener {
     45     private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
     46 
     47     private static final int[] CLASSIFIER_SENSORS = new int[] {
     48             Sensor.TYPE_PROXIMITY,
     49     };
     50 
     51     private static final int[] COLLECTOR_SENSORS = new int[] {
     52             Sensor.TYPE_ACCELEROMETER,
     53             Sensor.TYPE_GYROSCOPE,
     54             Sensor.TYPE_PROXIMITY,
     55             Sensor.TYPE_LIGHT,
     56             Sensor.TYPE_ROTATION_VECTOR,
     57     };
     58 
     59     private final Handler mHandler = new Handler();
     60     private final Context mContext;
     61 
     62     private final SensorManager mSensorManager;
     63     private final DataCollector mDataCollector;
     64     private final HumanInteractionClassifier mHumanInteractionClassifier;
     65     private final AccessibilityManager mAccessibilityManager;
     66 
     67     private static FalsingManager sInstance = null;
     68 
     69     private boolean mEnforceBouncer = false;
     70     private boolean mBouncerOn = false;
     71     private boolean mSessionActive = false;
     72     private int mState = StatusBarState.SHADE;
     73     private boolean mScreenOn;
     74 
     75     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
     76         @Override
     77         public void onChange(boolean selfChange) {
     78             updateConfiguration();
     79         }
     80     };
     81 
     82     private FalsingManager(Context context) {
     83         mContext = context;
     84         mSensorManager = mContext.getSystemService(SensorManager.class);
     85         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
     86         mDataCollector = DataCollector.getInstance(mContext);
     87         mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
     88         mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
     89 
     90         mContext.getContentResolver().registerContentObserver(
     91                 Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
     92                 mSettingsObserver,
     93                 UserHandle.USER_ALL);
     94 
     95         updateConfiguration();
     96     }
     97 
     98     public static FalsingManager getInstance(Context context) {
     99         if (sInstance == null) {
    100             sInstance = new FalsingManager(context);
    101         }
    102         return sInstance;
    103     }
    104 
    105     private void updateConfiguration() {
    106         mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
    107                 ENFORCE_BOUNCER, 0);
    108     }
    109 
    110     private boolean shouldSessionBeActive() {
    111         if (FalsingLog.ENABLED && FalsingLog.VERBOSE)
    112             FalsingLog.v("shouldBeActive", new StringBuilder()
    113                     .append("enabled=").append(isEnabled() ? 1 : 0)
    114                     .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
    115                     .append(" mState=").append(StatusBarState.toShortString(mState))
    116                     .toString()
    117             );
    118         return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD);
    119     }
    120 
    121     private boolean sessionEntrypoint() {
    122         if (!mSessionActive && shouldSessionBeActive()) {
    123             onSessionStart();
    124             return true;
    125         }
    126         return false;
    127     }
    128 
    129     private void sessionExitpoint(boolean force) {
    130         if (mSessionActive && (force || !shouldSessionBeActive())) {
    131             mSessionActive = false;
    132             mSensorManager.unregisterListener(this);
    133         }
    134     }
    135 
    136     private void onSessionStart() {
    137         if (FalsingLog.ENABLED) {
    138             FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
    139         }
    140         mBouncerOn = false;
    141         mSessionActive = true;
    142 
    143         if (mHumanInteractionClassifier.isEnabled()) {
    144             registerSensors(CLASSIFIER_SENSORS);
    145         }
    146         if (mDataCollector.isEnabledFull()) {
    147             registerSensors(COLLECTOR_SENSORS);
    148         }
    149     }
    150 
    151     private void registerSensors(int [] sensors) {
    152         for (int sensorType : sensors) {
    153             Sensor s = mSensorManager.getDefaultSensor(sensorType);
    154             if (s != null) {
    155                 mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
    156             }
    157         }
    158     }
    159 
    160     public boolean isClassiferEnabled() {
    161         return mHumanInteractionClassifier.isEnabled();
    162     }
    163 
    164     private boolean isEnabled() {
    165         return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
    166     }
    167 
    168     /**
    169      * @return true if the classifier determined that this is not a human interacting with the phone
    170      */
    171     public boolean isFalseTouch() {
    172         if (FalsingLog.ENABLED) {
    173             // We're getting some false wtfs from touches that happen after the device went
    174             // to sleep. Only report missing sessions that happen when the device is interactive.
    175             if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()) {
    176                 FalsingLog.wtf("isFalseTouch", new StringBuilder()
    177                         .append("Session is not active, yet there's a query for a false touch.")
    178                         .append(" enabled=").append(isEnabled() ? 1 : 0)
    179                         .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
    180                         .append(" mState=").append(StatusBarState.toShortString(mState))
    181                         .toString());
    182             }
    183         }
    184         if (mAccessibilityManager.isTouchExplorationEnabled()) {
    185             // Touch exploration triggers false positives in the classifier and
    186             // already sufficiently prevents false unlocks.
    187             return false;
    188         }
    189         return mHumanInteractionClassifier.isFalseTouch();
    190     }
    191 
    192     @Override
    193     public synchronized void onSensorChanged(SensorEvent event) {
    194         mDataCollector.onSensorChanged(event);
    195         mHumanInteractionClassifier.onSensorChanged(event);
    196     }
    197 
    198     @Override
    199     public void onAccuracyChanged(Sensor sensor, int accuracy) {
    200         mDataCollector.onAccuracyChanged(sensor, accuracy);
    201     }
    202 
    203     public boolean shouldEnforceBouncer() {
    204         return mEnforceBouncer;
    205     }
    206 
    207     public void setStatusBarState(int state) {
    208         if (FalsingLog.ENABLED) {
    209             FalsingLog.i("setStatusBarState", new StringBuilder()
    210                     .append("from=").append(StatusBarState.toShortString(mState))
    211                     .append(" to=").append(StatusBarState.toShortString(state))
    212                     .toString());
    213         }
    214         mState = state;
    215         if (shouldSessionBeActive()) {
    216             sessionEntrypoint();
    217         } else {
    218             sessionExitpoint(false /* force */);
    219         }
    220     }
    221 
    222     public void onScreenTurningOn() {
    223         if (FalsingLog.ENABLED) {
    224             FalsingLog.i("onScreenTurningOn", new StringBuilder()
    225                     .append("from=").append(mScreenOn ? 1 : 0)
    226                     .toString());
    227         }
    228         mScreenOn = true;
    229         if (sessionEntrypoint()) {
    230             mDataCollector.onScreenTurningOn();
    231         }
    232     }
    233 
    234     public void onScreenOnFromTouch() {
    235         if (FalsingLog.ENABLED) {
    236             FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
    237                     .append("from=").append(mScreenOn ? 1 : 0)
    238                     .toString());
    239         }
    240         mScreenOn = true;
    241         if (sessionEntrypoint()) {
    242             mDataCollector.onScreenOnFromTouch();
    243         }
    244     }
    245 
    246     public void onScreenOff() {
    247         if (FalsingLog.ENABLED) {
    248             FalsingLog.i("onScreenOff", new StringBuilder()
    249                     .append("from=").append(mScreenOn ? 1 : 0)
    250                     .toString());
    251         }
    252         mDataCollector.onScreenOff();
    253         mScreenOn = false;
    254         sessionExitpoint(false /* force */);
    255     }
    256 
    257     public void onSucccessfulUnlock() {
    258         if (FalsingLog.ENABLED) {
    259             FalsingLog.i("onSucccessfulUnlock", "");
    260         }
    261         mDataCollector.onSucccessfulUnlock();
    262     }
    263 
    264     public void onBouncerShown() {
    265         if (FalsingLog.ENABLED) {
    266             FalsingLog.i("onBouncerShown", new StringBuilder()
    267                     .append("from=").append(mBouncerOn ? 1 : 0)
    268                     .toString());
    269         }
    270         if (!mBouncerOn) {
    271             mBouncerOn = true;
    272             mDataCollector.onBouncerShown();
    273         }
    274     }
    275 
    276     public void onBouncerHidden() {
    277         if (FalsingLog.ENABLED) {
    278             FalsingLog.i("onBouncerHidden", new StringBuilder()
    279                     .append("from=").append(mBouncerOn ? 1 : 0)
    280                     .toString());
    281         }
    282         if (mBouncerOn) {
    283             mBouncerOn = false;
    284             mDataCollector.onBouncerHidden();
    285         }
    286     }
    287 
    288     public void onQsDown() {
    289         if (FalsingLog.ENABLED) {
    290             FalsingLog.i("onQsDown", "");
    291         }
    292         mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
    293         mDataCollector.onQsDown();
    294     }
    295 
    296     public void setQsExpanded(boolean expanded) {
    297         mDataCollector.setQsExpanded(expanded);
    298     }
    299 
    300     public void onTrackingStarted() {
    301         if (FalsingLog.ENABLED) {
    302             FalsingLog.i("onTrackingStarted", "");
    303         }
    304         mHumanInteractionClassifier.setType(Classifier.UNLOCK);
    305         mDataCollector.onTrackingStarted();
    306     }
    307 
    308     public void onTrackingStopped() {
    309         mDataCollector.onTrackingStopped();
    310     }
    311 
    312     public void onNotificationActive() {
    313         mDataCollector.onNotificationActive();
    314     }
    315 
    316     public void onNotificationDoubleTap() {
    317         mDataCollector.onNotificationDoubleTap();
    318     }
    319 
    320     public void setNotificationExpanded() {
    321         mDataCollector.setNotificationExpanded();
    322     }
    323 
    324     public void onNotificatonStartDraggingDown() {
    325         if (FalsingLog.ENABLED) {
    326             FalsingLog.i("onNotificatonStartDraggingDown", "");
    327         }
    328         mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
    329         mDataCollector.onNotificatonStartDraggingDown();
    330     }
    331 
    332     public void onNotificatonStopDraggingDown() {
    333         mDataCollector.onNotificatonStopDraggingDown();
    334     }
    335 
    336     public void onNotificationDismissed() {
    337         mDataCollector.onNotificationDismissed();
    338     }
    339 
    340     public void onNotificatonStartDismissing() {
    341         if (FalsingLog.ENABLED) {
    342             FalsingLog.i("onNotificatonStartDismissing", "");
    343         }
    344         mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
    345         mDataCollector.onNotificatonStartDismissing();
    346     }
    347 
    348     public void onNotificatonStopDismissing() {
    349         mDataCollector.onNotificatonStopDismissing();
    350     }
    351 
    352     public void onCameraOn() {
    353         mDataCollector.onCameraOn();
    354     }
    355 
    356     public void onLeftAffordanceOn() {
    357         mDataCollector.onLeftAffordanceOn();
    358     }
    359 
    360     public void onAffordanceSwipingStarted(boolean rightCorner) {
    361         if (FalsingLog.ENABLED) {
    362             FalsingLog.i("onAffordanceSwipingStarted", "");
    363         }
    364         if (rightCorner) {
    365             mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
    366         } else {
    367             mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
    368         }
    369         mDataCollector.onAffordanceSwipingStarted(rightCorner);
    370     }
    371 
    372     public void onAffordanceSwipingAborted() {
    373         mDataCollector.onAffordanceSwipingAborted();
    374     }
    375 
    376     public void onUnlockHintStarted() {
    377         mDataCollector.onUnlockHintStarted();
    378     }
    379 
    380     public void onCameraHintStarted() {
    381         mDataCollector.onCameraHintStarted();
    382     }
    383 
    384     public void onLeftAffordanceHintStarted() {
    385         mDataCollector.onLeftAffordanceHintStarted();
    386     }
    387 
    388     public void onTouchEvent(MotionEvent event, int width, int height) {
    389         if (mSessionActive && !mBouncerOn) {
    390             mDataCollector.onTouchEvent(event, width, height);
    391             mHumanInteractionClassifier.onTouchEvent(event);
    392         }
    393     }
    394 
    395     public void dump(PrintWriter pw) {
    396         pw.println("FALSING MANAGER");
    397         pw.print("classifierEnabled="); pw.println(isClassiferEnabled() ? 1 : 0);
    398         pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
    399         pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
    400         pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
    401         pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
    402         pw.println();
    403     }
    404 
    405     public Uri reportRejectedTouch() {
    406         if (mDataCollector.isEnabled()) {
    407             return mDataCollector.reportRejectedTouch();
    408         }
    409         return null;
    410     }
    411 
    412     public boolean isReportingEnabled() {
    413         return mDataCollector.isReportingEnabled();
    414     }
    415 }
    416