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