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