• 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.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 
FalsingManager(Context context)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 
getInstance(Context context)98     public static FalsingManager getInstance(Context context) {
99         if (sInstance == null) {
100             sInstance = new FalsingManager(context);
101         }
102         return sInstance;
103     }
104 
updateConfiguration()105     private void updateConfiguration() {
106         mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
107                 ENFORCE_BOUNCER, 0);
108     }
109 
shouldSessionBeActive()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 
sessionEntrypoint()121     private boolean sessionEntrypoint() {
122         if (!mSessionActive && shouldSessionBeActive()) {
123             onSessionStart();
124             return true;
125         }
126         return false;
127     }
128 
sessionExitpoint(boolean force)129     private void sessionExitpoint(boolean force) {
130         if (mSessionActive && (force || !shouldSessionBeActive())) {
131             mSessionActive = false;
132             mSensorManager.unregisterListener(this);
133         }
134     }
135 
onSessionStart()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 
registerSensors(int [] sensors)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 
isClassiferEnabled()160     public boolean isClassiferEnabled() {
161         return mHumanInteractionClassifier.isEnabled();
162     }
163 
isEnabled()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      */
isFalseTouch()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
onSensorChanged(SensorEvent event)193     public synchronized void onSensorChanged(SensorEvent event) {
194         mDataCollector.onSensorChanged(event);
195         mHumanInteractionClassifier.onSensorChanged(event);
196     }
197 
198     @Override
onAccuracyChanged(Sensor sensor, int accuracy)199     public void onAccuracyChanged(Sensor sensor, int accuracy) {
200         mDataCollector.onAccuracyChanged(sensor, accuracy);
201     }
202 
shouldEnforceBouncer()203     public boolean shouldEnforceBouncer() {
204         return mEnforceBouncer;
205     }
206 
setStatusBarState(int state)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 
onScreenTurningOn()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 
onScreenOnFromTouch()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 
onScreenOff()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 
onSucccessfulUnlock()257     public void onSucccessfulUnlock() {
258         if (FalsingLog.ENABLED) {
259             FalsingLog.i("onSucccessfulUnlock", "");
260         }
261         mDataCollector.onSucccessfulUnlock();
262     }
263 
onBouncerShown()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 
onBouncerHidden()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 
onQsDown()288     public void onQsDown() {
289         if (FalsingLog.ENABLED) {
290             FalsingLog.i("onQsDown", "");
291         }
292         mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
293         mDataCollector.onQsDown();
294     }
295 
setQsExpanded(boolean expanded)296     public void setQsExpanded(boolean expanded) {
297         mDataCollector.setQsExpanded(expanded);
298     }
299 
onTrackingStarted()300     public void onTrackingStarted() {
301         if (FalsingLog.ENABLED) {
302             FalsingLog.i("onTrackingStarted", "");
303         }
304         mHumanInteractionClassifier.setType(Classifier.UNLOCK);
305         mDataCollector.onTrackingStarted();
306     }
307 
onTrackingStopped()308     public void onTrackingStopped() {
309         mDataCollector.onTrackingStopped();
310     }
311 
onNotificationActive()312     public void onNotificationActive() {
313         mDataCollector.onNotificationActive();
314     }
315 
onNotificationDoubleTap()316     public void onNotificationDoubleTap() {
317         mDataCollector.onNotificationDoubleTap();
318     }
319 
setNotificationExpanded()320     public void setNotificationExpanded() {
321         mDataCollector.setNotificationExpanded();
322     }
323 
onNotificatonStartDraggingDown()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 
onNotificatonStopDraggingDown()332     public void onNotificatonStopDraggingDown() {
333         mDataCollector.onNotificatonStopDraggingDown();
334     }
335 
onNotificationDismissed()336     public void onNotificationDismissed() {
337         mDataCollector.onNotificationDismissed();
338     }
339 
onNotificatonStartDismissing()340     public void onNotificatonStartDismissing() {
341         if (FalsingLog.ENABLED) {
342             FalsingLog.i("onNotificatonStartDismissing", "");
343         }
344         mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
345         mDataCollector.onNotificatonStartDismissing();
346     }
347 
onNotificatonStopDismissing()348     public void onNotificatonStopDismissing() {
349         mDataCollector.onNotificatonStopDismissing();
350     }
351 
onCameraOn()352     public void onCameraOn() {
353         mDataCollector.onCameraOn();
354     }
355 
onLeftAffordanceOn()356     public void onLeftAffordanceOn() {
357         mDataCollector.onLeftAffordanceOn();
358     }
359 
onAffordanceSwipingStarted(boolean rightCorner)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 
onAffordanceSwipingAborted()372     public void onAffordanceSwipingAborted() {
373         mDataCollector.onAffordanceSwipingAborted();
374     }
375 
onUnlockHintStarted()376     public void onUnlockHintStarted() {
377         mDataCollector.onUnlockHintStarted();
378     }
379 
onCameraHintStarted()380     public void onCameraHintStarted() {
381         mDataCollector.onCameraHintStarted();
382     }
383 
onLeftAffordanceHintStarted()384     public void onLeftAffordanceHintStarted() {
385         mDataCollector.onLeftAffordanceHintStarted();
386     }
387 
onTouchEvent(MotionEvent event, int width, int height)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 
dump(PrintWriter pw)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 
reportRejectedTouch()405     public Uri reportRejectedTouch() {
406         if (mDataCollector.isEnabled()) {
407             return mDataCollector.reportRejectedTouch();
408         }
409         return null;
410     }
411 
isReportingEnabled()412     public boolean isReportingEnabled() {
413         return mDataCollector.isReportingEnabled();
414     }
415 }
416