1 /* 2 * Copyright (C) 2016 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 package com.android.car.am; 17 18 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 19 20 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import static org.junit.Assert.assertEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertNotNull; 28 import static org.junit.Assert.assertThrows; 29 import static org.junit.Assert.assertTrue; 30 import static org.mockito.ArgumentMatchers.anyInt; 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.Mockito.verify; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.app.Activity; 37 import android.app.ActivityManager; 38 import android.app.ActivityOptions; 39 import android.app.Instrumentation.ActivityMonitor; 40 import android.app.TaskInfo; 41 import android.car.test.util.DisplayUtils.VirtualDisplaySession; 42 import android.content.ComponentName; 43 import android.content.Context; 44 import android.content.Intent; 45 import android.graphics.Rect; 46 import android.os.Binder; 47 import android.os.Bundle; 48 import android.os.IBinder; 49 import android.os.SystemClock; 50 import android.util.Log; 51 import android.view.Display; 52 import android.view.SurfaceControl; 53 54 import androidx.test.filters.MediumTest; 55 56 import com.android.compatibility.common.util.PollingCheck; 57 import com.android.wm.shell.ShellTaskOrganizer; 58 import com.android.wm.shell.common.HandlerExecutor; 59 import com.android.wm.shell.common.SyncTransactionQueue; 60 import com.android.wm.shell.common.TransactionPool; 61 import com.android.wm.shell.fullscreen.FullscreenTaskListener; 62 63 import com.google.common.truth.Expect; 64 65 import org.junit.After; 66 import org.junit.Before; 67 import org.junit.Rule; 68 import org.junit.Test; 69 import org.junit.rules.TestName; 70 import org.junit.runner.RunWith; 71 import org.mockito.ArgumentCaptor; 72 import org.mockito.Captor; 73 import org.mockito.Mock; 74 import org.mockito.junit.MockitoJUnitRunner; 75 76 import java.util.concurrent.CopyOnWriteArrayList; 77 import java.util.concurrent.CountDownLatch; 78 import java.util.concurrent.TimeUnit; 79 import java.util.function.BooleanSupplier; 80 81 @RunWith(MockitoJUnitRunner.class) 82 @MediumTest 83 public class CarActivityServiceTaskMonitorUnitTest { 84 private static final String TAG = CarActivityServiceTaskMonitorUnitTest.class.getSimpleName(); 85 86 private static final long ACTIVITY_TIMEOUT_MS = 5000; 87 private static final long NO_ACTIVITY_TIMEOUT_MS = 1000; 88 private static final long DEFAULT_TIMEOUT_MS = 10_000; 89 private static final int SLEEP_MS = 50; 90 private static final long SHORT_MIRRORING_TOKEN_TIMEOUT_MS = 100; 91 92 private static final CopyOnWriteArrayList<TempActivity> sTestActivities = 93 new CopyOnWriteArrayList<>(); 94 95 private CarActivityService mService; 96 @Mock 97 private IBinder mToken; 98 @Captor 99 ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor; 100 101 private ShellTaskOrganizer mTaskOrganizer; 102 private FullscreenTaskListener mFullscreenTaskListener; 103 104 private final ComponentName mActivityA = new ComponentName(getTestContext(), ActivityA.class); 105 private final ComponentName mActivityB = new ComponentName(getTestContext(), ActivityB.class); 106 private final ComponentName mActivityC = new ComponentName(getTestContext(), ActivityC.class); 107 private final ComponentName mBlockingActivity = new ComponentName( 108 getTestContext(), BlockingActivity.class); 109 110 @Rule 111 public final Expect expect = Expect.create(); 112 @Rule 113 public TestName mTestName = new TestName(); 114 115 @Before setUp()116 public void setUp() throws Exception { 117 long timeOutMs = DEFAULT_TIMEOUT_MS; 118 if (mTestName.getMethodName().contains("ExpiredToken")) { 119 timeOutMs = SHORT_MIRRORING_TOKEN_TIMEOUT_MS; 120 } 121 mService = new CarActivityService(getContext(), timeOutMs); 122 mService.init(); 123 mService.registerTaskMonitor(mToken); 124 setUpTaskOrganizer(); 125 } 126 127 @After tearDown()128 public void tearDown() throws InterruptedException { 129 tearDownTaskOrganizer(); 130 for (TempActivity activity : sTestActivities) { 131 activity.finish(); 132 activity.waitForDestroyed(); 133 } 134 sTestActivities.clear(); 135 mService.unregisterTaskMonitor(mToken); 136 // Any remaining ActivityListeners will be flushed in release(). 137 mService.release(); 138 mService = null; 139 } 140 setUpTaskOrganizer()141 private void setUpTaskOrganizer() throws Exception { 142 Context context = getContext(); 143 HandlerExecutor mExecutor = new HandlerExecutor(context.getMainThreadHandler()); 144 mTaskOrganizer = new ShellTaskOrganizer(mExecutor); 145 TransactionPool transactionPool = new TransactionPool(); 146 SyncTransactionQueue syncQueue = new SyncTransactionQueue(transactionPool, mExecutor); 147 mFullscreenTaskListener = new TestTaskListener(syncQueue); 148 mTaskOrganizer.addListenerForType(mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN); 149 mTaskOrganizer.registerOrganizer(); 150 } 151 tearDownTaskOrganizer()152 private void tearDownTaskOrganizer() { 153 mTaskOrganizer.removeListener(mFullscreenTaskListener); 154 mTaskOrganizer.unregisterOrganizer(); 155 } 156 157 private class TestTaskListener extends FullscreenTaskListener { TestTaskListener(SyncTransactionQueue syncQueue)158 TestTaskListener(SyncTransactionQueue syncQueue) { 159 super(syncQueue); 160 } 161 162 @Override onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)163 public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { 164 super.onTaskAppeared(taskInfo, leash); 165 mService.onTaskAppeared(mToken, taskInfo, leash); 166 } 167 168 @Override onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)169 public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { 170 super.onTaskInfoChanged(taskInfo); 171 mService.onTaskInfoChanged(mToken, taskInfo); 172 } 173 174 @Override onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)175 public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { 176 super.onTaskVanished(taskInfo); 177 mService.onTaskVanished(mToken, taskInfo); 178 } 179 } 180 181 @Test testActivityCameOnTop()182 public void testActivityCameOnTop() throws Exception { 183 startActivityAndAssertCameOnTop(mActivityA); 184 185 startActivityAndAssertCameOnTop(mActivityB); 186 } 187 188 @Test testActivityChangedInBackstackOnTaskInfoChanged()189 public void testActivityChangedInBackstackOnTaskInfoChanged() throws Exception { 190 FilteredListener listener = startActivityAndAssertCameOnTop(mActivityA); 191 192 // When some activity is launched from another, the baseIntent of the activity becomes 193 // the launching activity due to which an onActivityLaunched callback is received. The 194 // purpose of launching home here is that the baseIntent is not set and the correct 195 // onActivityChanged callback is received. 196 launchHomeScreenUsingIntent(); 197 198 listener.assertTopTaskActivityChangedInBackstack(); 199 } 200 launchHomeScreenUsingIntent()201 private void launchHomeScreenUsingIntent() { 202 Intent intent = new Intent(Intent.ACTION_MAIN) 203 .addCategory(Intent.CATEGORY_HOME) 204 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 205 getTestContext().startActivity(intent); 206 } 207 208 @Test testMultipleActivityListeners()209 public void testMultipleActivityListeners() throws Exception { 210 FilteredListener listener1 = new FilteredListener(mActivityA); 211 mService.registerActivityListener(listener1); 212 FilteredListener listener2 = new FilteredListener(mActivityA); 213 mService.registerActivityListener(listener2); 214 215 startActivity(mActivityA, Display.DEFAULT_DISPLAY); 216 217 listener2.assertTopTaskActivityCameOnTop(); 218 assertThat(listener1.mActivityCameOnTop.getCount()).isEqualTo(0); 219 } 220 221 @Test testUnregisterActivityListener()222 public void testUnregisterActivityListener() throws Exception { 223 FilteredListener listener1 = new FilteredListener(mActivityA); 224 mService.registerActivityListener(listener1); 225 FilteredListener listener2 = new FilteredListener(mActivityA); 226 mService.registerActivityListener(listener2); 227 mService.unregisterActivityListener(listener1); 228 229 startActivity(mActivityA, Display.DEFAULT_DISPLAY); 230 231 listener2.assertTopTaskActivityCameOnTop(); 232 assertThat(listener1.mActivityCameOnTop.getCount()).isEqualTo(1); 233 } 234 235 @Test testDeathRecipientIsSet()236 public void testDeathRecipientIsSet() throws Exception { 237 FilteredListener listenerA = new FilteredListener(mActivityA); 238 mService.registerActivityListener(listenerA); 239 240 verify(mToken).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); 241 } 242 243 @Test testBinderDied_cleansUpDeathRecipient()244 public void testBinderDied_cleansUpDeathRecipient() throws Exception { 245 FilteredListener listenerA = new FilteredListener(mActivityA); 246 mService.registerActivityListener(listenerA); 247 248 verify(mToken).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); 249 mDeathRecipientCaptor.getValue().binderDied(); 250 251 // Checks if binderDied() will clean-up the death recipient. 252 verify(mToken).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt()); 253 254 startActivity(mActivityA); 255 // Starting a Activity shouldn't trigger the listener since the token is invalid. 256 assertWithMessage("Shouldn't trigger the ActivityListener") 257 .that(listenerA.waitForTopTaskActivityCameOnTop(NO_ACTIVITY_TIMEOUT_MS)).isFalse(); 258 } 259 260 @Test testActivityBlocking()261 public void testActivityBlocking() throws Exception { 262 Intent blockingIntent = new Intent().setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 263 blockingIntent.setComponent(mBlockingActivity); 264 265 // start a deny listed activity 266 FilteredListener listenerDenyListed = startActivityAndAssertCameOnTop(mActivityC); 267 268 // Instead of start activity, invoke blockActivity. 269 FilteredListener listenerBlocking = new FilteredListener(mBlockingActivity); 270 mService.registerActivityListener(listenerBlocking); 271 mService.blockActivity(listenerDenyListed.mTopTask, blockingIntent); 272 listenerBlocking.assertTopTaskActivityCameOnTop(); 273 } 274 275 @Test testRemovesFromTopTasks()276 public void testRemovesFromTopTasks() throws Exception { 277 FilteredListener listenerA = new FilteredListener(mActivityA); 278 mService.registerActivityListener(listenerA); 279 Activity launchedActivity = startActivity(mActivityA); 280 listenerA.assertTopTaskActivityCameOnTop(); 281 assertTrue(topTasksHasComponent(mActivityA)); 282 283 getInstrumentation().runOnMainSync(launchedActivity::finish); 284 listenerA.assertTopTaskActivityChangedInBackstack(); 285 } 286 287 @Test testGetTopTasksOnMultiDisplay()288 public void testGetTopTasksOnMultiDisplay() throws Exception { 289 // TaskOrganizer gets the callbacks only on the tasks launched in the actual Surface. 290 try (VirtualDisplaySession session = new VirtualDisplaySession()) { 291 int virtualDisplayId = session.createDisplayWithDefaultDisplayMetricsAndWait( 292 getTestContext(), /* isPrivate= */ false).getDisplayId(); 293 294 startActivityAndAssertCameOnTop(mActivityA); 295 assertTrue(topTasksHasComponent(mActivityA)); 296 297 startActivityAndAssertCameOnTop(mActivityB, virtualDisplayId); 298 assertTrue(topTasksHasComponent(mActivityB)); 299 assertTrue(topTasksHasComponent(mActivityA)); 300 301 startActivityAndAssertCameOnTop(mActivityC, virtualDisplayId); 302 assertTrue(topTasksHasComponent(mActivityC)); 303 assertFalse(topTasksHasComponent(mActivityB)); 304 assertTrue(topTasksHasComponent(mActivityA)); 305 } 306 } 307 308 @Test testGetTopTasksOnDefaultDisplay()309 public void testGetTopTasksOnDefaultDisplay() throws Exception { 310 startActivityAndAssertCameOnTop(mActivityA); 311 assertTrue(topTasksHasComponent(mActivityA)); 312 313 startActivityAndAssertCameOnTop(mActivityB); 314 assertTrue(topTasksHasComponent(mActivityB)); 315 assertFalse(topTasksHasComponent(mActivityA)); 316 } 317 318 @Test testGetTaskInfoForTopActivity()319 public void testGetTaskInfoForTopActivity() throws Exception { 320 startActivityAndAssertCameOnTop(mActivityA); 321 322 TaskInfo taskInfo = mService.getTaskInfoForTopActivity(mActivityA); 323 assertNotNull(taskInfo); 324 assertEquals(mActivityA, taskInfo.topActivity); 325 } 326 327 @Test testRestartTask()328 public void testRestartTask() throws Exception { 329 startActivityAndAssertCameOnTop(mActivityA); 330 331 startActivityAndAssertCameOnTop(mActivityB); 332 333 FilteredListener listenerRestartA = new FilteredListener(mActivityA); 334 mService.registerActivityListener(listenerRestartA); 335 336 // ActivityA and ActivityB are in the same package, so ActivityA becomes the root task of 337 // ActivityB, so when we restarts ActivityB, it'll start ActivityA. 338 TaskInfo taskInfo = mService.getTaskInfoForTopActivity(mActivityB); 339 mService.restartTask(taskInfo.taskId); 340 341 listenerRestartA.assertTopTaskActivityCameOnTop(); 342 } 343 344 @Test testCreateMirroredToken_throwsExceptionForNonExistentTask()345 public void testCreateMirroredToken_throwsExceptionForNonExistentTask() { 346 int nonExistentTaskId = -999; 347 assertThrows(IllegalArgumentException.class, 348 () -> mService.createTaskMirroringToken(nonExistentTaskId)); 349 } 350 351 @Test testCreateMirroredToken_returnsToken()352 public void testCreateMirroredToken_returnsToken() throws Exception { 353 FilteredListener listenerA = startActivityAndAssertCameOnTop(mActivityA); 354 355 IBinder token = mService.createTaskMirroringToken(listenerA.mTopTask.taskId); 356 assertThat(token).isNotNull(); 357 } 358 359 @Test testGetMirroredSurface_throwsExceptionForInvalidToken()360 public void testGetMirroredSurface_throwsExceptionForInvalidToken() { 361 IBinder invalidToken = new Binder(); 362 Rect outBounds = new Rect(); 363 assertThrows(IllegalArgumentException.class, 364 () -> mService.getMirroredSurface(invalidToken, outBounds)); 365 } 366 367 @Test testGetMirroredSurface_throwsExceptionForForgedToken()368 public void testGetMirroredSurface_throwsExceptionForForgedToken() { 369 CarActivityService fakeService = new CarActivityService(getContext()); 370 IBinder forgedToken = fakeService.createDisplayMirroringToken(Display.DEFAULT_DISPLAY); 371 Rect outBounds = new Rect(); 372 assertThrows(IllegalArgumentException.class, 373 () -> mService.getMirroredSurface(forgedToken, outBounds)); 374 } 375 376 @Test testGetMirroredSurface_throwsExceptionForExpiredToken()377 public void testGetMirroredSurface_throwsExceptionForExpiredToken() throws Exception { 378 FilteredListener listenerA = startActivityAndAssertCameOnTop(mActivityA); 379 380 IBinder token = mService.createTaskMirroringToken(listenerA.mTopTask.taskId); 381 Rect outBounds = new Rect(); 382 383 SystemClock.sleep(SHORT_MIRRORING_TOKEN_TIMEOUT_MS * 2); 384 385 assertThrows(IllegalArgumentException.class, 386 () -> mService.getMirroredSurface(token, outBounds)); 387 } 388 389 @Test testGetMirroredSurface_returnsNullForInvisibleToken()390 public void testGetMirroredSurface_returnsNullForInvisibleToken() throws Exception { 391 FilteredListener listenerA = startActivityAndAssertCameOnTop(mActivityA); 392 393 IBinder token = mService.createTaskMirroringToken(listenerA.mTopTask.taskId); 394 395 // Uses the Activity with the different taskAffinity to make the previous Task hidden. 396 startActivityAndAssertCameOnTop(mBlockingActivity); 397 // Now the Surface of the token will be invisible. 398 399 Rect outBounds = new Rect(); 400 PollingCheck.waitFor(() -> mService.getMirroredSurface(token, outBounds) == null, 401 "The mirrored surface couldn't become invisible"); 402 } 403 404 @Test testGetMirroredSurface_returnsSurface()405 public void testGetMirroredSurface_returnsSurface() throws Exception { 406 FilteredListener listenerA = startActivityAndAssertCameOnTop(mActivityA); 407 408 IBinder token = mService.createTaskMirroringToken(listenerA.mTopTask.taskId); 409 Rect outBounds = new Rect(); 410 411 SurfaceControl mirror = mService.getMirroredSurface(token, outBounds); 412 413 expect.that(outBounds.isEmpty()).isFalse(); 414 expect.that(outBounds).isEqualTo( 415 listenerA.mTopTask.getConfiguration().windowConfiguration.getBounds()); 416 assertThat(mirror).isNotNull(); 417 assertThat(mirror.isValid()).isTrue(); 418 } 419 startActivityAndAssertCameOnTop(ComponentName activity)420 private FilteredListener startActivityAndAssertCameOnTop(ComponentName activity) 421 throws InterruptedException { 422 return startActivityAndAssertCameOnTop(activity, Display.DEFAULT_DISPLAY); 423 } 424 startActivityAndAssertCameOnTop( ComponentName activity, int displayId)425 private FilteredListener startActivityAndAssertCameOnTop( 426 ComponentName activity, int displayId) throws InterruptedException { 427 FilteredListener listener = new FilteredListener(activity); 428 mService.registerActivityListener(listener); 429 startActivity(activity, displayId); 430 listener.assertTopTaskActivityCameOnTop(); 431 return listener; 432 } 433 waitUntil(BooleanSupplier condition)434 private void waitUntil(BooleanSupplier condition) { 435 for (long i = DEFAULT_TIMEOUT_MS / SLEEP_MS; !condition.getAsBoolean() && i > 0; --i) { 436 SystemClock.sleep(SLEEP_MS); 437 } 438 if (!condition.getAsBoolean()) { 439 throw new RuntimeException("failed while waiting for condition to become true"); 440 } 441 } 442 topTasksHasComponent(ComponentName component)443 private boolean topTasksHasComponent(ComponentName component) { 444 for (TaskInfo topTaskInfoContainer : mService.getVisibleTasksInternal()) { 445 if (topTaskInfoContainer.topActivity.equals(component)) { 446 return true; 447 } 448 } 449 return false; 450 } 451 452 /** Activity that closes itself after some timeout to clean up the screen. */ 453 public static class TempActivity extends Activity { 454 private final CountDownLatch mDestroyed = new CountDownLatch(1); 455 private static final long QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE = 1000; // ms 456 @Override onCreate(@ullable Bundle savedInstanceState)457 protected void onCreate(@Nullable Bundle savedInstanceState) { 458 super.onCreate(savedInstanceState); 459 sTestActivities.add(this); 460 } 461 462 @Override onDestroy()463 protected void onDestroy() { 464 super.onDestroy(); 465 mDestroyed.countDown(); 466 } 467 waitForDestroyed()468 private boolean waitForDestroyed() throws InterruptedException { 469 return mDestroyed.await(QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TimeUnit.MILLISECONDS); 470 } 471 } 472 473 public static class ActivityA extends TempActivity {} 474 475 public static class ActivityB extends TempActivity {} 476 477 public static class ActivityC extends TempActivity {} 478 479 public static class BlockingActivity extends TempActivity {} 480 getContext()481 private static Context getContext() { 482 return getInstrumentation().getTargetContext(); 483 } 484 getTestContext()485 private static Context getTestContext() { 486 return getInstrumentation().getContext(); 487 } 488 startActivity(ComponentName name)489 private static Activity startActivity(ComponentName name) { 490 return startActivity(name, Display.DEFAULT_DISPLAY); 491 } 492 startActivity(ComponentName name, int displayId)493 private static Activity startActivity(ComponentName name, int displayId) { 494 ActivityMonitor monitor = new ActivityMonitor(name.getClassName(), null, false); 495 getInstrumentation().addMonitor(monitor); 496 497 Intent intent = new Intent(); 498 intent.setComponent(name); 499 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 500 501 Bundle bundle = null; 502 if (displayId != Display.DEFAULT_DISPLAY) { 503 ActivityOptions options = ActivityOptions.makeBasic(); 504 options.setLaunchDisplayId(displayId); 505 bundle = options.toBundle(); 506 } 507 getContext().startActivity(intent, bundle); 508 return monitor.waitForActivityWithTimeout(ACTIVITY_TIMEOUT_MS); 509 } 510 511 private static final class FilteredListener implements CarActivityService.ActivityListener { 512 private final ComponentName mDesiredComponent; 513 private final CountDownLatch mActivityCameOnTop = new CountDownLatch(1); 514 private final CountDownLatch mActivityChangedInBackstack = new CountDownLatch(1); 515 private TaskInfo mTopTask; 516 517 /** 518 * Creates an instance of a {@link CarActivityService.ActivityListener} 519 * that filters based on the component name or does not filter if component name is null. 520 */ FilteredListener(@onNull ComponentName desiredComponent)521 private FilteredListener(@NonNull ComponentName desiredComponent) { 522 mDesiredComponent = desiredComponent; 523 } 524 525 @Override onActivityCameOnTop(TaskInfo topTask)526 public void onActivityCameOnTop(TaskInfo topTask) { 527 if (isActivityOutsideTestPackage(topTask)) { 528 return; 529 } 530 if (!topTask.topActivity.equals(mDesiredComponent)) { 531 Log.d(TAG, 532 String.format("onActivityCameOnTop#Unexpected component: %s. Expected: %s", 533 topTask.topActivity.getClassName(), mDesiredComponent)); 534 return; 535 } 536 if (mTopTask == null) { // We are interested in the first one only. 537 mTopTask = topTask; 538 } 539 mActivityCameOnTop.countDown(); 540 } 541 542 @Override onActivityChangedInBackstack(TaskInfo taskInfo)543 public void onActivityChangedInBackstack(TaskInfo taskInfo) { 544 if (isActivityOutsideTestPackage(taskInfo)) { 545 return; 546 } 547 if (!taskInfo.baseIntent.getComponent().equals(mDesiredComponent)) { 548 Log.d(TAG, String.format( 549 "onActivityChangedInBackstack#Unexpected component: %s. Expected: %s", 550 taskInfo.baseIntent.getComponent(), mDesiredComponent)); 551 return; 552 } 553 mActivityChangedInBackstack.countDown(); 554 } 555 isActivityOutsideTestPackage(TaskInfo taskInfo)556 private boolean isActivityOutsideTestPackage(TaskInfo taskInfo) { 557 if (taskInfo.topActivity != null && !getTestContext().getPackageName().equals( 558 taskInfo.topActivity.getPackageName())) { 559 Log.d(TAG, "Component launched from other package: " 560 + taskInfo.topActivity.getClassName()); 561 return true; 562 } 563 return false; 564 } 565 assertTopTaskActivityCameOnTop()566 private void assertTopTaskActivityCameOnTop() throws InterruptedException { 567 assertThat(waitForTopTaskActivityCameOnTop(DEFAULT_TIMEOUT_MS)).isTrue(); 568 } 569 waitForTopTaskActivityCameOnTop(long timeoutMs)570 private boolean waitForTopTaskActivityCameOnTop(long timeoutMs) 571 throws InterruptedException { 572 return mActivityCameOnTop.await(timeoutMs, TimeUnit.MILLISECONDS); 573 } 574 assertTopTaskActivityChangedInBackstack()575 private void assertTopTaskActivityChangedInBackstack() throws InterruptedException { 576 assertThat(waitForTopTaskActivityChangedInBackstack(DEFAULT_TIMEOUT_MS)).isTrue(); 577 } 578 waitForTopTaskActivityChangedInBackstack(long timeoutMs)579 private boolean waitForTopTaskActivityChangedInBackstack(long timeoutMs) 580 throws InterruptedException { 581 return mActivityChangedInBackstack.await(timeoutMs, TimeUnit.MILLISECONDS); 582 } 583 } 584 } 585