Home | History | Annotate | Download | only in classifier
      1 /*
      2  * Copyright (C) 2016 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.incallui.answer.impl.classifier;
     18 
     19 import android.content.Context;
     20 import android.hardware.Sensor;
     21 import android.hardware.SensorEvent;
     22 import android.hardware.SensorEventListener;
     23 import android.hardware.SensorManager;
     24 import android.os.PowerManager;
     25 import android.view.MotionEvent;
     26 import android.view.accessibility.AccessibilityManager;
     27 
     28 /**
     29  * When the phone is locked, listens to touch, sensor and phone events and sends them to
     30  * HumanInteractionClassifier to determine if touches are coming from a human.
     31  */
     32 public class FalsingManager implements SensorEventListener {
     33   private static final int[] CLASSIFIER_SENSORS =
     34       new int[] {
     35         Sensor.TYPE_PROXIMITY,
     36       };
     37 
     38   private final SensorManager mSensorManager;
     39   private final HumanInteractionClassifier mHumanInteractionClassifier;
     40   private final AccessibilityManager mAccessibilityManager;
     41 
     42   private boolean mSessionActive = false;
     43   private boolean mScreenOn;
     44 
     45   public FalsingManager(Context context) {
     46     mSensorManager = context.getSystemService(SensorManager.class);
     47     mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
     48     mHumanInteractionClassifier = new HumanInteractionClassifier(context);
     49     mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
     50   }
     51 
     52   /** Returns {@code true} iff the FalsingManager is enabled and able to classify touches */
     53   public boolean isEnabled() {
     54     return mHumanInteractionClassifier.isEnabled();
     55   }
     56 
     57   /**
     58    * Returns {@code true} iff the classifier determined that this is not a human interacting with
     59    * the phone.
     60    */
     61   public boolean isFalseTouch() {
     62     // Touch exploration triggers false positives in the classifier and
     63     // already sufficiently prevents false unlocks.
     64     return !mAccessibilityManager.isTouchExplorationEnabled()
     65         && mHumanInteractionClassifier.isFalseTouch();
     66   }
     67 
     68   /**
     69    * Should be called when the screen turns on and the related Views become visible. This will start
     70    * tracking changes if the manager is enabled.
     71    */
     72   public void onScreenOn() {
     73     mScreenOn = true;
     74     sessionEntrypoint();
     75   }
     76 
     77   /**
     78    * Should be called when the screen turns off or the related Views are no longer visible. This
     79    * will cause the manager to stop tracking changes.
     80    */
     81   public void onScreenOff() {
     82     mScreenOn = false;
     83     sessionExitpoint();
     84   }
     85 
     86   /**
     87    * Should be called when a new touch event has been received and should be classified.
     88    *
     89    * @param event MotionEvent to be classified as human or false.
     90    */
     91   public void onTouchEvent(MotionEvent event) {
     92     if (mSessionActive) {
     93       mHumanInteractionClassifier.onTouchEvent(event);
     94     }
     95   }
     96 
     97   @Override
     98   public synchronized void onSensorChanged(SensorEvent event) {
     99     mHumanInteractionClassifier.onSensorChanged(event);
    100   }
    101 
    102   @Override
    103   public void onAccuracyChanged(Sensor sensor, int accuracy) {}
    104 
    105   private boolean shouldSessionBeActive() {
    106     return isEnabled() && mScreenOn;
    107   }
    108 
    109   private boolean sessionEntrypoint() {
    110     if (!mSessionActive && shouldSessionBeActive()) {
    111       onSessionStart();
    112       return true;
    113     }
    114     return false;
    115   }
    116 
    117   private void sessionExitpoint() {
    118     if (mSessionActive && !shouldSessionBeActive()) {
    119       mSessionActive = false;
    120       mSensorManager.unregisterListener(this);
    121     }
    122   }
    123 
    124   private void onSessionStart() {
    125     mSessionActive = true;
    126 
    127     if (mHumanInteractionClassifier.isEnabled()) {
    128       registerSensors(CLASSIFIER_SENSORS);
    129     }
    130   }
    131 
    132   private void registerSensors(int[] sensors) {
    133     for (int sensorType : sensors) {
    134       Sensor s = mSensorManager.getDefaultSensor(sensorType);
    135       if (s != null) {
    136         mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
    137       }
    138     }
    139   }
    140 }
    141