blob: f9d7f9d4904af24e0411acfff6c9a6820f2e7139 [file] [log] [blame]
Benjamin Franza83859f2017-07-03 16:34:14 +01001/*
2 * Copyright 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.am;
18
19import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
20import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
21import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
22import static android.os.Process.SYSTEM_UID;
Charles He520b2832017-09-02 15:27:16 +010023
Benjamin Franza83859f2017-07-03 16:34:14 +010024import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
25import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
Charles He520b2832017-09-02 15:27:16 +010026
27import static org.junit.Assert.*;
28import static org.mockito.ArgumentMatchers.*;
29import static org.mockito.Mockito.*;
Benjamin Franza83859f2017-07-03 16:34:14 +010030
31import android.app.StatusBarManager;
32import android.app.admin.IDevicePolicyManager;
33import android.content.ComponentName;
34import android.content.Context;
35import android.content.Intent;
36import android.os.Handler;
37import android.os.IBinder;
38import android.os.Looper;
39import android.os.Message;
40import android.os.UserHandle;
41import android.platform.test.annotations.Presubmit;
42import android.provider.Settings;
43import android.support.test.InstrumentationRegistry;
44import android.support.test.filters.SmallTest;
45
46import com.android.internal.statusbar.IStatusBarService;
47import com.android.internal.widget.LockPatternUtils;
48import com.android.server.LocalServices;
49import com.android.server.statusbar.StatusBarManagerInternal;
50import com.android.server.wm.WindowManagerService;
51
52import org.junit.After;
53import org.junit.Before;
54import org.junit.Test;
55import org.mockito.Mock;
56import org.mockito.MockitoAnnotations;
57import org.mockito.verification.VerificationMode;
58
59/**
60 * Unit tests for {@link LockTaskController}.
61 *
62 * Build/Install/Run:
63 * bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest
64 */
65@Presubmit
66@SmallTest
67public class LockTaskControllerTest {
68 private static final String TEST_PACKAGE_NAME = "com.test.package";
Charles He520b2832017-09-02 15:27:16 +010069 private static final String TEST_PACKAGE_NAME_2 = "com.test.package2";
70 private static final String TEST_CLASS_NAME = ".TestClass";
Benjamin Franza83859f2017-07-03 16:34:14 +010071 private static final int TEST_USER_ID = 123;
72 private static final int TEST_UID = 10467;
73
74 @Mock private ActivityStackSupervisor mSupervisor;
75 @Mock private IDevicePolicyManager mDevicePolicyManager;
76 @Mock private IStatusBarService mStatusBarService;
77 @Mock private WindowManagerService mWindowManager;
78 @Mock private LockPatternUtils mLockPatternUtils;
79 @Mock private LockTaskNotify mLockTaskNotify;
80 @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
81
82 private LockTaskController mLockTaskController;
83 private Context mContext;
84 private String mLockToAppSetting;
85
86 @Before
87 public void setUp() throws Exception {
88 // This property is used to allow mocking of package private classes with mockito
89 System.setProperty("dexmaker.share_classloader", "true");
90
91 MockitoAnnotations.initMocks(this);
92
93 mContext = InstrumentationRegistry.getTargetContext();
94 mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
95 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
96
97 if (Looper.myLooper() == null) {
98 Looper.prepare();
99 }
100
101 mLockTaskController = new LockTaskController(mContext, mSupervisor,
102 new ImmediatelyExecuteHandler());
103
104 mLockTaskController.setWindowManager(mWindowManager);
105 mLockTaskController.mStatusBarService = mStatusBarService;
106 mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
107 mLockTaskController.mLockPatternUtils = mLockPatternUtils;
108 mLockTaskController.mLockTaskNotify = mLockTaskNotify;
109
110 LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
111 LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
112 }
113
114 @After
115 public void tearDown() throws Exception {
116 Settings.Secure.putString(mContext.getContentResolver(),
117 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
118 }
119
120 @Test
121 public void testPreconditions() {
122 // GIVEN nothing has happened
123
124 // THEN current lock task mode should be NONE
125 assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
126 }
127
128 @Test
129 public void testStartLockTaskMode_once() throws Exception {
130 // GIVEN a task record with whitelisted auth
131 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
132
133 // WHEN calling setLockTaskMode for LOCKED mode without resuming
134 mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
135
136 // THEN the lock task mode state should be LOCKED
137 assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
138 // THEN the task should be locked
139 assertTrue(mLockTaskController.checkLockedTask(tr));
140
141 // THEN lock task mode should be started
142 verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED);
143 }
144
145 @Test
146 public void testStartLockTaskMode_twice() throws Exception {
147 // GIVEN two task records with whitelisted auth
148 TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
149 TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
150
151 // WHEN calling setLockTaskMode for LOCKED mode on both tasks
152 mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
153 mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
154
155 // THEN the lock task mode state should be LOCKED
156 assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
157 // THEN neither of the tasks should be able to move to back of stack
158 assertTrue(mLockTaskController.checkLockedTask(tr1));
159 assertTrue(mLockTaskController.checkLockedTask(tr2));
160
161 // THEN lock task mode should be started
162 verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED);
163 }
164
165 @Test
166 public void testStartLockTaskMode_pinningRequest() throws Exception {
167 // GIVEN a task record that is not whitelisted, i.e. with pinned auth
168 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
169
170 // WHEN calling startLockTaskMode
171 mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
172
173 // THEN a pinning request should be shown
174 verify(mStatusBarManagerInternal).showScreenPinningRequest(anyInt());
175 }
176
177 @Test
178 public void testStartLockTaskMode_pinnedBySystem() throws Exception {
179 // GIVEN a task record with pinned auth
180 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
181
182 // WHEN the system calls startLockTaskMode
183 mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
184
185 // THEN the lock task mode state should be PINNED
186 assertEquals(LOCK_TASK_MODE_PINNED, mLockTaskController.getLockTaskModeState());
187 // THEN the task should be locked
188 assertTrue(mLockTaskController.checkLockedTask(tr));
189
190 // THEN lock task mode should be started
191 verifyLockTaskStarted(STATUS_BAR_MASK_PINNED);
192 }
193
194 @Test
195 public void testLockTaskViolation() throws Exception {
196 // GIVEN one task records with whitelisted auth that is in lock task mode
197 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
198 mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
199
200 // THEN it's not a lock task violation to try and launch this task without clearing
201 assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false));
202
203 // THEN it's a lock task violation to launch another task that is not whitelisted
204 assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
205 TaskRecord.LOCK_TASK_AUTH_PINNABLE)));
206 // THEN it's a lock task violation to launch another task that is disallowed from lock task
207 assertTrue(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
208 TaskRecord.LOCK_TASK_AUTH_DONT_LOCK)));
209
210 // THEN it's no a lock task violation to launch another task that is whitelisted
211 assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
212 TaskRecord.LOCK_TASK_AUTH_WHITELISTED)));
213 assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
214 TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE)));
215 // THEN it's not a lock task violation to launch another task that is priv launchable
216 assertFalse(mLockTaskController.isLockTaskModeViolation(getTaskRecord(
217 TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV)));
218 }
219
220 @Test
221 public void testStopLockTaskMode() throws Exception {
222 // GIVEN one task record with whitelisted auth that is in lock task mode
223 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
224 mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
225
226 // WHEN the same caller calls stopLockTaskMode
227 mLockTaskController.stopLockTaskMode(false, TEST_UID);
228
229 // THEN the lock task mode should be NONE
230 assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
231 // THEN the task should no longer be locked
232 assertFalse(mLockTaskController.checkLockedTask(tr));
233 // THEN lock task mode should have been finished
234 verifyLockTaskStopped(times(1));
235 }
236
237 @Test(expected = SecurityException.class)
238 public void testStopLockTaskMode_DifferentCaller() throws Exception {
239 // GIVEN one task record with whitelisted auth that is in lock task mode
240 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
241 mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
242
243 // WHEN a different caller calls stopLockTaskMode
244 mLockTaskController.stopLockTaskMode(false, TEST_UID + 1);
245
246 // THEN security exception should be thrown, because different caller tried to unlock
247 }
248
249 @Test
250 public void testStopLockTaskMode_SystemCaller() throws Exception {
251 // GIVEN one task record with whitelisted auth that is in lock task mode
252 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
253 mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
254
255 // WHEN system calls stopLockTaskMode
256 mLockTaskController.stopLockTaskMode(true, SYSTEM_UID);
257
258 // THEN a lock tash toast should be shown
259 verify(mLockTaskNotify).showToast(LOCK_TASK_MODE_LOCKED);
260 // THEN lock task mode should still be active
261 assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
262 }
263
264 @Test
265 public void testStopLockTaskMode_twoTasks() throws Exception {
266 // GIVEN two task records with whitelisted auth that is in lock task mode
267 TaskRecord tr1 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
268 TaskRecord tr2 = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
269 mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
270 mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
271
272 // WHEN calling stopLockTaskMode
273 mLockTaskController.stopLockTaskMode(false, TEST_UID);
274
275 // THEN the lock task mode should still be active
276 assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
277 // THEN the first task should still be locked
278 assertTrue(mLockTaskController.checkLockedTask(tr1));
279 // THEN the top task should no longer be locked
280 assertFalse(mLockTaskController.checkLockedTask(tr2));
281 // THEN lock task mode should not have been finished
282 verifyLockTaskStopped(never());
283 }
284
285 @Test
286 public void testStopLockTaskMode_pinned() throws Exception {
287 // GIVEN one task records that is in pinned mode
288 TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_PINNABLE);
289 mLockTaskController.startLockTaskMode(tr, true, SYSTEM_UID);
290 // GIVEN that the keyguard is required to show after unlocking
291 Settings.Secure.putInt(mContext.getContentResolver(),
292 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1);
293
294 // WHEN calling stopLockTask
295 mLockTaskController.stopLockTaskMode(true, SYSTEM_UID);
296
297 // THEN the lock task mode should no longer be active
298 assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
299 // THEN the task should no longer be locked
300 assertFalse(mLockTaskController.checkLockedTask(tr));
301 // THEN lock task mode should have been finished
302 verifyLockTaskStopped(times(1));
303 // THEN the keyguard should be shown
304 verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
305 }
306
Charles He520b2832017-09-02 15:27:16 +0100307 @Test
308 public void testUpdateLockTaskPackages() throws Exception {
309 String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
310 String[] whitelist2 = {TEST_PACKAGE_NAME};
311
312 // No package is whitelisted initially
313 for (String pkg : whitelist1) {
314 assertFalse("Package shouldn't be whitelisted: " + pkg,
315 mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
316 assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
317 mLockTaskController.isPackageWhitelisted(0, pkg));
318 }
319
320 // Apply whitelist
321 mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1);
322
323 // Assert the whitelist is applied to the correct user
324 for (String pkg : whitelist1) {
325 assertTrue("Package should be whitelisted: " + pkg,
326 mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg));
327 assertFalse("Package shouldn't be whitelisted for user 0: " + pkg,
328 mLockTaskController.isPackageWhitelisted(0, pkg));
329 }
330
331 // Update whitelist
332 mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2);
333
334 // Assert the new whitelist is applied
335 assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME,
336 mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME));
337 assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2,
338 mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2));
339 }
340
341 @Test
342 public void testUpdateLockTaskPackages_taskRemoved() throws Exception {
343 // GIVEN two tasks which are whitelisted initially
344 TaskRecord tr1 = getTaskRecordForUpdate(TEST_PACKAGE_NAME, true);
345 TaskRecord tr2 = getTaskRecordForUpdate(TEST_PACKAGE_NAME_2, false);
346 String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2};
347 mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
348
349 // GIVEN the tasks are launched into LockTask mode
350 mLockTaskController.startLockTaskMode(tr1, false, TEST_UID);
351 mLockTaskController.startLockTaskMode(tr2, false, TEST_UID);
352 assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
353 assertTrue(mLockTaskController.checkLockedTask(tr1));
354 assertTrue(mLockTaskController.checkLockedTask(tr2));
355 verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED);
356
357 // WHEN removing one package from whitelist
358 whitelist = new String[] {TEST_PACKAGE_NAME};
359 mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
360
361 // THEN the task running that package should be stopped
362 verify(tr2).performClearTaskLocked();
363 assertFalse(mLockTaskController.checkLockedTask(tr2));
364 // THEN the other task should remain locked
365 assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState());
366 assertTrue(mLockTaskController.checkLockedTask(tr1));
367 verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED);
368
369 // WHEN removing the last package from whitelist
370 whitelist = new String[] {};
371 mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
372
373 // THEN the last task should be cleared, and the system should quit LockTask mode
374 verify(tr1).performClearTaskLocked();
375 assertFalse(mLockTaskController.checkLockedTask(tr1));
376 assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState());
377 verifyLockTaskStopped(times(1));
378 }
379
Benjamin Franza83859f2017-07-03 16:34:14 +0100380 private TaskRecord getTaskRecord(int lockTaskAuth) {
Charles He520b2832017-09-02 15:27:16 +0100381 return getTaskRecord(TEST_PACKAGE_NAME, lockTaskAuth);
382 }
383
384 private TaskRecord getTaskRecord(String pkg, int lockTaskAuth) {
Benjamin Franza83859f2017-07-03 16:34:14 +0100385 TaskRecord tr = mock(TaskRecord.class);
386 tr.mLockTaskAuth = lockTaskAuth;
387 tr.intent = new Intent()
Charles He520b2832017-09-02 15:27:16 +0100388 .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
Benjamin Franza83859f2017-07-03 16:34:14 +0100389 tr.userId = TEST_USER_ID;
390 return tr;
391 }
392
Charles He520b2832017-09-02 15:27:16 +0100393 /**
394 * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest
395 */
396 private TaskRecord getTaskRecordForUpdate(String pkg, boolean isAppAware) {
397 final int authIfWhitelisted = isAppAware
398 ? TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE
399 : TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
400 TaskRecord tr = getTaskRecord(pkg, authIfWhitelisted);
401 doAnswer((invocation) -> {
402 boolean isWhitelisted =
403 mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg);
404 tr.mLockTaskAuth = isWhitelisted
405 ? authIfWhitelisted
406 : TaskRecord.LOCK_TASK_AUTH_PINNABLE;
407 return null;
408 }).when(tr).setLockTaskAuth();
409 return tr;
410 }
411
Benjamin Franza83859f2017-07-03 16:34:14 +0100412 private void verifyLockTaskStarted(int statusBarMask) throws Exception {
413 // THEN the keyguard should have been disabled
414 verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString());
415 // THEN the status bar should have been disabled
416 verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
417 eq(mContext.getPackageName()));
418 // THEN the DO/PO should be informed about the operation
419 verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
420 TEST_USER_ID);
421 }
422
423 private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
424 // THEN the keyguard should have been disabled
425 verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class));
426 // THEN the status bar should have been disabled
427 verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
428 any(IBinder.class), eq(mContext.getPackageName()));
429 // THEN the DO/PO should be informed about the operation
430 verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
431 }
432
433 /**
434 * Special handler implementation that executes any message / runnable posted immediately on the
435 * thread that it's posted on rather than enqueuing them on its looper.
436 */
437 private static class ImmediatelyExecuteHandler extends Handler {
438 @Override
439 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
440 if (msg.getCallback() != null) {
441 msg.getCallback().run();
442 }
443 return true;
444 }
445 }
446}