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