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