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