blob: 1abea37519e6c71a2059166dea6cdd128601f949 [file] [log] [blame]
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -07001/*
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
17package com.android.systemui.classifier;
18
19import android.content.Context;
20import android.database.ContentObserver;
21import android.hardware.Sensor;
22import android.hardware.SensorEvent;
23import android.hardware.SensorEventListener;
24import android.hardware.SensorManager;
Adrian Roos7bb38a92016-07-21 11:44:01 -070025import android.net.Uri;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070026import android.os.Handler;
Adrian Roosc5584ce2016-02-24 14:17:19 -080027import android.os.PowerManager;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070028import android.os.UserHandle;
29import android.provider.Settings;
30import android.view.MotionEvent;
Adrian Roosca664b92016-04-18 14:40:27 -070031import android.view.accessibility.AccessibilityManager;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070032
33import com.android.systemui.analytics.DataCollector;
34import com.android.systemui.statusbar.StatusBarState;
35
Adrian Roos401caae2016-03-04 13:35:21 -080036import java.io.PrintWriter;
37
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070038/**
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 */
44public class FalsingManager implements SensorEventListener {
45 private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
46
Blazej Magnowski6dc59b42015-09-22 15:14:20 -070047 private static final int[] CLASSIFIER_SENSORS = new int[] {
48 Sensor.TYPE_PROXIMITY,
49 };
50
51 private static final int[] COLLECTOR_SENSORS = new int[] {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070052 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;
Adrian Roosca664b92016-04-18 14:40:27 -070065 private final AccessibilityManager mAccessibilityManager;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070066
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;
Adrian Roosc5584ce2016-02-24 14:17:19 -080073 private boolean mScreenOn;
Adrian Roos8e291a52016-12-09 16:10:19 -080074 private Runnable mPendingWtf;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070075
76 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
77 @Override
78 public void onChange(boolean selfChange) {
79 updateConfiguration();
80 }
81 };
82
83 private FalsingManager(Context context) {
84 mContext = context;
Adrian Roosca664b92016-04-18 14:40:27 -070085 mSensorManager = mContext.getSystemService(SensorManager.class);
86 mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070087 mDataCollector = DataCollector.getInstance(mContext);
88 mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
Adrian Roosc5584ce2016-02-24 14:17:19 -080089 mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070090
91 mContext.getContentResolver().registerContentObserver(
92 Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
93 mSettingsObserver,
94 UserHandle.USER_ALL);
95
96 updateConfiguration();
97 }
98
99 public static FalsingManager getInstance(Context context) {
100 if (sInstance == null) {
101 sInstance = new FalsingManager(context);
102 }
103 return sInstance;
104 }
105
106 private void updateConfiguration() {
107 mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
108 ENFORCE_BOUNCER, 0);
109 }
110
Adrian Roosc5584ce2016-02-24 14:17:19 -0800111 private boolean shouldSessionBeActive() {
Adrian Roos401caae2016-03-04 13:35:21 -0800112 if (FalsingLog.ENABLED && FalsingLog.VERBOSE)
113 FalsingLog.v("shouldBeActive", new StringBuilder()
114 .append("enabled=").append(isEnabled() ? 1 : 0)
115 .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
116 .append(" mState=").append(StatusBarState.toShortString(mState))
117 .toString()
118 );
119 return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD);
Adrian Roosc5584ce2016-02-24 14:17:19 -0800120 }
121
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700122 private boolean sessionEntrypoint() {
Adrian Roosc5584ce2016-02-24 14:17:19 -0800123 if (!mSessionActive && shouldSessionBeActive()) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700124 onSessionStart();
125 return true;
126 }
127 return false;
128 }
129
Adrian Roosc5584ce2016-02-24 14:17:19 -0800130 private void sessionExitpoint(boolean force) {
131 if (mSessionActive && (force || !shouldSessionBeActive())) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700132 mSessionActive = false;
133 mSensorManager.unregisterListener(this);
134 }
135 }
136
137 private void onSessionStart() {
Adrian Roos401caae2016-03-04 13:35:21 -0800138 if (FalsingLog.ENABLED) {
139 FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
Adrian Roos8e291a52016-12-09 16:10:19 -0800140 clearPendingWtf();
Adrian Roos401caae2016-03-04 13:35:21 -0800141 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700142 mBouncerOn = false;
143 mSessionActive = true;
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700144
145 if (mHumanInteractionClassifier.isEnabled()) {
146 registerSensors(CLASSIFIER_SENSORS);
147 }
Adrian Roos7bb38a92016-07-21 11:44:01 -0700148 if (mDataCollector.isEnabledFull()) {
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700149 registerSensors(COLLECTOR_SENSORS);
150 }
151 }
152
153 private void registerSensors(int [] sensors) {
154 for (int sensorType : sensors) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700155 Sensor s = mSensorManager.getDefaultSensor(sensorType);
156 if (s != null) {
157 mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME);
158 }
159 }
160 }
161
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700162 public boolean isClassiferEnabled() {
163 return mHumanInteractionClassifier.isEnabled();
164 }
165
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700166 private boolean isEnabled() {
167 return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
168 }
169
170 /**
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700171 * @return true if the classifier determined that this is not a human interacting with the phone
172 */
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700173 public boolean isFalseTouch() {
Adrian Roos401caae2016-03-04 13:35:21 -0800174 if (FalsingLog.ENABLED) {
Adrian Roos6a04cb12016-03-14 20:21:02 -0700175 // We're getting some false wtfs from touches that happen after the device went
176 // to sleep. Only report missing sessions that happen when the device is interactive.
Adrian Roos8e291a52016-12-09 16:10:19 -0800177 if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()
178 && mPendingWtf == null) {
179 int enabled = isEnabled() ? 1 : 0;
180 int screenOn = mScreenOn ? 1 : 0;
181 String state = StatusBarState.toShortString(mState);
182 Throwable here = new Throwable("here");
183 FalsingLog.wLogcat("isFalseTouch", new StringBuilder()
Adrian Roos401caae2016-03-04 13:35:21 -0800184 .append("Session is not active, yet there's a query for a false touch.")
Adrian Roos8e291a52016-12-09 16:10:19 -0800185 .append(" enabled=").append(enabled)
186 .append(" mScreenOn=").append(screenOn)
187 .append(" mState=").append(state)
188 .append(". Escalating to WTF if screen does not turn on soon.")
Adrian Roos401caae2016-03-04 13:35:21 -0800189 .toString());
Adrian Roos8e291a52016-12-09 16:10:19 -0800190
191 // Unfortunately we're also getting false positives for touches that happen right
192 // after the screen turns on, but before that notification has made it to us.
193 // Unfortunately there's no good way to catch that, except to wait and see if we get
194 // the screen on notification soon.
195 mPendingWtf = () -> FalsingLog.wtf("isFalseTouch", new StringBuilder()
196 .append("Session did not become active after query for a false touch.")
197 .append(" enabled=").append(enabled)
198 .append('/').append(isEnabled() ? 1 : 0)
199 .append(" mScreenOn=").append(screenOn)
200 .append('/').append(mScreenOn ? 1 : 0)
201 .append(" mState=").append(state)
202 .append('/').append(StatusBarState.toShortString(mState))
203 .append(". Look for warnings ~1000ms earlier to see root cause.")
204 .toString(), here);
205 mHandler.postDelayed(mPendingWtf, 1000);
Adrian Roos401caae2016-03-04 13:35:21 -0800206 }
207 }
Adrian Roosca664b92016-04-18 14:40:27 -0700208 if (mAccessibilityManager.isTouchExplorationEnabled()) {
209 // Touch exploration triggers false positives in the classifier and
210 // already sufficiently prevents false unlocks.
211 return false;
212 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700213 return mHumanInteractionClassifier.isFalseTouch();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700214 }
215
Adrian Roos8e291a52016-12-09 16:10:19 -0800216 private void clearPendingWtf() {
217 if (mPendingWtf != null) {
218 mHandler.removeCallbacks(mPendingWtf);
219 mPendingWtf = null;
220 }
221 }
222
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700223 @Override
224 public synchronized void onSensorChanged(SensorEvent event) {
225 mDataCollector.onSensorChanged(event);
226 mHumanInteractionClassifier.onSensorChanged(event);
227 }
228
229 @Override
230 public void onAccuracyChanged(Sensor sensor, int accuracy) {
231 mDataCollector.onAccuracyChanged(sensor, accuracy);
232 }
233
234 public boolean shouldEnforceBouncer() {
235 return mEnforceBouncer;
236 }
237
238 public void setStatusBarState(int state) {
Adrian Roos401caae2016-03-04 13:35:21 -0800239 if (FalsingLog.ENABLED) {
240 FalsingLog.i("setStatusBarState", new StringBuilder()
241 .append("from=").append(StatusBarState.toShortString(mState))
242 .append(" to=").append(StatusBarState.toShortString(state))
243 .toString());
244 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700245 mState = state;
Adrian Roosc5584ce2016-02-24 14:17:19 -0800246 if (shouldSessionBeActive()) {
247 sessionEntrypoint();
248 } else {
249 sessionExitpoint(false /* force */);
250 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700251 }
252
253 public void onScreenTurningOn() {
Adrian Roos401caae2016-03-04 13:35:21 -0800254 if (FalsingLog.ENABLED) {
255 FalsingLog.i("onScreenTurningOn", new StringBuilder()
256 .append("from=").append(mScreenOn ? 1 : 0)
257 .toString());
Adrian Roos8e291a52016-12-09 16:10:19 -0800258 clearPendingWtf();
Adrian Roos401caae2016-03-04 13:35:21 -0800259 }
Adrian Roosc5584ce2016-02-24 14:17:19 -0800260 mScreenOn = true;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700261 if (sessionEntrypoint()) {
262 mDataCollector.onScreenTurningOn();
263 }
264 }
265
266 public void onScreenOnFromTouch() {
Adrian Roos401caae2016-03-04 13:35:21 -0800267 if (FalsingLog.ENABLED) {
268 FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
269 .append("from=").append(mScreenOn ? 1 : 0)
270 .toString());
271 }
Adrian Roosc5584ce2016-02-24 14:17:19 -0800272 mScreenOn = true;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700273 if (sessionEntrypoint()) {
274 mDataCollector.onScreenOnFromTouch();
275 }
276 }
277
278 public void onScreenOff() {
Adrian Roos401caae2016-03-04 13:35:21 -0800279 if (FalsingLog.ENABLED) {
280 FalsingLog.i("onScreenOff", new StringBuilder()
281 .append("from=").append(mScreenOn ? 1 : 0)
282 .toString());
283 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700284 mDataCollector.onScreenOff();
Adrian Roosc5584ce2016-02-24 14:17:19 -0800285 mScreenOn = false;
286 sessionExitpoint(false /* force */);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700287 }
288
289 public void onSucccessfulUnlock() {
Adrian Roos401caae2016-03-04 13:35:21 -0800290 if (FalsingLog.ENABLED) {
291 FalsingLog.i("onSucccessfulUnlock", "");
292 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700293 mDataCollector.onSucccessfulUnlock();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700294 }
295
296 public void onBouncerShown() {
Adrian Roos401caae2016-03-04 13:35:21 -0800297 if (FalsingLog.ENABLED) {
298 FalsingLog.i("onBouncerShown", new StringBuilder()
299 .append("from=").append(mBouncerOn ? 1 : 0)
300 .toString());
301 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700302 if (!mBouncerOn) {
303 mBouncerOn = true;
304 mDataCollector.onBouncerShown();
305 }
306 }
307
308 public void onBouncerHidden() {
Adrian Roos401caae2016-03-04 13:35:21 -0800309 if (FalsingLog.ENABLED) {
310 FalsingLog.i("onBouncerHidden", new StringBuilder()
311 .append("from=").append(mBouncerOn ? 1 : 0)
312 .toString());
313 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700314 if (mBouncerOn) {
315 mBouncerOn = false;
316 mDataCollector.onBouncerHidden();
317 }
318 }
319
320 public void onQsDown() {
Adrian Roos401caae2016-03-04 13:35:21 -0800321 if (FalsingLog.ENABLED) {
322 FalsingLog.i("onQsDown", "");
323 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700324 mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700325 mDataCollector.onQsDown();
326 }
327
328 public void setQsExpanded(boolean expanded) {
329 mDataCollector.setQsExpanded(expanded);
330 }
331
332 public void onTrackingStarted() {
Adrian Roos401caae2016-03-04 13:35:21 -0800333 if (FalsingLog.ENABLED) {
334 FalsingLog.i("onTrackingStarted", "");
335 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700336 mHumanInteractionClassifier.setType(Classifier.UNLOCK);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700337 mDataCollector.onTrackingStarted();
338 }
339
340 public void onTrackingStopped() {
341 mDataCollector.onTrackingStopped();
342 }
343
344 public void onNotificationActive() {
345 mDataCollector.onNotificationActive();
346 }
347
Adrian Roos9f0b0022016-11-09 15:56:50 -0800348 public void onNotificationDoubleTap(boolean accepted, float dx, float dy) {
349 if (FalsingLog.ENABLED) {
350 FalsingLog.i("onNotificationDoubleTap", "accepted=" + accepted
351 + " dx=" + dx + " dy=" + dy + " (px)");
352 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700353 mDataCollector.onNotificationDoubleTap();
354 }
355
356 public void setNotificationExpanded() {
357 mDataCollector.setNotificationExpanded();
358 }
359
360 public void onNotificatonStartDraggingDown() {
Adrian Roos401caae2016-03-04 13:35:21 -0800361 if (FalsingLog.ENABLED) {
362 FalsingLog.i("onNotificatonStartDraggingDown", "");
363 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700364 mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700365 mDataCollector.onNotificatonStartDraggingDown();
366 }
367
368 public void onNotificatonStopDraggingDown() {
369 mDataCollector.onNotificatonStopDraggingDown();
370 }
371
372 public void onNotificationDismissed() {
373 mDataCollector.onNotificationDismissed();
374 }
375
376 public void onNotificatonStartDismissing() {
Adrian Roos401caae2016-03-04 13:35:21 -0800377 if (FalsingLog.ENABLED) {
378 FalsingLog.i("onNotificatonStartDismissing", "");
379 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700380 mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700381 mDataCollector.onNotificatonStartDismissing();
382 }
383
384 public void onNotificatonStopDismissing() {
385 mDataCollector.onNotificatonStopDismissing();
386 }
387
388 public void onCameraOn() {
389 mDataCollector.onCameraOn();
390 }
391
392 public void onLeftAffordanceOn() {
393 mDataCollector.onLeftAffordanceOn();
394 }
395
396 public void onAffordanceSwipingStarted(boolean rightCorner) {
Adrian Roos401caae2016-03-04 13:35:21 -0800397 if (FalsingLog.ENABLED) {
398 FalsingLog.i("onAffordanceSwipingStarted", "");
399 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700400 if (rightCorner) {
401 mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
402 } else {
403 mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
404 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700405 mDataCollector.onAffordanceSwipingStarted(rightCorner);
406 }
407
408 public void onAffordanceSwipingAborted() {
409 mDataCollector.onAffordanceSwipingAborted();
410 }
411
412 public void onUnlockHintStarted() {
413 mDataCollector.onUnlockHintStarted();
414 }
415
416 public void onCameraHintStarted() {
417 mDataCollector.onCameraHintStarted();
418 }
419
420 public void onLeftAffordanceHintStarted() {
421 mDataCollector.onLeftAffordanceHintStarted();
422 }
423
424 public void onTouchEvent(MotionEvent event, int width, int height) {
425 if (mSessionActive && !mBouncerOn) {
426 mDataCollector.onTouchEvent(event, width, height);
427 mHumanInteractionClassifier.onTouchEvent(event);
428 }
429 }
Adrian Roos401caae2016-03-04 13:35:21 -0800430
431 public void dump(PrintWriter pw) {
432 pw.println("FALSING MANAGER");
433 pw.print("classifierEnabled="); pw.println(isClassiferEnabled() ? 1 : 0);
434 pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
435 pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
436 pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
437 pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
438 pw.println();
439 }
Adrian Roos7bb38a92016-07-21 11:44:01 -0700440
441 public Uri reportRejectedTouch() {
442 if (mDataCollector.isEnabled()) {
443 return mDataCollector.reportRejectedTouch();
444 }
445 return null;
446 }
447
448 public boolean isReportingEnabled() {
449 return mDataCollector.isReportingEnabled();
450 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700451}