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