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 17 package com.android.server; 18 19 import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis; 20 21 import android.app.ActivityManager; 22 import android.app.StatusBarManager; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.PackageManager; 28 import android.content.res.Resources; 29 import android.database.ContentObserver; 30 import android.hardware.Sensor; 31 import android.hardware.SensorEvent; 32 import android.hardware.SensorEventListener; 33 import android.hardware.SensorManager; 34 import android.hardware.TriggerEvent; 35 import android.hardware.TriggerEventListener; 36 import android.os.Handler; 37 import android.os.PowerManager; 38 import android.os.PowerManager.WakeLock; 39 import android.os.SystemClock; 40 import android.os.SystemProperties; 41 import android.os.Trace; 42 import android.os.UserHandle; 43 import android.provider.Settings; 44 import android.util.MutableBoolean; 45 import android.util.Slog; 46 import android.view.KeyEvent; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.internal.logging.MetricsLogger; 50 import com.android.internal.logging.UiEvent; 51 import com.android.internal.logging.UiEventLogger; 52 import com.android.internal.logging.UiEventLoggerImpl; 53 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 54 import com.android.server.statusbar.StatusBarManagerInternal; 55 import com.android.server.wm.WindowManagerInternal; 56 57 /** 58 * The service that listens for gestures detected in sensor firmware and starts the intent 59 * accordingly. 60 * <p>For now, only camera launch gesture is supported, and in the future, more gestures can be 61 * added.</p> 62 * @hide 63 */ 64 public class GestureLauncherService extends SystemService { 65 private static final boolean DBG = false; 66 private static final boolean DBG_CAMERA_LIFT = false; 67 private static final String TAG = "GestureLauncherService"; 68 69 /** 70 * Time in milliseconds in which the power button must be pressed twice so it will be considered 71 * as a camera launch. 72 */ 73 @VisibleForTesting static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300; 74 75 76 /** 77 * Interval in milliseconds in which the power button must be depressed in succession to be 78 * considered part of an extended sequence of taps. Note that this is a looser threshold than 79 * the camera launch gesture, because the purpose of this threshold is to measure the 80 * frequency of consecutive taps, for evaluation for future gestures. 81 */ 82 @VisibleForTesting static final long POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS = 500; 83 84 /** 85 * Number of taps required to launch emergency gesture ui. 86 */ 87 private static final int EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD = 5; 88 89 /** 90 * Default value of the power button "cooldown" period after the Emergency gesture is triggered. 91 * See {@link Settings.Global#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS} 92 */ 93 private static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT = 3000; 94 95 /** 96 * Maximum value of the power button "cooldown" period after the Emergency gesture is triggered. 97 * The value read from {@link Settings.Global#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS} 98 * is capped at this maximum. 99 */ 100 @VisibleForTesting 101 static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000; 102 103 /** 104 * Number of taps required to launch camera shortcut. 105 */ 106 private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2; 107 108 /** The listener that receives the gesture event. */ 109 private final GestureEventListener mGestureListener = new GestureEventListener(); 110 private final CameraLiftTriggerEventListener mCameraLiftTriggerListener = 111 new CameraLiftTriggerEventListener(); 112 113 private Sensor mCameraLaunchSensor; 114 private Sensor mCameraLiftTriggerSensor; 115 private Context mContext; 116 private final MetricsLogger mMetricsLogger; 117 private PowerManager mPowerManager; 118 private WindowManagerInternal mWindowManagerInternal; 119 120 /** The wake lock held when a gesture is detected. */ 121 private WakeLock mWakeLock; 122 private boolean mCameraLaunchRegistered; 123 private boolean mCameraLiftRegistered; 124 private int mUserId; 125 126 // Below are fields used for event logging only. 127 /** Elapsed real time when the camera gesture is turned on. */ 128 private long mCameraGestureOnTimeMs = 0L; 129 130 /** Elapsed real time when the last camera gesture was detected. */ 131 private long mCameraGestureLastEventTime = 0L; 132 133 /** 134 * How long the sensor 1 has been turned on since camera launch sensor was 135 * subscribed to and when the last camera launch gesture was detected. 136 * <p>Sensor 1 is the main sensor used to detect camera launch gesture.</p> 137 */ 138 private long mCameraGestureSensor1LastOnTimeMs = 0L; 139 140 /** 141 * If applicable, how long the sensor 2 has been turned on since camera 142 * launch sensor was subscribed to and when the last camera launch 143 * gesture was detected. 144 * <p>Sensor 2 is the secondary sensor used to detect camera launch gesture. 145 * This is optional and if only sensor 1 is used for detect camera launch 146 * gesture, this value would always be 0.</p> 147 */ 148 private long mCameraGestureSensor2LastOnTimeMs = 0L; 149 150 /** 151 * Extra information about the event when the last camera launch gesture 152 * was detected. 153 */ 154 private int mCameraLaunchLastEventExtra = 0; 155 156 /** 157 * Whether camera double tap power button gesture is currently enabled; 158 */ 159 private boolean mCameraDoubleTapPowerEnabled; 160 161 /** 162 * Whether emergency gesture is currently enabled 163 */ 164 private boolean mEmergencyGestureEnabled; 165 166 /** 167 * Power button cooldown period in milliseconds, after emergency gesture is triggered. A zero 168 * value means the cooldown period is disabled. 169 */ 170 private int mEmergencyGesturePowerButtonCooldownPeriodMs; 171 172 private long mLastPowerDown; 173 private long mFirstPowerDown; 174 private long mLastEmergencyGestureTriggered; 175 private int mPowerButtonConsecutiveTaps; 176 private int mPowerButtonSlowConsecutiveTaps; 177 private final UiEventLogger mUiEventLogger; 178 179 private boolean mHasFeatureWatch; 180 181 @VisibleForTesting 182 public enum GestureLauncherEvent implements UiEventLogger.UiEventEnum { 183 @UiEvent(doc = "The user lifted the device just the right way to launch the camera.") 184 GESTURE_CAMERA_LIFT(658), 185 186 @UiEvent(doc = "The user wiggled the device just the right way to launch the camera.") 187 GESTURE_CAMERA_WIGGLE(659), 188 189 @UiEvent(doc = "The user double-tapped power quickly enough to launch the camera.") 190 GESTURE_CAMERA_DOUBLE_TAP_POWER(660), 191 192 @UiEvent(doc = "The user multi-tapped power quickly enough to signal an emergency.") 193 GESTURE_EMERGENCY_TAP_POWER(661); 194 195 private final int mId; 196 GestureLauncherEvent(int id)197 GestureLauncherEvent(int id) { 198 mId = id; 199 } 200 201 @Override getId()202 public int getId() { 203 return mId; 204 } 205 } GestureLauncherService(Context context)206 public GestureLauncherService(Context context) { 207 this(context, new MetricsLogger(), new UiEventLoggerImpl()); 208 } 209 210 @VisibleForTesting GestureLauncherService(Context context, MetricsLogger metricsLogger, UiEventLogger uiEventLogger)211 GestureLauncherService(Context context, MetricsLogger metricsLogger, 212 UiEventLogger uiEventLogger) { 213 super(context); 214 mContext = context; 215 mMetricsLogger = metricsLogger; 216 mUiEventLogger = uiEventLogger; 217 } 218 219 @Override onStart()220 public void onStart() { 221 LocalServices.addService(GestureLauncherService.class, this); 222 } 223 224 @Override onBootPhase(int phase)225 public void onBootPhase(int phase) { 226 if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { 227 Resources resources = mContext.getResources(); 228 if (!isGestureLauncherEnabled(resources)) { 229 if (DBG) Slog.d(TAG, "Gesture launcher is disabled in system properties."); 230 return; 231 } 232 233 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); 234 mPowerManager = (PowerManager) mContext.getSystemService( 235 Context.POWER_SERVICE); 236 mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 237 "GestureLauncherService"); 238 updateCameraRegistered(); 239 updateCameraDoubleTapPowerEnabled(); 240 updateEmergencyGestureEnabled(); 241 updateEmergencyGesturePowerButtonCooldownPeriodMs(); 242 243 mUserId = ActivityManager.getCurrentUser(); 244 mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); 245 registerContentObservers(); 246 247 mHasFeatureWatch = 248 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); 249 } 250 } 251 registerContentObservers()252 private void registerContentObservers() { 253 mContext.getContentResolver().registerContentObserver( 254 Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED), 255 false, mSettingObserver, mUserId); 256 mContext.getContentResolver().registerContentObserver( 257 Settings.Secure.getUriFor(Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED), 258 false, mSettingObserver, mUserId); 259 mContext.getContentResolver().registerContentObserver( 260 Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED), 261 false, mSettingObserver, mUserId); 262 mContext.getContentResolver().registerContentObserver( 263 Settings.Secure.getUriFor(Settings.Secure.EMERGENCY_GESTURE_ENABLED), 264 false, mSettingObserver, mUserId); 265 mContext.getContentResolver().registerContentObserver( 266 Settings.Global.getUriFor( 267 Settings.Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS), 268 false, mSettingObserver, mUserId); 269 } 270 updateCameraRegistered()271 private void updateCameraRegistered() { 272 Resources resources = mContext.getResources(); 273 if (isCameraLaunchSettingEnabled(mContext, mUserId)) { 274 registerCameraLaunchGesture(resources); 275 } else { 276 unregisterCameraLaunchGesture(); 277 } 278 279 if (isCameraLiftTriggerSettingEnabled(mContext, mUserId)) { 280 registerCameraLiftTrigger(resources); 281 } else { 282 unregisterCameraLiftTrigger(); 283 } 284 } 285 286 @VisibleForTesting updateCameraDoubleTapPowerEnabled()287 void updateCameraDoubleTapPowerEnabled() { 288 boolean enabled = isCameraDoubleTapPowerSettingEnabled(mContext, mUserId); 289 synchronized (this) { 290 mCameraDoubleTapPowerEnabled = enabled; 291 } 292 } 293 294 @VisibleForTesting updateEmergencyGestureEnabled()295 void updateEmergencyGestureEnabled() { 296 boolean enabled = isEmergencyGestureSettingEnabled(mContext, mUserId); 297 synchronized (this) { 298 mEmergencyGestureEnabled = enabled; 299 } 300 } 301 302 @VisibleForTesting updateEmergencyGesturePowerButtonCooldownPeriodMs()303 void updateEmergencyGesturePowerButtonCooldownPeriodMs() { 304 int cooldownPeriodMs = getEmergencyGesturePowerButtonCooldownPeriodMs(mContext, mUserId); 305 synchronized (this) { 306 mEmergencyGesturePowerButtonCooldownPeriodMs = cooldownPeriodMs; 307 } 308 } 309 unregisterCameraLaunchGesture()310 private void unregisterCameraLaunchGesture() { 311 if (mCameraLaunchRegistered) { 312 mCameraLaunchRegistered = false; 313 mCameraGestureOnTimeMs = 0L; 314 mCameraGestureLastEventTime = 0L; 315 mCameraGestureSensor1LastOnTimeMs = 0; 316 mCameraGestureSensor2LastOnTimeMs = 0; 317 mCameraLaunchLastEventExtra = 0; 318 319 SensorManager sensorManager = (SensorManager) mContext.getSystemService( 320 Context.SENSOR_SERVICE); 321 sensorManager.unregisterListener(mGestureListener); 322 } 323 } 324 325 /** 326 * Registers for the camera launch gesture. 327 */ registerCameraLaunchGesture(Resources resources)328 private void registerCameraLaunchGesture(Resources resources) { 329 if (mCameraLaunchRegistered) { 330 return; 331 } 332 mCameraGestureOnTimeMs = SystemClock.elapsedRealtime(); 333 mCameraGestureLastEventTime = mCameraGestureOnTimeMs; 334 SensorManager sensorManager = (SensorManager) mContext.getSystemService( 335 Context.SENSOR_SERVICE); 336 int cameraLaunchGestureId = resources.getInteger( 337 com.android.internal.R.integer.config_cameraLaunchGestureSensorType); 338 if (cameraLaunchGestureId != -1) { 339 mCameraLaunchRegistered = false; 340 String sensorName = resources.getString( 341 com.android.internal.R.string.config_cameraLaunchGestureSensorStringType); 342 mCameraLaunchSensor = sensorManager.getDefaultSensor( 343 cameraLaunchGestureId, 344 true /*wakeUp*/); 345 346 // Compare the camera gesture string type to that in the resource file to make 347 // sure we are registering the correct sensor. This is redundant check, it 348 // makes the code more robust. 349 if (mCameraLaunchSensor != null) { 350 if (sensorName.equals(mCameraLaunchSensor.getStringType())) { 351 mCameraLaunchRegistered = sensorManager.registerListener(mGestureListener, 352 mCameraLaunchSensor, 0); 353 } else { 354 String message = String.format("Wrong configuration. Sensor type and sensor " 355 + "string type don't match: %s in resources, %s in the sensor.", 356 sensorName, mCameraLaunchSensor.getStringType()); 357 throw new RuntimeException(message); 358 } 359 } 360 if (DBG) Slog.d(TAG, "Camera launch sensor registered: " + mCameraLaunchRegistered); 361 } else { 362 if (DBG) Slog.d(TAG, "Camera launch sensor is not specified."); 363 } 364 } 365 unregisterCameraLiftTrigger()366 private void unregisterCameraLiftTrigger() { 367 if (mCameraLiftRegistered) { 368 mCameraLiftRegistered = false; 369 370 SensorManager sensorManager = (SensorManager) mContext.getSystemService( 371 Context.SENSOR_SERVICE); 372 sensorManager.cancelTriggerSensor(mCameraLiftTriggerListener, mCameraLiftTriggerSensor); 373 } 374 } 375 376 /** 377 * Registers for the camera lift trigger. 378 */ registerCameraLiftTrigger(Resources resources)379 private void registerCameraLiftTrigger(Resources resources) { 380 if (mCameraLiftRegistered) { 381 return; 382 } 383 SensorManager sensorManager = (SensorManager) mContext.getSystemService( 384 Context.SENSOR_SERVICE); 385 int cameraLiftTriggerId = resources.getInteger( 386 com.android.internal.R.integer.config_cameraLiftTriggerSensorType); 387 if (cameraLiftTriggerId != -1) { 388 mCameraLiftRegistered = false; 389 String sensorName = resources.getString( 390 com.android.internal.R.string.config_cameraLiftTriggerSensorStringType); 391 mCameraLiftTriggerSensor = sensorManager.getDefaultSensor( 392 cameraLiftTriggerId, 393 true /*wakeUp*/); 394 395 // Compare the camera lift trigger string type to that in the resource file to make 396 // sure we are registering the correct sensor. This is redundant check, it 397 // makes the code more robust. 398 if (mCameraLiftTriggerSensor != null) { 399 if (sensorName.equals(mCameraLiftTriggerSensor.getStringType())) { 400 mCameraLiftRegistered = sensorManager.requestTriggerSensor(mCameraLiftTriggerListener, 401 mCameraLiftTriggerSensor); 402 } else { 403 String message = String.format("Wrong configuration. Sensor type and sensor " 404 + "string type don't match: %s in resources, %s in the sensor.", 405 sensorName, mCameraLiftTriggerSensor.getStringType()); 406 throw new RuntimeException(message); 407 } 408 } 409 if (DBG) Slog.d(TAG, "Camera lift trigger sensor registered: " + mCameraLiftRegistered); 410 } else { 411 if (DBG) Slog.d(TAG, "Camera lift trigger sensor is not specified."); 412 } 413 } 414 isCameraLaunchSettingEnabled(Context context, int userId)415 public static boolean isCameraLaunchSettingEnabled(Context context, int userId) { 416 return isCameraLaunchEnabled(context.getResources()) 417 && (Settings.Secure.getIntForUser(context.getContentResolver(), 418 Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0); 419 } 420 isCameraDoubleTapPowerSettingEnabled(Context context, int userId)421 public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) { 422 return isCameraDoubleTapPowerEnabled(context.getResources()) 423 && (Settings.Secure.getIntForUser(context.getContentResolver(), 424 Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0); 425 } 426 isCameraLiftTriggerSettingEnabled(Context context, int userId)427 public static boolean isCameraLiftTriggerSettingEnabled(Context context, int userId) { 428 return isCameraLiftTriggerEnabled(context.getResources()) 429 && (Settings.Secure.getIntForUser(context.getContentResolver(), 430 Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED, 431 Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED_DEFAULT, userId) != 0); 432 } 433 434 /** 435 * Whether to enable emergency gesture. 436 */ isEmergencyGestureSettingEnabled(Context context, int userId)437 public static boolean isEmergencyGestureSettingEnabled(Context context, int userId) { 438 return isEmergencyGestureEnabled(context.getResources()) 439 && Settings.Secure.getIntForUser(context.getContentResolver(), 440 Settings.Secure.EMERGENCY_GESTURE_ENABLED, 441 isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0; 442 } 443 444 /** 445 * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The 446 * value is capped at a maximum 447 * {@link GestureLauncherService#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX}. If the 448 * value is zero, it means the cooldown period is disabled. 449 */ 450 @VisibleForTesting getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId)451 static int getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId) { 452 int cooldown = Settings.Global.getInt(context.getContentResolver(), 453 Settings.Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS, 454 EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT); 455 456 return Math.min(cooldown, EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX); 457 } 458 459 /** 460 * Whether to enable the camera launch gesture. 461 */ isCameraLaunchEnabled(Resources resources)462 private static boolean isCameraLaunchEnabled(Resources resources) { 463 boolean configSet = resources.getInteger( 464 com.android.internal.R.integer.config_cameraLaunchGestureSensorType) != -1; 465 return configSet && 466 !SystemProperties.getBoolean("gesture.disable_camera_launch", false); 467 } 468 469 @VisibleForTesting isCameraDoubleTapPowerEnabled(Resources resources)470 static boolean isCameraDoubleTapPowerEnabled(Resources resources) { 471 return resources.getBoolean( 472 com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled); 473 } 474 isCameraLiftTriggerEnabled(Resources resources)475 private static boolean isCameraLiftTriggerEnabled(Resources resources) { 476 boolean configSet = resources.getInteger( 477 com.android.internal.R.integer.config_cameraLiftTriggerSensorType) != -1; 478 return configSet; 479 } 480 481 /** 482 * Whether or not the emergency gesture feature is enabled by platform 483 */ isEmergencyGestureEnabled(Resources resources)484 private static boolean isEmergencyGestureEnabled(Resources resources) { 485 return resources.getBoolean(com.android.internal.R.bool.config_emergencyGestureEnabled); 486 } 487 isDefaultEmergencyGestureEnabled(Resources resources)488 private static boolean isDefaultEmergencyGestureEnabled(Resources resources) { 489 return resources.getBoolean( 490 com.android.internal.R.bool.config_defaultEmergencyGestureEnabled); 491 } 492 493 /** 494 * Whether GestureLauncherService should be enabled according to system properties. 495 */ isGestureLauncherEnabled(Resources resources)496 public static boolean isGestureLauncherEnabled(Resources resources) { 497 return isCameraLaunchEnabled(resources) 498 || isCameraDoubleTapPowerEnabled(resources) 499 || isCameraLiftTriggerEnabled(resources) 500 || isEmergencyGestureEnabled(resources); 501 } 502 503 /** 504 * Attempts to intercept power key down event by detecting certain gesture patterns 505 * 506 * @param interactive true if the event's policy contains {@code FLAG_INTERACTIVE} 507 * @param outLaunched true if some action is taken as part of the key intercept (eg, app launch) 508 * @return true if the key down event is intercepted 509 */ interceptPowerKeyDown(KeyEvent event, boolean interactive, MutableBoolean outLaunched)510 public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive, 511 MutableBoolean outLaunched) { 512 if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0 513 && event.getEventTime() - mLastEmergencyGestureTriggered 514 < mEmergencyGesturePowerButtonCooldownPeriodMs) { 515 Slog.i(TAG, String.format( 516 "Suppressing power button: within %dms cooldown period after Emergency " 517 + "Gesture. Begin=%dms, end=%dms.", 518 mEmergencyGesturePowerButtonCooldownPeriodMs, 519 mLastEmergencyGestureTriggered, 520 mLastEmergencyGestureTriggered + mEmergencyGesturePowerButtonCooldownPeriodMs)); 521 outLaunched.value = false; 522 return true; 523 } 524 525 if (event.isLongPress()) { 526 // Long presses are sent as a second key down. If the long press threshold is set lower 527 // than the double tap of sequence interval thresholds, this could cause false double 528 // taps or consecutive taps, so we want to ignore the long press event. 529 outLaunched.value = false; 530 return false; 531 } 532 boolean launchCamera = false; 533 boolean launchEmergencyGesture = false; 534 boolean intercept = false; 535 long powerTapInterval; 536 synchronized (this) { 537 powerTapInterval = event.getEventTime() - mLastPowerDown; 538 mLastPowerDown = event.getEventTime(); 539 if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) { 540 // Tap too slow, reset consecutive tap counts. 541 mFirstPowerDown = event.getEventTime(); 542 mPowerButtonConsecutiveTaps = 1; 543 mPowerButtonSlowConsecutiveTaps = 1; 544 } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) { 545 // Tap too slow for shortcuts 546 mFirstPowerDown = event.getEventTime(); 547 mPowerButtonConsecutiveTaps = 1; 548 mPowerButtonSlowConsecutiveTaps++; 549 } else { 550 // Fast consecutive tap 551 mPowerButtonConsecutiveTaps++; 552 mPowerButtonSlowConsecutiveTaps++; 553 } 554 // Check if we need to launch camera or emergency gesture flows 555 if (mEmergencyGestureEnabled) { 556 // Commit to intercepting the powerkey event after the second "quick" tap to avoid 557 // lockscreen changes between launching camera and the emergency gesture flow. 558 // Since watch doesn't have camera gesture, only intercept power key event after 559 // emergency gesture tap count. 560 if (mPowerButtonConsecutiveTaps 561 > (mHasFeatureWatch ? EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD : 1)) { 562 intercept = interactive; 563 } 564 if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) { 565 long emergencyGestureSpentTime = event.getEventTime() - mFirstPowerDown; 566 long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt( 567 mContext.getContentResolver(), 568 Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS, 569 mContext.getResources().getInteger( 570 config_defaultMinEmergencyGestureTapDurationMillis)); 571 if (emergencyGestureSpentTime <= emergencyGestureTapDetectionMinTimeMs) { 572 Slog.i(TAG, "Emergency gesture detected but it's too fast. Gesture time: " 573 + emergencyGestureSpentTime + " ms"); 574 // Reset consecutive tap counts. 575 mFirstPowerDown = event.getEventTime(); 576 mPowerButtonConsecutiveTaps = 1; 577 mPowerButtonSlowConsecutiveTaps = 1; 578 } else { 579 Slog.i(TAG, "Emergency gesture detected. Gesture time: " 580 + emergencyGestureSpentTime + " ms"); 581 launchEmergencyGesture = true; 582 mMetricsLogger.histogram("emergency_gesture_spent_time", 583 (int) emergencyGestureSpentTime); 584 585 } 586 } 587 } 588 if (mCameraDoubleTapPowerEnabled 589 && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS 590 && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) { 591 launchCamera = true; 592 intercept = interactive; 593 } 594 } 595 if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) { 596 Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps) 597 + " consecutive power button taps detected, " 598 + Long.valueOf(mPowerButtonSlowConsecutiveTaps) 599 + " consecutive slow power button taps detected"); 600 } 601 if (launchCamera) { 602 Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval=" 603 + powerTapInterval + "ms"); 604 launchCamera = handleCameraGesture(false /* useWakelock */, 605 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP); 606 if (launchCamera) { 607 mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, 608 (int) powerTapInterval); 609 mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER); 610 } 611 } else if (launchEmergencyGesture) { 612 Slog.i(TAG, "Emergency gesture detected, launching."); 613 launchEmergencyGesture = handleEmergencyGesture(); 614 mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER); 615 // Record emergency trigger time if emergency UI was launched 616 if (launchEmergencyGesture) { 617 synchronized (this) { 618 mLastEmergencyGestureTriggered = event.getEventTime(); 619 } 620 } 621 } 622 mMetricsLogger.histogram("power_consecutive_short_tap_count", 623 mPowerButtonSlowConsecutiveTaps); 624 mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval); 625 626 outLaunched.value = launchCamera || launchEmergencyGesture; 627 // Intercept power key event if the press is part of a gesture (camera, eGesture) and the 628 // user has completed setup. 629 return intercept && isUserSetupComplete(); 630 } 631 /** 632 * @return true if camera was launched, false otherwise. 633 */ 634 @VisibleForTesting handleCameraGesture(boolean useWakelock, int source)635 boolean handleCameraGesture(boolean useWakelock, int source) { 636 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "GestureLauncher:handleCameraGesture"); 637 try { 638 boolean userSetupComplete = isUserSetupComplete(); 639 if (!userSetupComplete) { 640 if (DBG) { 641 Slog.d(TAG, String.format( 642 "userSetupComplete = %s, ignoring camera gesture.", 643 userSetupComplete)); 644 } 645 return false; 646 } 647 if (DBG) { 648 Slog.d(TAG, String.format( 649 "userSetupComplete = %s, performing camera gesture.", 650 userSetupComplete)); 651 } 652 653 if (useWakelock) { 654 // Make sure we don't sleep too early 655 mWakeLock.acquire(500L); 656 } 657 StatusBarManagerInternal service = LocalServices.getService( 658 StatusBarManagerInternal.class); 659 service.onCameraLaunchGestureDetected(source); 660 return true; 661 } finally { 662 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 663 } 664 } 665 666 /** 667 * @return true if emergency gesture UI was launched, false otherwise. 668 */ 669 @VisibleForTesting handleEmergencyGesture()670 boolean handleEmergencyGesture() { 671 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, 672 "GestureLauncher:handleEmergencyGesture"); 673 try { 674 boolean userSetupComplete = isUserSetupComplete(); 675 if (!userSetupComplete) { 676 if (DBG) { 677 Slog.d(TAG, String.format( 678 "userSetupComplete = %s, ignoring emergency gesture.", 679 userSetupComplete)); 680 } 681 return false; 682 } 683 if (DBG) { 684 Slog.d(TAG, String.format( 685 "userSetupComplete = %s, performing emergency gesture.", 686 userSetupComplete)); 687 } 688 689 StatusBarManagerInternal service = LocalServices.getService( 690 StatusBarManagerInternal.class); 691 service.onEmergencyActionLaunchGestureDetected(); 692 return true; 693 } finally { 694 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 695 } 696 } 697 isUserSetupComplete()698 private boolean isUserSetupComplete() { 699 return Settings.Secure.getIntForUser(mContext.getContentResolver(), 700 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; 701 } 702 703 private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() { 704 @Override 705 public void onReceive(Context context, Intent intent) { 706 if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { 707 mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 708 mContext.getContentResolver().unregisterContentObserver(mSettingObserver); 709 registerContentObservers(); 710 updateCameraRegistered(); 711 updateCameraDoubleTapPowerEnabled(); 712 updateEmergencyGestureEnabled(); 713 updateEmergencyGesturePowerButtonCooldownPeriodMs(); 714 } 715 } 716 }; 717 718 private final ContentObserver mSettingObserver = new ContentObserver(new Handler()) { 719 public void onChange(boolean selfChange, android.net.Uri uri, int userId) { 720 if (userId == mUserId) { 721 updateCameraRegistered(); 722 updateCameraDoubleTapPowerEnabled(); 723 updateEmergencyGestureEnabled(); 724 updateEmergencyGesturePowerButtonCooldownPeriodMs(); 725 } 726 } 727 }; 728 729 private final class GestureEventListener implements SensorEventListener { 730 @Override onSensorChanged(SensorEvent event)731 public void onSensorChanged(SensorEvent event) { 732 if (!mCameraLaunchRegistered) { 733 if (DBG) Slog.d(TAG, "Ignoring gesture event because it's unregistered."); 734 return; 735 } 736 if (event.sensor == mCameraLaunchSensor) { 737 if (DBG) { 738 float[] values = event.values; 739 Slog.d(TAG, String.format("Received a camera launch event: " + 740 "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2])); 741 } 742 if (handleCameraGesture(true /* useWakelock */, 743 StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) { 744 mMetricsLogger.action(MetricsEvent.ACTION_WIGGLE_CAMERA_GESTURE); 745 mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_WIGGLE); 746 trackCameraLaunchEvent(event); 747 } 748 return; 749 } 750 } 751 752 @Override onAccuracyChanged(Sensor sensor, int accuracy)753 public void onAccuracyChanged(Sensor sensor, int accuracy) { 754 // Ignored. 755 } 756 trackCameraLaunchEvent(SensorEvent event)757 private void trackCameraLaunchEvent(SensorEvent event) { 758 long now = SystemClock.elapsedRealtime(); 759 long totalDuration = now - mCameraGestureOnTimeMs; 760 // values[0]: ratio between total time duration when accel is turned on and time 761 // duration since camera launch gesture is subscribed. 762 // values[1]: ratio between total time duration when gyro is turned on and time duration 763 // since camera launch gesture is subscribed. 764 // values[2]: extra information 765 float[] values = event.values; 766 767 long sensor1OnTime = (long) (totalDuration * (double) values[0]); 768 long sensor2OnTime = (long) (totalDuration * (double) values[1]); 769 int extra = (int) values[2]; 770 771 // We only log the difference in the event log to make aggregation easier. 772 long gestureOnTimeDiff = now - mCameraGestureLastEventTime; 773 long sensor1OnTimeDiff = sensor1OnTime - mCameraGestureSensor1LastOnTimeMs; 774 long sensor2OnTimeDiff = sensor2OnTime - mCameraGestureSensor2LastOnTimeMs; 775 int extraDiff = extra - mCameraLaunchLastEventExtra; 776 777 // Gating against negative time difference. This doesn't usually happen, but it may 778 // happen because of numeric errors. 779 if (gestureOnTimeDiff < 0 || sensor1OnTimeDiff < 0 || sensor2OnTimeDiff < 0) { 780 if (DBG) Slog.d(TAG, "Skipped event logging because negative numbers."); 781 return; 782 } 783 784 if (DBG) Slog.d(TAG, String.format("totalDuration: %d, sensor1OnTime: %s, " + 785 "sensor2OnTime: %d, extra: %d", 786 gestureOnTimeDiff, 787 sensor1OnTimeDiff, 788 sensor2OnTimeDiff, 789 extraDiff)); 790 EventLogTags.writeCameraGestureTriggered( 791 gestureOnTimeDiff, 792 sensor1OnTimeDiff, 793 sensor2OnTimeDiff, 794 extraDiff); 795 796 mCameraGestureLastEventTime = now; 797 mCameraGestureSensor1LastOnTimeMs = sensor1OnTime; 798 mCameraGestureSensor2LastOnTimeMs = sensor2OnTime; 799 mCameraLaunchLastEventExtra = extra; 800 } 801 } 802 803 private final class CameraLiftTriggerEventListener extends TriggerEventListener { 804 @Override onTrigger(TriggerEvent event)805 public void onTrigger(TriggerEvent event) { 806 if (DBG_CAMERA_LIFT) Slog.d(TAG, String.format("onTrigger event - time: %d, name: %s", 807 event.timestamp, event.sensor.getName())); 808 if (!mCameraLiftRegistered) { 809 if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring camera lift event because it's " + 810 "unregistered."); 811 return; 812 } 813 if (event.sensor == mCameraLiftTriggerSensor) { 814 Resources resources = mContext.getResources(); 815 SensorManager sensorManager = (SensorManager) mContext.getSystemService( 816 Context.SENSOR_SERVICE); 817 boolean keyguardShowingAndNotOccluded = 818 mWindowManagerInternal.isKeyguardShowingAndNotOccluded(); 819 boolean interactive = mPowerManager.isInteractive(); 820 if (DBG_CAMERA_LIFT) { 821 float[] values = event.values; 822 Slog.d(TAG, String.format("Received a camera lift trigger " + 823 "event: values=[%.4f], keyguard showing: %b, interactive: %b", values[0], 824 keyguardShowingAndNotOccluded, interactive)); 825 } 826 if (keyguardShowingAndNotOccluded || !interactive) { 827 if (handleCameraGesture(true /* useWakelock */, 828 StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)) { 829 MetricsLogger.action(mContext, MetricsEvent.ACTION_CAMERA_LIFT_TRIGGER); 830 mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_LIFT); 831 } 832 } else { 833 if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring lift event"); 834 } 835 836 mCameraLiftRegistered = sensorManager.requestTriggerSensor( 837 mCameraLiftTriggerListener, mCameraLiftTriggerSensor); 838 839 if (DBG_CAMERA_LIFT) Slog.d(TAG, "Camera lift trigger sensor re-registered: " + 840 mCameraLiftRegistered); 841 return; 842 } 843 } 844 } 845 } 846