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