1 /*
2  * Copyright (C) 2018 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.wm;
18 
19 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
20 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
21 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
22 import static android.app.StatusBarManager.DISABLE2_MASK;
23 import static android.app.StatusBarManager.DISABLE2_NONE;
24 import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
25 import static android.app.StatusBarManager.DISABLE_HOME;
26 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
27 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
28 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
29 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
30 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
31 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
32 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
33 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
34 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
35 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
36 import static android.os.Process.SYSTEM_UID;
37 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
38 
39 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
40 
41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
45 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
46 import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull;
47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
48 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
49 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
50 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
51 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
52 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
53 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
54 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
55 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
56 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
57 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
58 import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_LOCKED;
59 import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_PINNED;
60 
61 import static org.junit.Assert.assertEquals;
62 import static org.junit.Assert.assertFalse;
63 import static org.junit.Assert.assertTrue;
64 
65 import android.app.StatusBarManager;
66 import android.app.admin.DevicePolicyManager;
67 import android.app.admin.IDevicePolicyManager;
68 import android.content.ComponentName;
69 import android.content.Context;
70 import android.content.Intent;
71 import android.os.Handler;
72 import android.os.IBinder;
73 import android.os.Looper;
74 import android.os.Message;
75 import android.os.UserHandle;
76 import android.platform.test.annotations.Presubmit;
77 import android.provider.Settings;
78 import android.telecom.TelecomManager;
79 import android.testing.DexmakerShareClassLoaderRule;
80 import android.util.Pair;
81 
82 import androidx.test.filters.SmallTest;
83 
84 import com.android.internal.statusbar.IStatusBarService;
85 import com.android.internal.widget.LockPatternUtils;
86 import com.android.server.LocalServices;
87 import com.android.server.statusbar.StatusBarManagerInternal;
88 
89 import org.junit.After;
90 import org.junit.Before;
91 import org.junit.Rule;
92 import org.junit.Test;
93 import org.mockito.Mock;
94 import org.mockito.MockitoAnnotations;
95 import org.mockito.verification.VerificationMode;
96 
97 /**
98  * Unit tests for {@link LockTaskController}.
99  *
100  * Build/Install/Run:
101  *  atest WmTests:LockTaskControllerTest
102  */
103 @SmallTest
104 @Presubmit
105 public class LockTaskControllerTest {
106     private static final String TEST_PACKAGE_NAME = "com.test.package";
107     private static final String TEST_PACKAGE_NAME_2 = "com.test.package2";
108     private static final String TEST_CLASS_NAME = ".TestClass";
109     private static final int TEST_USER_ID = 123;
110     private static final int TEST_UID = 10467;
111 
112     @Rule
113     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
114             new DexmakerShareClassLoaderRule();
115 
116     @Mock private ActivityTaskSupervisor mSupervisor;
117     @Mock private RootWindowContainer mRootWindowContainer;
118     @Mock private IDevicePolicyManager mDevicePolicyManager;
119     @Mock private IStatusBarService mStatusBarService;
120     @Mock private WindowManagerService mWindowManager;
121     @Mock private LockPatternUtils mLockPatternUtils;
122     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
123     @Mock private TelecomManager mTelecomManager;
124     @Mock private RecentTasks mRecentTasks;
125     @Mock private TaskChangeNotificationController mTaskChangeNotificationController;
126 
127     private LockTaskController mLockTaskController;
128     private Context mContext;
129     private String mPackageName;
130     private String mLockToAppSetting;
131 
132     @Before
setUp()133     public void setUp() throws Exception {
134         MockitoAnnotations.initMocks(this);
135 
136         mContext = getInstrumentation().getTargetContext();
137         mPackageName = mContext.getPackageName();
138         mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
139                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
140 
141         if (Looper.myLooper() == null) {
142             Looper.prepare();
143         }
144 
145         mSupervisor.mRecentTasks = mRecentTasks;
146         mSupervisor.mRootWindowContainer = mRootWindowContainer;
147 
148         mLockTaskController = new LockTaskController(mContext, mSupervisor,
149                 new ImmediatelyExecuteHandler(), mTaskChangeNotificationController);
150         mLockTaskController.setWindowManager(mWindowManager);
151         mLockTaskController.mStatusBarService = mStatusBarService;
152         mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
153         mLockTaskController.mTelecomManager = mTelecomManager;
154         mLockTaskController.mLockPatternUtils = mLockPatternUtils;
155 
156         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
157         LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
158     }
159 
160     @After
tearDown()161     public void tearDown() throws Exception {
162         mLockTaskController.setWindowManager(null);
163         Settings.Secure.putString(mContext.getContentResolver(),
164                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
165     }
166 
167     @Test
testPreconditions()168     public void testPreconditions() {
169         // GIVEN nothing has happened
170 
171         // THEN current lock task mode should be NONE
172         assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
173     }
174 
175     @Test
testStartLockTaskMode_once()176     public void testStartLockTaskMode_once() throws Exception {
177         // GIVEN a task record with allowlisted auth
178         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
179 
180         // WHEN calling setLockTaskMode for LOCKED mode without resuming
181         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
182 
183         // THEN the lock task mode state should be LOCKED
184         assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
185         // THEN the task should be locked
186         assertTrue(mLockTaskController.isTaskLocked(tr));
187 
188         // THEN lock task mode should be started
189         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
190     }
191 
192     @Test
testStartLockTaskMode_twice()193     public void testStartLockTaskMode_twice() throws Exception {
194         // GIVEN two task records with allowlisted auth
195         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
196         Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
197 
198         // WHEN calling setLockTaskMode for LOCKED mode on both tasks
199         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
200         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
201 
202         // THEN the lock task mode state should be LOCKED
203         assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
204         // THEN neither of the tasks should be able to move to back of stack
205         assertTrue(mLockTaskController.isTaskLocked(tr1));
206         assertTrue(mLockTaskController.isTaskLocked(tr2));
207 
208         // THEN lock task mode should be started
209         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
210     }
211 
212     @Test
testStartLockTaskMode_pinningRequest()213     public void testStartLockTaskMode_pinningRequest() {
214         // GIVEN a task record that is not allowlisted, i.e. with pinned auth
215         Task tr = getTask(LOCK_TASK_AUTH_PINNABLE);
216 
217         // WHEN calling startLockTaskMode
218         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
219 
220         // THEN a pinning request should be shown
221         verify(mStatusBarManagerInternal).showScreenPinningRequest(anyInt());
222     }
223 
224     @Test
testStartLockTaskMode_pinnedBySystem()225     public void testStartLockTaskMode_pinnedBySystem() throws Exception {
226         // GIVEN a task record with pinned auth
227         Task tr = getTask(LOCK_TASK_AUTH_PINNABLE);
228 
229         // WHEN the system calls startLockTaskMode
230         mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
231 
232         // THEN the lock task mode state should be PINNED
233         assertEquals(LOCK_TASK_MODE_PINNED, mLockTaskController.getLockTaskModeState());
234         // THEN the task should be locked
235         assertTrue(mLockTaskController.isTaskLocked(tr));
236 
237         // THEN lock task mode should be started
238         verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
239         // THEN screen pinning toast should be shown
240         verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
241     }
242 
243     @Test
testLockTaskViolation()244     public void testLockTaskViolation() {
245         // GIVEN one task record with allowlisted auth that is in lock task mode
246         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
247         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
248 
249         // THEN it's not a lock task violation to try and launch this task without clearing
250         assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
251 
252         // THEN it's a lock task violation to launch another task that is not allowlisted
253         assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(LOCK_TASK_AUTH_PINNABLE)));
254         // THEN it's a lock task violation to launch another task that is disallowed from lock task
255         assertTrue(mLockTaskController.isLockTaskModeViolation(getTask(LOCK_TASK_AUTH_DONT_LOCK)));
256 
257         // THEN it's no a lock task violation to launch another task that is allowlisted
258         assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
259                 LOCK_TASK_AUTH_ALLOWLISTED)));
260         assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
261                 LOCK_TASK_AUTH_LAUNCHABLE)));
262         // THEN it's not a lock task violation to launch another task that is priv launchable
263         assertFalse(mLockTaskController.isLockTaskModeViolation(getTask(
264                 LOCK_TASK_AUTH_LAUNCHABLE_PRIV)));
265     }
266 
267     @Test
testLockTaskViolation_emergencyCall()268     public void testLockTaskViolation_emergencyCall() {
269         // GIVEN one task record with allowlisted auth that is in lock task mode
270         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
271         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
272 
273         // GIVEN tasks necessary for emergency calling
274         Task keypad = getTask(new Intent().setComponent(EMERGENCY_DIALER_COMPONENT),
275                 LOCK_TASK_AUTH_PINNABLE);
276         Task callAction = getTask(new Intent(Intent.ACTION_CALL_EMERGENCY),
277                 LOCK_TASK_AUTH_PINNABLE);
278         Task dialer = getTask("com.example.dialer", LOCK_TASK_AUTH_PINNABLE);
279         when(mTelecomManager.getSystemDialerPackage())
280                 .thenReturn(dialer.intent.getComponent().getPackageName());
281 
282         // GIVEN keyguard is allowed for lock task mode
283         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
284 
285         // THEN the above tasks should all be allowed
286         assertFalse(mLockTaskController.isLockTaskModeViolation(keypad));
287         assertFalse(mLockTaskController.isLockTaskModeViolation(callAction));
288         assertFalse(mLockTaskController.isLockTaskModeViolation(dialer));
289 
290         // GIVEN keyguard is disallowed for lock task mode (default)
291         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
292 
293         // THEN the above tasks should all be blocked
294         assertTrue(mLockTaskController.isLockTaskModeViolation(keypad));
295         assertTrue(mLockTaskController.isLockTaskModeViolation(callAction));
296         assertTrue(mLockTaskController.isLockTaskModeViolation(dialer));
297     }
298 
299     @Test
testStopLockTaskMode()300     public void testStopLockTaskMode() throws Exception {
301         // GIVEN one task record with allowlisted auth that is in lock task mode
302         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
303         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
304 
305         // WHEN the same caller calls stopLockTaskMode
306         mLockTaskController.stopLockTaskMode(tr, false, TEST_UID);
307 
308         // THEN the lock task mode should be NONE
309         assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
310         // THEN the task should no longer be locked
311         assertFalse(mLockTaskController.isTaskLocked(tr));
312         // THEN lock task mode should have been finished
313         verifyLockTaskStopped(times(1));
314     }
315 
316     @Test(expected = SecurityException.class)
testStopLockTaskMode_differentCaller()317     public void testStopLockTaskMode_differentCaller() {
318         // GIVEN one task record with allowlisted auth that is in lock task mode
319         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
320         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
321 
322         // WHEN a different caller calls stopLockTaskMode
323         mLockTaskController.stopLockTaskMode(tr, false, TEST_UID + 1);
324 
325         // THEN security exception should be thrown, because different caller tried to unlock
326     }
327 
328     @Test
testStopLockTaskMode_systemCaller()329     public void testStopLockTaskMode_systemCaller() {
330         // GIVEN one task record with allowlisted auth that is in lock task mode
331         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
332         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
333 
334         // WHEN system calls stopLockTaskMode
335         mLockTaskController.stopLockTaskMode(tr, true, SYSTEM_UID);
336 
337         // THEN lock task mode should still be active
338         assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
339     }
340 
341     @Test
testStopLockTaskMode_twoTasks()342     public void testStopLockTaskMode_twoTasks() throws Exception {
343         // GIVEN two task records with allowlisted auth that is in lock task mode
344         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
345         Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
346         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
347         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
348 
349         // WHEN calling stopLockTaskMode
350         mLockTaskController.stopLockTaskMode(tr2, false, TEST_UID);
351 
352         // THEN the lock task mode should still be active
353         assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
354         // THEN the first task should still be locked
355         assertTrue(mLockTaskController.isTaskLocked(tr1));
356         // THEN the top task should no longer be locked
357         assertFalse(mLockTaskController.isTaskLocked(tr2));
358         // THEN lock task mode should not have been finished
359         verifyLockTaskStopped(never());
360     }
361 
362     @Test
testStopLockTaskMode_rootTask()363     public void testStopLockTaskMode_rootTask() throws Exception {
364         // GIVEN two task records with allowlisted auth that is in lock task mode
365         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
366         Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
367         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
368         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
369 
370         // WHEN calling stopLockTaskMode on the root task
371         mLockTaskController.stopLockTaskMode(tr1, false, TEST_UID);
372 
373         // THEN the lock task mode should be inactive
374         assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
375         // THEN the first task should no longer be locked
376         assertFalse(mLockTaskController.isTaskLocked(tr1));
377         // THEN the top task should no longer be locked
378         assertFalse(mLockTaskController.isTaskLocked(tr2));
379         // THEN lock task mode should be finished
380         verifyLockTaskStopped(times(1));
381     }
382 
383     @Test
testStopLockTaskMode_pinned()384     public void testStopLockTaskMode_pinned() throws Exception {
385         // GIVEN one task records that is in pinned mode
386         Task tr = getTask(LOCK_TASK_AUTH_PINNABLE);
387         mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
388         // GIVEN that the keyguard is required to show after unlocking
389         Settings.Secure.putInt(mContext.getContentResolver(),
390                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1);
391 
392         // reset invocation counter
393         reset(mStatusBarService);
394 
395         // WHEN calling stopLockTask
396         mLockTaskController.stopLockTaskMode(null, true, SYSTEM_UID);
397 
398         // THEN the lock task mode should no longer be active
399         assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
400         // THEN the task should no longer be locked
401         assertFalse(mLockTaskController.isTaskLocked(tr));
402         // THEN lock task mode should have been finished
403         verifyLockTaskStopped(times(1));
404         // THEN the keyguard should be shown
405         verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL));
406         // THEN screen pinning toast should be shown
407         verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */));
408     }
409 
410     @Test
testClearLockedTasks()411     public void testClearLockedTasks() throws Exception {
412         // GIVEN two task records with allowlisted auth that is in lock task mode
413         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
414         Task tr2 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
415         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
416         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
417 
418         // WHEN calling clearLockedTasks on the root task
419         mLockTaskController.clearLockedTasks("testClearLockedTasks");
420 
421         // THEN the lock task mode should be inactive
422         assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
423         // THEN the first task should no longer be locked
424         assertFalse(mLockTaskController.isTaskLocked(tr1));
425         // THEN the top task should no longer be locked
426         assertFalse(mLockTaskController.isTaskLocked(tr2));
427         // THEN lock task mode should be finished
428         verifyLockTaskStopped(times(1));
429     }
430 
431     @Test
testClearLockedTasks_noLockSetting_noPassword_deviceIsUnlocked()432     public void testClearLockedTasks_noLockSetting_noPassword_deviceIsUnlocked() throws Exception {
433         // GIVEN There is no setting set for LOCK_TO_APP_EXIT_LOCKED
434         Settings.Secure.clearProviderForTest();
435 
436         // AND no password is set
437         when(mLockPatternUtils.getKeyguardStoredPasswordQuality(anyInt()))
438                 .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
439 
440         // AND there is a task record
441         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
442         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
443 
444         // WHEN calling clearLockedTasks on the root task
445         mLockTaskController.clearLockedTasks("testClearLockedTasks");
446 
447         // THEN the device should not be locked
448         verify(mWindowManager, never()).lockNow(any());
449     }
450 
451     @Test
testClearLockedTasks_noLockSetting_password_deviceIsLocked()452     public void testClearLockedTasks_noLockSetting_password_deviceIsLocked() throws Exception {
453         // GIVEN There is no setting set for LOCK_TO_APP_EXIT_LOCKED
454         Settings.Secure.clearProviderForTest();
455 
456         // AND a password is set
457         when(mLockPatternUtils.isSecure(TEST_USER_ID))
458                 .thenReturn(true);
459 
460         // AND there is a task record
461         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
462         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
463 
464         // WHEN calling clearLockedTasks on the root task
465         mLockTaskController.clearLockedTasks("testClearLockedTasks");
466 
467         // THEN the device should be locked
468         verify(mWindowManager, times(1)).lockNow(any());
469     }
470 
471     @Test
testClearLockedTasks_lockSettingTrue_deviceIsLocked()472     public void testClearLockedTasks_lockSettingTrue_deviceIsLocked() throws Exception {
473         // GIVEN LOCK_TO_APP_EXIT_LOCKED is set to 1
474         Settings.Secure.putIntForUser(mContext.getContentResolver(),
475                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId());
476 
477         // AND there is a task record
478         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
479         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
480 
481         // WHEN calling clearLockedTasks on the root task
482         mLockTaskController.clearLockedTasks("testClearLockedTasks");
483 
484         // THEN the device should be locked
485         verify(mWindowManager, times(1)).lockNow(any());
486     }
487 
488     @Test
testClearLockedTasks_lockSettingFalse_doesNotRequirePassword()489     public void testClearLockedTasks_lockSettingFalse_doesNotRequirePassword() throws Exception {
490         // GIVEN LOCK_TO_APP_EXIT_LOCKED is set to 1
491         Settings.Secure.putIntForUser(mContext.getContentResolver(),
492                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId());
493 
494         // AND there is a task record
495         Task tr1 = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
496         mLockTaskController.startLockTaskMode(tr1, true, TEST_UID);
497 
498         // WHEN calling clearLockedTasks on the root task
499         mLockTaskController.clearLockedTasks("testClearLockedTasks");
500 
501         // THEN the device should be unlocked
502         verify(mWindowManager, never()).lockNow(any());
503     }
504 
505     @Test
testUpdateLockTaskPackages()506     public void testUpdateLockTaskPackages() {
507         String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
508         String[] allowlist2 = {TEST_PACKAGE_NAME};
509 
510         // No package is allowlisted initially
511         for (String pkg : allowlist1) {
512             assertFalse("Package shouldn't be allowlisted: " + pkg,
513                     mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg));
514             assertFalse("Package shouldn't be allowlisted for user 0: " + pkg,
515                     mLockTaskController.isPackageAllowlisted(0, pkg));
516         }
517 
518         // Apply allowlist
519         mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist1);
520 
521         // Assert the allowlist is applied to the correct user
522         for (String pkg : allowlist1) {
523             assertTrue("Package should be allowlisted: " + pkg,
524                     mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg));
525             assertFalse("Package shouldn't be allowlisted for user 0: " + pkg,
526                     mLockTaskController.isPackageAllowlisted(0, pkg));
527         }
528 
529         // Update allowlist
530         mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist2);
531 
532         // Assert the new allowlist is applied
533         assertTrue("Package should remain allowlisted: " + TEST_PACKAGE_NAME,
534                 mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME));
535         assertFalse("Package should no longer be allowlisted: " + TEST_PACKAGE_NAME_2,
536                 mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
537     }
538 
539     @Test
testUpdateLockTaskPackages_taskRemoved()540     public void testUpdateLockTaskPackages_taskRemoved() throws Exception {
541         // GIVEN two tasks which are allowlisted initially
542         Task tr1 = getTaskForUpdate(TEST_PACKAGE_NAME, true);
543         Task tr2 = getTaskForUpdate(TEST_PACKAGE_NAME_2, false);
544         String[] allowlist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
545         mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
546 
547         // GIVEN the tasks are launched into LockTask mode
548         mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
549         mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
550         assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
551         assertTrue(mLockTaskController.isTaskLocked(tr1));
552         assertTrue(mLockTaskController.isTaskLocked(tr2));
553         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
554 
555         // WHEN removing one package from allowlist
556         allowlist = new String[] {TEST_PACKAGE_NAME};
557         mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
558 
559         // THEN the task running that package should be stopped
560         verify(tr2).performClearTaskLocked();
561         assertFalse(mLockTaskController.isTaskLocked(tr2));
562         // THEN the other task should remain locked
563         assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
564         assertTrue(mLockTaskController.isTaskLocked(tr1));
565         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
566 
567         // WHEN removing the last package from allowlist
568         allowlist = new String[] {};
569         mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
570 
571         // THEN the last task should be cleared, and the system should quit LockTask mode
572         verify(tr1).performClearTaskLocked();
573         assertFalse(mLockTaskController.isTaskLocked(tr1));
574         assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
575         verifyLockTaskStopped(times(1));
576     }
577 
578     @Test
testUpdateLockTaskFeatures()579     public void testUpdateLockTaskFeatures() throws Exception {
580         // GIVEN a locked task
581         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
582         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
583 
584         // THEN lock task mode should be started with default status bar masks
585         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
586 
587         // reset invocation counter
588         reset(mStatusBarService);
589 
590         // WHEN home button is enabled for lock task mode
591         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_HOME);
592 
593         // THEN status bar should be updated to reflect this change
594         int expectedFlags = STATUS_BAR_MASK_LOCKED
595                 & ~DISABLE_HOME;
596         int expectedFlags2 = DISABLE2_MASK;
597         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
598                 eq(mPackageName));
599         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
600                 eq(mPackageName));
601 
602         // reset invocation counter
603         reset(mStatusBarService);
604 
605         // WHEN notifications are enabled for lock task mode
606         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NOTIFICATIONS);
607 
608         // THEN status bar should be updated to reflect this change
609         expectedFlags = STATUS_BAR_MASK_LOCKED
610                 & ~DISABLE_NOTIFICATION_ICONS
611                 & ~DISABLE_NOTIFICATION_ALERTS;
612         expectedFlags2 = DISABLE2_MASK
613                 & ~DISABLE2_NOTIFICATION_SHADE;
614         verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
615                 eq(mPackageName));
616         verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
617                 eq(mPackageName));
618     }
619 
620     @Test
testUpdateLockTaskFeatures_differentUser()621     public void testUpdateLockTaskFeatures_differentUser() throws Exception {
622         // GIVEN a locked task
623         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
624         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
625 
626         // THEN lock task mode should be started with default status bar masks
627         verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK);
628 
629         // reset invocation counter
630         reset(mStatusBarService);
631 
632         // WHEN home button is enabled for lock task mode for another user
633         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID + 1, LOCK_TASK_FEATURE_HOME);
634 
635         // THEN status bar shouldn't change
636         verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
637                 eq(mPackageName));
638         verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
639                 eq(mPackageName));
640     }
641 
642     @Test
testUpdateLockTaskFeatures_keyguard()643     public void testUpdateLockTaskFeatures_keyguard() {
644         // GIVEN a locked task
645         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
646         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
647 
648         // THEN keyguard should be disabled
649         verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
650 
651         // WHEN keyguard is enabled for lock task mode
652         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
653 
654         // THEN keyguard should be enabled
655         verify(mWindowManager).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
656 
657         // WHEN keyguard is disabled again for lock task mode
658         mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
659 
660         // THEN keyguard should be disabled
661         verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString(),
662                 eq(TEST_USER_ID));
663     }
664 
665     @Test
testGetStatusBarDisableFlags()666     public void testGetStatusBarDisableFlags() {
667         // Note that we don't enumerate all StatusBarManager flags, but only choose a subset to test
668 
669         // WHEN nothing is enabled
670         Pair<Integer, Integer> flags = mLockTaskController.getStatusBarDisableFlags(
671                 LOCK_TASK_FEATURE_NONE);
672         // THEN unsupported feature flags should still be untouched
673         assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
674         // THEN everything else should be disabled
675         assertTrue((StatusBarManager.DISABLE_CLOCK & flags.first) != 0);
676         assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
677 
678         // WHEN only home button is enabled
679         flags = mLockTaskController.getStatusBarDisableFlags(
680                 LOCK_TASK_FEATURE_HOME);
681         // THEN unsupported feature flags should still be untouched
682         assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
683         // THEN home button should indeed be enabled
684         assertTrue((StatusBarManager.DISABLE_HOME & flags.first) == 0);
685         // THEN other feature flags should remain disabled
686         assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) != 0);
687 
688         // WHEN only global actions menu and notifications are enabled
689         flags = mLockTaskController.getStatusBarDisableFlags(
690                 DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
691                         | DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS);
692         // THEN unsupported feature flags should still be untouched
693         assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0);
694         // THEN notifications should be enabled
695         assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ICONS & flags.first) == 0);
696         assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ALERTS & flags.first) == 0);
697         assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) == 0);
698         // THEN global actions should be enabled
699         assertTrue((StatusBarManager.DISABLE2_GLOBAL_ACTIONS & flags.second) == 0);
700         // THEN quick settings should still be disabled
701         assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
702     }
703 
704     @Test
testIsActivityAllowed()705     public void testIsActivityAllowed() {
706         // WHEN lock task mode is not enabled
707         assertTrue(mLockTaskController.isActivityAllowed(
708                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
709 
710         // Start lock task mode
711         Task tr = getTask(LOCK_TASK_AUTH_ALLOWLISTED);
712         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
713 
714         // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled
715         assertTrue(mLockTaskController.isActivityAllowed(
716                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
717 
718         // Enable LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK feature
719         mLockTaskController.updateLockTaskFeatures(
720                 TEST_USER_ID, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK);
721 
722         // package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed
723         assertTrue(mLockTaskController.isActivityAllowed(
724                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
725 
726         // unallowlisted package should not be allowed
727         assertFalse(mLockTaskController.isActivityAllowed(
728                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
729 
730         // update the allowlist
731         String[] allowlist = new String[] { TEST_PACKAGE_NAME };
732         mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist);
733 
734         // allowlisted package should be allowed
735         assertTrue(mLockTaskController.isActivityAllowed(
736                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
737 
738         // package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed
739         assertFalse(mLockTaskController.isActivityAllowed(
740                 TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER));
741     }
742 
getTask(int lockTaskAuth)743     private Task getTask(int lockTaskAuth) {
744         return getTask(TEST_PACKAGE_NAME, lockTaskAuth);
745     }
746 
getTask(String pkg, int lockTaskAuth)747     private Task getTask(String pkg, int lockTaskAuth) {
748         final Intent intent = new Intent()
749                 .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
750         return getTask(intent, lockTaskAuth);
751     }
752 
getTask(Intent intent, int lockTaskAuth)753     private Task getTask(Intent intent, int lockTaskAuth) {
754         Task tr = mock(Task.class);
755         tr.mLockTaskAuth = lockTaskAuth;
756         tr.intent = intent;
757         tr.mUserId = TEST_USER_ID;
758         return tr;
759     }
760 
761     /**
762      * @param isAppAware {@code true} if the app has marked if allowlisted in its manifest
763      */
getTaskForUpdate(String pkg, boolean isAppAware)764     private Task getTaskForUpdate(String pkg, boolean isAppAware) {
765         final int authIfAllowlisted =
766                 isAppAware ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_ALLOWLISTED;
767         Task tr = getTask(pkg, authIfAllowlisted);
768         doAnswer((invocation) -> {
769             boolean isAllowlisted =
770                     mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg);
771             tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : LOCK_TASK_AUTH_PINNABLE;
772             return null;
773         }).when(tr).setLockTaskAuth();
774         return tr;
775     }
776 
verifyLockTaskStarted(int statusBarMask, int statusBarMask2)777     private void verifyLockTaskStarted(int statusBarMask, int statusBarMask2) throws Exception {
778         // THEN the keyguard should have been disabled
779         verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
780         // THEN the status bar should have been disabled
781         verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
782                 eq(mPackageName));
783         verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
784                 eq(mPackageName));
785         // THEN recents should have been notified
786         verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
787         // THEN the DO/PO should be informed about the operation
788         verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME),
789                 eq(TEST_USER_ID));
790     }
791 
verifyLockTaskStopped(VerificationMode mode)792     private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
793         // THEN the keyguard should have been disabled
794         verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
795         // THEN the status bar should have been disabled
796         verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
797                 any(IBinder.class), eq(mPackageName));
798         verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
799                 any(IBinder.class), eq(mPackageName));
800         // THEN the DO/PO should be informed about the operation
801         verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(),
802                 eq(TEST_USER_ID));
803     }
804 
805     /**
806      * Special handler implementation that executes any message / runnable posted immediately on the
807      * thread that it's posted on rather than enqueuing them on its looper.
808      */
809     private static class ImmediatelyExecuteHandler extends Handler {
810         @Override
sendMessageAtTime(Message msg, long uptimeMillis)811         public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
812             if (msg.getCallback() != null) {
813                 msg.getCallback().run();
814             }
815             return true;
816         }
817     }
818 }
819