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