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