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