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 17 package com.android.server.am; 18 19 import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 26 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 27 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; 28 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; 29 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 30 import static android.view.Display.DEFAULT_DISPLAY; 31 32 import static org.junit.Assert.assertFalse; 33 import static org.junit.Assert.assertTrue; 34 import static org.junit.Assert.fail; 35 import static org.mockito.ArgumentMatchers.anyInt; 36 import static org.mockito.ArgumentMatchers.anyString; 37 import static org.mockito.Mockito.doReturn; 38 import static org.mockito.Mockito.mock; 39 import static org.mockito.Mockito.spy; 40 41 import static java.lang.Integer.MAX_VALUE; 42 43 import android.annotation.TestApi; 44 import android.app.ActivityManager; 45 import android.app.ActivityManager.RecentTaskInfo; 46 import android.app.ActivityManager.RunningTaskInfo; 47 import android.app.WindowConfiguration; 48 import android.content.ComponentName; 49 import android.content.Context; 50 import android.content.pm.PackageManager; 51 import android.content.pm.ParceledListSlice; 52 import android.content.pm.UserInfo; 53 import android.content.res.Configuration; 54 import android.graphics.Rect; 55 import android.os.Bundle; 56 import android.os.Debug; 57 import android.os.Looper; 58 import android.os.RemoteException; 59 import android.os.SystemClock; 60 import android.platform.test.annotations.Presubmit; 61 import android.support.test.InstrumentationRegistry; 62 import android.support.test.filters.MediumTest; 63 import android.support.test.runner.AndroidJUnit4; 64 import android.util.MutableLong; 65 import android.util.SparseArray; 66 import android.util.SparseBooleanArray; 67 68 import com.android.server.am.RecentTasks.Callbacks; 69 70 import org.junit.Before; 71 import org.junit.Test; 72 import org.junit.runner.RunWith; 73 74 import java.io.File; 75 import java.util.ArrayList; 76 import java.util.Arrays; 77 import java.util.HashSet; 78 import java.util.List; 79 import java.util.Random; 80 import java.util.Set; 81 82 /** 83 * atest FrameworksServicesTests:RecentTasksTest 84 */ 85 @MediumTest 86 @Presubmit 87 @RunWith(AndroidJUnit4.class) 88 public class RecentTasksTest extends ActivityTestsBase { 89 private static final int TEST_USER_0_ID = 0; 90 private static final int TEST_USER_1_ID = 10; 91 private static final int TEST_QUIET_USER_ID = 20; 92 private static final UserInfo DEFAULT_USER_INFO = new UserInfo(); 93 private static final UserInfo QUIET_USER_INFO = new UserInfo(); 94 private static int LAST_TASK_ID = 1; 95 private static int LAST_STACK_ID = 1; 96 private static int INVALID_STACK_ID = 999; 97 98 private Context mContext = InstrumentationRegistry.getContext(); 99 private ActivityManagerService mService; 100 private ActivityDisplay mDisplay; 101 private ActivityDisplay mOtherDisplay; 102 private ActivityStack mStack; 103 private ActivityStack mHomeStack; 104 private TestTaskPersister mTaskPersister; 105 private TestRecentTasks mRecentTasks; 106 private TestRunningTasks mRunningTasks; 107 108 private ArrayList<TaskRecord> mTasks; 109 private ArrayList<TaskRecord> mSameDocumentTasks; 110 111 private CallbacksRecorder mCallbacksRecorder; 112 113 class TestUserController extends UserController { 114 TestUserController(ActivityManagerService service) { 115 super(service); 116 } 117 118 @Override 119 int[] getCurrentProfileIds() { 120 return new int[] { TEST_USER_0_ID, TEST_QUIET_USER_ID }; 121 } 122 123 @Override 124 Set<Integer> getProfileIds(int userId) { 125 Set<Integer> profileIds = new HashSet<>(); 126 profileIds.add(TEST_USER_0_ID); 127 profileIds.add(TEST_QUIET_USER_ID); 128 return profileIds; 129 } 130 131 @Override 132 UserInfo getUserInfo(int userId) { 133 switch (userId) { 134 case TEST_USER_0_ID: 135 case TEST_USER_1_ID: 136 return DEFAULT_USER_INFO; 137 case TEST_QUIET_USER_ID: 138 return QUIET_USER_INFO; 139 } 140 return null; 141 } 142 } 143 144 @Before 145 @Override 146 public void setUp() throws Exception { 147 super.setUp(); 148 149 mTaskPersister = new TestTaskPersister(mContext.getFilesDir()); 150 mService = setupActivityManagerService(new MyTestActivityManagerService(mContext)); 151 mRecentTasks = (TestRecentTasks) mService.getRecentTasks(); 152 mRecentTasks.loadParametersFromResources(mContext.getResources()); 153 mHomeStack = mService.mStackSupervisor.getDefaultDisplay().createStack( 154 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); 155 mStack = mService.mStackSupervisor.getDefaultDisplay().createStack( 156 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); 157 ((MyTestActivityStackSupervisor) mService.mStackSupervisor).setHomeStack(mHomeStack); 158 mCallbacksRecorder = new CallbacksRecorder(); 159 mRecentTasks.registerCallback(mCallbacksRecorder); 160 QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE; 161 162 mTasks = new ArrayList<>(); 163 mTasks.add(createTaskBuilder(".Task1").build()); 164 mTasks.add(createTaskBuilder(".Task2").build()); 165 mTasks.add(createTaskBuilder(".Task3").build()); 166 mTasks.add(createTaskBuilder(".Task4").build()); 167 mTasks.add(createTaskBuilder(".Task5").build()); 168 169 mSameDocumentTasks = new ArrayList<>(); 170 mSameDocumentTasks.add(createDocumentTask(".DocumentTask1")); 171 mSameDocumentTasks.add(createDocumentTask(".DocumentTask1")); 172 } 173 174 @Test 175 public void testCallbacks() throws Exception { 176 // Add some tasks 177 mRecentTasks.add(mTasks.get(0)); 178 mRecentTasks.add(mTasks.get(1)); 179 assertTrue(mCallbacksRecorder.added.contains(mTasks.get(0)) 180 && mCallbacksRecorder.added.contains(mTasks.get(1))); 181 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 182 assertTrue(mCallbacksRecorder.removed.isEmpty()); 183 mCallbacksRecorder.clear(); 184 185 // Remove some tasks 186 mRecentTasks.remove(mTasks.get(0)); 187 mRecentTasks.remove(mTasks.get(1)); 188 assertTrue(mCallbacksRecorder.added.isEmpty()); 189 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 190 assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(0))); 191 assertTrue(mCallbacksRecorder.removed.contains(mTasks.get(1))); 192 mCallbacksRecorder.clear(); 193 194 // Remove the callback, ensure we don't get any calls 195 mRecentTasks.unregisterCallback(mCallbacksRecorder); 196 mRecentTasks.add(mTasks.get(0)); 197 mRecentTasks.remove(mTasks.get(0)); 198 assertTrue(mCallbacksRecorder.added.isEmpty()); 199 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 200 assertTrue(mCallbacksRecorder.removed.isEmpty()); 201 } 202 203 @Test 204 public void testAddTasksNoMultiple_expectNoTrim() throws Exception { 205 // Add same non-multiple-task document tasks will remove the task (to re-add it) but not 206 // trim it 207 TaskRecord documentTask1 = createDocumentTask(".DocumentTask1"); 208 TaskRecord documentTask2 = createDocumentTask(".DocumentTask1"); 209 mRecentTasks.add(documentTask1); 210 mRecentTasks.add(documentTask2); 211 assertTrue(mCallbacksRecorder.added.contains(documentTask1)); 212 assertTrue(mCallbacksRecorder.added.contains(documentTask2)); 213 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 214 assertTrue(mCallbacksRecorder.removed.contains(documentTask1)); 215 } 216 217 @Test 218 public void testAddTasksMaxTaskRecents_expectNoTrim() throws Exception { 219 // Add a task hitting max-recents for that app will remove the task (to add the next one) 220 // but not trim it 221 TaskRecord documentTask1 = createDocumentTask(".DocumentTask1"); 222 TaskRecord documentTask2 = createDocumentTask(".DocumentTask1"); 223 documentTask1.maxRecents = 1; 224 documentTask2.maxRecents = 1; 225 mRecentTasks.add(documentTask1); 226 mRecentTasks.add(documentTask2); 227 assertTrue(mCallbacksRecorder.added.contains(documentTask1)); 228 assertTrue(mCallbacksRecorder.added.contains(documentTask2)); 229 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 230 assertTrue(mCallbacksRecorder.removed.contains(documentTask1)); 231 } 232 233 @Test 234 public void testAddTasksSameTask_expectNoTrim() throws Exception { 235 // Add a task that is already in the task list does not trigger any callbacks, it just 236 // moves in the list 237 TaskRecord documentTask1 = createDocumentTask(".DocumentTask1"); 238 mRecentTasks.add(documentTask1); 239 mRecentTasks.add(documentTask1); 240 assertTrue(mCallbacksRecorder.added.size() == 1); 241 assertTrue(mCallbacksRecorder.added.contains(documentTask1)); 242 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 243 assertTrue(mCallbacksRecorder.removed.isEmpty()); 244 } 245 246 @Test 247 public void testAddTasksMultipleDocumentTasks_expectNoTrim() throws Exception { 248 // Add same multiple-task document tasks does not trim the first tasks 249 TaskRecord documentTask1 = createDocumentTask(".DocumentTask1", 250 FLAG_ACTIVITY_MULTIPLE_TASK); 251 TaskRecord documentTask2 = createDocumentTask(".DocumentTask1", 252 FLAG_ACTIVITY_MULTIPLE_TASK); 253 mRecentTasks.add(documentTask1); 254 mRecentTasks.add(documentTask2); 255 assertTrue(mCallbacksRecorder.added.size() == 2); 256 assertTrue(mCallbacksRecorder.added.contains(documentTask1)); 257 assertTrue(mCallbacksRecorder.added.contains(documentTask2)); 258 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 259 assertTrue(mCallbacksRecorder.removed.isEmpty()); 260 } 261 262 @Test 263 public void testAddTasksMultipleTasks_expectRemovedNoTrim() throws Exception { 264 // Add multiple same-affinity non-document tasks, ensure that it removes the other task, 265 // but that it does not trim it 266 TaskRecord task1 = createTaskBuilder(".Task1") 267 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) 268 .build(); 269 TaskRecord task2 = createTaskBuilder(".Task1") 270 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) 271 .build(); 272 mRecentTasks.add(task1); 273 assertTrue(mCallbacksRecorder.added.size() == 1); 274 assertTrue(mCallbacksRecorder.added.contains(task1)); 275 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 276 assertTrue(mCallbacksRecorder.removed.isEmpty()); 277 mCallbacksRecorder.clear(); 278 mRecentTasks.add(task2); 279 assertTrue(mCallbacksRecorder.added.size() == 1); 280 assertTrue(mCallbacksRecorder.added.contains(task2)); 281 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 282 assertTrue(mCallbacksRecorder.removed.size() == 1); 283 assertTrue(mCallbacksRecorder.removed.contains(task1)); 284 } 285 286 @Test 287 public void testAddTasksDifferentStacks_expectNoRemove() throws Exception { 288 // Adding the same task with different activity types should not trigger removal of the 289 // other task 290 TaskRecord task1 = createTaskBuilder(".Task1") 291 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) 292 .setStack(mHomeStack).build(); 293 TaskRecord task2 = createTaskBuilder(".Task1") 294 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) 295 .setStack(mStack).build(); 296 mRecentTasks.add(task1); 297 mRecentTasks.add(task2); 298 assertTrue(mCallbacksRecorder.added.size() == 2); 299 assertTrue(mCallbacksRecorder.added.contains(task1)); 300 assertTrue(mCallbacksRecorder.added.contains(task2)); 301 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 302 assertTrue(mCallbacksRecorder.removed.isEmpty()); 303 } 304 305 @Test 306 public void testAddTaskCompatibleActivityType_expectRemove() throws Exception { 307 // Test with undefined activity type since the type is not persisted by the task persister 308 // and we want to ensure that a new task will match a restored task 309 Configuration config1 = new Configuration(); 310 config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); 311 TaskRecord task1 = createTaskBuilder(".Task1") 312 .setFlags(FLAG_ACTIVITY_NEW_TASK) 313 .setStack(mStack) 314 .build(); 315 task1.onConfigurationChanged(config1); 316 assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED); 317 mRecentTasks.add(task1); 318 mCallbacksRecorder.clear(); 319 320 TaskRecord task2 = createTaskBuilder(".Task1") 321 .setFlags(FLAG_ACTIVITY_NEW_TASK) 322 .setStack(mStack) 323 .build(); 324 assertTrue(task2.getActivityType() == ACTIVITY_TYPE_STANDARD); 325 mRecentTasks.add(task2); 326 assertTrue(mCallbacksRecorder.added.size() == 1); 327 assertTrue(mCallbacksRecorder.added.contains(task2)); 328 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 329 assertTrue(mCallbacksRecorder.removed.size() == 1); 330 assertTrue(mCallbacksRecorder.removed.contains(task1)); 331 } 332 333 @Test 334 public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() throws Exception { 335 Configuration config1 = new Configuration(); 336 config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); 337 TaskRecord task1 = createTaskBuilder(".Task1") 338 .setFlags(FLAG_ACTIVITY_NEW_TASK) 339 .setStack(mStack) 340 .setUserId(TEST_USER_0_ID) 341 .build(); 342 task1.onConfigurationChanged(config1); 343 assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED); 344 mRecentTasks.add(task1); 345 mCallbacksRecorder.clear(); 346 347 TaskRecord task2 = createTaskBuilder(".Task1") 348 .setFlags(FLAG_ACTIVITY_NEW_TASK) 349 .setStack(mStack) 350 .setUserId(TEST_USER_1_ID) 351 .build(); 352 assertTrue(task2.getActivityType() == ACTIVITY_TYPE_STANDARD); 353 mRecentTasks.add(task2); 354 assertTrue(mCallbacksRecorder.added.size() == 1); 355 assertTrue(mCallbacksRecorder.added.contains(task2)); 356 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 357 assertTrue(mCallbacksRecorder.removed.isEmpty()); 358 } 359 360 @Test 361 public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception { 362 Configuration config1 = new Configuration(); 363 config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); 364 TaskRecord task1 = createTaskBuilder(".Task1") 365 .setFlags(FLAG_ACTIVITY_NEW_TASK) 366 .setStack(mStack) 367 .build(); 368 task1.onConfigurationChanged(config1); 369 assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED); 370 mRecentTasks.add(task1); 371 mCallbacksRecorder.clear(); 372 373 Configuration config2 = new Configuration(); 374 config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 375 TaskRecord task2 = createTaskBuilder(".Task1") 376 .setFlags(FLAG_ACTIVITY_NEW_TASK) 377 .setStack(mStack) 378 .build(); 379 task2.onConfigurationChanged(config2); 380 assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); 381 mRecentTasks.add(task2); 382 383 assertTrue(mCallbacksRecorder.added.size() == 1); 384 assertTrue(mCallbacksRecorder.added.contains(task2)); 385 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 386 assertTrue(mCallbacksRecorder.removed.size() == 1); 387 assertTrue(mCallbacksRecorder.removed.contains(task1)); 388 } 389 390 @Test 391 public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception { 392 Configuration config1 = new Configuration(); 393 config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 394 TaskRecord task1 = createTaskBuilder(".Task1") 395 .setFlags(FLAG_ACTIVITY_NEW_TASK) 396 .setStack(mStack) 397 .build(); 398 task1.onConfigurationChanged(config1); 399 assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN); 400 mRecentTasks.add(task1); 401 402 Configuration config2 = new Configuration(); 403 config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED); 404 TaskRecord task2 = createTaskBuilder(".Task1") 405 .setFlags(FLAG_ACTIVITY_NEW_TASK) 406 .setStack(mStack) 407 .build(); 408 task2.onConfigurationChanged(config2); 409 assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED); 410 mRecentTasks.add(task2); 411 412 assertTrue(mCallbacksRecorder.added.size() == 2); 413 assertTrue(mCallbacksRecorder.added.contains(task1)); 414 assertTrue(mCallbacksRecorder.added.contains(task2)); 415 assertTrue(mCallbacksRecorder.trimmed.isEmpty()); 416 assertTrue(mCallbacksRecorder.removed.isEmpty()); 417 } 418 419 @Test 420 public void testUsersTasks() throws Exception { 421 mRecentTasks.setOnlyTestVisibleRange(); 422 423 // Setup some tasks for the users 424 mTaskPersister.userTaskIdsOverride = new SparseBooleanArray(); 425 mTaskPersister.userTaskIdsOverride.put(1, true); 426 mTaskPersister.userTaskIdsOverride.put(2, true); 427 mTaskPersister.userTasksOverride = new ArrayList<>(); 428 mTaskPersister.userTasksOverride.add(createTaskBuilder(".UserTask1").build()); 429 mTaskPersister.userTasksOverride.add(createTaskBuilder(".UserTask2").build()); 430 431 // Assert no user tasks are initially loaded 432 assertTrue(mRecentTasks.usersWithRecentsLoadedLocked().length == 0); 433 434 // Load user 0 tasks 435 mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID); 436 assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); 437 assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID)); 438 assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID)); 439 440 // Load user 1 tasks 441 mRecentTasks.loadUserRecentsLocked(TEST_USER_1_ID); 442 assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); 443 assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID)); 444 assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID)); 445 assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID)); 446 assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_1_ID)); 447 assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_1_ID)); 448 449 // Unload user 1 tasks 450 mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_1_ID); 451 assertTrue(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); 452 assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID)); 453 assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID)); 454 assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID)); 455 456 // Unload user 0 tasks 457 mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID); 458 assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_0_ID)); 459 assertFalse(arrayContainsUser(mRecentTasks.usersWithRecentsLoadedLocked(), TEST_USER_1_ID)); 460 } 461 462 @Test 463 public void testOrderedIteration() throws Exception { 464 mRecentTasks.setOnlyTestVisibleRange(); 465 TaskRecord task1 = createTaskBuilder(".Task1").build(); 466 task1.lastActiveTime = new Random().nextInt(); 467 TaskRecord task2 = createTaskBuilder(".Task1").build(); 468 task2.lastActiveTime = new Random().nextInt(); 469 TaskRecord task3 = createTaskBuilder(".Task1").build(); 470 task3.lastActiveTime = new Random().nextInt(); 471 TaskRecord task4 = createTaskBuilder(".Task1").build(); 472 task4.lastActiveTime = new Random().nextInt(); 473 mRecentTasks.add(task1); 474 mRecentTasks.add(task2); 475 mRecentTasks.add(task3); 476 mRecentTasks.add(task4); 477 478 MutableLong prevLastActiveTime = new MutableLong(0); 479 final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks(); 480 for (int i = 0; i < tasks.size(); i++) { 481 final TaskRecord task = tasks.get(i); 482 assertTrue(task.lastActiveTime >= prevLastActiveTime.value); 483 prevLastActiveTime.value = task.lastActiveTime; 484 } 485 } 486 487 @Test 488 public void testTrimToGlobalMaxNumRecents() throws Exception { 489 mRecentTasks.setOnlyTestVisibleRange(); 490 491 // Limit the global maximum number of recent tasks to a fixed size 492 mRecentTasks.setGlobalMaxNumTasks(2 /* globalMaxNumTasks */); 493 494 // Add N+1 tasks 495 mRecentTasks.add(mTasks.get(0)); 496 mRecentTasks.add(mTasks.get(1)); 497 mRecentTasks.add(mTasks.get(2)); 498 499 // Ensure that the last task was trimmed as an inactive task 500 assertTrimmed(mTasks.get(0)); 501 } 502 503 @Test 504 public void testTrimQuietProfileTasks() throws Exception { 505 mRecentTasks.setOnlyTestVisibleRange(); 506 TaskRecord qt1 = createTaskBuilder(".QuietTask1").setUserId(TEST_QUIET_USER_ID).build(); 507 TaskRecord qt2 = createTaskBuilder(".QuietTask2").setUserId(TEST_QUIET_USER_ID).build(); 508 mRecentTasks.add(qt1); 509 mRecentTasks.add(qt2); 510 511 mRecentTasks.add(mTasks.get(0)); 512 mRecentTasks.add(mTasks.get(1)); 513 514 // Ensure that the quiet user's tasks was trimmed once the new tasks were added 515 assertTrimmed(qt1, qt2); 516 } 517 518 @Test 519 public void testSessionDuration() throws Exception { 520 mRecentTasks.setOnlyTestVisibleRange(); 521 mRecentTasks.setParameters(-1 /* min */, -1 /* max */, 50 /* ms */); 522 523 TaskRecord t1 = createTaskBuilder(".Task1").build(); 524 t1.touchActiveTime(); 525 mRecentTasks.add(t1); 526 527 // Force a small sleep just beyond the session duration 528 SystemClock.sleep(75); 529 530 TaskRecord t2 = createTaskBuilder(".Task2").build(); 531 t2.touchActiveTime(); 532 mRecentTasks.add(t2); 533 534 // Assert that the old task has been removed due to being out of the active session 535 assertTrimmed(t1); 536 } 537 538 @Test 539 public void testVisibleTasks_excludedFromRecents() throws Exception { 540 mRecentTasks.setOnlyTestVisibleRange(); 541 mRecentTasks.setParameters(-1 /* min */, 4 /* max */, -1 /* ms */); 542 543 TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1") 544 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) 545 .build(); 546 TaskRecord excludedTask2 = createTaskBuilder(".ExcludedTask2") 547 .setFlags(FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) 548 .build(); 549 550 mRecentTasks.add(excludedTask1); 551 mRecentTasks.add(mTasks.get(0)); 552 mRecentTasks.add(mTasks.get(1)); 553 mRecentTasks.add(mTasks.get(2)); 554 mRecentTasks.add(excludedTask2); 555 556 // The last excluded task should be trimmed, while the first-most excluded task should not 557 assertTrimmed(excludedTask1); 558 } 559 560 @Test 561 public void testVisibleTasks_minNum() throws Exception { 562 mRecentTasks.setOnlyTestVisibleRange(); 563 mRecentTasks.setParameters(5 /* min */, -1 /* max */, 25 /* ms */); 564 565 for (int i = 0; i < 4; i++) { 566 final TaskRecord task = mTasks.get(i); 567 task.touchActiveTime(); 568 mRecentTasks.add(task); 569 } 570 571 // Force a small sleep just beyond the session duration 572 SystemClock.sleep(50); 573 574 // Add a new task to trigger tasks to be trimmed 575 mRecentTasks.add(mTasks.get(4)); 576 577 // Ensure that there are a minimum number of tasks regardless of session length 578 assertNoTasksTrimmed(); 579 } 580 581 @Test 582 public void testVisibleTasks_maxNum() throws Exception { 583 mRecentTasks.setOnlyTestVisibleRange(); 584 mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */); 585 586 for (int i = 0; i < 5; i++) { 587 final TaskRecord task = mTasks.get(i); 588 task.touchActiveTime(); 589 mRecentTasks.add(task); 590 } 591 592 // Ensure that only the last number of max tasks are kept 593 assertTrimmed(mTasks.get(0), mTasks.get(1)); 594 } 595 596 @Test 597 public void testBackStackTasks_expectNoTrim() throws Exception { 598 mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); 599 600 final MyTestActivityStackSupervisor supervisor = 601 (MyTestActivityStackSupervisor) mService.mStackSupervisor; 602 final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor); 603 final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor); 604 supervisor.setHomeStack(homeStack); 605 606 // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all 607 // the tasks belong in stacks above the home stack 608 mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build()); 609 mRecentTasks.add(createTaskBuilder(".Task1").setStack(aboveHomeStack).build()); 610 mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build()); 611 mRecentTasks.add(createTaskBuilder(".Task3").setStack(aboveHomeStack).build()); 612 613 assertNoTasksTrimmed(); 614 } 615 616 @Test 617 public void testBehindHomeStackTasks_expectTaskTrimmed() throws Exception { 618 mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); 619 620 final MyTestActivityStackSupervisor supervisor = 621 (MyTestActivityStackSupervisor) mService.mStackSupervisor; 622 final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor); 623 final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor); 624 final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor); 625 supervisor.setHomeStack(homeStack); 626 627 // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind 628 // the home stack is trimmed once a new task is added 629 final TaskRecord behindHomeTask = createTaskBuilder(".Task1") 630 .setStack(behindHomeStack) 631 .build(); 632 mRecentTasks.add(behindHomeTask); 633 mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build()); 634 mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build()); 635 636 assertTrimmed(behindHomeTask); 637 } 638 639 @Test 640 public void testOtherDisplayTasks_expectNoTrim() throws Exception { 641 mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); 642 643 final MyTestActivityStackSupervisor supervisor = 644 (MyTestActivityStackSupervisor) mService.mStackSupervisor; 645 final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor); 646 final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor); 647 supervisor.setHomeStack(homeStack); 648 649 // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not 650 // removed 651 mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build()); 652 mRecentTasks.add(createTaskBuilder(".Task1").setStack(otherDisplayStack).build()); 653 mRecentTasks.add(createTaskBuilder(".Task2").setStack(otherDisplayStack).build()); 654 mRecentTasks.add(createTaskBuilder(".HomeTask2").setStack(homeStack).build()); 655 656 assertNoTasksTrimmed(); 657 } 658 659 @Test 660 public void testRemovePackageByName() throws Exception { 661 // Add a number of tasks with the same package name 662 mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task1").build()); 663 mRecentTasks.add(createTaskBuilder("com.android.pkg2", ".Task2").build()); 664 mRecentTasks.add(createTaskBuilder("com.android.pkg3", ".Task3").build()); 665 mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task4").build()); 666 mRecentTasks.removeTasksByPackageName("com.android.pkg1", TEST_USER_0_ID); 667 668 final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks(); 669 for (int i = 0; i < tasks.size(); i++) { 670 if (tasks.get(i).intent.getComponent().getPackageName().equals("com.android.pkg1")) { 671 fail("Expected com.android.pkg1 tasks to be removed"); 672 } 673 } 674 } 675 676 @Test 677 public void testNotRecentsComponent_denyApiAccess() throws Exception { 678 doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(), 679 anyInt(), anyInt()); 680 681 // Expect the following methods to fail due to recents component not being set 682 mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION); 683 testRecentTasksApis(false /* expectNoSecurityException */); 684 // Don't throw for the following tests 685 mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY); 686 testGetTasksApis(false /* expectNoSecurityException */); 687 } 688 689 @Test 690 public void testRecentsComponent_allowApiAccessWithoutPermissions() throws Exception { 691 doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(), 692 anyInt(), anyInt()); 693 694 // Set the recents component and ensure that the following calls do not fail 695 mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.GRANT); 696 testRecentTasksApis(true /* expectNoSecurityException */); 697 testGetTasksApis(true /* expectNoSecurityException */); 698 } 699 700 private void testRecentTasksApis(boolean expectCallable) { 701 assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID)); 702 assertSecurityException(expectCallable, 703 () -> mService.removeStacksInWindowingModes(new int[] {WINDOWING_MODE_UNDEFINED})); 704 assertSecurityException(expectCallable, 705 () -> mService.removeStacksWithActivityTypes(new int[] {ACTIVITY_TYPE_UNDEFINED})); 706 assertSecurityException(expectCallable, () -> mService.removeTask(0)); 707 assertSecurityException(expectCallable, 708 () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true)); 709 assertSecurityException(expectCallable, 710 () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true)); 711 assertSecurityException(expectCallable, 712 () -> mService.setTaskWindowingModeSplitScreenPrimary(0, 713 SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true)); 714 assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true)); 715 assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0)); 716 assertSecurityException(expectCallable, 717 () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect())); 718 assertSecurityException(expectCallable, 719 () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0)); 720 assertSecurityException(expectCallable, 721 () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(), 722 new Rect())); 723 assertSecurityException(expectCallable, 724 () -> mService.resizePinnedStack(new Rect(), new Rect())); 725 assertSecurityException(expectCallable, () -> mService.getAllStackInfos()); 726 assertSecurityException(expectCallable, 727 () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED)); 728 assertSecurityException(expectCallable, () -> { 729 try { 730 mService.getFocusedStackInfo(); 731 } catch (RemoteException e) { 732 // Ignore 733 } 734 }); 735 assertSecurityException(expectCallable, 736 () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true)); 737 assertSecurityException(expectCallable, 738 () -> mService.startActivityFromRecents(0, new Bundle())); 739 assertSecurityException(expectCallable, 740 () -> mService.getTaskSnapshot(0, true)); 741 assertSecurityException(expectCallable, () -> { 742 try { 743 mService.registerTaskStackListener(null); 744 } catch (RemoteException e) { 745 // Ignore 746 } 747 }); 748 assertSecurityException(expectCallable, () -> { 749 try { 750 mService.unregisterTaskStackListener(null); 751 } catch (RemoteException e) { 752 // Ignore 753 } 754 }); 755 assertSecurityException(expectCallable, () -> mService.getTaskDescription(0)); 756 assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0)); 757 assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null, 758 null)); 759 assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true)); 760 assertSecurityException(expectCallable, () -> mService.stopAppSwitches()); 761 assertSecurityException(expectCallable, () -> mService.resumeAppSwitches()); 762 } 763 764 private void testGetTasksApis(boolean expectCallable) { 765 mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID); 766 mService.getTasks(MAX_VALUE); 767 if (expectCallable) { 768 assertTrue(mRecentTasks.lastAllowed); 769 assertTrue(mRunningTasks.lastAllowed); 770 } else { 771 assertFalse(mRecentTasks.lastAllowed); 772 assertFalse(mRunningTasks.lastAllowed); 773 } 774 } 775 776 private TaskBuilder createTaskBuilder(String className) { 777 return createTaskBuilder(mContext.getPackageName(), className); 778 } 779 780 private TaskBuilder createTaskBuilder(String packageName, String className) { 781 return new TaskBuilder(mService.mStackSupervisor) 782 .setComponent(new ComponentName(packageName, className)) 783 .setStack(mStack) 784 .setTaskId(LAST_TASK_ID++) 785 .setUserId(TEST_USER_0_ID); 786 } 787 788 private TaskRecord createDocumentTask(String className) { 789 return createDocumentTask(className, 0); 790 } 791 792 private TaskRecord createDocumentTask(String className, int flags) { 793 TaskRecord task = createTaskBuilder(className) 794 .setFlags(FLAG_ACTIVITY_NEW_DOCUMENT | flags) 795 .build(); 796 task.affinity = null; 797 task.maxRecents = ActivityManager.getMaxAppRecentsLimitStatic(); 798 return task; 799 } 800 801 private boolean arrayContainsUser(int[] userIds, int targetUserId) { 802 Arrays.sort(userIds); 803 return Arrays.binarySearch(userIds, targetUserId) >= 0; 804 } 805 806 private void assertNoTasksTrimmed() { 807 assertTrimmed(); 808 } 809 810 private void assertTrimmed(TaskRecord... tasks) { 811 final ArrayList<TaskRecord> trimmed = mCallbacksRecorder.trimmed; 812 final ArrayList<TaskRecord> removed = mCallbacksRecorder.removed; 813 assertTrue("Expected " + tasks.length + " trimmed tasks, got " + trimmed.size(), 814 trimmed.size() == tasks.length); 815 assertTrue("Expected " + tasks.length + " removed tasks, got " + removed.size(), 816 removed.size() == tasks.length); 817 for (TaskRecord task : tasks) { 818 assertTrue("Expected trimmed task: " + task, trimmed.contains(task)); 819 assertTrue("Expected removed task: " + task, removed.contains(task)); 820 } 821 } 822 823 private void assertSecurityException(boolean expectCallable, Runnable runnable) { 824 boolean noSecurityException = true; 825 try { 826 runnable.run(); 827 } catch (SecurityException se) { 828 noSecurityException = false; 829 } catch (Exception e) { 830 // We only care about SecurityExceptions, fall through here 831 e.printStackTrace(); 832 } 833 if (noSecurityException != expectCallable) { 834 fail("Expected callable: " + expectCallable + " but got no security exception: " 835 + noSecurityException); 836 } 837 } 838 839 private class MyTestActivityManagerService extends TestActivityManagerService { 840 MyTestActivityManagerService(Context context) { 841 super(context); 842 } 843 844 @Override 845 protected ActivityStackSupervisor createTestSupervisor() { 846 return new MyTestActivityStackSupervisor(this, mHandlerThread.getLooper()); 847 } 848 849 @Override 850 protected RecentTasks createRecentTasks() { 851 return new TestRecentTasks(this, mTaskPersister, new TestUserController(this)); 852 } 853 854 @Override 855 public boolean isUserRunning(int userId, int flags) { 856 return true; 857 } 858 } 859 860 private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor { 861 public MyTestActivityStackSupervisor(ActivityManagerService service, Looper looper) { 862 super(service, looper); 863 } 864 865 @Override 866 public void initialize() { 867 super.initialize(); 868 mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY); 869 mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY); 870 attachDisplay(mOtherDisplay); 871 attachDisplay(mDisplay); 872 } 873 874 @Override 875 RunningTasks createRunningTasks() { 876 mRunningTasks = new TestRunningTasks(); 877 return mRunningTasks; 878 } 879 880 void setHomeStack(ActivityStack stack) { 881 mHomeStack = stack; 882 } 883 } 884 885 private class MyTestActivityStack extends TestActivityStack { 886 private ActivityDisplay mDisplay = null; 887 888 MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) { 889 super(display, LAST_STACK_ID++, supervisor, WINDOWING_MODE_FULLSCREEN, 890 ACTIVITY_TYPE_STANDARD, true); 891 mDisplay = display; 892 } 893 894 @Override 895 ActivityDisplay getDisplay() { 896 if (mDisplay != null) { 897 return mDisplay; 898 } 899 return super.getDisplay(); 900 } 901 } 902 903 private static class CallbacksRecorder implements Callbacks { 904 ArrayList<TaskRecord> added = new ArrayList<>(); 905 ArrayList<TaskRecord> trimmed = new ArrayList<>(); 906 ArrayList<TaskRecord> removed = new ArrayList<>(); 907 908 void clear() { 909 added.clear(); 910 trimmed.clear(); 911 removed.clear(); 912 } 913 914 @Override 915 public void onRecentTaskAdded(TaskRecord task) { 916 added.add(task); 917 } 918 919 @Override 920 public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) { 921 if (wasTrimmed) { 922 trimmed.add(task); 923 } 924 removed.add(task); 925 } 926 } 927 928 private static class TestTaskPersister extends TaskPersister { 929 SparseBooleanArray userTaskIdsOverride; 930 ArrayList<TaskRecord> userTasksOverride; 931 932 TestTaskPersister(File workingDir) { 933 super(workingDir); 934 } 935 936 @Override 937 SparseBooleanArray loadPersistedTaskIdsForUser(int userId) { 938 if (userTaskIdsOverride != null) { 939 return userTaskIdsOverride; 940 } 941 return super.loadPersistedTaskIdsForUser(userId); 942 } 943 944 @Override 945 List<TaskRecord> restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks) { 946 if (userTasksOverride != null) { 947 return userTasksOverride; 948 } 949 return super.restoreTasksForUserLocked(userId, preaddedTasks); 950 } 951 } 952 953 private static class TestRecentTasks extends RecentTasks { 954 static final int GRANT = 0; 955 static final int DENY = 1; 956 static final int DENY_THROW_SECURITY_EXCEPTION = 2; 957 958 private boolean mOverrideIsCallerRecents; 959 private boolean mIsTrimmableOverride; 960 private int mIsCallerRecentsPolicy; 961 962 boolean lastAllowed; 963 964 TestRecentTasks(ActivityManagerService service, TaskPersister taskPersister, 965 UserController userController) { 966 super(service, taskPersister, userController); 967 } 968 969 @Override 970 boolean isCallerRecents(int callingUid) { 971 if (mOverrideIsCallerRecents) { 972 switch (mIsCallerRecentsPolicy) { 973 case GRANT: 974 return true; 975 case DENY: 976 return false; 977 case DENY_THROW_SECURITY_EXCEPTION: 978 throw new SecurityException(); 979 } 980 } 981 return super.isCallerRecents(callingUid); 982 } 983 984 void setIsCallerRecentsOverride(int policy) { 985 mOverrideIsCallerRecents = true; 986 mIsCallerRecentsPolicy = policy; 987 } 988 989 /** 990 * To simplify the setup for some tests, the caller can request that we only rely on the 991 * visible range test to determine what is trimmable. In this case, we don't try to 992 * use the stack order to determine additionally if the task is trimmable when it is not 993 * in the visible range. 994 */ 995 void setOnlyTestVisibleRange() { 996 mIsTrimmableOverride = true; 997 } 998 999 @Override 1000 ParceledListSlice<RecentTaskInfo> getRecentTasks(int maxNum, int flags, 1001 boolean getTasksAllowed, 1002 boolean getDetailedTasks, int userId, int callingUid) { 1003 lastAllowed = getTasksAllowed; 1004 return super.getRecentTasks(maxNum, flags, getTasksAllowed, getDetailedTasks, userId, 1005 callingUid); 1006 } 1007 1008 @Override 1009 protected boolean isTrimmable(TaskRecord task) { 1010 return mIsTrimmableOverride || super.isTrimmable(task); 1011 } 1012 } 1013 1014 private static class TestRunningTasks extends RunningTasks { 1015 boolean lastAllowed; 1016 1017 @Override 1018 void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType, 1019 int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays, 1020 int callingUid, boolean allowed) { 1021 lastAllowed = allowed; 1022 super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays, 1023 callingUid, allowed); 1024 } 1025 } 1026 } 1027