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.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 24 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; 25 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 26 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 27 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; 28 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 29 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 30 import static android.view.WindowManager.TRANSIT_CHANGE; 31 import static android.view.WindowManager.TRANSIT_CLOSE; 32 import static android.view.WindowManager.TRANSIT_OPEN; 33 import static android.view.WindowManager.TRANSIT_TO_BACK; 34 import static android.window.TransitionInfo.FLAG_FILLS_TASK; 35 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; 36 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; 37 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; 38 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; 39 import static android.window.TransitionInfo.FLAG_TRANSLUCENT; 40 import static android.window.TransitionInfo.isIndependent; 41 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 45 import static com.android.server.wm.WindowContainer.POSITION_TOP; 46 47 import static org.junit.Assert.assertEquals; 48 import static org.junit.Assert.assertFalse; 49 import static org.junit.Assert.assertNotEquals; 50 import static org.junit.Assert.assertNotNull; 51 import static org.junit.Assert.assertNull; 52 import static org.junit.Assert.assertTrue; 53 import static org.junit.Assume.assumeFalse; 54 import static org.mockito.ArgumentMatchers.any; 55 import static org.mockito.ArgumentMatchers.anyInt; 56 import static org.mockito.ArgumentMatchers.eq; 57 import static org.mockito.Mockito.mock; 58 import static org.mockito.Mockito.spy; 59 import static org.mockito.Mockito.times; 60 import static org.mockito.Mockito.verify; 61 62 import android.app.ActivityManager; 63 import android.content.res.Configuration; 64 import android.graphics.Color; 65 import android.graphics.Point; 66 import android.graphics.Rect; 67 import android.os.IBinder; 68 import android.platform.test.annotations.Presubmit; 69 import android.util.ArrayMap; 70 import android.util.ArraySet; 71 import android.view.SurfaceControl; 72 import android.window.IDisplayAreaOrganizer; 73 import android.window.IRemoteTransition; 74 import android.window.ITaskFragmentOrganizer; 75 import android.window.ITaskOrganizer; 76 import android.window.ITransitionPlayer; 77 import android.window.RemoteTransition; 78 import android.window.TaskFragmentOrganizer; 79 import android.window.TransitionInfo; 80 81 import androidx.annotation.NonNull; 82 import androidx.test.filters.SmallTest; 83 84 import com.android.internal.graphics.ColorUtils; 85 86 import org.junit.Test; 87 import org.junit.runner.RunWith; 88 import org.mockito.ArgumentCaptor; 89 90 import java.util.ArrayList; 91 import java.util.Arrays; 92 import java.util.Objects; 93 import java.util.concurrent.CountDownLatch; 94 import java.util.concurrent.TimeUnit; 95 import java.util.function.Function; 96 97 /** 98 * Build/Install/Run: 99 * atest WmTests:TransitionTests 100 */ 101 @SmallTest 102 @Presubmit 103 @RunWith(WindowTestRunner.class) 104 public class TransitionTests extends WindowTestsBase { 105 final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class); 106 private BLASTSyncEngine mSyncEngine; 107 createTestTransition(int transitType)108 private Transition createTestTransition(int transitType) { 109 TransitionTracer tracer = mock(TransitionTracer.class); 110 final TransitionController controller = new TransitionController( 111 mock(ActivityTaskManagerService.class), mock(TaskSnapshotController.class), 112 mock(TransitionTracer.class)); 113 114 mSyncEngine = createTestBLASTSyncEngine(); 115 final Transition t = new Transition(transitType, 0 /* flags */, controller, mSyncEngine); 116 t.startCollecting(0 /* timeoutMs */); 117 return t; 118 } 119 120 @Test testCreateInfo_NewTask()121 public void testCreateInfo_NewTask() { 122 final Transition transition = createTestTransition(TRANSIT_OPEN); 123 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 124 ArraySet<WindowContainer> participants = transition.mParticipants; 125 126 final Task newTask = createTask(mDisplayContent); 127 final Task oldTask = createTask(mDisplayContent); 128 final ActivityRecord closing = createActivityRecord(oldTask); 129 final ActivityRecord opening = createActivityRecord(newTask); 130 // Start states. 131 changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 132 changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); 133 changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 134 changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); 135 fillChangeMap(changes, newTask); 136 // End states. 137 closing.setVisibleRequested(false); 138 opening.setVisibleRequested(true); 139 140 final int transit = transition.mType; 141 int flags = 0; 142 143 // Check basic both tasks participating 144 participants.add(oldTask); 145 participants.add(newTask); 146 ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); 147 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, 148 mMockT); 149 assertEquals(2, info.getChanges().size()); 150 assertEquals(transit, info.getType()); 151 152 // Check that children are pruned 153 participants.add(opening); 154 participants.add(closing); 155 targets = Transition.calculateTargets(participants, changes); 156 info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); 157 assertEquals(2, info.getChanges().size()); 158 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 159 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 160 161 // Check combined prune and promote 162 participants.remove(newTask); 163 targets = Transition.calculateTargets(participants, changes); 164 info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); 165 assertEquals(2, info.getChanges().size()); 166 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 167 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 168 169 // Check multi promote 170 participants.remove(oldTask); 171 targets = Transition.calculateTargets(participants, changes); 172 info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); 173 assertEquals(2, info.getChanges().size()); 174 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 175 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 176 } 177 178 @Test testCreateInfo_NestedTasks()179 public void testCreateInfo_NestedTasks() { 180 final Transition transition = createTestTransition(TRANSIT_OPEN); 181 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 182 ArraySet<WindowContainer> participants = transition.mParticipants; 183 184 final Task newTask = createTask(mDisplayContent); 185 final Task newNestedTask = createTaskInRootTask(newTask, 0); 186 final Task newNestedTask2 = createTaskInRootTask(newTask, 0); 187 final Task oldTask = createTask(mDisplayContent); 188 final ActivityRecord closing = createActivityRecord(oldTask); 189 final ActivityRecord opening = createActivityRecord(newNestedTask); 190 final ActivityRecord opening2 = createActivityRecord(newNestedTask2); 191 // Start states. 192 changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 193 changes.put(newNestedTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 194 changes.put(newNestedTask2, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 195 changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); 196 changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 197 changes.put(opening2, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 198 changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); 199 fillChangeMap(changes, newTask); 200 // End states. 201 closing.setVisibleRequested(false); 202 opening.setVisibleRequested(true); 203 opening2.setVisibleRequested(true); 204 205 final int transit = transition.mType; 206 int flags = 0; 207 208 // Check full promotion from leaf 209 participants.add(oldTask); 210 participants.add(opening); 211 participants.add(opening2); 212 ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); 213 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, 214 mMockT); 215 assertEquals(2, info.getChanges().size()); 216 assertEquals(transit, info.getType()); 217 assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); 218 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 219 220 // Check that unchanging but visible descendant of sibling prevents promotion 221 participants.remove(opening2); 222 targets = Transition.calculateTargets(participants, changes); 223 info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); 224 assertEquals(2, info.getChanges().size()); 225 assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken())); 226 assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); 227 } 228 229 @Test testCreateInfo_DisplayArea()230 public void testCreateInfo_DisplayArea() { 231 final Transition transition = createTestTransition(TRANSIT_OPEN); 232 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 233 ArraySet<WindowContainer> participants = transition.mParticipants; 234 final Task showTask = createTask(mDisplayContent); 235 final Task showNestedTask = createTaskInRootTask(showTask, 0); 236 final Task showTask2 = createTask(mDisplayContent); 237 final DisplayArea tda = showTask.getDisplayArea(); 238 final ActivityRecord showing = createActivityRecord(showNestedTask); 239 final ActivityRecord showing2 = createActivityRecord(showTask2); 240 // Start states. 241 changes.put(showTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 242 changes.put(showNestedTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 243 changes.put(showTask2, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 244 changes.put(tda, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 245 changes.put(showing, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 246 changes.put(showing2, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 247 fillChangeMap(changes, tda); 248 249 // End states. 250 showing.setVisibleRequested(true); 251 showing2.setVisibleRequested(true); 252 253 final int transit = transition.mType; 254 int flags = 0; 255 256 // Check promotion to DisplayArea 257 participants.add(showing); 258 participants.add(showing2); 259 ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); 260 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, 261 mMockT); 262 assertEquals(1, info.getChanges().size()); 263 assertEquals(transit, info.getType()); 264 assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); 265 266 // Check that organized tasks get reported even if not top 267 makeTaskOrganized(showTask); 268 targets = Transition.calculateTargets(participants, changes); 269 info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); 270 assertEquals(2, info.getChanges().size()); 271 assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); 272 assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken())); 273 // Even if DisplayArea explicitly participating 274 participants.add(tda); 275 targets = Transition.calculateTargets(participants, changes); 276 info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); 277 assertEquals(2, info.getChanges().size()); 278 } 279 280 @Test testCreateInfo_existenceChange()281 public void testCreateInfo_existenceChange() { 282 final Transition transition = createTestTransition(TRANSIT_OPEN); 283 284 final Task openTask = createTask(mDisplayContent); 285 final ActivityRecord opening = createActivityRecord(openTask); 286 opening.setVisibleRequested(false); // starts invisible 287 final Task closeTask = createTask(mDisplayContent); 288 final ActivityRecord closing = createActivityRecord(closeTask); 289 closing.setVisibleRequested(true); // starts visible 290 291 transition.collectExistenceChange(openTask); 292 transition.collect(opening); 293 transition.collect(closing); 294 opening.setVisibleRequested(true); 295 closing.setVisibleRequested(false); 296 297 ArrayList<WindowContainer> targets = Transition.calculateTargets( 298 transition.mParticipants, transition.mChanges); 299 TransitionInfo info = Transition.calculateTransitionInfo( 300 0, 0, targets, transition.mChanges, mMockT); 301 assertEquals(2, info.getChanges().size()); 302 // There was an existence change on open, so it should be OPEN rather than SHOW 303 assertEquals(TRANSIT_OPEN, 304 info.getChange(openTask.mRemoteToken.toWindowContainerToken()).getMode()); 305 // No exestence change on closing, so HIDE rather than CLOSE 306 assertEquals(TRANSIT_TO_BACK, 307 info.getChange(closeTask.mRemoteToken.toWindowContainerToken()).getMode()); 308 } 309 310 @Test testCreateInfo_ordering()311 public void testCreateInfo_ordering() { 312 final Transition transition = createTestTransition(TRANSIT_OPEN); 313 // pick some number with a high enough chance of being out-of-order when added to set. 314 final int taskCount = 6; 315 316 final Task[] tasks = new Task[taskCount]; 317 for (int i = 0; i < taskCount; ++i) { 318 // Each add goes on top, so at the end of this, task[9] should be on top 319 tasks[i] = createTask(mDisplayContent, 320 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); 321 final ActivityRecord act = createActivityRecord(tasks[i]); 322 // alternate so that the transition doesn't get promoted to the display area 323 act.setVisibleRequested((i % 2) == 0); // starts invisible 324 } 325 326 // doesn't matter which order collected since participants is a set 327 for (int i = 0; i < taskCount; ++i) { 328 transition.collectExistenceChange(tasks[i]); 329 final ActivityRecord act = tasks[i].getTopMostActivity(); 330 transition.collect(act); 331 tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0); 332 } 333 334 ArrayList<WindowContainer> targets = Transition.calculateTargets( 335 transition.mParticipants, transition.mChanges); 336 TransitionInfo info = Transition.calculateTransitionInfo( 337 0, 0, targets, transition.mChanges, mMockT); 338 assertEquals(taskCount, info.getChanges().size()); 339 // verify order is top-to-bottem 340 for (int i = 0; i < taskCount; ++i) { 341 assertEquals(tasks[taskCount - i - 1].mRemoteToken.toWindowContainerToken(), 342 info.getChanges().get(i).getContainer()); 343 } 344 } 345 346 @Test testCreateInfo_wallpaper()347 public void testCreateInfo_wallpaper() { 348 final Transition transition = createTestTransition(TRANSIT_OPEN); 349 // pick some number with a high enough chance of being out-of-order when added to set. 350 final int taskCount = 4; 351 final int showWallpaperTask = 2; 352 353 final Task[] tasks = new Task[taskCount]; 354 for (int i = 0; i < taskCount; ++i) { 355 // Each add goes on top, so at the end of this, task[9] should be on top 356 tasks[i] = createTask(mDisplayContent, 357 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 358 final ActivityRecord act = createActivityRecord(tasks[i]); 359 // alternate so that the transition doesn't get promoted to the display area 360 act.setVisibleRequested((i % 2) == 0); // starts invisible 361 act.visibleIgnoringKeyguard = (i % 2) == 0; 362 if (i == showWallpaperTask) { 363 doReturn(true).when(act).showWallpaper(); 364 } 365 } 366 367 final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm, 368 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */)); 369 final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, 370 "wallpaperWindow"); 371 wallpaperWindowToken.setVisibleRequested(false); 372 transition.collect(wallpaperWindowToken); 373 wallpaperWindowToken.setVisibleRequested(true); 374 wallpaperWindow.mHasSurface = true; 375 376 // doesn't matter which order collected since participants is a set 377 for (int i = 0; i < taskCount; ++i) { 378 transition.collectExistenceChange(tasks[i]); 379 final ActivityRecord act = tasks[i].getTopMostActivity(); 380 transition.collect(act); 381 tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0); 382 } 383 384 ArrayList<WindowContainer> targets = Transition.calculateTargets( 385 transition.mParticipants, transition.mChanges); 386 TransitionInfo info = Transition.calculateTransitionInfo( 387 0, 0, targets, transition.mChanges, mMockT); 388 // verify that wallpaper is at bottom 389 assertEquals(taskCount + 1, info.getChanges().size()); 390 // The wallpaper is not organized, so it won't have a token; however, it will be marked 391 // as IS_WALLPAPER 392 assertEquals(FLAG_IS_WALLPAPER, 393 info.getChanges().get(info.getChanges().size() - 1).getFlags()); 394 assertEquals(FLAG_SHOW_WALLPAPER, info.getChange( 395 tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags()); 396 } 397 398 @Test testCreateInfo_PromoteSimilarClose()399 public void testCreateInfo_PromoteSimilarClose() { 400 final Transition transition = createTestTransition(TRANSIT_CLOSE); 401 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 402 ArraySet<WindowContainer> participants = transition.mParticipants; 403 404 final Task topTask = createTask(mDisplayContent); 405 final Task belowTask = createTask(mDisplayContent); 406 final ActivityRecord showing = createActivityRecord(belowTask); 407 final ActivityRecord hiding = createActivityRecord(topTask); 408 final ActivityRecord closing = createActivityRecord(topTask); 409 // Start states. 410 changes.put(topTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 411 changes.put(belowTask, new Transition.ChangeInfo(false /* vis */, false /* exChg */)); 412 changes.put(showing, new Transition.ChangeInfo(false /* vis */, false /* exChg */)); 413 changes.put(hiding, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 414 changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); 415 fillChangeMap(changes, topTask); 416 // End states. 417 showing.setVisibleRequested(true); 418 closing.setVisibleRequested(false); 419 hiding.setVisibleRequested(false); 420 421 participants.add(belowTask); 422 participants.add(hiding); 423 participants.add(closing); 424 ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); 425 assertEquals(2, targets.size()); 426 assertTrue(targets.contains(belowTask)); 427 assertTrue(targets.contains(topTask)); 428 } 429 430 @Test testCreateInfo_PromoteSimilarOpen()431 public void testCreateInfo_PromoteSimilarOpen() { 432 final Transition transition = createTestTransition(TRANSIT_OPEN); 433 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 434 ArraySet<WindowContainer> participants = transition.mParticipants; 435 436 final Task topTask = createTask(mDisplayContent); 437 final Task belowTask = createTask(mDisplayContent); 438 final ActivityRecord showing = createActivityRecord(topTask); 439 final ActivityRecord opening = createActivityRecord(topTask); 440 final ActivityRecord closing = createActivityRecord(belowTask); 441 // Start states. 442 changes.put(topTask, new Transition.ChangeInfo(false /* vis */, false /* exChg */)); 443 changes.put(belowTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 444 changes.put(showing, new Transition.ChangeInfo(false /* vis */, false /* exChg */)); 445 changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 446 changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 447 fillChangeMap(changes, topTask); 448 // End states. 449 showing.setVisibleRequested(true); 450 opening.setVisibleRequested(true); 451 closing.setVisibleRequested(false); 452 453 participants.add(belowTask); 454 participants.add(showing); 455 participants.add(opening); 456 ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); 457 assertEquals(2, targets.size()); 458 assertTrue(targets.contains(belowTask)); 459 assertTrue(targets.contains(topTask)); 460 } 461 462 @Test testTargets_noIntermediatesToWallpaper()463 public void testTargets_noIntermediatesToWallpaper() { 464 final Transition transition = createTestTransition(TRANSIT_OPEN); 465 466 final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, 467 mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */); 468 // Make DA organized so we can check that they don't get included. 469 WindowContainer parent = wallpaperWindowToken.getParent(); 470 makeDisplayAreaOrganized(parent, mDisplayContent); 471 final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, 472 "wallpaperWindow"); 473 wallpaperWindowToken.setVisibleRequested(false); 474 transition.collect(wallpaperWindowToken); 475 wallpaperWindowToken.setVisibleRequested(true); 476 wallpaperWindow.mHasSurface = true; 477 doReturn(true).when(mDisplayContent).isAttached(); 478 transition.collect(mDisplayContent); 479 assertFalse("The change of non-interesting window container should be skipped", 480 transition.mChanges.containsKey(mDisplayContent.getParent())); 481 mDisplayContent.getWindowConfiguration().setRotation( 482 (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4); 483 484 ArrayList<WindowContainer> targets = Transition.calculateTargets( 485 transition.mParticipants, transition.mChanges); 486 TransitionInfo info = Transition.calculateTransitionInfo( 487 0, 0, targets, transition.mChanges, mMockT); 488 // The wallpaper is not organized, so it won't have a token; however, it will be marked 489 // as IS_WALLPAPER 490 assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags()); 491 // Make sure no intermediate display areas were pulled in between wallpaper and display. 492 assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(), 493 info.getChanges().get(0).getParent()); 494 } 495 496 @Test testRunningRemoteTransition()497 public void testRunningRemoteTransition() { 498 final TestTransitionPlayer testPlayer = new TestTransitionPlayer( 499 mAtm.getTransitionController(), mAtm.mWindowOrganizerController); 500 final WindowProcessController playerProc = mSystemServicesTestRule.addProcess( 501 "pkg.player", "proc.player", 5000 /* pid */, 5000 /* uid */); 502 testPlayer.mController.registerTransitionPlayer(testPlayer, playerProc); 503 doReturn(mock(IBinder.class)).when(playerProc.getThread()).asBinder(); 504 final WindowProcessController delegateProc = mSystemServicesTestRule.addProcess( 505 "pkg.delegate", "proc.delegate", 6000 /* pid */, 6000 /* uid */); 506 doReturn(mock(IBinder.class)).when(delegateProc.getThread()).asBinder(); 507 final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); 508 final TransitionController controller = app.mTransitionController; 509 final Transition transition = controller.createTransition(TRANSIT_OPEN); 510 final RemoteTransition remoteTransition = new RemoteTransition( 511 mock(IRemoteTransition.class)); 512 remoteTransition.setAppThread(delegateProc.getThread()); 513 transition.collectExistenceChange(app.getTask()); 514 controller.requestStartTransition(transition, app.getTask(), remoteTransition, 515 null /* displayChange */); 516 testPlayer.startTransition(); 517 testPlayer.onTransactionReady(app.getSyncTransaction()); 518 assertTrue(playerProc.isRunningRemoteTransition()); 519 assertTrue(delegateProc.isRunningRemoteTransition()); 520 assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread())); 521 522 testPlayer.finish(); 523 assertFalse(playerProc.isRunningRemoteTransition()); 524 assertFalse(delegateProc.isRunningRemoteTransition()); 525 assertFalse(controller.mRemotePlayer.reportRunning(delegateProc.getThread())); 526 } 527 528 @Test testOpenActivityInTheSameTaskWithDisplayChange()529 public void testOpenActivityInTheSameTaskWithDisplayChange() { 530 final ActivityRecord closing = createActivityRecord(mDisplayContent); 531 closing.setVisibleRequested(true); 532 final Task task = closing.getTask(); 533 makeTaskOrganized(task); 534 final ActivityRecord opening = createActivityRecord(task); 535 opening.setVisibleRequested(false); 536 makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent); 537 final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent }; 538 final Transition transition = createTestTransition(TRANSIT_OPEN); 539 for (WindowContainer<?> wc : wcs) { 540 transition.collect(wc); 541 } 542 closing.setVisibleRequested(false); 543 opening.setVisibleRequested(true); 544 final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1; 545 for (WindowContainer<?> wc : wcs) { 546 wc.getWindowConfiguration().setRotation(newRotation); 547 } 548 549 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 550 transition.mParticipants, transition.mChanges); 551 // Especially the activities must be in the targets. 552 assertTrue(targets.containsAll(Arrays.asList(wcs))); 553 } 554 555 @Test testIndependent()556 public void testIndependent() { 557 final Transition transition = createTestTransition(TRANSIT_OPEN); 558 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 559 ArraySet<WindowContainer> participants = transition.mParticipants; 560 561 final Task openTask = createTask(mDisplayContent); 562 final Task openInOpenTask = createTaskInRootTask(openTask, 0); 563 final ActivityRecord openInOpen = createActivityRecord(openInOpenTask); 564 565 final Task changeTask = createTask(mDisplayContent); 566 final Task changeInChangeTask = createTaskInRootTask(changeTask, 0); 567 final Task openInChangeTask = createTaskInRootTask(changeTask, 0); 568 final ActivityRecord changeInChange = createActivityRecord(changeInChangeTask); 569 final ActivityRecord openInChange = createActivityRecord(openInChangeTask); 570 // set organizer for everything so that they all get added to transition info 571 makeTaskOrganized(openTask, openInOpenTask, changeTask, changeInChangeTask, 572 openInChangeTask); 573 574 // Start states. 575 changes.put(openTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 576 changes.put(changeTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 577 changes.put(openInOpenTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 578 changes.put(openInChangeTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 579 changes.put(changeInChangeTask, 580 new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 581 changes.put(openInOpen, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 582 changes.put(openInChange, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 583 changes.put(changeInChange, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 584 fillChangeMap(changes, openTask); 585 // End states. 586 changeInChange.setVisibleRequested(true); 587 openInOpen.setVisibleRequested(true); 588 openInChange.setVisibleRequested(true); 589 590 final int transit = transition.mType; 591 int flags = 0; 592 593 // Check full promotion from leaf 594 participants.add(changeInChange); 595 participants.add(openInOpen); 596 participants.add(openInChange); 597 // Explicitly add changeTask (to test independence with parents) 598 participants.add(changeTask); 599 final ArrayList<WindowContainer> targets = 600 Transition.calculateTargets(participants, changes); 601 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, 602 mMockT); 603 // Root changes should always be considered independent 604 assertTrue(isIndependent( 605 info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info)); 606 assertTrue(isIndependent( 607 info.getChange(changeTask.mRemoteToken.toWindowContainerToken()), info)); 608 609 // Children of a open/close change are not independent 610 assertFalse(isIndependent( 611 info.getChange(openInOpenTask.mRemoteToken.toWindowContainerToken()), info)); 612 613 // Non-root changes are not independent 614 assertFalse(isIndependent( 615 info.getChange(changeInChangeTask.mRemoteToken.toWindowContainerToken()), info)); 616 617 // open/close within a change are independent 618 assertTrue(isIndependent( 619 info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info)); 620 } 621 622 @Test testOpenOpaqueTask()623 public void testOpenOpaqueTask() { 624 final Transition transition = createTestTransition(TRANSIT_OPEN); 625 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 626 ArraySet<WindowContainer> participants = transition.mParticipants; 627 628 final Task newTask = createTask(mDisplayContent); 629 doReturn(false).when(newTask).isTranslucent(any()); 630 final Task oldTask = createTask(mDisplayContent); 631 doReturn(false).when(oldTask).isTranslucent(any()); 632 633 final ActivityRecord closing = createActivityRecord(oldTask); 634 closing.setOccludesParent(true); 635 final ActivityRecord opening = createActivityRecord(newTask); 636 opening.setOccludesParent(false); 637 // Start states. 638 changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 639 changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 640 changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 641 changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 642 fillChangeMap(changes, newTask); 643 // End states. 644 closing.setVisibleRequested(true); 645 opening.setVisibleRequested(true); 646 647 final int transit = transition.mType; 648 int flags = 0; 649 650 // Check basic both tasks participating 651 participants.add(oldTask); 652 participants.add(newTask); 653 ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); 654 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, 655 mMockT); 656 assertEquals(2, info.getChanges().size()); 657 assertEquals(transit, info.getType()); 658 659 assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) == 0); 660 assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0); 661 } 662 663 @Test testOpenTranslucentTask()664 public void testOpenTranslucentTask() { 665 final Transition transition = createTestTransition(TRANSIT_OPEN); 666 ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 667 ArraySet<WindowContainer> participants = transition.mParticipants; 668 669 final Task newTask = createTask(mDisplayContent); 670 doReturn(true).when(newTask).isTranslucent(any()); 671 final Task oldTask = createTask(mDisplayContent); 672 doReturn(false).when(oldTask).isTranslucent(any()); 673 674 final ActivityRecord closing = createActivityRecord(oldTask); 675 closing.setOccludesParent(true); 676 final ActivityRecord opening = createActivityRecord(newTask); 677 opening.setOccludesParent(false); 678 // Start states. 679 changes.put(newTask, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 680 changes.put(oldTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 681 changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 682 changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 683 fillChangeMap(changes, newTask); 684 // End states. 685 closing.setVisibleRequested(true); 686 opening.setVisibleRequested(true); 687 688 final int transit = transition.mType; 689 int flags = 0; 690 691 // Check basic both tasks participating 692 participants.add(oldTask); 693 participants.add(newTask); 694 ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); 695 TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, 696 mMockT); 697 assertEquals(2, info.getChanges().size()); 698 assertEquals(transit, info.getType()); 699 700 assertTrue((info.getChanges().get(0).getFlags() & FLAG_TRANSLUCENT) != 0); 701 assertTrue((info.getChanges().get(1).getFlags() & FLAG_TRANSLUCENT) == 0); 702 } 703 704 @Test testTimeout()705 public void testTimeout() { 706 final TransitionController controller = new TransitionController(mAtm, 707 mock(TaskSnapshotController.class), mock(TransitionTracer.class)); 708 final BLASTSyncEngine sync = new BLASTSyncEngine(mWm); 709 final CountDownLatch latch = new CountDownLatch(1); 710 // When the timeout is reached, it will finish the sync-group and notify transaction ready. 711 final Transition t = new Transition(TRANSIT_OPEN, 0 /* flags */, controller, sync) { 712 @Override 713 public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) { 714 latch.countDown(); 715 } 716 }; 717 t.startCollecting(10 /* timeoutMs */); 718 assertTrue(awaitInWmLock(() -> latch.await(3, TimeUnit.SECONDS))); 719 } 720 721 @Test testTransitionBounds()722 public void testTransitionBounds() { 723 registerTestTransitionPlayer(); 724 final int offset = 10; 725 final Function<WindowContainer<?>, TransitionInfo.Change> test = wc -> { 726 final Transition transition = wc.mTransitionController.createTransition(TRANSIT_OPEN); 727 transition.collect(wc); 728 final int nextRotation = (wc.getWindowConfiguration().getRotation() + 1) % 4; 729 wc.getWindowConfiguration().setRotation(nextRotation); 730 wc.getWindowConfiguration().setDisplayRotation(nextRotation); 731 final Rect bounds = wc.getWindowConfiguration().getBounds(); 732 // Flip the bounds with offset. 733 wc.getWindowConfiguration().setBounds( 734 new Rect(offset, offset, bounds.height(), bounds.width())); 735 final int flags = 0; 736 final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, flags, 737 Transition.calculateTargets(transition.mParticipants, transition.mChanges), 738 transition.mChanges, mMockT); 739 transition.abort(); 740 return info.getChanges().get(0); 741 }; 742 743 final ActivityRecord app = createActivityRecord(mDisplayContent); 744 final TransitionInfo.Change changeOfActivity = test.apply(app); 745 // There will be letterbox if the activity bounds don't match parent, so always use its 746 // parent bounds for animation. 747 assertEquals(app.getParent().getBounds(), changeOfActivity.getEndAbsBounds()); 748 final int endRotation = app.mTransitionController.useShellTransitionsRotation() 749 ? app.getWindowConfiguration().getRotation() 750 // Without shell rotation, fixed rotation is done by core so the info should not 751 // contain rotation change. 752 : app.getParent().getWindowConfiguration().getRotation(); 753 assertEquals(endRotation, changeOfActivity.getEndRotation()); 754 755 // Non-activity target always uses its configuration for end info. 756 final Task task = app.getTask(); 757 final TransitionInfo.Change changeOfTask = test.apply(task); 758 assertEquals(task.getBounds(), changeOfTask.getEndAbsBounds()); 759 assertEquals(new Point(offset, offset), changeOfTask.getEndRelOffset()); 760 assertEquals(task.getWindowConfiguration().getRotation(), changeOfTask.getEndRotation()); 761 } 762 763 @Test testDisplayRotationChange()764 public void testDisplayRotationChange() { 765 final Task task = createActivityRecord(mDisplayContent).getTask(); 766 final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 767 final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); 768 final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime"); 769 final WindowToken decorToken = new WindowToken.Builder(mWm, mock(IBinder.class), 770 TYPE_NAVIGATION_BAR_PANEL).setDisplayContent(mDisplayContent) 771 .setRoundedCornerOverlay(true).build(); 772 final WindowState screenDecor = 773 createWindow(null, decorToken.windowType, decorToken, "screenDecor"); 774 final WindowState[] windows = { statusBar, navBar, ime, screenDecor }; 775 makeWindowVisible(windows); 776 mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs); 777 mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs); 778 final TestTransitionPlayer player = registerTestTransitionPlayer(); 779 780 mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); 781 mDisplayContent.setLastHasContent(); 782 mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */, 783 null /* displayChange */); 784 assertEquals(WindowContainer.SYNC_STATE_NONE, statusBar.mSyncState); 785 assertEquals(WindowContainer.SYNC_STATE_NONE, navBar.mSyncState); 786 assertEquals(WindowContainer.SYNC_STATE_NONE, screenDecor.mSyncState); 787 assertEquals(WindowContainer.SYNC_STATE_WAITING_FOR_DRAW, ime.mSyncState); 788 789 final AsyncRotationController asyncRotationController = 790 mDisplayContent.getAsyncRotationController(); 791 assertNotNull(asyncRotationController); 792 for (WindowState w : windows) { 793 w.setOrientationChanging(true); 794 } 795 player.startTransition(); 796 797 assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken)); 798 assertFalse(mDisplayContent.mTransitionController.isCollecting(decorToken)); 799 assertTrue(ime.mToken.inTransition()); 800 assertTrue(task.inTransition()); 801 assertTrue(asyncRotationController.isTargetToken(decorToken)); 802 assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true); 803 804 if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) { 805 // Only seamless window syncs its draw transaction with transition. 806 assertFalse(asyncRotationController.handleFinishDrawing(statusBar, mMockT)); 807 assertTrue(asyncRotationController.handleFinishDrawing(screenDecor, mMockT)); 808 } 809 screenDecor.setOrientationChanging(false); 810 // Status bar finishes drawing before the start transaction. Its fade-in animation will be 811 // executed until the transaction is committed, so it is still in target tokens. 812 statusBar.setOrientationChanging(false); 813 assertTrue(asyncRotationController.isTargetToken(statusBar.mToken)); 814 815 final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class); 816 final SurfaceControl.TransactionCommittedListener transactionCommittedListener = 817 onRotationTransactionReady(player, startTransaction); 818 819 // The transaction is committed, so fade-in animation for status bar is consumed. 820 transactionCommittedListener.onTransactionCommitted(); 821 assertFalse(asyncRotationController.isTargetToken(statusBar.mToken)); 822 assertShouldFreezeInsetsPosition(asyncRotationController, navBar, false); 823 824 // Navigation bar finishes drawing after the start transaction, so its fade-in animation 825 // can execute directly. 826 navBar.setOrientationChanging(false); 827 assertFalse(asyncRotationController.isTargetToken(navBar.mToken)); 828 assertNull(mDisplayContent.getAsyncRotationController()); 829 } 830 831 @Test testAppTransitionWithRotationChange()832 public void testAppTransitionWithRotationChange() { 833 final TestTransitionPlayer player = registerTestTransitionPlayer(); 834 final boolean useFixedRotation = !player.mController.useShellTransitionsRotation(); 835 if (useFixedRotation) { 836 testFixedRotationOpen(player); 837 } else { 838 testShellRotationOpen(player); 839 } 840 } 841 testShellRotationOpen(TestTransitionPlayer player)842 private void testShellRotationOpen(TestTransitionPlayer player) { 843 final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 844 makeWindowVisible(statusBar); 845 mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs); 846 final ActivityRecord app = createActivityRecord(mDisplayContent); 847 final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN); 848 app.mTransitionController.requestStartTransition(transition, app.getTask(), 849 null /* remoteTransition */, null /* displayChange */); 850 mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); 851 final int anyChanges = 1; 852 mDisplayContent.setLastHasContent(); 853 mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */); 854 transition.setKnownConfigChanges(mDisplayContent, anyChanges); 855 final AsyncRotationController asyncRotationController = 856 mDisplayContent.getAsyncRotationController(); 857 assertNotNull(asyncRotationController); 858 assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true); 859 860 statusBar.setOrientationChanging(true); 861 player.startTransition(); 862 // Non-app windows should not be collected. 863 assertFalse(statusBar.mToken.inTransition()); 864 assertTrue(app.getTask().inTransition()); 865 866 final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class); 867 final SurfaceControl leash = statusBar.mToken.getAnimationLeash(); 868 doReturn(true).when(leash).isValid(); 869 final SurfaceControl.TransactionCommittedListener transactionCommittedListener = 870 onRotationTransactionReady(player, startTransaction); 871 // The leash should be unrotated. 872 verify(startTransaction).setMatrix(eq(leash), any(), any()); 873 874 // The redrawn window will be faded in when the transition finishes. And because this test 875 // only use one non-activity window, the fade rotation controller should also be cleared. 876 statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING; 877 final SurfaceControl.Transaction postDrawTransaction = 878 mock(SurfaceControl.Transaction.class); 879 final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction, 880 Integer.MAX_VALUE); 881 assertFalse(layoutNeeded); 882 883 transactionCommittedListener.onTransactionCommitted(); 884 player.finish(); 885 // The controller should capture the draw transaction and merge it when preparing to run 886 // fade-in animation. 887 verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction)); 888 assertNull(mDisplayContent.getAsyncRotationController()); 889 } 890 testFixedRotationOpen(TestTransitionPlayer player)891 private void testFixedRotationOpen(TestTransitionPlayer player) { 892 final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 893 makeWindowVisible(statusBar); 894 mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs); 895 final ActivityRecord app = createActivityRecord(mDisplayContent); 896 final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN); 897 app.mTransitionController.requestStartTransition(transition, app.getTask(), 898 null /* remoteTransition */, null /* displayChange */); 899 app.mTransitionController.collectExistenceChange(app.getTask()); 900 mDisplayContent.setFixedRotationLaunchingAppUnchecked(app); 901 final AsyncRotationController asyncRotationController = 902 mDisplayContent.getAsyncRotationController(); 903 assertNotNull(asyncRotationController); 904 assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true); 905 assertTrue(app.getTask().inTransition()); 906 907 player.start(); 908 player.finish(); 909 app.getTask().finishSync(mWm.mTransactionFactory.get(), false /* cancel */); 910 911 // The open transition is finished. Continue to play seamless display change transition, 912 // so the previous async rotation controller should still exist. 913 mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); 914 mDisplayContent.setLastHasContent(); 915 mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */); 916 assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp()); 917 assertNotNull(mDisplayContent.getAsyncRotationController()); 918 919 // The app is still in transition, so the callback should be no-op. 920 mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app); 921 assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp()); 922 923 statusBar.setOrientationChanging(true); 924 player.startTransition(); 925 // Non-app windows should not be collected. 926 assertFalse(mDisplayContent.mTransitionController.isCollecting(statusBar.mToken)); 927 928 onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted(); 929 assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange( 930 mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation()); 931 player.finish(); 932 933 // The controller should be cleared if the target windows are drawn. 934 statusBar.finishDrawing(mWm.mTransactionFactory.get(), Integer.MAX_VALUE); 935 statusBar.setOrientationChanging(false); 936 assertNull(mDisplayContent.getAsyncRotationController()); 937 } 938 assertShouldFreezeInsetsPosition(AsyncRotationController controller, WindowState w, boolean freeze)939 private static void assertShouldFreezeInsetsPosition(AsyncRotationController controller, 940 WindowState w, boolean freeze) { 941 if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) { 942 // Non blast sync should never freeze insets position. 943 freeze = false; 944 } 945 assertEquals(freeze, controller.shouldFreezeInsetsPosition(w)); 946 } 947 948 @Test testDeferRotationForTransientLaunch()949 public void testDeferRotationForTransientLaunch() { 950 final TestTransitionPlayer player = registerTestTransitionPlayer(); 951 assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation()); 952 final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); 953 final ActivityRecord home = new ActivityBuilder(mAtm) 954 .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask()) 955 .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build(); 956 final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN); 957 final int prevRotation = mDisplayContent.getRotation(); 958 transition.setTransientLaunch(home, null /* restoreBelow */); 959 home.mTransitionController.requestStartTransition(transition, home.getTask(), 960 null /* remoteTransition */, null /* displayChange */); 961 transition.collectExistenceChange(home); 962 home.setVisibleRequested(true); 963 mDisplayContent.setFixedRotationLaunchingAppUnchecked(home); 964 doReturn(true).when(home).hasFixedRotationTransform(any()); 965 player.startTransition(); 966 player.onTransactionReady(mDisplayContent.getSyncTransaction()); 967 968 final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); 969 final RemoteDisplayChangeController displayChangeController = mDisplayContent 970 .mRemoteDisplayChangeController; 971 spyOn(displayRotation); 972 spyOn(displayChangeController); 973 doReturn(true).when(displayChangeController).isWaitingForRemoteDisplayChange(); 974 doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation( 975 anyInt() /* orientation */, anyInt() /* lastRotation */); 976 // Rotation update is skipped while the recents animation is running. 977 assertFalse(mDisplayContent.updateRotationUnchecked()); 978 assertEquals(SCREEN_ORIENTATION_UNSET, displayRotation.getLastOrientation()); 979 // Return to the app without fixed orientation from recents. 980 app.moveFocusableActivityToTop("test"); 981 player.finish(); 982 // The display should be updated to the changed orientation after the animation is finish. 983 assertNotEquals(mDisplayContent.getRotation(), prevRotation); 984 } 985 986 @Test testIntermediateVisibility()987 public void testIntermediateVisibility() { 988 final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); 989 final TransitionController controller = new TransitionController(mAtm, snapshotController, 990 mock(TransitionTracer.class)); 991 final ITransitionPlayer player = new ITransitionPlayer.Default(); 992 controller.registerTransitionPlayer(player, null /* playerProc */); 993 final Transition openTransition = controller.createTransition(TRANSIT_OPEN); 994 995 // Start out with task2 visible and set up a transition that closes task2 and opens task1 996 final Task task1 = createTask(mDisplayContent); 997 final ActivityRecord activity1 = createActivityRecord(task1); 998 activity1.setVisibleRequested(false); 999 activity1.setVisible(false); 1000 final Task task2 = createTask(mDisplayContent); 1001 makeTaskOrganized(task1, task2); 1002 final ActivityRecord activity2 = createActivityRecord(task1); 1003 activity2.setVisibleRequested(true); 1004 activity2.setVisible(true); 1005 1006 openTransition.collectExistenceChange(task1); 1007 openTransition.collectExistenceChange(activity1); 1008 openTransition.collectExistenceChange(task2); 1009 openTransition.collectExistenceChange(activity2); 1010 1011 activity1.setVisibleRequested(true); 1012 activity1.setVisible(true); 1013 activity2.setVisibleRequested(false); 1014 1015 // Using abort to force-finish the sync (since we can't wait for drawing in unit test). 1016 // We didn't call abort on the transition itself, so it will still run onTransactionReady 1017 // normally. 1018 mWm.mSyncEngine.abort(openTransition.getSyncId()); 1019 1020 // Before finishing openTransition, we are now going to simulate closing task1 to return 1021 // back to (open) task2. 1022 final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE); 1023 1024 closeTransition.collectExistenceChange(task1); 1025 closeTransition.collectExistenceChange(activity1); 1026 closeTransition.collectExistenceChange(task2); 1027 closeTransition.collectExistenceChange(activity2); 1028 1029 activity1.setVisibleRequested(false); 1030 activity2.setVisibleRequested(true); 1031 1032 openTransition.finishTransition(); 1033 1034 // We finished the openTransition. Even though activity1 is visibleRequested=false, since 1035 // the closeTransition animation hasn't played yet, make sure that we didn't commit 1036 // visible=false on activity1 since it needs to remain visible for the animation. 1037 assertTrue(activity1.isVisible()); 1038 assertTrue(activity2.isVisible()); 1039 1040 // Using abort to force-finish the sync (since we obviously can't wait for drawing). 1041 // We didn't call abort on the actual transition, so it will still run onTransactionReady 1042 // normally. 1043 mWm.mSyncEngine.abort(closeTransition.getSyncId()); 1044 1045 closeTransition.finishTransition(); 1046 1047 assertFalse(activity1.isVisible()); 1048 assertTrue(activity2.isVisible()); 1049 } 1050 1051 @Test testTransientLaunch()1052 public void testTransientLaunch() { 1053 final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); 1054 final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>(); 1055 final TransitionController controller = new TransitionController(mAtm, snapshotController, 1056 mock(TransitionTracer.class)) { 1057 @Override 1058 protected void dispatchLegacyAppTransitionFinished(ActivityRecord ar) { 1059 if (ar.mEnteringAnimation) { 1060 enteringAnimReports.add(ar); 1061 } 1062 super.dispatchLegacyAppTransitionFinished(ar); 1063 } 1064 }; 1065 final ITransitionPlayer player = new ITransitionPlayer.Default(); 1066 controller.registerTransitionPlayer(player, null /* playerProc */); 1067 final Transition openTransition = controller.createTransition(TRANSIT_OPEN); 1068 1069 // Start out with task2 visible and set up a transition that closes task2 and opens task1 1070 final Task task1 = createTask(mDisplayContent); 1071 final ActivityRecord activity1 = createActivityRecord(task1); 1072 activity1.setVisibleRequested(false); 1073 activity1.setVisible(false); 1074 final Task task2 = createTask(mDisplayContent); 1075 makeTaskOrganized(task1, task2); 1076 final ActivityRecord activity2 = createActivityRecord(task2); 1077 activity2.setVisibleRequested(true); 1078 activity2.setVisible(true); 1079 1080 openTransition.collectExistenceChange(task1); 1081 openTransition.collectExistenceChange(activity1); 1082 openTransition.collectExistenceChange(task2); 1083 openTransition.collectExistenceChange(activity2); 1084 1085 activity1.setVisibleRequested(true); 1086 activity1.setVisible(true); 1087 activity2.setVisibleRequested(false); 1088 1089 // Using abort to force-finish the sync (since we can't wait for drawing in unit test). 1090 // We didn't call abort on the transition itself, so it will still run onTransactionReady 1091 // normally. 1092 mWm.mSyncEngine.abort(openTransition.getSyncId()); 1093 1094 verify(snapshotController, times(1)).recordTaskSnapshot(eq(task2), eq(false)); 1095 1096 openTransition.finishTransition(); 1097 1098 // We are now going to simulate closing task1 to return back to (open) task2. 1099 final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE); 1100 1101 closeTransition.collectExistenceChange(task1); 1102 closeTransition.collectExistenceChange(activity1); 1103 closeTransition.collectExistenceChange(task2); 1104 closeTransition.collectExistenceChange(activity2); 1105 closeTransition.setTransientLaunch(activity2, null /* restoreBelow */); 1106 1107 activity1.setVisibleRequested(false); 1108 activity2.setVisibleRequested(true); 1109 activity2.setVisible(true); 1110 1111 // Using abort to force-finish the sync (since we obviously can't wait for drawing). 1112 // We didn't call abort on the actual transition, so it will still run onTransactionReady 1113 // normally. 1114 mWm.mSyncEngine.abort(closeTransition.getSyncId()); 1115 1116 // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be 1117 // called until finish). 1118 verify(snapshotController, times(0)).recordTaskSnapshot(eq(task1), eq(false)); 1119 1120 enteringAnimReports.clear(); 1121 closeTransition.finishTransition(); 1122 1123 verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false)); 1124 assertTrue(enteringAnimReports.contains(activity2)); 1125 } 1126 1127 @Test testNotReadyPushPop()1128 public void testNotReadyPushPop() { 1129 final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); 1130 final TransitionController controller = new TransitionController(mAtm, snapshotController, 1131 mock(TransitionTracer.class)); 1132 final ITransitionPlayer player = new ITransitionPlayer.Default(); 1133 controller.registerTransitionPlayer(player, null /* playerProc */); 1134 final Transition openTransition = controller.createTransition(TRANSIT_OPEN); 1135 1136 // Start out with task2 visible and set up a transition that closes task2 and opens task1 1137 final Task task1 = createTask(mDisplayContent); 1138 openTransition.collectExistenceChange(task1); 1139 1140 assertFalse(openTransition.allReady()); 1141 1142 openTransition.setAllReady(); 1143 1144 openTransition.deferTransitionReady(); 1145 assertFalse(openTransition.allReady()); 1146 1147 openTransition.continueTransitionReady(); 1148 assertTrue(openTransition.allReady()); 1149 } 1150 1151 @Test testIsBehindStartingWindowChange()1152 public void testIsBehindStartingWindowChange() { 1153 final Transition transition = createTestTransition(TRANSIT_OPEN); 1154 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1155 final ArraySet<WindowContainer> participants = transition.mParticipants; 1156 1157 final Task task = createTask(mDisplayContent); 1158 final ActivityRecord activity0 = createActivityRecord(task); 1159 final ActivityRecord activity1 = createActivityRecord(task); 1160 doReturn(true).when(activity1).hasStartingWindow(); 1161 1162 // Start states. 1163 changes.put(activity0, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 1164 changes.put(activity1, new Transition.ChangeInfo(false /* vis */, false /* exChg */)); 1165 // End states. 1166 activity0.setVisibleRequested(false); 1167 activity1.setVisibleRequested(true); 1168 1169 participants.add(activity0); 1170 participants.add(activity1); 1171 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1172 participants, changes); 1173 final TransitionInfo info = Transition.calculateTransitionInfo( 1174 transition.mType, 0 /* flags */, targets, changes, mMockT); 1175 1176 // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting 1177 // window should cover the whole Task. 1178 assertEquals(2, info.getChanges().size()); 1179 assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)); 1180 assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)); 1181 1182 } 1183 1184 @Test testFlagInTaskWithEmbeddedActivity()1185 public void testFlagInTaskWithEmbeddedActivity() { 1186 final Transition transition = createTestTransition(TRANSIT_OPEN); 1187 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1188 final ArraySet<WindowContainer> participants = transition.mParticipants; 1189 1190 final Task task = createTask(mDisplayContent); 1191 final ActivityRecord nonEmbeddedActivity = createActivityRecord(task); 1192 assertFalse(nonEmbeddedActivity.isEmbedded()); 1193 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1194 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1195 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1196 final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) 1197 .setParentTask(task) 1198 .createActivityCount(2) 1199 .setOrganizer(organizer) 1200 .build(); 1201 final ActivityRecord closingActivity = embeddedTf.getBottomMostActivity(); 1202 final ActivityRecord openingActivity = embeddedTf.getTopMostActivity(); 1203 // Start states. 1204 changes.put(embeddedTf, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 1205 changes.put(closingActivity, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 1206 changes.put(openingActivity, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 1207 changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */, 1208 false /* exChg */)); 1209 // End states. 1210 closingActivity.setVisibleRequested(false); 1211 openingActivity.setVisibleRequested(true); 1212 nonEmbeddedActivity.setVisibleRequested(false); 1213 1214 participants.add(closingActivity); 1215 participants.add(openingActivity); 1216 participants.add(nonEmbeddedActivity); 1217 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1218 participants, changes); 1219 final TransitionInfo info = Transition.calculateTransitionInfo( 1220 transition.mType, 0 /* flags */, targets, changes, mMockT); 1221 1222 // All windows in the Task should have FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY because the Task 1223 // contains embedded activity. 1224 assertEquals(3, info.getChanges().size()); 1225 assertTrue(info.getChanges().get(0).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)); 1226 assertTrue(info.getChanges().get(1).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)); 1227 assertTrue(info.getChanges().get(2).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)); 1228 } 1229 1230 @Test testFlagFillsTask_embeddingNotFillingTask()1231 public void testFlagFillsTask_embeddingNotFillingTask() { 1232 final Transition transition = createTestTransition(TRANSIT_OPEN); 1233 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1234 final ArraySet<WindowContainer> participants = transition.mParticipants; 1235 1236 final Task task = createTask(mDisplayContent); 1237 final Rect taskBounds = new Rect(0, 0, 500, 1000); 1238 task.getConfiguration().windowConfiguration.setBounds(taskBounds); 1239 final ActivityRecord nonEmbeddedActivity = createActivityRecord(task); 1240 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1241 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1242 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1243 final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) 1244 .setParentTask(task) 1245 .createActivityCount(1) 1246 .setOrganizer(organizer) 1247 .build(); 1248 final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity(); 1249 // Start states. 1250 changes.put(task, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 1251 changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */, 1252 false /* exChg */)); 1253 changes.put(embeddedTf, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); 1254 // End states. 1255 nonEmbeddedActivity.setVisibleRequested(false); 1256 embeddedActivity.setVisibleRequested(true); 1257 embeddedTf.setBounds(new Rect(0, 0, 500, 500)); 1258 1259 participants.add(nonEmbeddedActivity); 1260 participants.add(embeddedTf); 1261 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1262 participants, changes); 1263 final TransitionInfo info = Transition.calculateTransitionInfo( 1264 transition.mType, 0 /* flags */, targets, changes, mMockT); 1265 1266 // The embedded with bounds overridden should not have the flag. 1267 assertEquals(2, info.getChanges().size()); 1268 assertFalse(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); 1269 assertEquals(embeddedTf.getBounds(), info.getChanges().get(0).getEndAbsBounds()); 1270 assertFalse(info.getChanges().get(1).hasFlags(FLAG_FILLS_TASK)); 1271 } 1272 1273 @Test testFlagFillsTask_openActivityFillingTask()1274 public void testFlagFillsTask_openActivityFillingTask() { 1275 final Transition transition = createTestTransition(TRANSIT_OPEN); 1276 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1277 final ArraySet<WindowContainer> participants = transition.mParticipants; 1278 1279 final Task task = createTask(mDisplayContent); 1280 final Rect taskBounds = new Rect(0, 0, 500, 1000); 1281 task.getConfiguration().windowConfiguration.setBounds(taskBounds); 1282 final ActivityRecord activity = createActivityRecord(task); 1283 // Start states: set bounds to make sure the start bounds is ignored if it is not visible. 1284 activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); 1285 activity.setVisibleRequested(false); 1286 changes.put(activity, new Transition.ChangeInfo(activity)); 1287 // End states: reset bounds to fill Task. 1288 activity.getConfiguration().windowConfiguration.setBounds(taskBounds); 1289 activity.setVisibleRequested(true); 1290 1291 participants.add(activity); 1292 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1293 participants, changes); 1294 final TransitionInfo info = Transition.calculateTransitionInfo( 1295 transition.mType, 0 /* flags */, targets, changes, mMockT); 1296 1297 // Opening activity that is filling Task after transition should have the flag. 1298 assertEquals(1, info.getChanges().size()); 1299 assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); 1300 } 1301 1302 @Test testFlagFillsTask_closeActivityFillingTask()1303 public void testFlagFillsTask_closeActivityFillingTask() { 1304 final Transition transition = createTestTransition(TRANSIT_CLOSE); 1305 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1306 final ArraySet<WindowContainer> participants = transition.mParticipants; 1307 1308 final Task task = createTask(mDisplayContent); 1309 final Rect taskBounds = new Rect(0, 0, 500, 1000); 1310 task.getConfiguration().windowConfiguration.setBounds(taskBounds); 1311 final ActivityRecord activity = createActivityRecord(task); 1312 // Start states: fills Task without override. 1313 activity.setVisibleRequested(true); 1314 changes.put(activity, new Transition.ChangeInfo(activity)); 1315 // End states: set bounds to make sure the start bounds is ignored if it is not visible. 1316 activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); 1317 activity.setVisibleRequested(false); 1318 1319 participants.add(activity); 1320 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1321 participants, changes); 1322 final TransitionInfo info = Transition.calculateTransitionInfo( 1323 transition.mType, 0 /* flags */, targets, changes, mMockT); 1324 1325 // Closing activity that is filling Task before transition should have the flag. 1326 assertEquals(1, info.getChanges().size()); 1327 assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK)); 1328 } 1329 1330 @Test testReparentChangeLastParent()1331 public void testReparentChangeLastParent() { 1332 final Transition transition = createTestTransition(TRANSIT_CHANGE); 1333 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1334 final ArraySet<WindowContainer> participants = transition.mParticipants; 1335 1336 // Reparent activity in transition. 1337 final Task lastParent = createTask(mDisplayContent); 1338 final Task newParent = createTask(mDisplayContent); 1339 final ActivityRecord activity = createActivityRecord(lastParent); 1340 activity.setVisibleRequested(true); 1341 // Skip manipulate the SurfaceControl. 1342 doNothing().when(activity).setDropInputMode(anyInt()); 1343 changes.put(activity, new Transition.ChangeInfo(activity)); 1344 activity.reparent(newParent, POSITION_TOP); 1345 activity.setVisibleRequested(false); 1346 1347 participants.add(activity); 1348 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1349 participants, changes); 1350 final TransitionInfo info = Transition.calculateTransitionInfo( 1351 transition.mType, 0 /* flags */, targets, changes, mMockT); 1352 1353 // Change contains last parent info. 1354 assertEquals(1, info.getChanges().size()); 1355 assertEquals(lastParent.mRemoteToken.toWindowContainerToken(), 1356 info.getChanges().get(0).getLastParent()); 1357 } 1358 1359 @Test testIncludeEmbeddedActivityReparent()1360 public void testIncludeEmbeddedActivityReparent() { 1361 final Transition transition = createTestTransition(TRANSIT_OPEN); 1362 final Task task = createTask(mDisplayContent); 1363 task.setBounds(new Rect(0, 0, 2000, 1000)); 1364 final ActivityRecord activity = createActivityRecord(task); 1365 activity.setVisibleRequested(true); 1366 // Skip manipulate the SurfaceControl. 1367 doNothing().when(activity).setDropInputMode(anyInt()); 1368 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1369 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1370 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1371 final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) 1372 .setParentTask(task) 1373 .setOrganizer(organizer) 1374 .build(); 1375 // TaskFragment with different bounds from Task. 1376 embeddedTf.setBounds(new Rect(0, 0, 1000, 1000)); 1377 1378 // Start states. 1379 transition.collect(activity); 1380 transition.collectExistenceChange(embeddedTf); 1381 1382 // End states. 1383 activity.reparent(embeddedTf, POSITION_TOP); 1384 1385 // Verify that both activity and TaskFragment are included. 1386 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1387 transition.mParticipants, transition.mChanges); 1388 assertTrue(targets.contains(embeddedTf)); 1389 assertTrue(targets.contains(activity)); 1390 } 1391 1392 @Test testChangeSetBackgroundColor()1393 public void testChangeSetBackgroundColor() { 1394 final Transition transition = createTestTransition(TRANSIT_CHANGE); 1395 final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; 1396 final ArraySet<WindowContainer> participants = transition.mParticipants; 1397 1398 // Test background color for Activity and embedded TaskFragment. 1399 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1400 mAtm.mTaskFragmentOrganizerController.registerOrganizer( 1401 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 1402 final Task task = createTask(mDisplayContent); 1403 final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer); 1404 final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity(); 1405 final ActivityRecord nonEmbeddedActivity = createActivityRecord(task); 1406 final ActivityManager.TaskDescription taskDescription = 1407 new ActivityManager.TaskDescription.Builder() 1408 .setBackgroundColor(Color.YELLOW) 1409 .build(); 1410 task.setTaskDescription(taskDescription); 1411 1412 // Start states: 1413 embeddedActivity.setVisibleRequested(true); 1414 nonEmbeddedActivity.setVisibleRequested(false); 1415 changes.put(embeddedTf, new Transition.ChangeInfo(embeddedTf)); 1416 changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity)); 1417 // End states: 1418 embeddedActivity.setVisibleRequested(false); 1419 nonEmbeddedActivity.setVisibleRequested(true); 1420 1421 participants.add(embeddedTf); 1422 participants.add(nonEmbeddedActivity); 1423 final ArrayList<WindowContainer> targets = Transition.calculateTargets( 1424 participants, changes); 1425 final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, 1426 0 /* flags */, targets, changes, mMockT); 1427 1428 // Background color should be set on both Activity and embedded TaskFragment. 1429 final int expectedBackgroundColor = ColorUtils.setAlphaComponent( 1430 taskDescription.getBackgroundColor(), 255); 1431 assertEquals(2, info.getChanges().size()); 1432 assertEquals(expectedBackgroundColor, info.getChanges().get(0).getBackgroundColor()); 1433 assertEquals(expectedBackgroundColor, info.getChanges().get(1).getBackgroundColor()); 1434 } 1435 1436 @Test testTransitionVisibleChange()1437 public void testTransitionVisibleChange() { 1438 registerTestTransitionPlayer(); 1439 final ActivityRecord app = createActivityRecord(mDisplayContent); 1440 final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */, 1441 app.mTransitionController, mWm.mSyncEngine); 1442 app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE); 1443 final ArrayList<WindowContainer> freezeCalls = new ArrayList<>(); 1444 transition.setContainerFreezer(new Transition.IContainerFreezer() { 1445 @Override 1446 public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) { 1447 freezeCalls.add(wc); 1448 return true; 1449 } 1450 1451 @Override 1452 public void cleanUp(SurfaceControl.Transaction t) { 1453 } 1454 }); 1455 final Task task = app.getTask(); 1456 transition.collect(task); 1457 final Rect bounds = new Rect(task.getBounds()); 1458 Configuration c = new Configuration(task.getRequestedOverrideConfiguration()); 1459 bounds.inset(10, 10); 1460 c.windowConfiguration.setBounds(bounds); 1461 task.onRequestedOverrideConfigurationChanged(c); 1462 assertTrue(freezeCalls.contains(task)); 1463 transition.abort(); 1464 } 1465 1466 @Test testDeferTransitionReady_deferStartedTransition()1467 public void testDeferTransitionReady_deferStartedTransition() { 1468 final Transition transition = createTestTransition(TRANSIT_OPEN); 1469 transition.setAllReady(); 1470 transition.start(); 1471 1472 assertTrue(mSyncEngine.isReady(transition.getSyncId())); 1473 1474 transition.deferTransitionReady(); 1475 1476 // Both transition ready tracker and sync engine should be deferred. 1477 assertFalse(transition.allReady()); 1478 assertFalse(mSyncEngine.isReady(transition.getSyncId())); 1479 1480 transition.continueTransitionReady(); 1481 1482 assertTrue(transition.allReady()); 1483 assertTrue(mSyncEngine.isReady(transition.getSyncId())); 1484 } 1485 1486 @Test testVisibleChange_snapshot()1487 public void testVisibleChange_snapshot() { 1488 registerTestTransitionPlayer(); 1489 final ActivityRecord app = createActivityRecord(mDisplayContent); 1490 final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, 1491 app.mTransitionController, mWm.mSyncEngine); 1492 app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE); 1493 final SurfaceControl mockSnapshot = mock(SurfaceControl.class); 1494 transition.setContainerFreezer(new Transition.IContainerFreezer() { 1495 @Override 1496 public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) { 1497 Objects.requireNonNull(transition.mChanges.get(wc)).mSnapshot = mockSnapshot; 1498 return true; 1499 } 1500 1501 @Override 1502 public void cleanUp(SurfaceControl.Transaction t) { 1503 } 1504 }); 1505 final Task task = app.getTask(); 1506 transition.collect(task); 1507 final Rect bounds = new Rect(task.getBounds()); 1508 Configuration c = new Configuration(task.getRequestedOverrideConfiguration()); 1509 bounds.inset(10, 10); 1510 c.windowConfiguration.setBounds(bounds); 1511 task.onRequestedOverrideConfigurationChanged(c); 1512 1513 ArrayList<WindowContainer> targets = Transition.calculateTargets( 1514 transition.mParticipants, transition.mChanges); 1515 TransitionInfo info = Transition.calculateTransitionInfo( 1516 TRANSIT_CHANGE, 0, targets, transition.mChanges, mMockT); 1517 assertEquals(mockSnapshot, 1518 info.getChange(task.mRemoteToken.toWindowContainerToken()).getSnapshot()); 1519 transition.abort(); 1520 } 1521 1522 @Test testCollectReparentChange()1523 public void testCollectReparentChange() { 1524 registerTestTransitionPlayer(); 1525 1526 // Reparent activity in transition. 1527 final Task lastParent = createTask(mDisplayContent); 1528 final Task newParent = createTask(mDisplayContent); 1529 final ActivityRecord activity = createActivityRecord(lastParent); 1530 doReturn(true).when(lastParent).shouldRemoveSelfOnLastChildRemoval(); 1531 doNothing().when(activity).setDropInputMode(anyInt()); 1532 activity.setVisibleRequested(true); 1533 1534 final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, 1535 activity.mTransitionController, mWm.mSyncEngine); 1536 activity.mTransitionController.moveToCollecting(transition); 1537 transition.collect(activity); 1538 activity.reparent(newParent, POSITION_TOP); 1539 1540 // ChangeInfo#mCommonAncestor should be set after reparent. 1541 final Transition.ChangeInfo change = transition.mChanges.get(activity); 1542 assertEquals(newParent.getDisplayArea(), change.mCommonAncestor); 1543 } 1544 makeTaskOrganized(Task... tasks)1545 private static void makeTaskOrganized(Task... tasks) { 1546 final ITaskOrganizer organizer = mock(ITaskOrganizer.class); 1547 for (Task t : tasks) { 1548 t.mTaskOrganizer = organizer; 1549 } 1550 } 1551 makeDisplayAreaOrganized(WindowContainer<?> from, WindowContainer<?> end)1552 private static void makeDisplayAreaOrganized(WindowContainer<?> from, 1553 WindowContainer<?> end) { 1554 final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class); 1555 while (from != null && from != end) { 1556 if (from.asDisplayArea() != null) { 1557 from.asDisplayArea().mOrganizer = organizer; 1558 } 1559 from = from.getParent(); 1560 } 1561 if (end.asDisplayArea() != null) { 1562 end.asDisplayArea().mOrganizer = organizer; 1563 } 1564 } 1565 1566 /** Fill the change map with all the parents of top. Change maps are usually fully populated */ fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, WindowContainer top)1567 private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes, 1568 WindowContainer top) { 1569 for (WindowContainer curr = top.getParent(); curr != null; curr = curr.getParent()) { 1570 changes.put(curr, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); 1571 } 1572 } 1573 onRotationTransactionReady( TestTransitionPlayer player, SurfaceControl.Transaction startTransaction)1574 private static SurfaceControl.TransactionCommittedListener onRotationTransactionReady( 1575 TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) { 1576 final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor = 1577 ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class); 1578 player.onTransactionReady(startTransaction); 1579 verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture()); 1580 return listenerCaptor.getValue(); 1581 } 1582 } 1583