1 /* 2 * Copyright (C) 2020 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.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 25 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; 26 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 27 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 28 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 29 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; 30 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 31 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 32 import static android.view.WindowManager.TRANSIT_CHANGE; 33 import static android.view.WindowManager.TRANSIT_CLOSE; 34 import static android.view.WindowManager.TRANSIT_OPEN; 35 import static android.view.WindowManager.TRANSIT_TO_BACK; 36 import static android.window.TransitionInfo.FLAG_FILLS_TASK; 37 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; 38 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; 39 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; 40 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; 41 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; 42 import static android.window.TransitionInfo.FLAG_SYNC; 43 import static android.window.TransitionInfo.FLAG_TRANSLUCENT; 44 import static android.window.TransitionInfo.isIndependent; 45 46 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; 47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 48 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 49 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 50 import static com.android.server.wm.WindowContainer.POSITION_TOP; 51 52 import static org.junit.Assert.assertEquals; 53 import static org.junit.Assert.assertFalse; 54 import static org.junit.Assert.assertNotEquals; 55 import static org.junit.Assert.assertNotNull; 56 import static org.junit.Assert.assertNull; 57 import static org.junit.Assert.assertTrue; 58 import static org.junit.Assume.assumeFalse; 59 import static org.junit.Assume.assumeTrue; 60 import static org.mockito.ArgumentMatchers.any; 61 import static org.mockito.ArgumentMatchers.anyBoolean; 62 import static org.mockito.ArgumentMatchers.anyInt; 63 import static org.mockito.ArgumentMatchers.eq; 64 import static org.mockito.Mockito.doAnswer; 65 import static org.mockito.Mockito.mock; 66 import static org.mockito.Mockito.spy; 67 import static org.mockito.Mockito.times; 68 import static org.mockito.Mockito.verify; 69 70 import android.app.ActivityManager; 71 import android.content.res.Configuration; 72 import android.graphics.Color; 73 import android.graphics.Point; 74 import android.graphics.Rect; 75 import android.os.IBinder; 76 import android.platform.test.annotations.Presubmit; 77 import android.util.ArrayMap; 78 import android.util.ArraySet; 79 import android.view.SurfaceControl; 80 import android.view.WindowManager; 81 import android.window.IDisplayAreaOrganizer; 82 import android.window.IRemoteTransition; 83 import android.window.ITaskFragmentOrganizer; 84 import android.window.ITaskOrganizer; 85 import android.window.ITransitionPlayer; 86 import android.window.RemoteTransition; 87 import android.window.TaskFragmentOrganizer; 88 import android.window.TransitionInfo; 89 90 import androidx.annotation.NonNull; 91 import androidx.test.filters.SmallTest; 92 93 import com.android.internal.graphics.ColorUtils; 94 95 import org.junit.Test; 96 import org.junit.runner.RunWith; 97 import org.mockito.ArgumentCaptor; 98 99 import java.util.ArrayList; 100 import java.util.Objects; 101 import java.util.concurrent.CountDownLatch; 102 import java.util.concurrent.TimeUnit; 103 import java.util.function.Consumer; 104 import java.util.function.Function; 105 106 /** 107 * Build/Install/Run: 108 * atest WmTests:TransitionTests 109 */ 110 @SmallTest 111 @Presubmit 112 @RunWith(WindowTestRunner.class) 113 public class TransitionTests extends WindowTestsBase { 114 final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class); 115 private BLASTSyncEngine mSyncEngine; 116 createTestTransition(int transitType, TransitionController controller)117 private Transition createTestTransition(int transitType, TransitionController controller) { 118 return new Transition(transitType, 0 /* flags */, controller, controller.mSyncEngine); 119 } 120 createTestTransition(int transitType)121 private Transition createTestTransition(int transitType) { 122 final TransitionController controller = new TestTransitionController( 123 mock(ActivityTaskManagerService.class)); 124 125 mSyncEngine = createTestBLASTSyncEngine(); 126 controller.setSyncEngine(mSyncEngine); 127 final Transition out = createTestTransition(transitType, controller); 128 out.startCollecting(0 /* timeoutMs */); 129 return out; 130 } 131 132 @Test testCreateInfo_NewTask()133 public void testCreateInfo_NewTask() { 134 final Transition transition = createTestTransition(TRANSIT_OPEN); 135 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 136 ArraySet<WindowContainer> participants = transition.mParticipants; 137 138 final Task newTask = createTask(mDisplayContent); 139 final Task oldTask = createTask(mDisplayContent); 140 final ActivityRecord closing = createActivityRecord(oldTask); 141 final ActivityRecord opening = createActivityRecord(newTask); 142 // Start states. 143 changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */)); 144 changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */)); 145 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 146 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */)); 147 fillChangeMap(changes, newTask); 148 // End states. 149 closing.setVisibleRequested(false); 150 opening.setVisibleRequested(true); 151 152 final int transit = transition.mType; 153 int flags = 0; 154 155 // Check basic both tasks participating 156 participants.add(oldTask); 157 participants.add(newTask); 158 ArrayList<Transition.ChangeInfo> targets = 159 Transition.calculateTargets(participants, changes); 160 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 161 assertEquals(2, info.getChanges().size()); 162 assertEquals(transit, info.getType()); 163 164 // Check that children are pruned 165 participants.add(opening); 166 participants.add(closing); 167 targets = Transition.calculateTargets(participants, changes); 168 info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 169 assertEquals(2, info.getChanges().size()); 170 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 171 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 172 173 // Check combined prune and promote 174 participants.remove(newTask); 175 targets = Transition.calculateTargets(participants, changes); 176 info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 177 assertEquals(2, info.getChanges().size()); 178 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 179 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 180 181 // Check multi promote 182 participants.remove(oldTask); 183 targets = Transition.calculateTargets(participants, changes); 184 info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 185 assertEquals(2, info.getChanges().size()); 186 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 187 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 188 } 189 190 @Test testCreateInfo_NestedTasks()191 public void testCreateInfo_NestedTasks() { 192 final Transition transition = createTestTransition(TRANSIT_OPEN); 193 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 194 ArraySet<WindowContainer> participants = transition.mParticipants; 195 196 final Task newTask = createTask(mDisplayContent); 197 final Task newNestedTask = createTaskInRootTask(newTask, 0); 198 final Task newNestedTask2 = createTaskInRootTask(newTask, 0); 199 final Task oldTask = createTask(mDisplayContent); 200 final ActivityRecord closing = createActivityRecord(oldTask); 201 final ActivityRecord opening = createActivityRecord(newNestedTask); 202 final ActivityRecord opening2 = createActivityRecord(newNestedTask2); 203 // Start states. 204 changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */)); 205 changes.put(newNestedTask, 206 new Transition.ChangeInfo(newNestedTask, false /* vis */, true /* exChg */)); 207 changes.put(newNestedTask2, 208 new Transition.ChangeInfo(newNestedTask2, false /* vis */, true /* exChg */)); 209 changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */)); 210 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 211 changes.put(opening2, 212 new Transition.ChangeInfo(opening2, false /* vis */, true /* exChg */)); 213 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */)); 214 fillChangeMap(changes, newTask); 215 // End states. 216 closing.setVisibleRequested(false); 217 opening.setVisibleRequested(true); 218 opening2.setVisibleRequested(true); 219 220 final int transit = transition.mType; 221 int flags = 0; 222 223 // Check full promotion from leaf 224 participants.add(oldTask); 225 participants.add(opening); 226 participants.add(opening2); 227 ArrayList<Transition.ChangeInfo> targets = 228 Transition.calculateTargets(participants, changes); 229 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 230 assertEquals(2, info.getChanges().size()); 231 assertEquals(transit, info.getType()); 232 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 233 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 234 235 // Check that unchanging but visible descendant of sibling prevents promotion 236 participants.remove(opening2); 237 targets = Transition.calculateTargets(participants, changes); 238 info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 239 assertEquals(2, info.getChanges().size()); 240 assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken())); 241 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 242 } 243 244 @Test testCreateInfo_DisplayArea()245 public void testCreateInfo_DisplayArea() { 246 assumeTrue(mDisplayContent.mTransitionController.useShellTransitionsRotation()); 247 248 final Transition transition = createTestTransition(TRANSIT_OPEN); 249 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 250 ArraySet<WindowContainer> participants = transition.mParticipants; 251 final Task showTask = createTask(mDisplayContent); 252 final Task showNestedTask = createTaskInRootTask(showTask, 0); 253 final Task showTask2 = createTask(mDisplayContent); 254 final DisplayArea tda = showTask.getDisplayArea(); 255 final ActivityRecord showing = createActivityRecord(showNestedTask); 256 final ActivityRecord showing2 = createActivityRecord(showTask2); 257 // Start states. 258 changes.put(showTask, 259 new Transition.ChangeInfo(showTask, false /* vis */, true /* exChg */)); 260 changes.put(showNestedTask, 261 new Transition.ChangeInfo(showNestedTask, false /* vis */, true /* exChg */)); 262 changes.put(showTask2, 263 new Transition.ChangeInfo(showTask2, false /* vis */, true /* exChg */)); 264 changes.put(tda, new Transition.ChangeInfo(tda, false /* vis */, true /* exChg */)); 265 changes.put(showing, new Transition.ChangeInfo(showing, false /* vis */, true /* exChg */)); 266 changes.put(showing2, 267 new Transition.ChangeInfo(showing2, false /* vis */, true /* exChg */)); 268 fillChangeMap(changes, tda); 269 270 // End states. 271 showing.setVisibleRequested(true); 272 showing2.setVisibleRequested(true); 273 274 final int transit = transition.mType; 275 int flags = 0; 276 277 // Check promotion to DisplayArea 278 participants.add(showing); 279 participants.add(showing2); 280 ArrayList<Transition.ChangeInfo> targets = 281 Transition.calculateTargets(participants, changes); 282 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 283 assertEquals(1, info.getChanges().size()); 284 assertEquals(transit, info.getType()); 285 assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); 286 287 // Check that organized tasks get reported even if not top 288 makeTaskOrganized(showTask); 289 targets = Transition.calculateTargets(participants, changes); 290 info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 291 assertEquals(2, info.getChanges().size()); 292 assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); 293 assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken())); 294 // Even if DisplayArea explicitly participating 295 participants.add(tda); 296 targets = Transition.calculateTargets(participants, changes); 297 info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 298 assertEquals(2, info.getChanges().size()); 299 } 300 301 @Test testCreateInfo_existenceChange()302 public void testCreateInfo_existenceChange() { 303 final Transition transition = createTestTransition(TRANSIT_OPEN); 304 305 final Task openTask = createTask(mDisplayContent); 306 final ActivityRecord opening = createActivityRecord(openTask); 307 opening.setVisibleRequested(false); // starts invisible 308 final Task closeTask = createTask(mDisplayContent); 309 final ActivityRecord closing = createActivityRecord(closeTask); 310 closing.setVisibleRequested(true); // starts visible 311 312 transition.collectExistenceChange(openTask); 313 transition.collect(opening); 314 transition.collect(closing); 315 opening.setVisibleRequested(true); 316 closing.setVisibleRequested(false); 317 318 ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 319 transition.mParticipants, transition.mChanges); 320 TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT); 321 assertEquals(2, info.getChanges().size()); 322 // There was an existence change on open, so it should be OPEN rather than SHOW 323 assertEquals(TRANSIT_OPEN, 324 info.getChange(openTask.mRemoteToken.toWindowContainerToken()).getMode()); 325 // No exestence change on closing, so HIDE rather than CLOSE 326 assertEquals(TRANSIT_TO_BACK, 327 info.getChange(closeTask.mRemoteToken.toWindowContainerToken()).getMode()); 328 } 329 330 @Test testCreateInfo_ordering()331 public void testCreateInfo_ordering() { 332 final Transition transition = createTestTransition(TRANSIT_OPEN); 333 // pick some number with a high enough chance of being out-of-order when added to set. 334 final int taskCount = 6; 335 336 final Task[] tasks = new Task[taskCount]; 337 for (int i = 0; i < taskCount; ++i) { 338 // Each add goes on top, so at the end of this, task[9] should be on top 339 tasks[i] = createTask(mDisplayContent, 340 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); 341 final ActivityRecord act = createActivityRecord(tasks[i]); 342 // alternate so that the transition doesn't get promoted to the display area 343 act.setVisibleRequested((i % 2) == 0); // starts invisible 344 } 345 346 // doesn't matter which order collected since participants is a set 347 for (int i = 0; i < taskCount; ++i) { 348 transition.collectExistenceChange(tasks[i]); 349 final ActivityRecord act = tasks[i].getTopMostActivity(); 350 transition.collect(act); 351 tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0); 352 } 353 354 ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 355 transition.mParticipants, transition.mChanges); 356 TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT); 357 assertEquals(taskCount, info.getChanges().size()); 358 // verify order is top-to-bottem 359 for (int i = 0; i < taskCount; ++i) { 360 assertEquals(tasks[taskCount - i - 1].mRemoteToken.toWindowContainerToken(), 361 info.getChanges().get(i).getContainer()); 362 } 363 } 364 365 @Test testCreateInfo_wallpaper()366 public void testCreateInfo_wallpaper() { 367 final Transition transition = createTestTransition(TRANSIT_OPEN); 368 // pick some number with a high enough chance of being out-of-order when added to set. 369 final int taskCount = 4; 370 final int showWallpaperTask = 2; 371 372 final Task[] tasks = new Task[taskCount]; 373 for (int i = 0; i < taskCount; ++i) { 374 // Each add goes on top, so at the end of this, task[9] should be on top 375 tasks[i] = createTask(mDisplayContent, 376 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 377 final ActivityRecord act = createActivityRecord(tasks[i]); 378 // alternate so that the transition doesn't get promoted to the display area 379 act.setVisibleRequested((i % 2) == 0); // starts invisible 380 if (i == showWallpaperTask) { 381 doReturn(true).when(act).showWallpaper(); 382 } 383 } 384 385 final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm, 386 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */)); 387 final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, 388 "wallpaperWindow"); 389 wallpaperWindowToken.setVisibleRequested(false); 390 transition.collect(wallpaperWindowToken); 391 wallpaperWindowToken.setVisibleRequested(true); 392 wallpaperWindow.mHasSurface = true; 393 394 // doesn't matter which order collected since participants is a set 395 for (int i = 0; i < taskCount; ++i) { 396 transition.collectExistenceChange(tasks[i]); 397 final ActivityRecord act = tasks[i].getTopMostActivity(); 398 transition.collect(act); 399 tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0); 400 } 401 402 ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 403 transition.mParticipants, transition.mChanges); 404 TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT); 405 // verify that wallpaper is at bottom 406 assertEquals(taskCount + 1, info.getChanges().size()); 407 // The wallpaper is not organized, so it won't have a token; however, it will be marked 408 // as IS_WALLPAPER 409 assertEquals(FLAG_IS_WALLPAPER, 410 info.getChanges().get(info.getChanges().size() - 1).getFlags()); 411 assertEquals(FLAG_SHOW_WALLPAPER, info.getChange( 412 tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags()); 413 } 414 415 @Test testCreateInfo_PromoteSimilarClose()416 public void testCreateInfo_PromoteSimilarClose() { 417 final Transition transition = createTestTransition(TRANSIT_CLOSE); 418 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 419 ArraySet<WindowContainer> participants = transition.mParticipants; 420 421 final Task topTask = createTask(mDisplayContent); 422 final Task belowTask = createTask(mDisplayContent); 423 final ActivityRecord showing = createActivityRecord(belowTask); 424 final ActivityRecord hiding = createActivityRecord(topTask); 425 final ActivityRecord closing = createActivityRecord(topTask); 426 // Start states. 427 changes.put(topTask, new Transition.ChangeInfo(topTask, true /* vis */, false /* exChg */)); 428 changes.put(belowTask, 429 new Transition.ChangeInfo(belowTask, false /* vis */, false /* exChg */)); 430 changes.put(showing, 431 new Transition.ChangeInfo(showing, false /* vis */, false /* exChg */)); 432 changes.put(hiding, new Transition.ChangeInfo(hiding, true /* vis */, false /* exChg */)); 433 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */)); 434 fillChangeMap(changes, topTask); 435 // End states. 436 showing.setVisibleRequested(true); 437 closing.setVisibleRequested(false); 438 hiding.setVisibleRequested(false); 439 440 participants.add(belowTask); 441 participants.add(hiding); 442 participants.add(closing); 443 ArrayList<Transition.ChangeInfo> targets = 444 Transition.calculateTargets(participants, changes); 445 assertEquals(2, targets.size()); 446 assertTrue(Transition.containsChangeFor(belowTask, targets)); 447 assertTrue(Transition.containsChangeFor(topTask, targets)); 448 } 449 450 @Test testCreateInfo_PromoteSimilarOpen()451 public void testCreateInfo_PromoteSimilarOpen() { 452 final Transition transition = createTestTransition(TRANSIT_OPEN); 453 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 454 ArraySet<WindowContainer> participants = transition.mParticipants; 455 456 final Task topTask = createTask(mDisplayContent); 457 final Task belowTask = createTask(mDisplayContent); 458 final ActivityRecord showing = createActivityRecord(topTask); 459 final ActivityRecord opening = createActivityRecord(topTask); 460 final ActivityRecord closing = createActivityRecord(belowTask); 461 // Start states. 462 changes.put(topTask, 463 new Transition.ChangeInfo(topTask, false /* vis */, false /* exChg */)); 464 changes.put(belowTask, 465 new Transition.ChangeInfo(belowTask, true /* vis */, false /* exChg */)); 466 changes.put(showing, 467 new Transition.ChangeInfo(showing, false /* vis */, false /* exChg */)); 468 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 469 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */)); 470 fillChangeMap(changes, topTask); 471 // End states. 472 showing.setVisibleRequested(true); 473 opening.setVisibleRequested(true); 474 closing.setVisibleRequested(false); 475 476 participants.add(belowTask); 477 participants.add(showing); 478 participants.add(opening); 479 ArrayList<Transition.ChangeInfo> targets = 480 Transition.calculateTargets(participants, changes); 481 assertEquals(2, targets.size()); 482 assertTrue(Transition.containsChangeFor(belowTask, targets)); 483 assertTrue(Transition.containsChangeFor(topTask, targets)); 484 } 485 486 @Test testCreateInfo_NoAnimation()487 public void testCreateInfo_NoAnimation() { 488 final Transition transition = createTestTransition(TRANSIT_OPEN); 489 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 490 ArraySet<WindowContainer> participants = transition.mParticipants; 491 492 final Task newTask = createTask(mDisplayContent); 493 final Task oldTask = createTask(mDisplayContent); 494 final ActivityRecord closing = createActivityRecord(oldTask); 495 final ActivityRecord opening = createActivityRecord(newTask); 496 // Start states. 497 changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */)); 498 changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, true /* exChg */)); 499 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 500 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, true /* exChg */)); 501 transition.setNoAnimation(opening); 502 fillChangeMap(changes, newTask); 503 // End states. 504 closing.setVisibleRequested(false); 505 opening.setVisibleRequested(true); 506 507 final int transit = transition.mType; 508 int flags = 0; 509 510 // Check that no-animation flag is promoted 511 participants.add(oldTask); 512 participants.add(newTask); 513 participants.add(opening); 514 participants.add(closing); 515 ArrayList<Transition.ChangeInfo> targets = 516 Transition.calculateTargets(participants, changes); 517 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 518 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 519 assertTrue(info.getChange(newTask.mRemoteToken.toWindowContainerToken()) 520 .hasFlags(TransitionInfo.FLAG_NO_ANIMATION)); 521 522 // Check that no-animation flag is NOT promoted if at-least on child *is* animated 523 final ActivityRecord opening2 = createActivityRecord(newTask); 524 changes.put(opening2, 525 new Transition.ChangeInfo(opening2, false /* vis */, true /* exChg */)); 526 participants.add(opening2); 527 targets = Transition.calculateTargets(participants, changes); 528 info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 529 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 530 assertFalse(info.getChange(newTask.mRemoteToken.toWindowContainerToken()) 531 .hasFlags(TransitionInfo.FLAG_NO_ANIMATION)); 532 } 533 534 @Test testCreateInfo_MultiDisplay()535 public void testCreateInfo_MultiDisplay() { 536 DisplayContent otherDisplay = createNewDisplay(); 537 final Transition transition = createTestTransition(TRANSIT_OPEN); 538 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 539 ArraySet<WindowContainer> participants = transition.mParticipants; 540 541 final Task display0Task = createTask(mDisplayContent); 542 final Task display1Task = createTask(otherDisplay); 543 // Start states. 544 changes.put(display0Task, 545 new Transition.ChangeInfo(display0Task, false /* vis */, true /* exChg */)); 546 changes.put(display1Task, 547 new Transition.ChangeInfo(display1Task, false /* vis */, true /* exChg */)); 548 fillChangeMap(changes, display0Task); 549 fillChangeMap(changes, display1Task); 550 // End states. 551 display0Task.setVisibleRequested(true); 552 display1Task.setVisibleRequested(true); 553 554 final int transit = transition.mType; 555 int flags = 0; 556 557 participants.add(display0Task); 558 participants.add(display1Task); 559 ArrayList<Transition.ChangeInfo> targets = 560 Transition.calculateTargets(participants, changes); 561 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 562 assertEquals(2, info.getRootCount()); 563 // Check that the changes are assigned to the correct display 564 assertEquals(mDisplayContent.getDisplayId(), info.getChange( 565 display0Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId()); 566 assertEquals(otherDisplay.getDisplayId(), info.getChange( 567 display1Task.mRemoteToken.toWindowContainerToken()).getEndDisplayId()); 568 // Check that roots can be found by display and have the correct display 569 assertEquals(mDisplayContent.getDisplayId(), 570 info.getRoot(info.findRootIndex(mDisplayContent.getDisplayId())).getDisplayId()); 571 assertEquals(otherDisplay.getDisplayId(), 572 info.getRoot(info.findRootIndex(otherDisplay.getDisplayId())).getDisplayId()); 573 } 574 575 @Test testTargets_noIntermediatesToWallpaper()576 public void testTargets_noIntermediatesToWallpaper() { 577 final Transition transition = createTestTransition(TRANSIT_OPEN); 578 579 final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, 580 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */); 581 // Make DA organized so we can check that they don't get included. 582 WindowContainer parent = wallpaperWindowToken.getParent(); 583 makeDisplayAreaOrganized(parent, mDisplayContent); 584 final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, 585 "wallpaperWindow"); 586 wallpaperWindowToken.setVisibleRequested(false); 587 transition.collect(wallpaperWindowToken); 588 wallpaperWindowToken.setVisibleRequested(true); 589 wallpaperWindow.mHasSurface = true; 590 doReturn(true).when(mDisplayContent).isAttached(); 591 transition.collect(mDisplayContent); 592 assertFalse("The change of non-interesting window container should be skipped", 593 transition.mChanges.containsKey(mDisplayContent.getParent())); 594 mDisplayContent.getWindowConfiguration().setRotation( 595 (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4); 596 597 ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 598 transition.mParticipants, transition.mChanges); 599 TransitionInfo info = Transition.calculateTransitionInfo(0, 0, targets, mMockT); 600 // The wallpaper is not organized, so it won't have a token; however, it will be marked 601 // as IS_WALLPAPER 602 assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags()); 603 // Make sure no intermediate display areas were pulled in between wallpaper and display. 604 assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(), 605 info.getChanges().get(0).getParent()); 606 } 607 608 @Test testRunningRemoteTransition()609 public void testRunningRemoteTransition() { 610 final TestTransitionPlayer testPlayer = new TestTransitionPlayer( 611 mAtm.getTransitionController(), mAtm.mWindowOrganizerController); 612 final WindowProcessController playerProc = mSystemServicesTestRule.addProcess( 613 "pkg.player", "proc.player", 5000 /* pid */, 5000 /* uid */); 614 testPlayer.mController.registerTransitionPlayer(testPlayer, playerProc); 615 doReturn(mock(IBinder.class)).when(playerProc.getThread()).asBinder(); 616 final WindowProcessController delegateProc = mSystemServicesTestRule.addProcess( 617 "pkg.delegate", "proc.delegate", 6000 /* pid */, 6000 /* uid */); 618 doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder(); 619 final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true) 620 .setVisible(false).build(); 621 final Task task = app.getTask(); 622 task.setTaskOrganizer(mock(ITaskOrganizer.class), true /* skipTaskAppeared */); 623 app.setVisibleRequested(true); 624 final TransitionController controller = app.mTransitionController; 625 final Transition transition = controller.createTransition(TRANSIT_OPEN); 626 final RemoteTransition remoteTransition = new RemoteTransition( 627 mock(IRemoteTransition.class)); 628 remoteTransition.setAppThread(delegateProc.getThread()); 629 transition.collect(app); 630 controller.requestStartTransition(transition, null /* startTask */, remoteTransition, 631 null /* displayChange */); 632 testPlayer.startTransition(); 633 app.onStartingWindowDrawn(); 634 // The task appeared event should be deferred until transition ready. 635 assertFalse(task.taskAppearedReady()); 636 testPlayer.onTransactionReady(app.getSyncTransaction()); 637 assertTrue(task.taskAppearedReady()); 638 assertTrue(playerProc.isRunningRemoteTransition()); 639 assertTrue(delegateProc.isRunningRemoteTransition()); 640 assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread())); 641 assertTrue(app.isVisible()); 642 643 testPlayer.finish(); 644 assertFalse(playerProc.isRunningRemoteTransition()); 645 assertFalse(delegateProc.isRunningRemoteTransition()); 646 assertFalse(controller.mRemotePlayer.reportRunning(delegateProc.getThread())); 647 } 648 649 @Test testOpenActivityInTheSameTaskWithDisplayChange()650 public void testOpenActivityInTheSameTaskWithDisplayChange() { 651 final ActivityRecord closing = createActivityRecord(mDisplayContent); 652 closing.setVisibleRequested(true); 653 final Task task = closing.getTask(); 654 makeTaskOrganized(task); 655 final ActivityRecord opening = createActivityRecord(task); 656 opening.setVisibleRequested(false); 657 makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent); 658 final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent }; 659 final Transition transition = createTestTransition(TRANSIT_OPEN); 660 for (WindowContainer<?> wc : wcs) { 661 transition.collect(wc); 662 } 663 closing.setVisibleRequested(false); 664 opening.setVisibleRequested(true); 665 final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1; 666 for (WindowContainer<?> wc : wcs) { 667 wc.getWindowConfiguration().setRotation(newRotation); 668 } 669 670 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 671 transition.mParticipants, transition.mChanges); 672 // Especially the activities must be in the targets. 673 for (WindowContainer<?> wc : wcs) { 674 assertTrue(Transition.containsChangeFor(wc, targets)); 675 } 676 } 677 678 @Test testIndependent()679 public void testIndependent() { 680 final Transition transition = createTestTransition(TRANSIT_OPEN); 681 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 682 ArraySet<WindowContainer> participants = transition.mParticipants; 683 684 final Task openTask = createTask(mDisplayContent); 685 final Task openInOpenTask = createTaskInRootTask(openTask, 0); 686 final ActivityRecord openInOpen = createActivityRecord(openInOpenTask); 687 688 final Task changeTask = createTask(mDisplayContent); 689 final Task changeInChangeTask = createTaskInRootTask(changeTask, 0); 690 final Task openInChangeTask = createTaskInRootTask(changeTask, 0); 691 final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask); 692 final ActivityRecord openInChange = createActivityRecord(openInChangeTask); 693 // set organizer for everything so that they all get added to transition info 694 makeTaskOrganized(openTask, openInOpenTask, changeTask, changeInChangeTask, 695 openInChangeTask); 696 697 // Start states. 698 changes.put(openTask, 699 new Transition.ChangeInfo(openTask, false /* vis */, true /* exChg */)); 700 changes.put(changeTask, 701 new Transition.ChangeInfo(changeTask, true /* vis */, false /* exChg */)); 702 changes.put(openInOpenTask, 703 new Transition.ChangeInfo(openInOpenTask, false /* vis */, true /* exChg */)); 704 changes.put(openInChangeTask, 705 new Transition.ChangeInfo(openInChangeTask, false /* vis */, true /* exChg */)); 706 changes.put(changeInChangeTask, 707 new Transition.ChangeInfo(changeInChangeTask, true /* vis */, false /* exChg */)); 708 changes.put(openInOpen, 709 new Transition.ChangeInfo(openInOpen, false /* vis */, true /* exChg */)); 710 changes.put(openInChange, 711 new Transition.ChangeInfo(openInChange, false /* vis */, true /* exChg */)); 712 changes.put(changeInChange, 713 new Transition.ChangeInfo(changeInChange, true /* vis */, false /* exChg */)); 714 fillChangeMap(changes, openTask); 715 // End states. 716 changeInChange.setVisibleRequested(true); 717 openInOpen.setVisibleRequested(true); 718 openInChange.setVisibleRequested(true); 719 // Force the change-type changes to be "dirty" so they aren't skipped 720 changes.get(changeTask).mKnownConfigChanges = 1; 721 changes.get(changeInChangeTask).mKnownConfigChanges = 1; 722 changes.get(changeInChange).mKnownConfigChanges = 1; 723 724 final int transit = transition.mType; 725 int flags = 0; 726 727 // Check full promotion from leaf 728 participants.add(changeInChange); 729 participants.add(openInOpen); 730 participants.add(openInChange); 731 // Explicitly add changeTask (to test independence with parents) 732 participants.add(changeTask); 733 final ArrayList<Transition.ChangeInfo> targets = 734 Transition.calculateTargets(participants, changes); 735 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 736 // Root changes should always be considered independent 737 assertTrue(isIndependent( 738 info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info)); 739 assertTrue(isIndependent( 740 info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info)); 741 742 // Children of a open/close change are not independent 743 assertFalse(isIndependent( 744 info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info)); 745 746 // Non-root changes are not independent 747 assertFalse(isIndependent( 748 info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info)); 749 750 // open/close within a change are independent 751 assertTrue(isIndependent( 752 info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info)); 753 } 754 755 @Test testOpenOpaqueTask()756 public void testOpenOpaqueTask() { 757 final Transition transition = createTestTransition(TRANSIT_OPEN); 758 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 759 ArraySet<WindowContainer> participants = transition.mParticipants; 760 761 final Task oldTask = createTask(mDisplayContent); 762 final Task newTask = createTask(mDisplayContent); 763 764 final ActivityRecord closing = createActivityRecord(oldTask); 765 closing.setOccludesParent(true); 766 final ActivityRecord opening = createActivityRecord(newTask); 767 opening.setOccludesParent(true); 768 // Start states. 769 changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */)); 770 changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */)); 771 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 772 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */)); 773 fillChangeMap(changes, newTask); 774 // End states. 775 closing.setVisibleRequested(false); 776 opening.setVisibleRequested(true); 777 778 final int transit = transition.mType; 779 int flags = 0; 780 781 // Check basic both tasks participating 782 participants.add(oldTask); 783 participants.add(newTask); 784 ArrayList<Transition.ChangeInfo> targets = 785 Transition.calculateTargets(participants, changes); 786 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 787 assertEquals(2, info.getChanges().size()); 788 assertEquals(transit, info.getType()); 789 790 assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT)); 791 assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT)); 792 } 793 794 @Test testOpenTranslucentTask()795 public void testOpenTranslucentTask() { 796 final Transition transition = createTestTransition(TRANSIT_OPEN); 797 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 798 ArraySet<WindowContainer> participants = transition.mParticipants; 799 800 final Task oldTask = createTask(mDisplayContent); 801 final Task newTask = createTask(mDisplayContent); 802 803 final ActivityRecord closing = createActivityRecord(oldTask); 804 closing.setOccludesParent(true); 805 final ActivityRecord opening = createActivityRecord(newTask); 806 opening.setOccludesParent(false); 807 // Start states. 808 changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */)); 809 changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */)); 810 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 811 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */)); 812 fillChangeMap(changes, newTask); 813 // End states. 814 closing.setVisibleRequested(false); 815 opening.setVisibleRequested(true); 816 817 final int transit = transition.mType; 818 int flags = 0; 819 820 // Check basic both tasks participating 821 participants.add(oldTask); 822 participants.add(newTask); 823 ArrayList<Transition.ChangeInfo> targets = 824 Transition.calculateTargets(participants, changes); 825 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 826 assertEquals(2, info.getChanges().size()); 827 assertEquals(transit, info.getType()); 828 829 assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT)); 830 assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT)); 831 } 832 833 @Test testOpenOpaqueTaskFragment()834 public void testOpenOpaqueTaskFragment() { 835 final Transition transition = createTestTransition(TRANSIT_OPEN); 836 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 837 ArraySet<WindowContainer> participants = transition.mParticipants; 838 839 final Task task = createTask(mDisplayContent); 840 final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task); 841 final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task); 842 843 final ActivityRecord closing = closingTaskFragment.getTopMostActivity(); 844 closing.setOccludesParent(true); 845 final ActivityRecord opening = openingTaskFragment.getTopMostActivity(); 846 opening.setOccludesParent(true); 847 // Start states. 848 changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment, 849 false /* vis */, true /* exChg */)); 850 changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment, 851 true /* vis */, false /* exChg */)); 852 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 853 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */)); 854 fillChangeMap(changes, openingTaskFragment); 855 // End states. 856 closing.setVisibleRequested(false); 857 opening.setVisibleRequested(true); 858 859 final int transit = transition.mType; 860 int flags = 0; 861 862 // Check basic both tasks participating 863 participants.add(closingTaskFragment); 864 participants.add(openingTaskFragment); 865 ArrayList<Transition.ChangeInfo> targets = 866 Transition.calculateTargets(participants, changes); 867 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 868 assertEquals(2, info.getChanges().size()); 869 assertEquals(transit, info.getType()); 870 871 assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT)); 872 assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT)); 873 } 874 875 @Test testOpenTranslucentTaskFragment()876 public void testOpenTranslucentTaskFragment() { 877 final Transition transition = createTestTransition(TRANSIT_OPEN); 878 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 879 ArraySet<WindowContainer> participants = transition.mParticipants; 880 881 final Task task = createTask(mDisplayContent); 882 final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task); 883 final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task); 884 885 final ActivityRecord closing = closingTaskFragment.getTopMostActivity(); 886 closing.setOccludesParent(true); 887 final ActivityRecord opening = openingTaskFragment.getTopMostActivity(); 888 opening.setOccludesParent(false); 889 // Start states. 890 changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment, 891 false /* vis */, true /* exChg */)); 892 changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment, 893 true /* vis */, false /* exChg */)); 894 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 895 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */)); 896 fillChangeMap(changes, openingTaskFragment); 897 // End states. 898 closing.setVisibleRequested(false); 899 opening.setVisibleRequested(true); 900 901 final int transit = transition.mType; 902 int flags = 0; 903 904 // Check basic both tasks participating 905 participants.add(closingTaskFragment); 906 participants.add(openingTaskFragment); 907 ArrayList<Transition.ChangeInfo> targets = 908 Transition.calculateTargets(participants, changes); 909 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 910 assertEquals(2, info.getChanges().size()); 911 assertEquals(transit, info.getType()); 912 913 assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT)); 914 assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT)); 915 } 916 917 @Test testCloseOpaqueTaskFragment_withFinishingActivity()918 public void testCloseOpaqueTaskFragment_withFinishingActivity() { 919 final Transition transition = createTestTransition(TRANSIT_CLOSE); 920 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 921 ArraySet<WindowContainer> participants = transition.mParticipants; 922 923 final Task task = createTask(mDisplayContent); 924 final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task); 925 final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task); 926 927 final ActivityRecord opening = openingTaskFragment.getTopMostActivity(); 928 opening.setOccludesParent(true); 929 final ActivityRecord closing = closingTaskFragment.getTopMostActivity(); 930 closing.setOccludesParent(true); 931 closing.finishing = true; 932 // Start states. 933 changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment, 934 false /* vis */, true /* exChg */)); 935 changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment, 936 true /* vis */, false /* exChg */)); 937 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 938 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */)); 939 fillChangeMap(changes, openingTaskFragment); 940 // End states. 941 closing.setVisibleRequested(false); 942 opening.setVisibleRequested(true); 943 944 final int transit = transition.mType; 945 int flags = 0; 946 947 // Check basic both tasks participating 948 participants.add(closingTaskFragment); 949 participants.add(openingTaskFragment); 950 ArrayList<Transition.ChangeInfo> targets = 951 Transition.calculateTargets(participants, changes); 952 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 953 assertEquals(2, info.getChanges().size()); 954 assertEquals(transit, info.getType()); 955 956 assertFalse(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT)); 957 assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT)); 958 } 959 960 @Test testCloseTranslucentTaskFragment_withFinishingActivity()961 public void testCloseTranslucentTaskFragment_withFinishingActivity() { 962 final Transition transition = createTestTransition(TRANSIT_CLOSE); 963 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 964 ArraySet<WindowContainer> participants = transition.mParticipants; 965 966 final Task task = createTask(mDisplayContent); 967 final TaskFragment openingTaskFragment = createTaskFragmentWithActivity(task); 968 final TaskFragment closingTaskFragment = createTaskFragmentWithActivity(task); 969 970 final ActivityRecord opening = openingTaskFragment.getTopMostActivity(); 971 opening.setOccludesParent(true); 972 final ActivityRecord closing = closingTaskFragment.getTopMostActivity(); 973 closing.setOccludesParent(false); 974 closing.finishing = true; 975 // Start states. 976 changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment, 977 false /* vis */, true /* exChg */)); 978 changes.put(closingTaskFragment, new Transition.ChangeInfo(closingTaskFragment, 979 true /* vis */, false /* exChg */)); 980 changes.put(opening, new Transition.ChangeInfo(opening, false /* vis */, true /* exChg */)); 981 changes.put(closing, new Transition.ChangeInfo(closing, true /* vis */, false /* exChg */)); 982 fillChangeMap(changes, openingTaskFragment); 983 // End states. 984 closing.setVisibleRequested(false); 985 opening.setVisibleRequested(true); 986 987 final int transit = transition.mType; 988 int flags = 0; 989 990 // Check basic both tasks participating 991 participants.add(closingTaskFragment); 992 participants.add(openingTaskFragment); 993 ArrayList<Transition.ChangeInfo> targets = 994 Transition.calculateTargets(participants, changes); 995 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, mMockT); 996 assertEquals(2, info.getChanges().size()); 997 assertEquals(transit, info.getType()); 998 999 assertTrue(info.getChanges().get(0).hasFlags(FLAG_TRANSLUCENT)); 1000 assertFalse(info.getChanges().get(1).hasFlags(FLAG_TRANSLUCENT)); 1001 } 1002 1003 @Test testTimeout()1004 public void testTimeout() { 1005 final TransitionController controller = new TestTransitionController(mAtm); 1006 final BLASTSyncEngine sync = new BLASTSyncEngine(mWm); 1007 final CountDownLatch latch = new CountDownLatch(1); 1008 // When the timeout is reached, it will finish the sync-group and notify transaction ready. 1009 final Transition t = new Transition(TRANSIT_OPEN, 0 /* flags */, controller, sync) { 1010 @Override 1011 public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) { 1012 latch.countDown(); 1013 } 1014 }; 1015 t.startCollecting(10 /* timeoutMs */); 1016 assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS))); 1017 } 1018 1019 @Test testTransitionBounds()1020 public void testTransitionBounds() { 1021 registerTestTransitionPlayer(); 1022 final int offset = 10; 1023 final Function<WindowContainer<?>, TransitionInfo.Change> test = wc -> { 1024 final Transition transition = wc.mTransitionController.createTransition(TRANSIT_OPEN); 1025 transition.collect(wc); 1026 final int nextRotation = (wc.getWindowConfiguration().getRotation() + 1) % 4; 1027 wc.getWindowConfiguration().setRotation(nextRotation); 1028 wc.getWindowConfiguration().setDisplayRotation(nextRotation); 1029 final Rect bounds = wc.getWindowConfiguration().getBounds(); 1030 // Flip the bounds with offset. 1031 wc.getWindowConfiguration().setBounds( 1032 new Rect(offset, offset, bounds.height(), bounds.width())); 1033 final int flags = 0; 1034 final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, flags, 1035 Transition.calculateTargets(transition.mParticipants, transition.mChanges), 1036 mMockT); 1037 transition.abort(); 1038 return info.getChanges().get(0); 1039 }; 1040 1041 final ActivityRecord app = createActivityRecord(mDisplayContent); 1042 final TransitionInfo.Change changeOfActivity = test.apply(app); 1043 // There will be letterbox if the activity bounds don't match parent, so always use its 1044 // parent bounds for animation. 1045 assertEquals(app.getParent().getBounds(), changeOfActivity.getEndAbsBounds()); 1046 final int endRotation = app.mTransitionController.useShellTransitionsRotation() 1047 ? app.getWindowConfiguration().getRotation() 1048 // Without shell rotation, fixed rotation is done by core so the info should not 1049 // contain rotation change. 1050 : app.getParent().getWindowConfiguration().getRotation(); 1051 assertEquals(endRotation, changeOfActivity.getEndRotation()); 1052 1053 // Non-activity target always uses its configuration for end info. 1054 final Task task = app.getTask(); 1055 final TransitionInfo.Change changeOfTask = test.apply(task); 1056 assertEquals(task.getBounds(), changeOfTask.getEndAbsBounds()); 1057 assertEquals(new Point(offset, offset), changeOfTask.getEndRelOffset()); 1058 assertEquals(task.getWindowConfiguration().getRotation(), changeOfTask.getEndRotation()); 1059 } 1060 1061 @Test testDisplayRotationChange()1062 public void testDisplayRotationChange() { 1063 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 1064 spyOn(displayPolicy); 1065 // Simulate gesture navigation (non-movable) so it is not seamless. 1066 doReturn(false).when(displayPolicy).navigationBarCanMove(); 1067 final Task task = createActivityRecord(mDisplayContent).getTask(); 1068 final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 1069 final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); 1070 final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime"); 1071 final WindowToken decorToken = new WindowToken.Builder(mWm, mock(IBinder.class), 1072 TYPE_NAVIGATION_BAR_PANEL).setDisplayContent(mDisplayContent) 1073 .setRoundedCornerOverlay(true).build(); 1074 final WindowState screenDecor = 1075 createWindow(null, decorToken.windowType, decorToken, "screenDecor"); 1076 final WindowState[] windows = { statusBar, navBar, ime, screenDecor }; 1077 makeWindowVisible(windows); 1078 mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs); 1079 mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); 1080 final TestTransitionPlayer player = registerTestTransitionPlayer(); 1081 1082 mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); 1083 mDisplayContent.setLastHasContent(); 1084 mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */, 1085 null /* displayChange */); 1086 assertEquals(WindowContainer.SYNC_STATE_NONE, statusBar.mSyncState); 1087 assertEquals(WindowContainer.SYNC_STATE_NONE, navBar.mSyncState); 1088 assertEquals(WindowContainer.SYNC_STATE_NONE, screenDecor.mSyncState); 1089 assertEquals(WindowContainer.SYNC_STATE_WAITING_FOR_DRAW, ime.mSyncState); 1090 1091 final AsyncRotationController asyncRotationController = 1092 mDisplayContent.getAsyncRotationController(); 1093 assertNotNull(asyncRotationController); 1094 player.startTransition(); 1095 1096 assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken)); 1097 assertFalse(mDisplayContent.mTransitionController.isCollecting(decorToken)); 1098 assertTrue(ime.mToken.inTransition()); 1099 assertTrue(task.inTransition()); 1100 assertTrue(asyncRotationController.isTargetToken(decorToken)); 1101 assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true); 1102 1103 // Only seamless window syncs its draw transaction with transition. 1104 assertTrue(asyncRotationController.handleFinishDrawing(screenDecor, mMockT)); 1105 // Status bar finishes drawing before the start transaction. Its fade-in animation will be 1106 // executed until the transaction is committed, so it is still in target tokens. 1107 assertFalse(asyncRotationController.handleFinishDrawing(statusBar, mMockT)); 1108 assertTrue(asyncRotationController.isTargetToken(statusBar.mToken)); 1109 1110 final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class); 1111 final SurfaceControl.TransactionCommittedListener transactionCommittedListener = 1112 onRotationTransactionReady(player, startTransaction); 1113 1114 // The transaction is committed, so fade-in animation for status bar is consumed. 1115 transactionCommittedListener.onTransactionCommitted(); 1116 assertFalse(asyncRotationController.isTargetToken(statusBar.mToken)); 1117 assertShouldFreezeInsetsPosition(asyncRotationController, navBar, false); 1118 1119 // Navigation bar finishes drawing after the start transaction, so its fade-in animation 1120 // can execute directly. 1121 navBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; 1122 asyncRotationController.updateTargetWindows(); 1123 assertFalse(asyncRotationController.isTargetToken(navBar.mToken)); 1124 assertNull(mDisplayContent.getAsyncRotationController()); 1125 } 1126 1127 @Test testAppTransitionWithRotationChange()1128 public void testAppTransitionWithRotationChange() { 1129 final TestTransitionPlayer player = registerTestTransitionPlayer(); 1130 final boolean useFixedRotation = !player.mController.useShellTransitionsRotation(); 1131 if (useFixedRotation) { 1132 testFixedRotationOpen(player); 1133 } else { 1134 testShellRotationOpen(player); 1135 } 1136 } 1137 testShellRotationOpen(TestTransitionPlayer player)1138 private void testShellRotationOpen(TestTransitionPlayer player) { 1139 final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 1140 makeWindowVisible(statusBar); 1141 mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs); 1142 final ActivityRecord app = createActivityRecord(mDisplayContent); 1143 final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN); 1144 app.mTransitionController.requestStartTransition(transition, app.getTask(), 1145 null /* remoteTransition */, null /* displayChange */); 1146 mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); 1147 final int anyChanges = 1; 1148 mDisplayContent.setLastHasContent(); 1149 mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */); 1150 transition.setKnownConfigChanges(mDisplayContent, anyChanges); 1151 final AsyncRotationController asyncRotationController = 1152 mDisplayContent.getAsyncRotationController(); 1153 assertNotNull(asyncRotationController); 1154 assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true); 1155 1156 player.startTransition(); 1157 // Non-app windows should not be collected. 1158 assertFalse(statusBar.mToken.inTransition()); 1159 assertTrue(app.getTask().inTransition()); 1160 1161 final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class); 1162 final SurfaceControl leash = statusBar.mToken.getAnimationLeash(); 1163 doReturn(true).when(leash).isValid(); 1164 final SurfaceControl.TransactionCommittedListener transactionCommittedListener = 1165 onRotationTransactionReady(player, startTransaction); 1166 // The leash should be unrotated. 1167 verify(startTransaction).setMatrix(eq(leash), any(), any()); 1168 1169 // The redrawn window will be faded in when the transition finishes. And because this test 1170 // only use one non-activity window, the fade rotation controller should also be cleared. 1171 statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING; 1172 final SurfaceControl.Transaction postDrawTransaction = 1173 mock(SurfaceControl.Transaction.class); 1174 final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction, 1175 Integer.MAX_VALUE); 1176 assertFalse(layoutNeeded); 1177 1178 transactionCommittedListener.onTransactionCommitted(); 1179 player.finish(); 1180 // The controller should capture the draw transaction and merge it when preparing to run 1181 // fade-in animation. 1182 verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction)); 1183 assertNull(mDisplayContent.getAsyncRotationController()); 1184 } 1185 testFixedRotationOpen(TestTransitionPlayer player)1186 private void testFixedRotationOpen(TestTransitionPlayer player) { 1187 final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 1188 makeWindowVisible(statusBar); 1189 mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs); 1190 final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); 1191 final ActivityRecord app = createActivityRecord(mDisplayContent); 1192 final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN); 1193 app.mTransitionController.requestStartTransition(transition, app.getTask(), 1194 null /* remoteTransition */, null /* displayChange */); 1195 app.mTransitionController.collectExistenceChange(app.getTask()); 1196 mDisplayContent.setFixedRotationLaunchingAppUnchecked(app); 1197 final AsyncRotationController asyncRotationController = 1198 mDisplayContent.getAsyncRotationController(); 1199 assertNotNull(asyncRotationController); 1200 assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar)); 1201 assertTrue(app.getTask().inTransition()); 1202 1203 player.start(); 1204 player.finish(); 1205 app.getTask().finishSync(mWm.mTransactionFactory.get(), app.getTask().getSyncGroup(), 1206 false /* cancel */); 1207 1208 // The open transition is finished. Continue to play seamless display change transition, 1209 // so the previous async rotation controller should still exist. 1210 mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); 1211 mDisplayContent.setLastHasContent(); 1212 mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */); 1213 assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp()); 1214 assertNotNull(mDisplayContent.getAsyncRotationController()); 1215 1216 // The app is still in transition, so the callback should be no-op. 1217 mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app); 1218 assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp()); 1219 1220 // The bar was invisible so it is not handled by the controller. But if it becomes visible 1221 // and drawn before the transition starts, 1222 assertFalse(asyncRotationController.isTargetToken(navBar.mToken)); 1223 navBar.finishDrawing(null /* postDrawTransaction */, Integer.MAX_VALUE); 1224 assertTrue(asyncRotationController.isTargetToken(navBar.mToken)); 1225 assertTrue(asyncRotationController.shouldFreezeInsetsPosition(navBar)); 1226 1227 player.startTransition(); 1228 // Non-app windows should not be collected. 1229 assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken)); 1230 // Avoid DeviceStateController disturbing the test by triggering another rotation change. 1231 doReturn(false).when(mDisplayContent).updateRotationUnchecked(); 1232 1233 onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted(); 1234 assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange( 1235 mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation()); 1236 player.finish(); 1237 1238 // The controller should be cleared if the target windows are drawn. 1239 statusBar.finishDrawing(mWm.mTransactionFactory.get(), Integer.MAX_VALUE); 1240 assertNull(mDisplayContent.getAsyncRotationController()); 1241 } 1242 assertShouldFreezeInsetsPosition(AsyncRotationController controller, WindowState w, boolean freeze)1243 private static void assertShouldFreezeInsetsPosition(AsyncRotationController controller, 1244 WindowState w, boolean freeze) { 1245 if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) { 1246 // Non blast sync should never freeze insets position. 1247 freeze = false; 1248 } 1249 assertEquals(freeze, controller.shouldFreezeInsetsPosition(w)); 1250 } 1251 1252 @Test testFinishRotationControllerWithFixedRotation()1253 public void testFinishRotationControllerWithFixedRotation() { 1254 final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1255 mDisplayContent.setFixedRotationLaunchingAppUnchecked(app); 1256 registerTestTransitionPlayer(); 1257 mDisplayContent.setLastHasContent(); 1258 mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */); 1259 assertNotNull(mDisplayContent.getAsyncRotationController()); 1260 mDisplayContent.setFixedRotationLaunchingAppUnchecked(null); 1261 assertNull("Clear rotation controller if rotation is not changed", 1262 mDisplayContent.getAsyncRotationController()); 1263 1264 mDisplayContent.setFixedRotationLaunchingAppUnchecked(app); 1265 assertNotNull(mDisplayContent.getAsyncRotationController()); 1266 mDisplayContent.getDisplayRotation().setRotation( 1267 mDisplayContent.getWindowConfiguration().getRotation() + 1); 1268 mDisplayContent.setFixedRotationLaunchingAppUnchecked(null); 1269 assertNotNull("Keep rotation controller if rotation will be changed", 1270 mDisplayContent.getAsyncRotationController()); 1271 } 1272 1273 @Test testDeferRotationForTransientLaunch()1274 public void testDeferRotationForTransientLaunch() { 1275 final TestTransitionPlayer player = registerTestTransitionPlayer(); 1276 assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation()); 1277 final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1278 final ActivityRecord home = new ActivityBuilder(mAtm) 1279 .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask()) 1280 .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build(); 1281 final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN); 1282 final int prevRotation = mDisplayContent.getRotation(); 1283 transition.setTransientLaunch(home, null /* restoreBelow */); 1284 home.mTransitionController.requestStartTransition(transition, home.getTask(), 1285 null /* remoteTransition */, null /* displayChange */); 1286 transition.collectExistenceChange(home); 1287 home.setVisibleRequested(true); 1288 mDisplayContent.setFixedRotationLaunchingAppUnchecked(home); 1289 doReturn(true).when(home).hasFixedRotationTransform(any()); 1290 player.startTransition(); 1291 player.onTransactionReady(mDisplayContent.getSyncTransaction()); 1292 1293 final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); 1294 final RemoteDisplayChangeController displayChangeController = mDisplayContent 1295 .mRemoteDisplayChangeController; 1296 spyOn(displayRotation); 1297 spyOn(displayChangeController); 1298 doReturn(true).when(displayChangeController).isWaitingForRemoteDisplayChange(); 1299 doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation( 1300 anyInt() /* orientation */, anyInt() /* lastRotation */); 1301 // Rotation update is skipped while the recents animation is running. 1302 assertFalse(mDisplayContent.updateRotationUnchecked()); 1303 assertEquals(SCREEN_ORIENTATION_UNSET, displayRotation.getLastOrientation()); 1304 // Return to the app without fixed orientation from recents. 1305 app.moveFocusableActivityToTop("test"); 1306 player.finish(); 1307 // The display should be updated to the changed orientation after the animation is finish. 1308 assertNotEquals(mDisplayContent.getRotation(), prevRotation); 1309 } 1310 1311 @Test testIntermediateVisibility()1312 public void testIntermediateVisibility() { 1313 final TransitionController controller = new TestTransitionController(mAtm); 1314 controller.setSyncEngine(mWm.mSyncEngine); 1315 final ITransitionPlayer player = new ITransitionPlayer.Default(); 1316 controller.registerTransitionPlayer(player, null /* playerProc */); 1317 final Transition openTransition = controller.createTransition(TRANSIT_OPEN); 1318 1319 // Start out with task2 visible and set up a transition that closes task2 and opens task1 1320 final Task task1 = createTask(mDisplayContent); 1321 final ActivityRecord activity1 = createActivityRecord(task1); 1322 activity1.setVisibleRequested(false); 1323 activity1.setVisible(false); 1324 final Task task2 = createTask(mDisplayContent); 1325 makeTaskOrganized(task1, task2); 1326 final ActivityRecord activity2 = createActivityRecord(task1); 1327 activity2.setVisibleRequested(true); 1328 activity2.setVisible(true); 1329 1330 openTransition.collectExistenceChange(task1); 1331 openTransition.collectExistenceChange(activity1); 1332 openTransition.collectExistenceChange(task2); 1333 openTransition.collectExistenceChange(activity2); 1334 1335 activity1.setVisibleRequested(true); 1336 activity1.setVisible(true); 1337 activity2.setVisibleRequested(false); 1338 1339 // Using abort to force-finish the sync (since we can't wait for drawing in unit test). 1340 // We didn't call abort on the transition itself, so it will still run onTransactionReady 1341 // normally. 1342 mWm.mSyncEngine.abort(openTransition.getSyncId()); 1343 1344 // Before finishing openTransition, we are now going to simulate closing task1 to return 1345 // back to (open) task2. 1346 final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE); 1347 1348 closeTransition.collectExistenceChange(task1); 1349 closeTransition.collectExistenceChange(activity1); 1350 closeTransition.collectExistenceChange(task2); 1351 closeTransition.collectExistenceChange(activity2); 1352 1353 activity1.setVisibleRequested(false); 1354 activity2.setVisibleRequested(true); 1355 1356 openTransition.finishTransition(); 1357 1358 // We finished the openTransition. Even though activity1 is visibleRequested=false, since 1359 // the closeTransition animation hasn't played yet, make sure that we didn't commit 1360 // visible=false on activity1 since it needs to remain visible for the animation. 1361 assertTrue(activity1.isVisible()); 1362 assertTrue(activity2.isVisible()); 1363 1364 // Using abort to force-finish the sync (since we obviously can't wait for drawing). 1365 // We didn't call abort on the actual transition, so it will still run onTransactionReady 1366 // normally. 1367 mWm.mSyncEngine.abort(closeTransition.getSyncId()); 1368 1369 closeTransition.finishTransition(); 1370 1371 assertFalse(activity1.isVisible()); 1372 assertTrue(activity2.isVisible()); 1373 1374 // The abort should still commit visible-requested to visible. 1375 final Transition abortTransition = controller.createTransition(TRANSIT_OPEN); 1376 abortTransition.collect(activity1); 1377 activity1.setVisibleRequested(true); 1378 activity1.setVisible(false); 1379 abortTransition.abort(); 1380 assertTrue(activity1.isVisible()); 1381 1382 // The mLaunchTaskBehind flag of an invisible initializing activity should not be cleared. 1383 final Transition noChangeTransition = controller.createTransition(TRANSIT_OPEN); 1384 noChangeTransition.collect(activity1); 1385 activity1.setVisibleRequested(false); 1386 activity1.setState(ActivityRecord.State.INITIALIZING, "test"); 1387 activity1.mLaunchTaskBehind = true; 1388 mWm.mSyncEngine.abort(noChangeTransition.getSyncId()); 1389 noChangeTransition.finishTransition(); 1390 assertTrue(activity1.mLaunchTaskBehind); 1391 } 1392 1393 @Test testTransientLaunch()1394 public void testTransientLaunch() { 1395 spyOn(mWm.mSnapshotController.mTaskSnapshotController); 1396 final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>(); 1397 final TransitionController controller = new TestTransitionController(mAtm) { 1398 @Override 1399 protected void dispatchLegacyAppTransitionFinished(ActivityRecord ar) { 1400 if (ar.mEnteringAnimation) { 1401 enteringAnimReports.add(ar); 1402 } 1403 super.dispatchLegacyAppTransitionFinished(ar); 1404 } 1405 }; 1406 controller.setSyncEngine(mWm.mSyncEngine); 1407 controller.mSnapshotController = mWm.mSnapshotController; 1408 final TaskSnapshotController taskSnapshotController = controller.mSnapshotController 1409 .mTaskSnapshotController; 1410 final ITransitionPlayer player = new ITransitionPlayer.Default(); 1411 controller.registerTransitionPlayer(player, null /* playerProc */); 1412 final Transition openTransition = controller.createTransition(TRANSIT_OPEN); 1413 1414 // Start out with task2 visible and set up a transition that closes task2 and opens task1 1415 final Task task1 = createTask(mDisplayContent); 1416 final ActivityRecord activity1 = createActivityRecord(task1); 1417 activity1.setVisibleRequested(false); 1418 activity1.setVisible(false); 1419 final Task task2 = createTask(mDisplayContent); 1420 makeTaskOrganized(task1, task2); 1421 final ActivityRecord activity2 = createActivityRecord(task2); 1422 activity2.setVisibleRequested(true); 1423 activity2.setVisible(true); 1424 1425 openTransition.collectExistenceChange(task1); 1426 openTransition.collectExistenceChange(activity1); 1427 openTransition.collectExistenceChange(task2); 1428 openTransition.collectExistenceChange(activity2); 1429 1430 activity1.setVisibleRequested(true); 1431 activity1.setVisible(true); 1432 activity2.setVisibleRequested(false); 1433 1434 // Using abort to force-finish the sync (since we can't wait for drawing in unit test). 1435 // We didn't call abort on the transition itself, so it will still run onTransactionReady 1436 // normally. 1437 mWm.mSyncEngine.abort(openTransition.getSyncId()); 1438 1439 verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2), eq(false)); 1440 1441 controller.finishTransition(openTransition); 1442 1443 // We are now going to simulate closing task1 to return back to (open) task2. 1444 final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE); 1445 1446 closeTransition.collectExistenceChange(task2); 1447 closeTransition.collectExistenceChange(activity2); 1448 closeTransition.setTransientLaunch(activity2, task1); 1449 final Transition.ChangeInfo task1ChangeInfo = closeTransition.mChanges.get(task1); 1450 assertNotNull(task1ChangeInfo); 1451 assertTrue(task1ChangeInfo.hasChanged()); 1452 // Make sure the unrelated activity is NOT collected. 1453 final Transition.ChangeInfo activity1ChangeInfo = closeTransition.mChanges.get(activity1); 1454 assertNull(activity1ChangeInfo); 1455 // No need to wait for the activity in transient hide task. 1456 assertEquals(WindowContainer.SYNC_STATE_NONE, activity1.mSyncState); 1457 1458 // An active transient launch overrides idle state to avoid clearing power mode before the 1459 // transition is finished. 1460 spyOn(mRootWindowContainer.mTransitionController); 1461 doAnswer(invocation -> controller.isTransientLaunch(invocation.getArgument(0))).when( 1462 mRootWindowContainer.mTransitionController).isTransientLaunch(any()); 1463 activity2.getTask().setResumedActivity(activity2, "test"); 1464 activity2.idle = true; 1465 assertFalse(mRootWindowContainer.allResumedActivitiesIdle()); 1466 1467 activity1.setVisibleRequested(false); 1468 activity2.setVisibleRequested(true); 1469 activity2.setVisible(true); 1470 1471 // Using abort to force-finish the sync (since we obviously can't wait for drawing). 1472 // We didn't call abort on the actual transition, so it will still run onTransactionReady 1473 // normally. 1474 mWm.mSyncEngine.abort(closeTransition.getSyncId()); 1475 1476 // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be 1477 // called until finish). 1478 verify(taskSnapshotController, times(0)).recordSnapshot(eq(task1), eq(false)); 1479 1480 enteringAnimReports.clear(); 1481 doCallRealMethod().when(mWm.mRoot).ensureActivitiesVisible(any(), 1482 anyInt(), anyBoolean(), anyBoolean()); 1483 final boolean[] wasInFinishingTransition = { false }; 1484 controller.registerLegacyListener(new WindowManagerInternal.AppTransitionListener() { 1485 @Override 1486 public void onAppTransitionFinishedLocked(IBinder token) { 1487 final ActivityRecord r = ActivityRecord.forToken(token); 1488 if (r != null) { 1489 wasInFinishingTransition[0] = controller.inFinishingTransition(r); 1490 } 1491 } 1492 }); 1493 assertTrue(activity1.isVisible()); 1494 doReturn(false).when(task1).isTranslucent(null); 1495 assertTrue(controller.canApplyDim(task1)); 1496 doReturn(true).when(task1).isTranslucent(null); 1497 assertFalse(controller.canApplyDim(task1)); 1498 1499 controller.finishTransition(closeTransition); 1500 assertTrue(wasInFinishingTransition[0]); 1501 assertNull(controller.mFinishingTransition); 1502 1503 assertTrue(activity2.isVisible()); 1504 assertEquals(ActivityTaskManagerService.APP_SWITCH_DISALLOW, mAtm.getBalAppSwitchesState()); 1505 // Because task1 is occluded by task2, finishTransition should make activity1 invisible. 1506 assertFalse(activity1.isVisibleRequested()); 1507 // Make sure activity1 visibility was committed 1508 assertFalse(activity1.isVisible()); 1509 assertFalse(activity1.app.hasActivityInVisibleTask()); 1510 1511 verify(taskSnapshotController, times(1)).recordSnapshot(eq(task1), eq(false)); 1512 assertTrue(enteringAnimReports.contains(activity2)); 1513 } 1514 1515 @Test testIsTransientVisible()1516 public void testIsTransientVisible() { 1517 final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true) 1518 .setVisible(false).build(); 1519 final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true) 1520 .setVisible(false).build(); 1521 final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1522 final Task taskA = appA.getTask(); 1523 final Task taskB = appB.getTask(); 1524 final Task taskRecent = recent.getTask(); 1525 registerTestTransitionPlayer(); 1526 final TransitionController controller = mRootWindowContainer.mTransitionController; 1527 final Transition transition = createTestTransition(TRANSIT_OPEN, controller); 1528 controller.moveToCollecting(transition); 1529 transition.collect(recent); 1530 transition.collect(taskA); 1531 transition.setTransientLaunch(recent, taskA); 1532 taskRecent.moveToFront("move-recent-to-front"); 1533 1534 // During collecting and playing, the recent is on top so it is visible naturally. 1535 // While B needs isTransientVisible to keep visibility because it is occluded by recents. 1536 assertFalse(controller.isTransientVisible(taskB)); 1537 assertTrue(controller.isTransientVisible(taskA)); 1538 assertFalse(controller.isTransientVisible(taskRecent)); 1539 // Switch to playing state. 1540 transition.onTransactionReady(transition.getSyncId(), mMockT); 1541 assertTrue(controller.isTransientVisible(taskA)); 1542 1543 // Switch to another task. For example, use gesture navigation to switch tasks. 1544 taskB.moveToFront("move-b-to-front"); 1545 // The previous app (taskA) should be paused first so it loses transient visible. Because 1546 // visually it is taskA -> taskB, the pause -> resume order should be the same. 1547 assertFalse(controller.isTransientVisible(taskA)); 1548 // Keep the recent visible so there won't be 2 activities pausing at the same time. It is 1549 // to avoid the latency to resume the current top, i.e. appB. 1550 assertTrue(controller.isTransientVisible(taskRecent)); 1551 // The recent is paused after the transient transition is finished. 1552 controller.finishTransition(transition); 1553 assertFalse(controller.isTransientVisible(taskRecent)); 1554 } 1555 1556 @Test testNotReadyPushPop()1557 public void testNotReadyPushPop() { 1558 final TransitionController controller = new TestTransitionController(mAtm); 1559 controller.setSyncEngine(mWm.mSyncEngine); 1560 final ITransitionPlayer player = new ITransitionPlayer.Default(); 1561 controller.registerTransitionPlayer(player, null /* playerProc */); 1562 final Transition openTransition = controller.createTransition(TRANSIT_OPEN); 1563 1564 // Start out with task2 visible and set up a transition that closes task2 and opens task1 1565 final Task task1 = createTask(mDisplayContent); 1566 openTransition.collectExistenceChange(task1); 1567 1568 assertFalse(openTransition.allReady()); 1569 1570 openTransition.setAllReady(); 1571 1572 openTransition.deferTransitionReady(); 1573 assertFalse(openTransition.allReady()); 1574 1575 openTransition.continueTransitionReady(); 1576 assertTrue(openTransition.allReady()); 1577 } 1578 1579 @Test testIsBehindStartingWindowChange()1580 public void testIsBehindStartingWindowChange() { 1581 final Transition transition = createTestTransition(TRANSIT_OPEN); 1582 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1583 final ArraySet<WindowContainer> participants = transition.mParticipants; 1584 1585 final Task task = createTask(mDisplayContent); 1586 final ActivityRecord activity0 = createActivityRecord(task); 1587 final ActivityRecord activity1 = createActivityRecord(task); 1588 doReturn(true).when(activity1).hasStartingWindow(); 1589 1590 // Start states. 1591 changes.put(activity0, 1592 new Transition.ChangeInfo(activity0, true /* vis */, false /* exChg */)); 1593 changes.put(activity1, 1594 new Transition.ChangeInfo(activity1, false /* vis */, false /* exChg */)); 1595 // End states. 1596 activity0.setVisibleRequested(false); 1597 activity1.setVisibleRequested(true); 1598 1599 participants.add(activity0); 1600 participants.add(activity1); 1601 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1602 participants, changes); 1603 final TransitionInfo info = Transition.calculateTransitionInfo( 1604 transition.mType, 0 /* flags */, targets, mMockT); 1605 1606 // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting 1607 // window should cover the whole Task. 1608 assertEquals(2, info.getChanges().size()); 1609 assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)); 1610 assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)); 1611 1612 } 1613 1614 @Test testFlagInTaskWithEmbeddedActivity()1615 public void testFlagInTaskWithEmbeddedActivity() { 1616 final Transition transition = createTestTransition(TRANSIT_OPEN); 1617 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1618 final ArraySet<WindowContainer> participants = transition.mParticipants; 1619 1620 final Task task = createTask(mDisplayContent); 1621 final ActivityRecord nonEmbeddedActivity = createActivityRecord(task); 1622 assertFalse(nonEmbeddedActivity.isEmbedded()); 1623 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1624 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1625 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1626 final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) 1627 .setParentTask(task) 1628 .createActivityCount(2) 1629 .setOrganizer(organizer) 1630 .build(); 1631 final ActivityRecord closingActivity = embeddedTf.getBottomMostActivity(); 1632 final ActivityRecord openingActivity = embeddedTf.getTopMostActivity(); 1633 // Start states. 1634 changes.put(embeddedTf, 1635 new Transition.ChangeInfo(embeddedTf, true /* vis */, false /* exChg */)); 1636 changes.put(closingActivity, 1637 new Transition.ChangeInfo(closingActivity, true /* vis */, false /* exChg */)); 1638 changes.put(openingActivity, 1639 new Transition.ChangeInfo(openingActivity, false /* vis */, true /* exChg */)); 1640 changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity, 1641 true /* vis */, false /* exChg */)); 1642 // End states. 1643 closingActivity.setVisibleRequested(false); 1644 openingActivity.setVisibleRequested(true); 1645 nonEmbeddedActivity.setVisibleRequested(false); 1646 1647 participants.add(closingActivity); 1648 participants.add(openingActivity); 1649 participants.add(nonEmbeddedActivity); 1650 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1651 participants, changes); 1652 final TransitionInfo info = Transition.calculateTransitionInfo( 1653 transition.mType, 0 /* flags */, targets, mMockT); 1654 1655 // All windows in the Task should have FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY because the Task 1656 // contains embedded activity. 1657 assertEquals(3, info.getChanges().size()); 1658 assertTrue(info.getChanges().get(0).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)); 1659 assertTrue(info.getChanges().get(1).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)); 1660 assertTrue(info.getChanges().get(2).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)); 1661 } 1662 1663 @Test testFlagFillsTask_embeddingNotFillingTask()1664 public void testFlagFillsTask_embeddingNotFillingTask() { 1665 final Transition transition = createTestTransition(TRANSIT_OPEN); 1666 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1667 final ArraySet<WindowContainer> participants = transition.mParticipants; 1668 1669 final Task task = createTask(mDisplayContent); 1670 final Rect taskBounds = new Rect(0, 0, 500, 1000); 1671 task.getConfiguration().windowConfiguration.setBounds(taskBounds); 1672 final ActivityRecord nonEmbeddedActivity = createActivityRecord(task); 1673 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1674 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1675 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1676 final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) 1677 .setParentTask(task) 1678 .createActivityCount(1) 1679 .setOrganizer(organizer) 1680 .build(); 1681 final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity(); 1682 // Start states. 1683 changes.put(task, new Transition.ChangeInfo(task, true /* vis */, false /* exChg */)); 1684 changes.put(nonEmbeddedActivity, 1685 new Transition.ChangeInfo(nonEmbeddedActivity, true /* vis */, false /* exChg */)); 1686 changes.put(embeddedTf, 1687 new Transition.ChangeInfo(embeddedTf, false /* vis */, true /* exChg */)); 1688 // End states. 1689 nonEmbeddedActivity.setVisibleRequested(false); 1690 embeddedActivity.setVisibleRequested(true); 1691 embeddedTf.setBounds(new Rect(0, 0, 500, 500)); 1692 1693 participants.add(nonEmbeddedActivity); 1694 participants.add(embeddedTf); 1695 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1696 participants, changes); 1697 final TransitionInfo info = Transition.calculateTransitionInfo( 1698 transition.mType, 0 /* flags */, targets, mMockT); 1699 1700 // The embedded with bounds overridden should not have the flag. 1701 assertEquals(2, info.getChanges().size()); 1702 assertFalse(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); 1703 assertEquals(embeddedTf.getBounds(), info.getChanges().get(0).getEndAbsBounds()); 1704 assertTrue(info.getChanges().get(1).hasFlags(FLAG_FILLS_TASK)); 1705 } 1706 1707 @Test testFlagFillsTask_openActivityFillingTask()1708 public void testFlagFillsTask_openActivityFillingTask() { 1709 final Transition transition = createTestTransition(TRANSIT_OPEN); 1710 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1711 final ArraySet<WindowContainer> participants = transition.mParticipants; 1712 1713 final Task task = createTask(mDisplayContent); 1714 final Rect taskBounds = new Rect(0, 0, 500, 1000); 1715 task.getConfiguration().windowConfiguration.setBounds(taskBounds); 1716 final ActivityRecord activity = createActivityRecord(task); 1717 // Start states: set bounds to make sure the start bounds is ignored if it is not visible. 1718 activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); 1719 activity.setVisibleRequested(false); 1720 changes.put(activity, new Transition.ChangeInfo(activity)); 1721 // End states: reset bounds to fill Task. 1722 activity.getConfiguration().windowConfiguration.setBounds(taskBounds); 1723 activity.setVisibleRequested(true); 1724 1725 participants.add(activity); 1726 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1727 participants, changes); 1728 final TransitionInfo info = Transition.calculateTransitionInfo( 1729 transition.mType, 0 /* flags */, targets, mMockT); 1730 1731 // Opening activity that is filling Task after transition should have the flag. 1732 assertEquals(1, info.getChanges().size()); 1733 assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); 1734 } 1735 1736 @Test testFlagFillsTask_closeActivityFillingTask()1737 public void testFlagFillsTask_closeActivityFillingTask() { 1738 final Transition transition = createTestTransition(TRANSIT_CLOSE); 1739 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1740 final ArraySet<WindowContainer> participants = transition.mParticipants; 1741 1742 final Task task = createTask(mDisplayContent); 1743 final Rect taskBounds = new Rect(0, 0, 500, 1000); 1744 task.getConfiguration().windowConfiguration.setBounds(taskBounds); 1745 final ActivityRecord activity = createActivityRecord(task); 1746 // Start states: fills Task without override. 1747 activity.setVisibleRequested(true); 1748 changes.put(activity, new Transition.ChangeInfo(activity)); 1749 // End states: set bounds to make sure the start bounds is ignored if it is not visible. 1750 activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); 1751 activity.setVisibleRequested(false); 1752 1753 participants.add(activity); 1754 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1755 participants, changes); 1756 final TransitionInfo info = Transition.calculateTransitionInfo( 1757 transition.mType, 0 /* flags */, targets, mMockT); 1758 1759 // Closing activity that is filling Task before transition should have the flag. 1760 assertEquals(1, info.getChanges().size()); 1761 assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); 1762 } 1763 1764 @Test testReparentChangeLastParent()1765 public void testReparentChangeLastParent() { 1766 final Transition transition = createTestTransition(TRANSIT_CHANGE); 1767 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1768 final ArraySet<WindowContainer> participants = transition.mParticipants; 1769 1770 // Reparent activity in transition. 1771 final Task lastParent = createTask(mDisplayContent); 1772 final Task newParent = createTask(mDisplayContent); 1773 final ActivityRecord activity = createActivityRecord(lastParent); 1774 activity.setVisibleRequested(true); 1775 // Skip manipulate the SurfaceControl. 1776 doNothing().when(activity).setDropInputMode(anyInt()); 1777 changes.put(activity, new Transition.ChangeInfo(activity)); 1778 activity.reparent(newParent, POSITION_TOP); 1779 activity.setVisibleRequested(false); 1780 1781 participants.add(activity); 1782 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1783 participants, changes); 1784 final TransitionInfo info = Transition.calculateTransitionInfo( 1785 transition.mType, 0 /* flags */, targets, mMockT); 1786 1787 // Change contains last parent info. 1788 assertEquals(1, info.getChanges().size()); 1789 assertEquals(lastParent.mRemoteToken.toWindowContainerToken(), 1790 info.getChanges().get(0).getLastParent()); 1791 } 1792 1793 @Test testIncludeEmbeddedActivityReparent()1794 public void testIncludeEmbeddedActivityReparent() { 1795 final Transition transition = createTestTransition(TRANSIT_OPEN); 1796 final Task task = createTask(mDisplayContent); 1797 task.setBounds(new Rect(0, 0, 2000, 1000)); 1798 final ActivityRecord activity = createActivityRecord(task); 1799 activity.setVisibleRequested(true); 1800 // Skip manipulate the SurfaceControl. 1801 doNothing().when(activity).setDropInputMode(anyInt()); 1802 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1803 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1804 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1805 final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) 1806 .setParentTask(task) 1807 .setOrganizer(organizer) 1808 .build(); 1809 // TaskFragment with different bounds from Task. 1810 embeddedTf.setBounds(new Rect(0, 0, 1000, 1000)); 1811 1812 // Start states. 1813 transition.collect(activity); 1814 transition.collectExistenceChange(embeddedTf); 1815 1816 // End states. 1817 activity.reparent(embeddedTf, POSITION_TOP); 1818 1819 // Verify that both activity and TaskFragment are included. 1820 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1821 transition.mParticipants, transition.mChanges); 1822 assertTrue(Transition.containsChangeFor(embeddedTf, targets)); 1823 assertTrue(Transition.containsChangeFor(activity, targets)); 1824 } 1825 1826 @Test testChangeSetBackgroundColor()1827 public void testChangeSetBackgroundColor() { 1828 final Transition transition = createTestTransition(TRANSIT_CHANGE); 1829 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1830 final ArraySet<WindowContainer> participants = transition.mParticipants; 1831 1832 // Test background color for Activity and embedded TaskFragment. 1833 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1834 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1835 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1836 final Task task = createTask(mDisplayContent); 1837 final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer); 1838 final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity(); 1839 final ActivityRecord nonEmbeddedActivity = createActivityRecord(task); 1840 final ActivityManager.TaskDescription taskDescription = 1841 new ActivityManager.TaskDescription.Builder() 1842 .setBackgroundColor(Color.YELLOW) 1843 .build(); 1844 task.setTaskDescription(taskDescription); 1845 1846 // Start states: 1847 embeddedActivity.setVisibleRequested(true); 1848 nonEmbeddedActivity.setVisibleRequested(false); 1849 changes.put(embeddedTf, new Transition.ChangeInfo(embeddedTf)); 1850 changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity)); 1851 // End states: 1852 embeddedActivity.setVisibleRequested(false); 1853 nonEmbeddedActivity.setVisibleRequested(true); 1854 1855 participants.add(embeddedTf); 1856 participants.add(nonEmbeddedActivity); 1857 final ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1858 participants, changes); 1859 final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, 1860 0 /* flags */, targets, mMockT); 1861 1862 // Background color should be set on both Activity and embedded TaskFragment. 1863 final int expectedBackgroundColor = ColorUtils.setAlphaComponent( 1864 taskDescription.getBackgroundColor(), 255); 1865 assertEquals(2, info.getChanges().size()); 1866 assertEquals(expectedBackgroundColor, info.getChanges().get(0).getBackgroundColor()); 1867 assertEquals(expectedBackgroundColor, info.getChanges().get(1).getBackgroundColor()); 1868 } 1869 1870 @Test testTransitionVisibleChange()1871 public void testTransitionVisibleChange() { 1872 registerTestTransitionPlayer(); 1873 final ActivityRecord app = createActivityRecord( 1874 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 1875 final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */, 1876 app.mTransitionController, mWm.mSyncEngine); 1877 app.mTransitionController.moveToCollecting(transition); 1878 mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE); 1879 final ArrayList<WindowContainer> freezeCalls = new ArrayList<>(); 1880 transition.setContainerFreezer(new Transition.IContainerFreezer() { 1881 @Override 1882 public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) { 1883 freezeCalls.add(wc); 1884 return true; 1885 } 1886 1887 @Override 1888 public void cleanUp(SurfaceControl.Transaction t) { 1889 } 1890 }); 1891 final Task task = app.getTask(); 1892 transition.collect(task); 1893 final Rect bounds = new Rect(task.getBounds()); 1894 Configuration c = new Configuration(task.getRequestedOverrideConfiguration()); 1895 bounds.inset(10, 10); 1896 c.windowConfiguration.setBounds(bounds); 1897 task.onRequestedOverrideConfigurationChanged(c); 1898 assertTrue(freezeCalls.contains(task)); 1899 transition.abort(); 1900 } 1901 1902 @Test testDeferTransitionReady_deferStartedTransition()1903 public void testDeferTransitionReady_deferStartedTransition() { 1904 final Transition transition = createTestTransition(TRANSIT_OPEN); 1905 transition.setAllReady(); 1906 transition.start(); 1907 1908 assertTrue(mSyncEngine.isReady(transition.getSyncId())); 1909 1910 transition.deferTransitionReady(); 1911 1912 // Both transition ready tracker and sync engine should be deferred. 1913 assertFalse(transition.allReady()); 1914 assertFalse(mSyncEngine.isReady(transition.getSyncId())); 1915 1916 transition.continueTransitionReady(); 1917 1918 assertTrue(transition.allReady()); 1919 assertTrue(mSyncEngine.isReady(transition.getSyncId())); 1920 } 1921 1922 @Test testVisibleChange_snapshot()1923 public void testVisibleChange_snapshot() { 1924 registerTestTransitionPlayer(); 1925 final ActivityRecord app = createActivityRecord( 1926 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 1927 final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, 1928 app.mTransitionController, mWm.mSyncEngine); 1929 app.mTransitionController.moveToCollecting(transition); 1930 mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE); 1931 final SurfaceControl mockSnapshot = mock(SurfaceControl.class); 1932 transition.setContainerFreezer(new Transition.IContainerFreezer() { 1933 @Override 1934 public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) { 1935 Objects.requireNonNull(transition.mChanges.get(wc)).mSnapshot = mockSnapshot; 1936 return true; 1937 } 1938 1939 @Override 1940 public void cleanUp(SurfaceControl.Transaction t) { 1941 } 1942 }); 1943 final Task task = app.getTask(); 1944 transition.collect(task); 1945 final Rect bounds = new Rect(task.getBounds()); 1946 Configuration c = new Configuration(task.getRequestedOverrideConfiguration()); 1947 bounds.inset(10, 10); 1948 c.windowConfiguration.setBounds(bounds); 1949 task.onRequestedOverrideConfigurationChanged(c); 1950 1951 ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 1952 transition.mParticipants, transition.mChanges); 1953 TransitionInfo info = Transition.calculateTransitionInfo( 1954 TRANSIT_CHANGE, 0, targets, mMockT); 1955 assertEquals(mockSnapshot, 1956 info.getChange(task.mRemoteToken.toWindowContainerToken()).getSnapshot()); 1957 transition.abort(); 1958 } 1959 1960 @Test testCollectReparentChange()1961 public void testCollectReparentChange() { 1962 registerTestTransitionPlayer(); 1963 1964 // Reparent activity in transition. 1965 final Task lastParent = createTask(mDisplayContent); 1966 final Task newParent = createTask(mDisplayContent); 1967 final ActivityRecord activity = createActivityRecord(lastParent); 1968 doReturn(true).when(lastParent).shouldRemoveSelfOnLastChildRemoval(); 1969 doNothing().when(activity).setDropInputMode(anyInt()); 1970 activity.setVisibleRequested(true); 1971 1972 final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, 1973 activity.mTransitionController, mWm.mSyncEngine); 1974 activity.mTransitionController.moveToCollecting(transition); 1975 transition.collect(activity); 1976 activity.reparent(newParent, POSITION_TOP); 1977 1978 // ChangeInfo#mCommonAncestor should be set after reparent. 1979 final Transition.ChangeInfo change = transition.mChanges.get(activity); 1980 assertEquals(newParent.getDisplayArea(), change.mCommonAncestor); 1981 } 1982 1983 @Test testMoveToTopWhileVisible()1984 public void testMoveToTopWhileVisible() { 1985 final Transition transition = createTestTransition(TRANSIT_OPEN); 1986 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1987 final ArraySet<WindowContainer> participants = transition.mParticipants; 1988 1989 // Start with taskB on top and taskA on bottom but both visible. 1990 final Task rootTaskA = createTask(mDisplayContent); 1991 final Task leafTaskA = createTaskInRootTask(rootTaskA, 0 /* userId */); 1992 final Task taskB = createTask(mDisplayContent); 1993 leafTaskA.setVisibleRequested(true); 1994 taskB.setVisibleRequested(true); 1995 // manually collect since this is a test transition and not known by transitionController. 1996 transition.collect(leafTaskA); 1997 rootTaskA.moveToFront("test", leafTaskA); 1998 1999 // All the tasks were already visible, so there shouldn't be any changes 2000 ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets( 2001 participants, changes); 2002 assertTrue(targets.isEmpty()); 2003 2004 // After collecting order changes, it should recognize that a task moved to top. 2005 transition.collectOrderChanges(true); 2006 targets = Transition.calculateTargets(participants, changes); 2007 assertEquals(1, targets.size()); 2008 2009 // Make sure the flag is set 2010 final TransitionInfo info = Transition.calculateTransitionInfo( 2011 transition.mType, 0 /* flags */, targets, mMockT); 2012 assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0); 2013 assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode()); 2014 } 2015 2016 private class OrderChangeTestSetup { 2017 final TransitionController mController; 2018 final TestTransitionPlayer mPlayer; 2019 final Transition mTransitA; 2020 final Transition mTransitB; 2021 OrderChangeTestSetup()2022 OrderChangeTestSetup() { 2023 mController = mAtm.getTransitionController(); 2024 mPlayer = registerTestTransitionPlayer(); 2025 mController.setSyncEngine(mWm.mSyncEngine); 2026 2027 mTransitA = createTestTransition(TRANSIT_OPEN, mController); 2028 mTransitA.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL; 2029 mTransitB = createTestTransition(TRANSIT_OPEN, mController); 2030 mTransitB.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL; 2031 } 2032 startParallelCollect(boolean activityLevelFirst)2033 void startParallelCollect(boolean activityLevelFirst) { 2034 // Start with taskB on top and taskA on bottom but both visible. 2035 final Task taskA = createTask(mDisplayContent); 2036 taskA.setVisibleRequested(true); 2037 final ActivityRecord actA = createActivityRecord(taskA); 2038 final TestWindowState winA = createWindowState( 2039 new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), actA); 2040 actA.addWindow(winA); 2041 final ActivityRecord actB = createActivityRecord(taskA); 2042 final TestWindowState winB = createWindowState( 2043 new WindowManager.LayoutParams(TYPE_BASE_APPLICATION), actB); 2044 actB.addWindow(winB); 2045 2046 final Task taskB = createTask(mDisplayContent); 2047 actA.setVisibleRequested(true); 2048 actB.setVisibleRequested(false); 2049 taskB.setVisibleRequested(true); 2050 assertTrue(actA.isAttached()); 2051 2052 final Consumer<Boolean> startAndCollectA = (doReady) -> { 2053 mController.startCollectOrQueue(mTransitA, (deferred) -> { 2054 }); 2055 2056 // Collect activity-level change into A 2057 mTransitA.collect(actA); 2058 actA.setVisibleRequested(false); 2059 winA.onSyncFinishedDrawing(); 2060 mTransitA.collect(actB); 2061 actB.setVisibleRequested(true); 2062 winB.onSyncFinishedDrawing(); 2063 mTransitA.start(); 2064 if (doReady) { 2065 mTransitA.setReady(mDisplayContent, true); 2066 } 2067 }; 2068 final Consumer<Boolean> startAndCollectB = (doReady) -> { 2069 mController.startCollectOrQueue(mTransitB, (deferred) -> { 2070 }); 2071 mTransitB.collect(taskA); 2072 taskA.moveToFront("test"); 2073 mTransitB.start(); 2074 if (doReady) { 2075 mTransitB.setReady(mDisplayContent, true); 2076 } 2077 }; 2078 2079 if (activityLevelFirst) { 2080 startAndCollectA.accept(true); 2081 startAndCollectB.accept(false); 2082 } else { 2083 startAndCollectB.accept(true); 2084 startAndCollectA.accept(false); 2085 } 2086 } 2087 } 2088 2089 @Test testMoveToTopStartAfterReadyAfterParallel()2090 public void testMoveToTopStartAfterReadyAfterParallel() { 2091 // Start collect activity-only transit A 2092 // Start collect task transit B in parallel 2093 // finish A first -> should not include order change from B. 2094 final OrderChangeTestSetup setup = new OrderChangeTestSetup(); 2095 setup.startParallelCollect(true /* activity first */); 2096 2097 mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId()); 2098 waitUntilHandlersIdle(); 2099 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2100 assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo()); 2101 } 2102 2103 setup.mTransitB.setAllReady(); 2104 mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId()); 2105 waitUntilHandlersIdle(); 2106 boolean hasOrderChange = false; 2107 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2108 final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i); 2109 if (chg.getTaskInfo() == null) continue; 2110 hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0; 2111 } 2112 assertTrue(hasOrderChange); 2113 } 2114 2115 @Test testMoveToTopStartAfterReadyBeforeParallel()2116 public void testMoveToTopStartAfterReadyBeforeParallel() { 2117 // Start collect activity-only transit A 2118 // Start collect task transit B in parallel 2119 // finish B first -> should include order change 2120 // then finish A -> should NOT include order change. 2121 final OrderChangeTestSetup setup = new OrderChangeTestSetup(); 2122 setup.startParallelCollect(true /* activity first */); 2123 // Make it unready now so that it doesn't get dequeued automatically. 2124 setup.mTransitA.setReady(mDisplayContent, false); 2125 2126 // Make task change ready first 2127 setup.mTransitB.setAllReady(); 2128 mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId()); 2129 waitUntilHandlersIdle(); 2130 boolean hasOrderChange = false; 2131 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2132 final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i); 2133 if (chg.getTaskInfo() == null) continue; 2134 hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0; 2135 } 2136 assertTrue(hasOrderChange); 2137 2138 setup.mTransitA.setAllReady(); 2139 mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId()); 2140 waitUntilHandlersIdle(); 2141 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2142 assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo()); 2143 } 2144 } 2145 2146 @Test testMoveToTopStartBeforeReadyAfterParallel()2147 public void testMoveToTopStartBeforeReadyAfterParallel() { 2148 // Start collect task transit B 2149 // Start collect activity-only transit A in parallel 2150 // finish A first -> should not include order change from B. 2151 final OrderChangeTestSetup setup = new OrderChangeTestSetup(); 2152 setup.startParallelCollect(false /* activity first */); 2153 // Make B unready now so that it doesn't get dequeued automatically. 2154 setup.mTransitB.setReady(mDisplayContent, false); 2155 2156 setup.mTransitA.setAllReady(); 2157 mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId()); 2158 waitUntilHandlersIdle(); 2159 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2160 assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo()); 2161 } 2162 2163 setup.mTransitB.setAllReady(); 2164 mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId()); 2165 waitUntilHandlersIdle(); 2166 boolean hasOrderChange = false; 2167 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2168 final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i); 2169 if (chg.getTaskInfo() == null) continue; 2170 hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0; 2171 } 2172 assertTrue(hasOrderChange); 2173 } 2174 2175 @Test testMoveToTopStartBeforeReadyBeforeParallel()2176 public void testMoveToTopStartBeforeReadyBeforeParallel() { 2177 // Start collect task transit B 2178 // Start collect activity-only transit A in parallel 2179 // finish B first -> should include order change 2180 // then finish A -> should NOT include order change. 2181 final OrderChangeTestSetup setup = new OrderChangeTestSetup(); 2182 setup.startParallelCollect(false /* activity first */); 2183 2184 mWm.mSyncEngine.tryFinishForTest(setup.mTransitB.getSyncId()); 2185 waitUntilHandlersIdle(); 2186 boolean hasOrderChange = false; 2187 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2188 final TransitionInfo.Change chg = setup.mPlayer.mLastReady.getChanges().get(i); 2189 if (chg.getTaskInfo() == null) continue; 2190 hasOrderChange = hasOrderChange || (chg.getFlags() & FLAG_MOVED_TO_TOP) != 0; 2191 } 2192 assertTrue(hasOrderChange); 2193 2194 setup.mTransitA.setAllReady(); 2195 mWm.mSyncEngine.tryFinishForTest(setup.mTransitA.getSyncId()); 2196 waitUntilHandlersIdle(); 2197 for (int i = 0; i < setup.mPlayer.mLastReady.getChanges().size(); ++i) { 2198 assertNull(setup.mPlayer.mLastReady.getChanges().get(i).getTaskInfo()); 2199 } 2200 } 2201 2202 @Test testQueueStartCollect()2203 public void testQueueStartCollect() { 2204 final TransitionController controller = mAtm.getTransitionController(); 2205 final TestTransitionPlayer player = registerTestTransitionPlayer(); 2206 2207 mSyncEngine = createTestBLASTSyncEngine(); 2208 controller.setSyncEngine(mSyncEngine); 2209 2210 final Transition transitA = createTestTransition(TRANSIT_OPEN, controller); 2211 final Transition transitB = createTestTransition(TRANSIT_OPEN, controller); 2212 final Transition transitC = createTestTransition(TRANSIT_OPEN, controller); 2213 2214 final boolean[] onStartA = new boolean[]{false, false}; 2215 final boolean[] onStartB = new boolean[]{false, false}; 2216 controller.startCollectOrQueue(transitA, (deferred) -> { 2217 onStartA[0] = true; 2218 onStartA[1] = deferred; 2219 }); 2220 controller.startCollectOrQueue(transitB, (deferred) -> { 2221 onStartB[0] = true; 2222 onStartB[1] = deferred; 2223 }); 2224 waitUntilHandlersIdle(); 2225 2226 assertTrue(onStartA[0]); 2227 assertFalse(onStartA[1]); 2228 assertTrue(transitA.isCollecting()); 2229 2230 // B should be queued, so no calls yet 2231 assertFalse(onStartB[0]); 2232 assertTrue(transitB.isPending()); 2233 2234 // finish collecting A 2235 transitA.start(); 2236 transitA.setAllReady(); 2237 mSyncEngine.tryFinishForTest(transitA.getSyncId()); 2238 waitUntilHandlersIdle(); 2239 2240 assertTrue(transitA.isPlaying()); 2241 assertTrue(transitB.isCollecting()); 2242 assertTrue(onStartB[0]); 2243 // Should receive deferred = true 2244 assertTrue(onStartB[1]); 2245 2246 // finish collecting B 2247 transitB.start(); 2248 transitB.setAllReady(); 2249 mSyncEngine.tryFinishForTest(transitB.getSyncId()); 2250 assertTrue(transitB.isPlaying()); 2251 2252 // Now we should be able to start collecting directly a new transition 2253 final boolean[] onStartC = new boolean[]{false, false}; 2254 controller.startCollectOrQueue(transitC, (deferred) -> { 2255 onStartC[0] = true; 2256 onStartC[1] = deferred; 2257 }); 2258 waitUntilHandlersIdle(); 2259 assertTrue(onStartC[0]); 2260 assertFalse(onStartC[1]); 2261 assertTrue(transitC.isCollecting()); 2262 } 2263 2264 @Test testQueueWithLegacy()2265 public void testQueueWithLegacy() { 2266 final TransitionController controller = mAtm.getTransitionController(); 2267 final TestTransitionPlayer player = registerTestTransitionPlayer(); 2268 2269 mSyncEngine = createTestBLASTSyncEngine(); 2270 controller.setSyncEngine(mSyncEngine); 2271 2272 final Transition transitA = createTestTransition(TRANSIT_OPEN, controller); 2273 final Transition transitB = createTestTransition(TRANSIT_OPEN, controller); 2274 2275 controller.startCollectOrQueue(transitA, (deferred) -> {}); 2276 2277 BLASTSyncEngine.SyncGroup legacySync = mSyncEngine.prepareSyncSet( 2278 mock(BLASTSyncEngine.TransactionReadyListener.class), "test"); 2279 final boolean[] applyLegacy = new boolean[2]; 2280 controller.startLegacySyncOrQueue(legacySync, (deferred) -> { 2281 applyLegacy[0] = true; 2282 applyLegacy[1] = deferred; 2283 }); 2284 assertFalse(applyLegacy[0]); 2285 waitUntilHandlersIdle(); 2286 2287 controller.startCollectOrQueue(transitB, (deferred) -> {}); 2288 assertTrue(transitA.isCollecting()); 2289 2290 // finish collecting A 2291 transitA.start(); 2292 transitA.setAllReady(); 2293 mSyncEngine.tryFinishForTest(transitA.getSyncId()); 2294 waitUntilHandlersIdle(); 2295 2296 assertTrue(transitA.isPlaying()); 2297 // legacy sync should start now 2298 assertTrue(applyLegacy[0]); 2299 assertTrue(applyLegacy[1]); 2300 // transitB must wait 2301 assertTrue(transitB.isPending()); 2302 2303 // finish legacy sync 2304 mSyncEngine.setReady(legacySync.mSyncId); 2305 mSyncEngine.tryFinishForTest(legacySync.mSyncId); 2306 // transitioncontroller should be notified so it can start collecting B 2307 assertTrue(transitB.isCollecting()); 2308 } 2309 2310 @Test testQueueParallel()2311 public void testQueueParallel() { 2312 final TransitionController controller = mAtm.getTransitionController(); 2313 final TestTransitionPlayer player = registerTestTransitionPlayer(); 2314 2315 mSyncEngine = createTestBLASTSyncEngine(); 2316 controller.setSyncEngine(mSyncEngine); 2317 2318 final Transition transitA = createTestTransition(TRANSIT_OPEN, controller); 2319 transitA.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL; 2320 final Transition transitB = createTestTransition(TRANSIT_OPEN, controller); 2321 transitB.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL; 2322 final Transition transitC = createTestTransition(TRANSIT_OPEN, controller); 2323 transitC.mParallelCollectType = Transition.PARALLEL_TYPE_MUTUAL; 2324 final Transition transitSync = createTestTransition(TRANSIT_OPEN, controller); 2325 final Transition transitD = createTestTransition(TRANSIT_OPEN, controller); 2326 2327 controller.startCollectOrQueue(transitA, (deferred) -> {}); 2328 controller.startCollectOrQueue(transitB, (deferred) -> {}); 2329 controller.startCollectOrQueue(transitC, (deferred) -> {}); 2330 controller.startCollectOrQueue(transitSync, (deferred) -> {}); 2331 controller.startCollectOrQueue(transitD, (deferred) -> {}); 2332 2333 assertTrue(transitA.isCollecting() && !transitA.isStarted()); 2334 // We still serialize on readiness 2335 assertTrue(transitB.isPending()); 2336 assertTrue(transitC.isPending()); 2337 2338 transitA.start(); 2339 transitA.setAllReady(); 2340 transitB.start(); 2341 transitB.setAllReady(); 2342 2343 // A, B, and C should be collecting in parallel now. 2344 assertTrue(transitA.isStarted()); 2345 assertTrue(transitB.isStarted()); 2346 assertTrue(transitC.isCollecting() && !transitC.isStarted()); 2347 2348 transitC.start(); 2349 transitC.setAllReady(); 2350 2351 assertTrue(transitA.isStarted()); 2352 assertTrue(transitB.isStarted()); 2353 assertTrue(transitC.isStarted()); 2354 // Not parallel so should remain pending 2355 assertTrue(transitSync.isPending()); 2356 // After Sync, so should also remain pending. 2357 assertTrue(transitD.isPending()); 2358 // There should always be a collector, since Sync can't collect yet, C should remain. 2359 assertEquals(transitC, controller.getCollectingTransition()); 2360 2361 mSyncEngine.tryFinishForTest(transitB.getSyncId()); 2362 2363 // The other transitions should remain waiting. 2364 assertTrue(transitA.isStarted()); 2365 assertTrue(transitB.isPlaying()); 2366 assertTrue(transitC.isStarted()); 2367 assertEquals(transitC, controller.getCollectingTransition()); 2368 2369 mSyncEngine.tryFinishForTest(transitC.getSyncId()); 2370 assertTrue(transitA.isStarted()); 2371 assertTrue(transitC.isPlaying()); 2372 // The "collecting" one became ready, so the first "waiting" should move back to collecting. 2373 assertEquals(transitA, controller.getCollectingTransition()); 2374 2375 assertTrue(transitSync.isPending()); 2376 assertTrue(transitD.isPending()); 2377 mSyncEngine.tryFinishForTest(transitA.getSyncId()); 2378 2379 // Now all collectors are done, so sync can be pulled-off the queue. 2380 assertTrue(transitSync.isCollecting() && !transitSync.isStarted()); 2381 transitSync.start(); 2382 transitSync.setAllReady(); 2383 // Since D can run in parallel, it should be pulled-off the queue. 2384 assertTrue(transitSync.isStarted()); 2385 assertTrue(transitD.isPending()); 2386 2387 mSyncEngine.tryFinishForTest(transitSync.getSyncId()); 2388 assertTrue(transitD.isCollecting()); 2389 2390 transitD.start(); 2391 transitD.setAllReady(); 2392 mSyncEngine.tryFinishForTest(transitD.getSyncId()); 2393 2394 // Now nothing should be collecting 2395 assertFalse(controller.isCollecting()); 2396 } 2397 2398 @Test testNoSyncFlagIfOneTrack()2399 public void testNoSyncFlagIfOneTrack() { 2400 final TransitionController controller = mAtm.getTransitionController(); 2401 final TestTransitionPlayer player = registerTestTransitionPlayer(); 2402 2403 mSyncEngine = createTestBLASTSyncEngine(); 2404 controller.setSyncEngine(mSyncEngine); 2405 2406 final Transition transitA = createTestTransition(TRANSIT_OPEN, controller); 2407 final Transition transitB = createTestTransition(TRANSIT_OPEN, controller); 2408 final Transition transitC = createTestTransition(TRANSIT_OPEN, controller); 2409 2410 controller.startCollectOrQueue(transitA, (deferred) -> {}); 2411 controller.startCollectOrQueue(transitB, (deferred) -> {}); 2412 controller.startCollectOrQueue(transitC, (deferred) -> {}); 2413 2414 // Verify that, as-long as there is <= 1 track, we won't get a SYNC flag 2415 transitA.start(); 2416 transitA.setAllReady(); 2417 mSyncEngine.tryFinishForTest(transitA.getSyncId()); 2418 assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0); 2419 transitB.start(); 2420 transitB.setAllReady(); 2421 mSyncEngine.tryFinishForTest(transitB.getSyncId()); 2422 assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0); 2423 transitC.start(); 2424 transitC.setAllReady(); 2425 mSyncEngine.tryFinishForTest(transitC.getSyncId()); 2426 assertTrue((player.mLastReady.getFlags() & FLAG_SYNC) == 0); 2427 } 2428 makeTaskOrganized(Task... tasks)2429 private static void makeTaskOrganized(Task... tasks) { 2430 final ITaskOrganizer organizer = mock(ITaskOrganizer.class); 2431 for (Task t : tasks) { 2432 t.mTaskOrganizer = organizer; 2433 } 2434 } 2435 makeDisplayAreaOrganized(WindowContainer<?> from, WindowContainer<?> end)2436 private static void makeDisplayAreaOrganized(WindowContainer<?> from, 2437 WindowContainer<?> end) { 2438 final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class); 2439 while (from != null && from != end) { 2440 if (from.asDisplayArea() != null) { 2441 from.asDisplayArea().mOrganizer = organizer; 2442 } 2443 from = from.getParent(); 2444 } 2445 if (end.asDisplayArea() != null) { 2446 end.asDisplayArea().mOrganizer = organizer; 2447 } 2448 } 2449 2450 /** Fill the change map with all the parents of top. Change maps are usually fully populated */ fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, WindowContainer top)2451 private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, 2452 WindowContainer top) { 2453 for (WindowContainer curr = top.getParent(); curr != null; curr = curr.getParent()) { 2454 changes.put(curr, new Transition.ChangeInfo(curr, true /* vis */, false /* exChg */)); 2455 } 2456 } 2457 onRotationTransactionReady( TestTransitionPlayer player, SurfaceControl.Transaction startTransaction)2458 private static SurfaceControl.TransactionCommittedListener onRotationTransactionReady( 2459 TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) { 2460 final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor = 2461 ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class); 2462 player.onTransactionReady(startTransaction); 2463 verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture()); 2464 return listenerCaptor.getValue(); 2465 } 2466 } 2467