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,
470                 isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0;
471     }
472 
473     /**
474      * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
475      * value is capped at a maximum
476      * {@link GestureLauncherService#EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX}. If the
477      * value is zero, it means the cooldown period is disabled.
478      */
479     @VisibleForTesting
getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId)480     static int getEmergencyGesturePowerButtonCooldownPeriodMs(Context context, int userId) {
481         int cooldown = Settings.Global.getInt(context.getContentResolver(),
482                 Settings.Global.EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS,
483                 EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_DEFAULT);
484 
485         return Math.min(cooldown, EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX);
486     }
487 
488     /**
489      * Whether to enable the camera launch gesture.
490      */
isCameraLaunchEnabled(Resources resources)491     private static boolean isCameraLaunchEnabled(Resources resources) {
492         boolean configSet = resources.getInteger(
493                 com.android.internal.R.integer.config_cameraLaunchGestureSensorType) != -1;
494         return configSet &&
495                 !SystemProperties.getBoolean("gesture.disable_camera_launch", false);
496     }
497 
498     @VisibleForTesting
isCameraDoubleTapPowerEnabled(Resources resources)499     static boolean isCameraDoubleTapPowerEnabled(Resources resources) {
500         return resources.getBoolean(
501                 com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled);
502     }
503 
isCameraLiftTriggerEnabled(Resources resources)504     private static boolean isCameraLiftTriggerEnabled(Resources resources) {
505         boolean configSet = resources.getInteger(
506                 com.android.internal.R.integer.config_cameraLiftTriggerSensorType) != -1;
507         return configSet;
508     }
509 
510     /**
511      * Whether or not the emergency gesture feature is enabled by platform
512      */
isEmergencyGestureEnabled(Resources resources)513     private static boolean isEmergencyGestureEnabled(Resources resources) {
514         return resources.getBoolean(com.android.internal.R.bool.config_emergencyGestureEnabled);
515     }
516 
isDefaultEmergencyGestureEnabled(Resources resources)517     private static boolean isDefaultEmergencyGestureEnabled(Resources resources) {
518         return resources.getBoolean(
519                 com.android.internal.R.bool.config_defaultEmergencyGestureEnabled);
520     }
521 
522     /**
523      * Whether GestureLauncherService should be enabled according to system properties.
524      */
isGestureLauncherEnabled(Resources resources)525     public static boolean isGestureLauncherEnabled(Resources resources) {
526         return isCameraLaunchEnabled(resources)
527                 || isCameraDoubleTapPowerEnabled(resources)
528                 || isCameraLiftTriggerEnabled(resources)
529                 || isEmergencyGestureEnabled(resources);
530     }
531 
532     /**
533      * Attempts to intercept power key down event by detecting certain gesture patterns
534      *
535      * @param interactive true if the event's policy contains {@code FLAG_INTERACTIVE}
536      * @param outLaunched true if some action is taken as part of the key intercept (eg, app launch)
537      * @return true if the key down event is intercepted
538      */
interceptPowerKeyDown(KeyEvent event, boolean interactive, MutableBoolean outLaunched)539     public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
540             MutableBoolean outLaunched) {
541         if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
542                 && event.getEventTime() - mLastEmergencyGestureTriggered
543                 < mEmergencyGesturePowerButtonCooldownPeriodMs) {
544             Slog.i(TAG, String.format(
545                     "Suppressing power button: within %dms cooldown period after Emergency "
546                             + "Gesture. Begin=%dms, end=%dms.",
547                     mEmergencyGesturePowerButtonCooldownPeriodMs,
548                     mLastEmergencyGestureTriggered,
549                     mLastEmergencyGestureTriggered + mEmergencyGesturePowerButtonCooldownPeriodMs));
550             outLaunched.value = false;
551             return true;
552         }
553 
554         if (event.isLongPress()) {
555             // Long presses are sent as a second key down. If the long press threshold is set lower
556             // than the double tap of sequence interval thresholds, this could cause false double
557             // taps or consecutive taps, so we want to ignore the long press event.
558             outLaunched.value = false;
559             return false;
560         }
561         boolean launchCamera = false;
562         boolean launchEmergencyGesture = false;
563         boolean intercept = false;
564         long powerTapInterval;
565         synchronized (this) {
566             powerTapInterval = event.getEventTime() - mLastPowerDown;
567             mLastPowerDown = event.getEventTime();
568             if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
569                 // Tap too slow, reset consecutive tap counts.
570                 mFirstPowerDown  = event.getEventTime();
571                 mPowerButtonConsecutiveTaps = 1;
572                 mPowerButtonSlowConsecutiveTaps = 1;
573             } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
574                 // Tap too slow for shortcuts
575                 mFirstPowerDown  = event.getEventTime();
576                 mPowerButtonConsecutiveTaps = 1;
577                 mPowerButtonSlowConsecutiveTaps++;
578             } else {
579                 // Fast consecutive tap
580                 mPowerButtonConsecutiveTaps++;
581                 mPowerButtonSlowConsecutiveTaps++;
582             }
583             // Check if we need to launch camera or emergency gesture flows
584             if (mEmergencyGestureEnabled) {
585                 // Commit to intercepting the powerkey event after the second "quick" tap to avoid
586                 // lockscreen changes between launching camera and the emergency gesture flow.
587                 // Since watch doesn't have camera gesture, only intercept power key event after
588                 // emergency gesture tap count.
589                 if (mPowerButtonConsecutiveTaps
590                         > (mHasFeatureWatch ? EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD : 1)) {
591                     intercept = interactive;
592                 }
593                 if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) {
594                     long emergencyGestureSpentTime = event.getEventTime() - mFirstPowerDown;
595                     long emergencyGestureTapDetectionMinTimeMs = Settings.Global.getInt(
596                             mContext.getContentResolver(),
597                             Settings.Global.EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS,
598                             EMERGENCY_GESTURE_TAP_DETECTION_MIN_TIME_MS);
599                     if (emergencyGestureSpentTime <= emergencyGestureTapDetectionMinTimeMs) {
600                         Slog.i(TAG, "Emergency gesture detected but it's too fast. Gesture time: "
601                                 + emergencyGestureSpentTime + " ms");
602                         // Reset consecutive tap counts.
603                         mFirstPowerDown = event.getEventTime();
604                         mPowerButtonConsecutiveTaps = 1;
605                         mPowerButtonSlowConsecutiveTaps = 1;
606                     } else {
607                         Slog.i(TAG, "Emergency gesture detected. Gesture time: "
608                                 + emergencyGestureSpentTime + " ms");
609                         launchEmergencyGesture = true;
610                         mMetricsLogger.histogram("emergency_gesture_spent_time",
611                                 (int) emergencyGestureSpentTime);
612 
613                     }
614                 }
615             }
616             if (mCameraDoubleTapPowerEnabled
617                     && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
618                     && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
619                 launchCamera = true;
620                 intercept = interactive;
621             }
622         }
623         if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
624             Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps)
625                     + " consecutive power button taps detected, "
626                     + Long.valueOf(mPowerButtonSlowConsecutiveTaps)
627                     + " consecutive slow power button taps detected");
628         }
629         if (launchCamera) {
630             Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
631                     + powerTapInterval + "ms");
632             launchCamera = handleCameraGesture(false /* useWakelock */,
633                     StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
634             if (launchCamera) {
635                 mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
636                         (int) powerTapInterval);
637                 mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
638             }
639         } else if (launchEmergencyGesture) {
640             Slog.i(TAG, "Emergency gesture detected, launching.");
641             launchEmergencyGesture = handleEmergencyGesture();
642             mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
643             // Record emergency trigger time if emergency UI was launched
644             if (launchEmergencyGesture) {
645                 synchronized (this) {
646                     mLastEmergencyGestureTriggered = event.getEventTime();
647                 }
648             }
649         }
650         mMetricsLogger.histogram("power_consecutive_short_tap_count",
651                 mPowerButtonSlowConsecutiveTaps);
652         mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);
653 
654         outLaunched.value = launchCamera || launchEmergencyGesture;
655         // Intercept power key event if the press is part of a gesture (camera, eGesture) and the
656         // user has completed setup.
657         return intercept && isUserSetupComplete();
658     }
659     /**
660      * @return true if camera was launched, false otherwise.
661      */
662     @VisibleForTesting
handleCameraGesture(boolean useWakelock, int source)663     boolean handleCameraGesture(boolean useWakelock, int source) {
664         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "GestureLauncher:handleCameraGesture");
665         try {
666             boolean userSetupComplete = isUserSetupComplete();
667             if (!userSetupComplete) {
668                 if (DBG) {
669                     Slog.d(TAG, String.format(
670                             "userSetupComplete = %s, ignoring camera gesture.",
671                             userSetupComplete));
672                 }
673                 return false;
674             }
675             if (DBG) {
676                 Slog.d(TAG, String.format(
677                         "userSetupComplete = %s, performing camera gesture.",
678                         userSetupComplete));
679             }
680 
681             if (useWakelock) {
682                 // Make sure we don't sleep too early
683                 mWakeLock.acquire(500L);
684             }
685             StatusBarManagerInternal service = LocalServices.getService(
686                     StatusBarManagerInternal.class);
687             service.onCameraLaunchGestureDetected(source);
688             return true;
689         } finally {
690             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
691         }
692     }
693 
694     /**
695      * @return true if emergency gesture UI was launched, false otherwise.
696      */
697     @VisibleForTesting
handleEmergencyGesture()698     boolean handleEmergencyGesture() {
699         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
700                 "GestureLauncher:handleEmergencyGesture");
701         try {
702             boolean userSetupComplete = isUserSetupComplete();
703             if (!userSetupComplete) {
704                 if (DBG) {
705                     Slog.d(TAG, String.format(
706                             "userSetupComplete = %s, ignoring emergency gesture.",
707                             userSetupComplete));
708                 }
709                 return false;
710             }
711             if (DBG) {
712                 Slog.d(TAG, String.format(
713                         "userSetupComplete = %s, performing emergency gesture.",
714                         userSetupComplete));
715             }
716 
717             if (mHasFeatureWatch) {
718                 onEmergencyGestureDetectedOnWatch();
719                 return true;
720             }
721 
722             StatusBarManagerInternal service = LocalServices.getService(
723                     StatusBarManagerInternal.class);
724             service.onEmergencyActionLaunchGestureDetected();
725             return true;
726         } finally {
727             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
728         }
729     }
730 
onEmergencyGestureDetectedOnWatch()731     private void onEmergencyGestureDetectedOnWatch() {
732         Intent emergencyIntent =
733                 new Intent(
734                         isInRetailMode()
735                                 ? WEAR_LAUNCH_EMERGENCY_RETAIL_ACTION
736                                 : WEAR_LAUNCH_EMERGENCY_ACTION);
737         PackageManager pm = mContext.getPackageManager();
738         ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0);
739         if (resolveInfo == null) {
740             Slog.w(TAG, "Couldn't find an app to process the emergency intent "
741                     + emergencyIntent.getAction());
742             return;
743         }
744 
745         Vibrator vibrator = mContext.getSystemService(Vibrator.class);
746         vibrator.vibrate(VibrationEffect.createOneShot(mVibrateMilliSecondsForPanicGesture,
747                 VibrationEffect.DEFAULT_AMPLITUDE));
748 
749         emergencyIntent.setComponent(
750                 new ComponentName(
751                         resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name));
752         emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
753         emergencyIntent.putExtra(EXTRA_LAUNCH_EMERGENCY_VIA_GESTURE, true);
754         mContext.startActivityAsUser(emergencyIntent, new UserHandle(mUserId));
755     }
756 
isInRetailMode()757     private boolean isInRetailMode() {
758         return Settings.Global.getInt(mContext.getContentResolver(),
759                 Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
760     }
761 
isUserSetupComplete()762     private boolean isUserSetupComplete() {
763         return Settings.Secure.getIntForUser(mContext.getContentResolver(),
764                 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
765     }
766 
767     private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
768         @Override
769         public void onReceive(Context context, Intent intent) {
770             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
771                 mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
772                 mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
773                 registerContentObservers();
774                 updateCameraRegistered();
775                 updateCameraDoubleTapPowerEnabled();
776                 updateEmergencyGestureEnabled();
777                 updateEmergencyGesturePowerButtonCooldownPeriodMs();
778             }
779         }
780     };
781 
782     private final ContentObserver mSettingObserver = new ContentObserver(new Handler()) {
783         public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
784             if (userId == mUserId) {
785                 updateCameraRegistered();
786                 updateCameraDoubleTapPowerEnabled();
787                 updateEmergencyGestureEnabled();
788                 updateEmergencyGesturePowerButtonCooldownPeriodMs();
789             }
790         }
791     };
792 
793     private final class GestureEventListener implements SensorEventListener {
794         @Override
onSensorChanged(SensorEvent event)795         public void onSensorChanged(SensorEvent event) {
796             if (!mCameraLaunchRegistered) {
797               if (DBG) Slog.d(TAG, "Ignoring gesture event because it's unregistered.");
798               return;
799             }
800             if (event.sensor == mCameraLaunchSensor) {
801                 if (DBG) {
802                     float[] values = event.values;
803                     Slog.d(TAG, String.format("Received a camera launch event: " +
804                             "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
805                 }
806                 if (handleCameraGesture(true /* useWakelock */,
807                         StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) {
808                     mMetricsLogger.action(MetricsEvent.ACTION_WIGGLE_CAMERA_GESTURE);
809                     mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_WIGGLE);
810                     trackCameraLaunchEvent(event);
811                 }
812                 return;
813             }
814         }
815 
816         @Override
onAccuracyChanged(Sensor sensor, int accuracy)817         public void onAccuracyChanged(Sensor sensor, int accuracy) {
818             // Ignored.
819         }
820 
trackCameraLaunchEvent(SensorEvent event)821         private void trackCameraLaunchEvent(SensorEvent event) {
822             long now = SystemClock.elapsedRealtime();
823             long totalDuration = now - mCameraGestureOnTimeMs;
824             // values[0]: ratio between total time duration when accel is turned on and time
825             //            duration since camera launch gesture is subscribed.
826             // values[1]: ratio between total time duration when gyro is turned on and time duration
827             //            since camera launch gesture is subscribed.
828             // values[2]: extra information
829             float[] values = event.values;
830 
831             long sensor1OnTime = (long) (totalDuration * (double) values[0]);
832             long sensor2OnTime = (long) (totalDuration * (double) values[1]);
833             int extra = (int) values[2];
834 
835             // We only log the difference in the event log to make aggregation easier.
836             long gestureOnTimeDiff = now - mCameraGestureLastEventTime;
837             long sensor1OnTimeDiff = sensor1OnTime - mCameraGestureSensor1LastOnTimeMs;
838             long sensor2OnTimeDiff = sensor2OnTime - mCameraGestureSensor2LastOnTimeMs;
839             int extraDiff = extra - mCameraLaunchLastEventExtra;
840 
841             // Gating against negative time difference. This doesn't usually happen, but it may
842             // happen because of numeric errors.
843             if (gestureOnTimeDiff < 0 || sensor1OnTimeDiff < 0 || sensor2OnTimeDiff < 0) {
844                 if (DBG) Slog.d(TAG, "Skipped event logging because negative numbers.");
845                 return;
846             }
847 
848             if (DBG) Slog.d(TAG, String.format("totalDuration: %d, sensor1OnTime: %s, " +
849                     "sensor2OnTime: %d, extra: %d",
850                     gestureOnTimeDiff,
851                     sensor1OnTimeDiff,
852                     sensor2OnTimeDiff,
853                     extraDiff));
854             EventLogTags.writeCameraGestureTriggered(
855                     gestureOnTimeDiff,
856                     sensor1OnTimeDiff,
857                     sensor2OnTimeDiff,
858                     extraDiff);
859 
860             mCameraGestureLastEventTime = now;
861             mCameraGestureSensor1LastOnTimeMs = sensor1OnTime;
862             mCameraGestureSensor2LastOnTimeMs = sensor2OnTime;
863             mCameraLaunchLastEventExtra = extra;
864         }
865     }
866 
867     private final class CameraLiftTriggerEventListener extends TriggerEventListener {
868         @Override
onTrigger(TriggerEvent event)869         public void onTrigger(TriggerEvent event) {
870             if (DBG_CAMERA_LIFT) Slog.d(TAG, String.format("onTrigger event - time: %d, name: %s",
871                     event.timestamp, event.sensor.getName()));
872             if (!mCameraLiftRegistered) {
873               if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring camera lift event because it's " +
874                       "unregistered.");
875               return;
876             }
877             if (event.sensor == mCameraLiftTriggerSensor) {
878                 Resources resources = mContext.getResources();
879                 SensorManager sensorManager = (SensorManager) mContext.getSystemService(
880                         Context.SENSOR_SERVICE);
881                 boolean keyguardShowingAndNotOccluded =
882                         mWindowManagerInternal.isKeyguardShowingAndNotOccluded();
883                 boolean interactive = mPowerManager.isInteractive();
884                 if (DBG_CAMERA_LIFT) {
885                     float[] values = event.values;
886                     Slog.d(TAG, String.format("Received a camera lift trigger " +
887                             "event: values=[%.4f], keyguard showing: %b, interactive: %b", values[0],
888                             keyguardShowingAndNotOccluded, interactive));
889                 }
890                 if (keyguardShowingAndNotOccluded || !interactive) {
891                     if (handleCameraGesture(true /* useWakelock */,
892                             StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)) {
893                         MetricsLogger.action(mContext, MetricsEvent.ACTION_CAMERA_LIFT_TRIGGER);
894                         mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_LIFT);
895                     }
896                 } else {
897                     if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring lift event");
898                 }
899 
900                 mCameraLiftRegistered = sensorManager.requestTriggerSensor(
901                         mCameraLiftTriggerListener, mCameraLiftTriggerSensor);
902 
903                 if (DBG_CAMERA_LIFT) Slog.d(TAG, "Camera lift trigger sensor re-registered: " +
904                         mCameraLiftRegistered);
905                 return;
906             }
907         }
908     }
909 }
910