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