[go: nahoru, domu]

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