blob: 8506734036dca0b1f33b530b7878c0286cfffff2 [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;
Selim Cinekf8c4add2017-06-08 09:54:58 -070027import android.os.Looper;
Adrian Roosc5584ce2016-02-24 14:17:19 -080028import android.os.PowerManager;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070029import android.os.UserHandle;
30import android.provider.Settings;
31import android.view.MotionEvent;
Adrian Roosca664b92016-04-18 14:40:27 -070032import android.view.accessibility.AccessibilityManager;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070033
Jorim Jaggie549a8d2017-05-15 02:40:05 +020034import com.android.systemui.Dependency;
35import com.android.systemui.UiOffloadThread;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070036import com.android.systemui.analytics.DataCollector;
Jorim Jaggie549a8d2017-05-15 02:40:05 +020037import com.android.systemui.recents.misc.SystemServicesProxy;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070038import com.android.systemui.statusbar.StatusBarState;
39
Adrian Roos401caae2016-03-04 13:35:21 -080040import java.io.PrintWriter;
41
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070042/**
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 */
48public class FalsingManager implements SensorEventListener {
49 private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
50
Blazej Magnowski6dc59b42015-09-22 15:14:20 -070051 private static final int[] CLASSIFIER_SENSORS = new int[] {
52 Sensor.TYPE_PROXIMITY,
53 };
54
55 private static final int[] COLLECTOR_SENSORS = new int[] {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070056 Sensor.TYPE_ACCELEROMETER,
57 Sensor.TYPE_GYROSCOPE,
58 Sensor.TYPE_PROXIMITY,
59 Sensor.TYPE_LIGHT,
60 Sensor.TYPE_ROTATION_VECTOR,
61 };
62
Selim Cinekf8c4add2017-06-08 09:54:58 -070063 private final Handler mHandler = new Handler(Looper.getMainLooper());
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070064 private final Context mContext;
65
66 private final SensorManager mSensorManager;
67 private final DataCollector mDataCollector;
68 private final HumanInteractionClassifier mHumanInteractionClassifier;
Adrian Roosca664b92016-04-18 14:40:27 -070069 private final AccessibilityManager mAccessibilityManager;
Jorim Jaggie549a8d2017-05-15 02:40:05 +020070 private final UiOffloadThread mUiOffloadThread;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070071
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;
Adrian Roosc5584ce2016-02-24 14:17:19 -080078 private boolean mScreenOn;
Adrian Roos8e291a52016-12-09 16:10:19 -080079 private Runnable mPendingWtf;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070080
81 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
82 @Override
83 public void onChange(boolean selfChange) {
84 updateConfiguration();
85 }
86 };
87
88 private FalsingManager(Context context) {
89 mContext = context;
Adrian Roosca664b92016-04-18 14:40:27 -070090 mSensorManager = mContext.getSystemService(SensorManager.class);
91 mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070092 mDataCollector = DataCollector.getInstance(mContext);
93 mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
Jorim Jaggie549a8d2017-05-15 02:40:05 +020094 mUiOffloadThread = Dependency.get(UiOffloadThread.class);
Adrian Roosc5584ce2016-02-24 14:17:19 -080095 mScreenOn = context.getSystemService(PowerManager.class).isInteractive();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070096
97 mContext.getContentResolver().registerContentObserver(
98 Settings.Secure.getUriFor(ENFORCE_BOUNCER), false,
99 mSettingsObserver,
100 UserHandle.USER_ALL);
101
102 updateConfiguration();
103 }
104
105 public static FalsingManager getInstance(Context context) {
106 if (sInstance == null) {
107 sInstance = new FalsingManager(context);
108 }
109 return sInstance;
110 }
111
112 private void updateConfiguration() {
113 mEnforceBouncer = 0 != Settings.Secure.getInt(mContext.getContentResolver(),
114 ENFORCE_BOUNCER, 0);
115 }
116
Adrian Roosc5584ce2016-02-24 14:17:19 -0800117 private boolean shouldSessionBeActive() {
Adrian Roos401caae2016-03-04 13:35:21 -0800118 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);
Adrian Roosc5584ce2016-02-24 14:17:19 -0800126 }
127
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700128 private boolean sessionEntrypoint() {
Adrian Roosc5584ce2016-02-24 14:17:19 -0800129 if (!mSessionActive && shouldSessionBeActive()) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700130 onSessionStart();
131 return true;
132 }
133 return false;
134 }
135
Adrian Roosc5584ce2016-02-24 14:17:19 -0800136 private void sessionExitpoint(boolean force) {
137 if (mSessionActive && (force || !shouldSessionBeActive())) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700138 mSessionActive = false;
Jorim Jaggie549a8d2017-05-15 02:40:05 +0200139
140 // This can be expensive, and doesn't need to happen on the main thread.
141 mUiOffloadThread.submit(() -> {
142 mSensorManager.unregisterListener(this);
143 });
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700144 }
145 }
146
147 private void onSessionStart() {
Adrian Roos401caae2016-03-04 13:35:21 -0800148 if (FalsingLog.ENABLED) {
149 FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
Adrian Roos8e291a52016-12-09 16:10:19 -0800150 clearPendingWtf();
Adrian Roos401caae2016-03-04 13:35:21 -0800151 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700152 mBouncerOn = false;
153 mSessionActive = true;
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700154
155 if (mHumanInteractionClassifier.isEnabled()) {
156 registerSensors(CLASSIFIER_SENSORS);
157 }
Adrian Roos7bb38a92016-07-21 11:44:01 -0700158 if (mDataCollector.isEnabledFull()) {
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700159 registerSensors(COLLECTOR_SENSORS);
160 }
161 }
162
163 private void registerSensors(int [] sensors) {
164 for (int sensorType : sensors) {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700165 Sensor s = mSensorManager.getDefaultSensor(sensorType);
166 if (s != null) {
Jorim Jaggie549a8d2017-05-15 02:40:05 +0200167
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 });
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700172 }
173 }
174 }
175
Blazej Magnowski6dc59b42015-09-22 15:14:20 -0700176 public boolean isClassiferEnabled() {
177 return mHumanInteractionClassifier.isEnabled();
178 }
179
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700180 private boolean isEnabled() {
181 return mHumanInteractionClassifier.isEnabled() || mDataCollector.isEnabled();
182 }
183
184 /**
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700185 * @return true if the classifier determined that this is not a human interacting with the phone
186 */
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700187 public boolean isFalseTouch() {
Adrian Roos401caae2016-03-04 13:35:21 -0800188 if (FalsingLog.ENABLED) {
Adrian Roos6a04cb12016-03-14 20:21:02 -0700189 // 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.
Adrian Roos8e291a52016-12-09 16:10:19 -0800191 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()
Adrian Roos401caae2016-03-04 13:35:21 -0800198 .append("Session is not active, yet there's a query for a false touch.")
Adrian Roos8e291a52016-12-09 16:10:19 -0800199 .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.")
Adrian Roos401caae2016-03-04 13:35:21 -0800203 .toString());
Adrian Roos8e291a52016-12-09 16:10:19 -0800204
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);
Adrian Roos401caae2016-03-04 13:35:21 -0800220 }
221 }
Adrian Roosca664b92016-04-18 14:40:27 -0700222 if (mAccessibilityManager.isTouchExplorationEnabled()) {
223 // Touch exploration triggers false positives in the classifier and
224 // already sufficiently prevents false unlocks.
225 return false;
226 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700227 return mHumanInteractionClassifier.isFalseTouch();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700228 }
229
Adrian Roos8e291a52016-12-09 16:10:19 -0800230 private void clearPendingWtf() {
231 if (mPendingWtf != null) {
232 mHandler.removeCallbacks(mPendingWtf);
233 mPendingWtf = null;
234 }
235 }
236
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700237 @Override
238 public synchronized void onSensorChanged(SensorEvent event) {
239 mDataCollector.onSensorChanged(event);
240 mHumanInteractionClassifier.onSensorChanged(event);
241 }
242
243 @Override
244 public void onAccuracyChanged(Sensor sensor, int accuracy) {
245 mDataCollector.onAccuracyChanged(sensor, accuracy);
246 }
247
248 public boolean shouldEnforceBouncer() {
249 return mEnforceBouncer;
250 }
251
252 public void setStatusBarState(int state) {
Adrian Roos401caae2016-03-04 13:35:21 -0800253 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 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700259 mState = state;
Adrian Roosc5584ce2016-02-24 14:17:19 -0800260 if (shouldSessionBeActive()) {
261 sessionEntrypoint();
262 } else {
263 sessionExitpoint(false /* force */);
264 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700265 }
266
267 public void onScreenTurningOn() {
Adrian Roos401caae2016-03-04 13:35:21 -0800268 if (FalsingLog.ENABLED) {
269 FalsingLog.i("onScreenTurningOn", new StringBuilder()
270 .append("from=").append(mScreenOn ? 1 : 0)
271 .toString());
Adrian Roos8e291a52016-12-09 16:10:19 -0800272 clearPendingWtf();
Adrian Roos401caae2016-03-04 13:35:21 -0800273 }
Adrian Roosc5584ce2016-02-24 14:17:19 -0800274 mScreenOn = true;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700275 if (sessionEntrypoint()) {
276 mDataCollector.onScreenTurningOn();
277 }
278 }
279
280 public void onScreenOnFromTouch() {
Adrian Roos401caae2016-03-04 13:35:21 -0800281 if (FalsingLog.ENABLED) {
282 FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
283 .append("from=").append(mScreenOn ? 1 : 0)
284 .toString());
285 }
Adrian Roosc5584ce2016-02-24 14:17:19 -0800286 mScreenOn = true;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700287 if (sessionEntrypoint()) {
288 mDataCollector.onScreenOnFromTouch();
289 }
290 }
291
292 public void onScreenOff() {
Adrian Roos401caae2016-03-04 13:35:21 -0800293 if (FalsingLog.ENABLED) {
294 FalsingLog.i("onScreenOff", new StringBuilder()
295 .append("from=").append(mScreenOn ? 1 : 0)
296 .toString());
297 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700298 mDataCollector.onScreenOff();
Adrian Roosc5584ce2016-02-24 14:17:19 -0800299 mScreenOn = false;
300 sessionExitpoint(false /* force */);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700301 }
302
303 public void onSucccessfulUnlock() {
Adrian Roos401caae2016-03-04 13:35:21 -0800304 if (FalsingLog.ENABLED) {
305 FalsingLog.i("onSucccessfulUnlock", "");
306 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700307 mDataCollector.onSucccessfulUnlock();
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700308 }
309
310 public void onBouncerShown() {
Adrian Roos401caae2016-03-04 13:35:21 -0800311 if (FalsingLog.ENABLED) {
312 FalsingLog.i("onBouncerShown", new StringBuilder()
313 .append("from=").append(mBouncerOn ? 1 : 0)
314 .toString());
315 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700316 if (!mBouncerOn) {
317 mBouncerOn = true;
318 mDataCollector.onBouncerShown();
319 }
320 }
321
322 public void onBouncerHidden() {
Adrian Roos401caae2016-03-04 13:35:21 -0800323 if (FalsingLog.ENABLED) {
324 FalsingLog.i("onBouncerHidden", new StringBuilder()
325 .append("from=").append(mBouncerOn ? 1 : 0)
326 .toString());
327 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700328 if (mBouncerOn) {
329 mBouncerOn = false;
330 mDataCollector.onBouncerHidden();
331 }
332 }
333
334 public void onQsDown() {
Adrian Roos401caae2016-03-04 13:35:21 -0800335 if (FalsingLog.ENABLED) {
336 FalsingLog.i("onQsDown", "");
337 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700338 mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700339 mDataCollector.onQsDown();
340 }
341
342 public void setQsExpanded(boolean expanded) {
343 mDataCollector.setQsExpanded(expanded);
344 }
345
346 public void onTrackingStarted() {
Adrian Roos401caae2016-03-04 13:35:21 -0800347 if (FalsingLog.ENABLED) {
348 FalsingLog.i("onTrackingStarted", "");
349 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700350 mHumanInteractionClassifier.setType(Classifier.UNLOCK);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700351 mDataCollector.onTrackingStarted();
352 }
353
354 public void onTrackingStopped() {
355 mDataCollector.onTrackingStopped();
356 }
357
358 public void onNotificationActive() {
359 mDataCollector.onNotificationActive();
360 }
361
Adrian Roos9f0b0022016-11-09 15:56:50 -0800362 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 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700367 mDataCollector.onNotificationDoubleTap();
368 }
369
370 public void setNotificationExpanded() {
371 mDataCollector.setNotificationExpanded();
372 }
373
374 public void onNotificatonStartDraggingDown() {
Adrian Roos401caae2016-03-04 13:35:21 -0800375 if (FalsingLog.ENABLED) {
376 FalsingLog.i("onNotificatonStartDraggingDown", "");
377 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700378 mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700379 mDataCollector.onNotificatonStartDraggingDown();
380 }
381
382 public void onNotificatonStopDraggingDown() {
383 mDataCollector.onNotificatonStopDraggingDown();
384 }
385
386 public void onNotificationDismissed() {
387 mDataCollector.onNotificationDismissed();
388 }
389
390 public void onNotificatonStartDismissing() {
Adrian Roos401caae2016-03-04 13:35:21 -0800391 if (FalsingLog.ENABLED) {
392 FalsingLog.i("onNotificatonStartDismissing", "");
393 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700394 mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700395 mDataCollector.onNotificatonStartDismissing();
396 }
397
398 public void onNotificatonStopDismissing() {
399 mDataCollector.onNotificatonStopDismissing();
400 }
401
402 public void onCameraOn() {
403 mDataCollector.onCameraOn();
404 }
405
406 public void onLeftAffordanceOn() {
407 mDataCollector.onLeftAffordanceOn();
408 }
409
410 public void onAffordanceSwipingStarted(boolean rightCorner) {
Adrian Roos401caae2016-03-04 13:35:21 -0800411 if (FalsingLog.ENABLED) {
412 FalsingLog.i("onAffordanceSwipingStarted", "");
413 }
Blazej Magnowski9f01c5b2015-09-17 15:14:29 -0700414 if (rightCorner) {
415 mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
416 } else {
417 mHumanInteractionClassifier.setType(Classifier.LEFT_AFFORDANCE);
418 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700419 mDataCollector.onAffordanceSwipingStarted(rightCorner);
420 }
421
422 public void onAffordanceSwipingAborted() {
423 mDataCollector.onAffordanceSwipingAborted();
424 }
425
426 public void onUnlockHintStarted() {
427 mDataCollector.onUnlockHintStarted();
428 }
429
430 public void onCameraHintStarted() {
431 mDataCollector.onCameraHintStarted();
432 }
433
434 public void onLeftAffordanceHintStarted() {
435 mDataCollector.onLeftAffordanceHintStarted();
436 }
437
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 }
Adrian Roos401caae2016-03-04 13:35:21 -0800444
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 }
Adrian Roos7bb38a92016-07-21 11:44:01 -0700454
455 public Uri reportRejectedTouch() {
456 if (mDataCollector.isEnabled()) {
457 return mDataCollector.reportRejectedTouch();
458 }
459 return null;
460 }
461
462 public boolean isReportingEnabled() {
463 return mDataCollector.isReportingEnabled();
464 }
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700465}