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