1/*
2 * Copyright (C) 2017 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
17package com.android.server;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertFalse;
21import static org.junit.Assert.assertTrue;
22import static org.mockito.Matchers.anyInt;
23import static org.mockito.Matchers.eq;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.times;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.when;
28
29import android.app.StatusBarManager;
30import android.content.Context;
31import android.content.res.Resources;
32import android.os.Looper;
33import android.os.UserHandle;
34import android.platform.test.annotations.Presubmit;
35import android.provider.Settings;
36import android.support.test.InstrumentationRegistry;
37import android.support.test.filters.SmallTest;
38import android.support.test.runner.AndroidJUnit4;
39import android.test.mock.MockContentResolver;
40import android.view.KeyEvent;
41import android.util.MutableBoolean;
42
43import com.android.internal.logging.MetricsLogger;
44import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
45import com.android.internal.util.test.FakeSettingsProvider;
46import com.android.server.LocalServices;
47import com.android.server.statusbar.StatusBarManagerInternal;
48
49import java.util.List;
50
51import org.junit.Before;
52import org.junit.BeforeClass;
53import org.junit.Test;
54import org.junit.runner.RunWith;
55import org.junit.runners.JUnit4;
56import org.mockito.ArgumentCaptor;
57import org.mockito.Mock;
58import org.mockito.MockitoAnnotations;
59
60/**
61 * Unit tests for {@link GestureLauncherService}.
62 * runtest frameworks-services -c com.android.server.GestureLauncherServiceTest
63 */
64@Presubmit
65@SmallTest
66@RunWith(AndroidJUnit4.class)
67public class GestureLauncherServiceTest {
68
69    private static final int FAKE_USER_ID = 1337;
70    private static final int FAKE_SOURCE = 1982;
71    private static final long INITIAL_EVENT_TIME_MILLIS = 20000L;
72    private static final long IGNORED_DOWN_TIME = 1234L;
73    private static final int IGNORED_ACTION = 13;
74    private static final int IGNORED_CODE = 1999;
75    private static final int IGNORED_REPEAT = 42;
76
77    private @Mock Context mContext;
78    private @Mock Resources mResources;
79    private @Mock StatusBarManagerInternal mStatusBarManagerInternal;
80    private @Mock MetricsLogger mMetricsLogger;
81    private MockContentResolver mContentResolver;
82    private GestureLauncherService mGestureLauncherService;
83
84    @BeforeClass
85    public static void oneTimeInitialization() {
86        if (Looper.myLooper() == null) {
87            Looper.prepare();
88        }
89    }
90
91    @Before
92    public void setup() {
93        MockitoAnnotations.initMocks(this);
94
95        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
96        LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
97
98        final Context originalContext = InstrumentationRegistry.getContext();
99        when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
100        when(mContext.getResources()).thenReturn(mResources);
101        mContentResolver = new MockContentResolver(mContext);
102        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
103        when(mContext.getContentResolver()).thenReturn(mContentResolver);
104
105        mGestureLauncherService = new GestureLauncherService(mContext, mMetricsLogger);
106    }
107
108    @Test
109    public void testIsCameraDoubleTapPowerEnabled_configFalse() {
110        withCameraDoubleTapPowerEnableConfigValue(false);
111        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerEnabled(mResources));
112    }
113
114    @Test
115    public void testIsCameraDoubleTapPowerEnabled_configTrue() {
116        withCameraDoubleTapPowerEnableConfigValue(true);
117        assertTrue(mGestureLauncherService.isCameraDoubleTapPowerEnabled(mResources));
118    }
119
120    @Test
121    public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingDisabled() {
122        withCameraDoubleTapPowerEnableConfigValue(false);
123        withCameraDoubleTapPowerDisableSettingValue(1);
124        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
125                mContext, FAKE_USER_ID));
126    }
127
128    @Test
129    public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingEnabled() {
130        withCameraDoubleTapPowerEnableConfigValue(false);
131        withCameraDoubleTapPowerDisableSettingValue(0);
132        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
133                mContext, FAKE_USER_ID));
134    }
135
136    @Test
137    public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingDisabled() {
138        withCameraDoubleTapPowerEnableConfigValue(true);
139        withCameraDoubleTapPowerDisableSettingValue(1);
140        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
141                mContext, FAKE_USER_ID));
142    }
143
144    @Test
145    public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingEnabled() {
146        withCameraDoubleTapPowerEnableConfigValue(true);
147        withCameraDoubleTapPowerDisableSettingValue(0);
148        assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
149                mContext, FAKE_USER_ID));
150    }
151
152    @Test
153    public void testHandleCameraLaunchGesture_userSetupComplete() {
154        withUserSetupCompleteValue(true);
155
156        boolean useWakeLock = false;
157        assertTrue(mGestureLauncherService.handleCameraGesture(useWakeLock, FAKE_SOURCE));
158        verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(FAKE_SOURCE);
159    }
160
161    @Test
162    public void testHandleCameraLaunchGesture_userSetupNotComplete() {
163        withUserSetupCompleteValue(false);
164
165        boolean useWakeLock = false;
166        assertFalse(mGestureLauncherService.handleCameraGesture(useWakeLock, FAKE_SOURCE));
167    }
168
169    @Test
170    public void testInterceptPowerKeyDown_firstPowerDownCameraPowerGestureOnInteractive() {
171        withCameraDoubleTapPowerEnableConfigValue(true);
172        withCameraDoubleTapPowerDisableSettingValue(0);
173        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
174
175        long eventTime = INITIAL_EVENT_TIME_MILLIS +
176                GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
177        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
178                IGNORED_REPEAT);
179        boolean interactive = true;
180        MutableBoolean outLaunched = new MutableBoolean(true);
181        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
182                outLaunched);
183        assertFalse(intercepted);
184        assertFalse(outLaunched.value);
185        verify(mMetricsLogger).histogram("power_consecutive_short_tap_count", 1);
186        verify(mMetricsLogger).histogram("power_double_tap_interval", (int) eventTime);
187    }
188
189    @Test
190    public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
191        withCameraDoubleTapPowerEnableConfigValue(false);
192        withCameraDoubleTapPowerDisableSettingValue(1);
193        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
194
195        long eventTime = INITIAL_EVENT_TIME_MILLIS;
196        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
197                IGNORED_REPEAT);
198        boolean interactive = true;
199        MutableBoolean outLaunched = new MutableBoolean(true);
200        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
201                outLaunched);
202        assertFalse(intercepted);
203        assertFalse(outLaunched.value);
204
205        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
206        eventTime += interval;
207        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
208                IGNORED_REPEAT);
209        outLaunched.value = true;
210        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
211                outLaunched);
212        assertFalse(intercepted);
213        assertFalse(outLaunched.value);
214
215        verify(mMetricsLogger, never())
216            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
217
218        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
219        verify(mMetricsLogger, times(2)).histogram(
220                eq("power_double_tap_interval"), intervalCaptor.capture());
221        List<Integer> intervals = intervalCaptor.getAllValues();
222        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
223        assertEquals((int) interval, intervals.get(1).intValue());
224
225        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
226        verify(mMetricsLogger, times(2)).histogram(
227                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
228        List<Integer> tapCounts = tapCountCaptor.getAllValues();
229        assertEquals(1, tapCounts.get(0).intValue());
230        assertEquals(2, tapCounts.get(1).intValue());
231    }
232
233    @Test
234    public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffInteractive() {
235        withCameraDoubleTapPowerEnableConfigValue(false);
236        withCameraDoubleTapPowerDisableSettingValue(1);
237        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
238
239        long eventTime = INITIAL_EVENT_TIME_MILLIS;
240        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
241                IGNORED_REPEAT);
242        boolean interactive = true;
243        MutableBoolean outLaunched = new MutableBoolean(true);
244        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
245                outLaunched);
246        assertFalse(intercepted);
247        assertFalse(outLaunched.value);
248
249        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
250        eventTime += interval;
251        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
252                IGNORED_REPEAT);
253        outLaunched.value = true;
254        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
255                outLaunched);
256        assertFalse(intercepted);
257        assertFalse(outLaunched.value);
258
259        verify(mMetricsLogger, never())
260            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
261
262        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
263        verify(mMetricsLogger, times(2)).histogram(
264                eq("power_double_tap_interval"), intervalCaptor.capture());
265        List<Integer> intervals = intervalCaptor.getAllValues();
266        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
267        assertEquals((int) interval, intervals.get(1).intValue());
268
269        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
270        verify(mMetricsLogger, times(2)).histogram(
271                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
272        List<Integer> tapCounts = tapCountCaptor.getAllValues();
273        assertEquals(1, tapCounts.get(0).intValue());
274        // The interval is too long to launch the camera, but short enough to count as a
275        // sequential tap.
276        assertEquals(2, tapCounts.get(1).intValue());
277    }
278
279    @Test
280    public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOffInteractive() {
281        withCameraDoubleTapPowerEnableConfigValue(false);
282        withCameraDoubleTapPowerDisableSettingValue(1);
283        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
284
285        long eventTime = INITIAL_EVENT_TIME_MILLIS;
286        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
287                IGNORED_REPEAT);
288        boolean interactive = true;
289        MutableBoolean outLaunched = new MutableBoolean(true);
290        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
291                outLaunched);
292        assertFalse(intercepted);
293        assertFalse(outLaunched.value);
294
295        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS;
296        eventTime += interval;
297        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
298                IGNORED_REPEAT);
299        outLaunched.value = true;
300        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
301                outLaunched);
302        assertFalse(intercepted);
303        assertFalse(outLaunched.value);
304
305        verify(mMetricsLogger, never())
306            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
307
308        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
309        verify(mMetricsLogger, times(2)).histogram(
310                eq("power_double_tap_interval"), intervalCaptor.capture());
311        List<Integer> intervals = intervalCaptor.getAllValues();
312        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
313        assertEquals((int) interval, intervals.get(1).intValue());
314
315        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
316        verify(mMetricsLogger, times(2)).histogram(
317                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
318        List<Integer> tapCounts = tapCountCaptor.getAllValues();
319        assertEquals(1, tapCounts.get(0).intValue());
320        assertEquals(1, tapCounts.get(1).intValue());
321    }
322
323    @Test
324    public void
325    testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupComplete() {
326        withCameraDoubleTapPowerEnableConfigValue(true);
327        withCameraDoubleTapPowerDisableSettingValue(0);
328        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
329        withUserSetupCompleteValue(true);
330
331        long eventTime = INITIAL_EVENT_TIME_MILLIS;
332        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
333                IGNORED_REPEAT);
334        boolean interactive = true;
335        MutableBoolean outLaunched = new MutableBoolean(true);
336        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
337                outLaunched);
338        assertFalse(intercepted);
339        assertFalse(outLaunched.value);
340
341        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
342        eventTime += interval;
343        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
344                IGNORED_REPEAT);
345        outLaunched.value = false;
346        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
347                outLaunched);
348        assertTrue(intercepted);
349        assertTrue(outLaunched.value);
350
351        verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
352                StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
353        verify(mMetricsLogger)
354            .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
355
356        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
357        verify(mMetricsLogger, times(2)).histogram(
358                eq("power_double_tap_interval"), intervalCaptor.capture());
359        List<Integer> intervals = intervalCaptor.getAllValues();
360        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
361        assertEquals((int) interval, intervals.get(1).intValue());
362
363        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
364        verify(mMetricsLogger, times(2)).histogram(
365                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
366        List<Integer> tapCounts = tapCountCaptor.getAllValues();
367        assertEquals(1, tapCounts.get(0).intValue());
368        assertEquals(2, tapCounts.get(1).intValue());
369    }
370
371    @Test
372    public void
373    testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() {
374        withCameraDoubleTapPowerEnableConfigValue(true);
375        withCameraDoubleTapPowerDisableSettingValue(0);
376        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
377        withUserSetupCompleteValue(false);
378
379        long eventTime = INITIAL_EVENT_TIME_MILLIS;
380        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
381                IGNORED_REPEAT);
382        boolean interactive = true;
383        MutableBoolean outLaunched = new MutableBoolean(true);
384        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
385                outLaunched);
386        assertFalse(intercepted);
387        assertFalse(outLaunched.value);
388
389        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
390        eventTime += interval;
391        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
392                IGNORED_REPEAT);
393        outLaunched.value = true;
394        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
395                outLaunched);
396        assertFalse(intercepted);
397        assertFalse(outLaunched.value);
398
399        verify(mMetricsLogger, never())
400            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
401
402        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
403        verify(mMetricsLogger, times(2)).histogram(
404                eq("power_double_tap_interval"), intervalCaptor.capture());
405        List<Integer> intervals = intervalCaptor.getAllValues();
406        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
407        assertEquals((int) interval, intervals.get(1).intValue());
408
409        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
410        verify(mMetricsLogger, times(2)).histogram(
411                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
412        List<Integer> tapCounts = tapCountCaptor.getAllValues();
413        assertEquals(1, tapCounts.get(0).intValue());
414        // The interval is too long to launch the camera, but short enough to count as a
415        // sequential tap.
416        assertEquals(2, tapCounts.get(1).intValue());
417    }
418
419    @Test
420    public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnInteractive() {
421        withCameraDoubleTapPowerEnableConfigValue(true);
422        withCameraDoubleTapPowerDisableSettingValue(0);
423        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
424
425        long eventTime = INITIAL_EVENT_TIME_MILLIS;
426        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
427                IGNORED_REPEAT);
428        boolean interactive = true;
429        MutableBoolean outLaunched = new MutableBoolean(true);
430        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
431                outLaunched);
432        assertFalse(intercepted);
433        assertFalse(outLaunched.value);
434
435        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
436        eventTime += interval;
437        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
438                IGNORED_REPEAT);
439        outLaunched.value = true;
440        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
441                outLaunched);
442        assertFalse(intercepted);
443        assertFalse(outLaunched.value);
444
445        verify(mMetricsLogger, never())
446            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
447
448        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
449        verify(mMetricsLogger, times(2)).histogram(
450                eq("power_double_tap_interval"), intervalCaptor.capture());
451        List<Integer> intervals = intervalCaptor.getAllValues();
452        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
453        assertEquals((int) interval, intervals.get(1).intValue());
454
455        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
456        verify(mMetricsLogger, times(2)).histogram(
457                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
458        List<Integer> tapCounts = tapCountCaptor.getAllValues();
459        assertEquals(1, tapCounts.get(0).intValue());
460        // The interval is too long to launch the camera, but short enough to count as a
461        // sequential tap.
462        assertEquals(2, tapCounts.get(1).intValue());
463    }
464
465    @Test
466    public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnInteractive() {
467        withCameraDoubleTapPowerEnableConfigValue(true);
468        withCameraDoubleTapPowerDisableSettingValue(0);
469        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
470
471        long eventTime = INITIAL_EVENT_TIME_MILLIS;
472        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
473                IGNORED_REPEAT);
474        boolean interactive = true;
475        MutableBoolean outLaunched = new MutableBoolean(true);
476        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
477                outLaunched);
478        assertFalse(intercepted);
479        assertFalse(outLaunched.value);
480
481        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS;
482        eventTime += interval;
483        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
484                IGNORED_REPEAT);
485        outLaunched.value = true;
486        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
487                outLaunched);
488        assertFalse(intercepted);
489        assertFalse(outLaunched.value);
490
491        verify(mMetricsLogger, never())
492            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
493
494        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
495        verify(mMetricsLogger, times(2)).histogram(
496                eq("power_double_tap_interval"), intervalCaptor.capture());
497        List<Integer> intervals = intervalCaptor.getAllValues();
498        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
499        assertEquals((int) interval, intervals.get(1).intValue());
500
501        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
502        verify(mMetricsLogger, times(2)).histogram(
503                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
504        List<Integer> tapCounts = tapCountCaptor.getAllValues();
505        assertEquals(1, tapCounts.get(0).intValue());
506        assertEquals(1, tapCounts.get(1).intValue());
507    }
508
509    @Test
510    public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffNotInteractive() {
511        withCameraDoubleTapPowerEnableConfigValue(false);
512        withCameraDoubleTapPowerDisableSettingValue(1);
513        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
514
515        long eventTime = INITIAL_EVENT_TIME_MILLIS;
516        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
517                IGNORED_REPEAT);
518        boolean interactive = false;
519        MutableBoolean outLaunched = new MutableBoolean(true);
520        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
521                outLaunched);
522        assertFalse(intercepted);
523        assertFalse(outLaunched.value);
524
525        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
526        eventTime += interval;
527        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
528                IGNORED_REPEAT);
529        outLaunched.value = true;
530        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
531                outLaunched);
532        assertFalse(intercepted);
533        assertFalse(outLaunched.value);
534
535        verify(mMetricsLogger, never())
536            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
537
538        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
539        verify(mMetricsLogger, times(2)).histogram(
540                eq("power_double_tap_interval"), intervalCaptor.capture());
541        List<Integer> intervals = intervalCaptor.getAllValues();
542        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
543        assertEquals((int) interval, intervals.get(1).intValue());
544
545        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
546        verify(mMetricsLogger, times(2)).histogram(
547                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
548        List<Integer> tapCounts = tapCountCaptor.getAllValues();
549        assertEquals(1, tapCounts.get(0).intValue());
550        assertEquals(2, tapCounts.get(1).intValue());
551    }
552
553    @Test
554    public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffNotInteractive() {
555        withCameraDoubleTapPowerEnableConfigValue(false);
556        withCameraDoubleTapPowerDisableSettingValue(1);
557        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
558
559        long eventTime = INITIAL_EVENT_TIME_MILLIS;
560        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
561                IGNORED_REPEAT);
562        boolean interactive = false;
563        MutableBoolean outLaunched = new MutableBoolean(true);
564        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
565                outLaunched);
566        assertFalse(intercepted);
567        assertFalse(outLaunched.value);
568
569        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
570        eventTime += interval;
571        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
572                IGNORED_REPEAT);
573        outLaunched.value = true;
574        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
575                outLaunched);
576        assertFalse(intercepted);
577        assertFalse(outLaunched.value);
578        verify(mMetricsLogger, never())
579            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
580
581        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
582        verify(mMetricsLogger, times(2)).histogram(
583                eq("power_double_tap_interval"), intervalCaptor.capture());
584        List<Integer> intervals = intervalCaptor.getAllValues();
585        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
586        assertEquals((int) interval, intervals.get(1).intValue());
587
588        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
589        verify(mMetricsLogger, times(2)).histogram(
590                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
591        List<Integer> tapCounts = tapCountCaptor.getAllValues();
592        assertEquals(1, tapCounts.get(0).intValue());
593        // The interval is too long to launch the camera, but short enough to count as a
594        // sequential tap.
595        assertEquals(2, tapCounts.get(1).intValue());
596    }
597
598    @Test
599    public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOffNotInteractive() {
600        withCameraDoubleTapPowerEnableConfigValue(false);
601        withCameraDoubleTapPowerDisableSettingValue(1);
602        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
603
604        long eventTime = INITIAL_EVENT_TIME_MILLIS;
605        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
606                IGNORED_REPEAT);
607        boolean interactive = false;
608        MutableBoolean outLaunched = new MutableBoolean(true);
609        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
610                outLaunched);
611        assertFalse(intercepted);
612        assertFalse(outLaunched.value);
613
614        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS;
615        eventTime += interval;
616        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
617                IGNORED_REPEAT);
618        outLaunched.value = true;
619        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
620                outLaunched);
621        assertFalse(intercepted);
622        assertFalse(outLaunched.value);
623        verify(mMetricsLogger, never())
624            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
625
626        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
627        verify(mMetricsLogger, times(2)).histogram(
628                eq("power_double_tap_interval"), intervalCaptor.capture());
629        List<Integer> intervals = intervalCaptor.getAllValues();
630        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
631        assertEquals((int) interval, intervals.get(1).intValue());
632
633        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
634        verify(mMetricsLogger, times(2)).histogram(
635                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
636        List<Integer> tapCounts = tapCountCaptor.getAllValues();
637        assertEquals(1, tapCounts.get(0).intValue());
638        assertEquals(1, tapCounts.get(1).intValue());
639    }
640
641    @Test
642    public void
643    testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupComplete() {
644        withCameraDoubleTapPowerEnableConfigValue(true);
645        withCameraDoubleTapPowerDisableSettingValue(0);
646        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
647        withUserSetupCompleteValue(true);
648
649        long eventTime = INITIAL_EVENT_TIME_MILLIS;
650        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
651                IGNORED_REPEAT);
652        boolean interactive = false;
653        MutableBoolean outLaunched = new MutableBoolean(true);
654        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
655                outLaunched);
656        assertFalse(intercepted);
657        assertFalse(outLaunched.value);
658
659        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
660        eventTime += interval;
661        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
662                IGNORED_REPEAT);
663        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
664                outLaunched);
665        assertFalse(intercepted);
666        assertTrue(outLaunched.value);
667
668        verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected(
669                StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
670        verify(mMetricsLogger)
671            .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval);
672
673        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
674        verify(mMetricsLogger, times(2)).histogram(
675                eq("power_double_tap_interval"), intervalCaptor.capture());
676        List<Integer> intervals = intervalCaptor.getAllValues();
677        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
678        assertEquals((int) interval, intervals.get(1).intValue());
679
680        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
681        verify(mMetricsLogger, times(2)).histogram(
682                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
683        List<Integer> tapCounts = tapCountCaptor.getAllValues();
684        assertEquals(1, tapCounts.get(0).intValue());
685        assertEquals(2, tapCounts.get(1).intValue());
686    }
687
688    @Test
689    public void
690    testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnNotInteractiveSetupIncomplete() {
691        withCameraDoubleTapPowerEnableConfigValue(true);
692        withCameraDoubleTapPowerDisableSettingValue(0);
693        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
694        withUserSetupCompleteValue(false);
695
696        long eventTime = INITIAL_EVENT_TIME_MILLIS;
697        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
698                IGNORED_REPEAT);
699        boolean interactive = false;
700        MutableBoolean outLaunched = new MutableBoolean(true);
701        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
702                outLaunched);
703        assertFalse(intercepted);
704        assertFalse(outLaunched.value);
705
706        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
707        eventTime += interval;
708        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
709                IGNORED_REPEAT);
710        outLaunched.value = true;
711        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
712                outLaunched);
713        assertFalse(intercepted);
714        assertFalse(outLaunched.value);
715
716        verify(mMetricsLogger, never())
717            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
718
719        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
720        verify(mMetricsLogger, times(2)).histogram(
721                eq("power_double_tap_interval"), intervalCaptor.capture());
722        List<Integer> intervals = intervalCaptor.getAllValues();
723        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
724        assertEquals((int) interval, intervals.get(1).intValue());
725
726        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
727        verify(mMetricsLogger, times(2)).histogram(
728                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
729        List<Integer> tapCounts = tapCountCaptor.getAllValues();
730        assertEquals(1, tapCounts.get(0).intValue());
731        assertEquals(2, tapCounts.get(1).intValue());
732    }
733
734    @Test
735    public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnNotInteractive() {
736        withCameraDoubleTapPowerEnableConfigValue(true);
737        withCameraDoubleTapPowerDisableSettingValue(0);
738        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
739
740        long eventTime = INITIAL_EVENT_TIME_MILLIS;
741        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
742                IGNORED_REPEAT);
743        boolean interactive = false;
744        MutableBoolean outLaunched = new MutableBoolean(true);
745        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
746                outLaunched);
747        assertFalse(intercepted);
748        assertFalse(outLaunched.value);
749
750        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS;
751        eventTime += interval;
752        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
753                IGNORED_REPEAT);
754        outLaunched.value = true;
755        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
756                outLaunched);
757        assertFalse(intercepted);
758        assertFalse(outLaunched.value);
759
760        verify(mMetricsLogger, never())
761            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
762
763        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
764        verify(mMetricsLogger, times(2)).histogram(
765                eq("power_double_tap_interval"), intervalCaptor.capture());
766        List<Integer> intervals = intervalCaptor.getAllValues();
767        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
768        assertEquals((int) interval, intervals.get(1).intValue());
769
770        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
771        verify(mMetricsLogger, times(2)).histogram(
772                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
773        List<Integer> tapCounts = tapCountCaptor.getAllValues();
774        assertEquals(1, tapCounts.get(0).intValue());
775        // The interval is too long to launch the camera, but short enough to count as a
776        // sequential tap.
777        assertEquals(2, tapCounts.get(1).intValue());
778    }
779
780    @Test
781    public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOnNotInteractive() {
782        withCameraDoubleTapPowerEnableConfigValue(true);
783        withCameraDoubleTapPowerDisableSettingValue(0);
784        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
785
786        long eventTime = INITIAL_EVENT_TIME_MILLIS;
787        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
788                IGNORED_REPEAT);
789        boolean interactive = false;
790        MutableBoolean outLaunched = new MutableBoolean(true);
791        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
792                outLaunched);
793        assertFalse(intercepted);
794        assertFalse(outLaunched.value);
795
796        long interval = GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS;
797        eventTime += interval;
798        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
799                IGNORED_REPEAT);
800        outLaunched.value = true;
801        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
802                outLaunched);
803        assertFalse(intercepted);
804        assertFalse(outLaunched.value);
805
806        verify(mMetricsLogger, never())
807            .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
808
809        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
810        verify(mMetricsLogger, times(2)).histogram(
811                eq("power_double_tap_interval"), intervalCaptor.capture());
812        List<Integer> intervals = intervalCaptor.getAllValues();
813        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
814        assertEquals((int) interval, intervals.get(1).intValue());
815
816        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
817        verify(mMetricsLogger, times(2)).histogram(
818                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
819        List<Integer> tapCounts = tapCountCaptor.getAllValues();
820        assertEquals(1, tapCounts.get(0).intValue());
821        assertEquals(1, tapCounts.get(1).intValue());
822    }
823
824    private void withCameraDoubleTapPowerEnableConfigValue(boolean enableConfigValue) {
825        when(mResources.getBoolean(
826                com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled))
827                .thenReturn(enableConfigValue);
828    }
829
830    private void withCameraDoubleTapPowerDisableSettingValue(int disableSettingValue) {
831        Settings.Secure.putIntForUser(
832                mContentResolver,
833                Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
834                disableSettingValue,
835                UserHandle.USER_CURRENT);
836    }
837
838    private void withUserSetupCompleteValue(boolean userSetupComplete) {
839        int userSetupCompleteValue = userSetupComplete ? 1 : 0;
840        Settings.Secure.putIntForUser(
841                mContentResolver,
842                Settings.Secure.USER_SETUP_COMPLETE,
843                userSetupCompleteValue,
844                UserHandle.USER_CURRENT);
845    }
846}
847