• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.car.pm;
18 
19 import static androidx.car.app.activity.CarAppActivity.ACTION_SHOW_DIALOG;
20 import static androidx.car.app.activity.CarAppActivity.ACTION_START_SECOND_INSTANCE;
21 import static androidx.car.app.activity.CarAppActivity.SECOND_INSTANCE_TITLE;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assume.assumeTrue;
27 
28 import android.app.Activity;
29 import android.app.ActivityOptions;
30 import android.app.AlertDialog;
31 import android.app.UiAutomation;
32 import android.car.Car;
33 import android.car.content.pm.CarPackageManager;
34 import android.car.drivingstate.CarDrivingStateEvent;
35 import android.car.drivingstate.CarDrivingStateManager;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.os.Bundle;
40 import android.support.test.uiautomator.By;
41 import android.support.test.uiautomator.Configurator;
42 import android.support.test.uiautomator.UiDevice;
43 import android.support.test.uiautomator.UiObject2;
44 import android.support.test.uiautomator.Until;
45 import android.view.Display;
46 
47 import androidx.car.app.activity.CarAppActivity;
48 import androidx.test.ext.junit.runners.AndroidJUnit4;
49 import androidx.test.filters.MediumTest;
50 import androidx.test.platform.app.InstrumentationRegistry;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 
57 import java.util.concurrent.CopyOnWriteArrayList;
58 import java.util.concurrent.CountDownLatch;
59 import java.util.concurrent.TimeUnit;
60 
61 @RunWith(AndroidJUnit4.class)
62 @MediumTest
63 public class CarPackageManagerServiceTest {
64     private static final String ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID =
65             "com.android.systemui:id/blocking_text";
66     private static final String ACTIVITY_BLOCKING_ACTIVITY_EXIT_BUTTON_ID =
67             "com.android.systemui:id/exit_button";
68 
69     // cf_x86_auto is very slow, so uses very long timeout.
70     private static final int UI_TIMEOUT_MS = 20_000;
71     private static final int NOT_FOUND_UI_TIMEOUT_MS = 10_000;
72     private static final long ACTIVITY_TIMEOUT_MS = 5000;
73     private static final int HOME_DISPLAYED_TIMEOUT_MS = 5_000;
74 
75     private CarDrivingStateManager mCarDrivingStateManager;
76     private CarPackageManager mCarPackageManager;
77 
78     private UiDevice mDevice;
79 
80     private static final CopyOnWriteArrayList<TempActivity> sTestingActivities =
81             new CopyOnWriteArrayList<>();
82 
83     @Before
setUp()84     public void setUp() throws Exception {
85         Car car = Car.createCar(getContext());
86         mCarDrivingStateManager = (CarDrivingStateManager)
87                 car.getCarManager(Car.CAR_DRIVING_STATE_SERVICE);
88         mCarPackageManager = (CarPackageManager)
89                 car.getCarManager(Car.PACKAGE_SERVICE);
90         assertNotNull(mCarDrivingStateManager);
91 
92         Configurator.getInstance()
93                 .setUiAutomationFlags(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
94         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
95         ensureHomeIsDisplayed();
96         setDrivingStateMoving();
97     }
98 
99     @After
tearDown()100     public void tearDown() throws Exception {
101         setDrivingStateParked();
102 
103         for (TempActivity testingActivity : sTestingActivities) {
104             testingActivity.finishCompletely();
105             sTestingActivities.remove(testingActivity);
106         }
107     }
108 
109     @Test
testBlockingActivity_doActivity_isNotBlocked()110     public void testBlockingActivity_doActivity_isNotBlocked() throws Exception {
111         startDoActivity(/* extra= */ null);
112 
113         assertThat(mDevice.wait(Until.findObject(By.text(
114                 DoActivity.class.getSimpleName())),
115                 UI_TIMEOUT_MS)).isNotNull();
116         assertBlockingActivityNotFound();
117     }
118 
119     @Test
testBlockingActivity_doActivity_showingDialog_isNotBlocked()120     public void testBlockingActivity_doActivity_showingDialog_isNotBlocked() throws Exception {
121         startDoActivity(DoActivity.INTENT_EXTRA_SHOW_DIALOG);
122 
123         assertThat(mDevice.wait(Until.findObject(By.text(
124                 DoActivity.DIALOG_TITLE)),
125                 UI_TIMEOUT_MS)).isNotNull();
126         assertBlockingActivityNotFound();
127     }
128 
129     @Test
testBlockingActivity_doTemplateActivity_isNotBlocked()130     public void testBlockingActivity_doTemplateActivity_isNotBlocked() throws Exception {
131         startActivity(toComponentName(getTestContext(), CarAppActivity.class));
132 
133         assertThat(mDevice.wait(Until.findObject(By.text(
134                 CarAppActivity.class.getSimpleName())),
135                 UI_TIMEOUT_MS)).isNotNull();
136         assertBlockingActivityNotFound();
137     }
138 
139     @Test
testBlockingActivity_multipleDoTemplateActivity_notBlocked()140     public void testBlockingActivity_multipleDoTemplateActivity_notBlocked() throws Exception {
141         startActivity(toComponentName(getTestContext(), CarAppActivity.class));
142         assertThat(mDevice.wait(Until.findObject(By.text(
143                 CarAppActivity.class.getSimpleName())),
144                 UI_TIMEOUT_MS)).isNotNull();
145         getContext().sendBroadcast(new Intent().setAction(ACTION_START_SECOND_INSTANCE)
146                 .setPackage(getTestContext().getPackageName()));
147         assertThat(mDevice.wait(Until.findObject(By.text(
148                 SECOND_INSTANCE_TITLE)),
149                 UI_TIMEOUT_MS)).isNotNull();
150         assertBlockingActivityNotFound();
151     }
152 
153     @Test
testBlockingActivity_doTemplateActivity_showingDialog_isBlocked()154     public void testBlockingActivity_doTemplateActivity_showingDialog_isBlocked() throws Exception {
155         startActivity(toComponentName(getTestContext(), CarAppActivity.class));
156         assertThat(mDevice.wait(Until.findObject(By.text(
157                 CarAppActivity.class.getSimpleName())),
158                 UI_TIMEOUT_MS)).isNotNull();
159         assertBlockingActivityNotFound();
160         assertThat(mCarPackageManager.isActivityDistractionOptimized(
161                 getTestContext().getPackageName(),
162                 CarAppActivity.class.getName()
163         )).isTrue();
164 
165         getContext().sendBroadcast(new Intent().setAction(ACTION_SHOW_DIALOG)
166                 .setPackage(getTestContext().getPackageName()));
167 
168         assertThat(mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)),
169                 UI_TIMEOUT_MS)).isNotNull();
170         assertThat(mCarPackageManager.isActivityDistractionOptimized(
171                 getTestContext().getPackageName(),
172                 CarAppActivity.class.getName()
173         )).isFalse();
174     }
175 
176     @Test
testBlockingActivity_nonDoActivity_isBlocked()177     public void testBlockingActivity_nonDoActivity_isBlocked() throws Exception {
178         testBlockingActivityShownForNdoActivity();
179     }
180 
181     @Test
testBlockingActivity_nonDoActivity_carIdling_isBlocked()182     public void testBlockingActivity_nonDoActivity_carIdling_isBlocked() throws Exception {
183         setDrivingStateIdling();
184         testBlockingActivityShownForNdoActivity();
185     }
186 
testBlockingActivityShownForNdoActivity()187     private void testBlockingActivityShownForNdoActivity() {
188         startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING);
189 
190         // The label should be 'Close app' since NonDoActivity is the root task.
191         assertBlockingActivityFoundAndExit("Close app");
192 
193         // To exit ABA will close nonDoActivity.
194         assertBlockingActivityNotFound();
195         assertThat(mDevice.wait(Until.findObject(By.text(NonDoActivity.class.getSimpleName())),
196                 UI_TIMEOUT_MS)).isNull();
197     }
198 
199     @Test
testBlockingActivity_DoLaunchesNonDoOnCreate_isBlocked()200     public void testBlockingActivity_DoLaunchesNonDoOnCreate_isBlocked() throws Exception {
201         startDoActivity(DoActivity.INTENT_EXTRA_LAUNCH_NONDO);
202 
203         // The label should be 'Back' since NonDo's root task is DO.
204         assertBlockingActivityFoundAndExit("Back");
205 
206         // To exit ABA will show the root task, DoActivity.
207         assertBlockingActivityNotFound();
208         assertThat(mDevice.wait(Until.findObject(By.text(DoActivity.class.getSimpleName())),
209                 UI_TIMEOUT_MS)).isNotNull();
210     }
211 
212     @Test
testBlockingActivity_DoLaunchesNonDo_nonDoIsKilled_noBlockingActivity()213     public void testBlockingActivity_DoLaunchesNonDo_nonDoIsKilled_noBlockingActivity()
214             throws Exception {
215         startDoActivity(DoActivity.INTENT_EXTRA_LAUNCH_NONDO_NEW_TASK);
216         assertBlockingActivityFound();
217 
218         for (TempActivity activity : sTestingActivities) {
219             if (activity instanceof NonDoActivity) {
220                 activity.finishCompletely();
221                 sTestingActivities.remove(activity);
222             }
223         }
224 
225         assertBlockingActivityNotFound();
226         // After NonDo & ABA finishes, DoActivity will come to the top.
227         assertActivityLaunched(DoActivity.class.getSimpleName());
228     }
229 
230     @Test
testBlockingActivity_DoLaunchesNonDo_DoIsKilled_isBlocked()231     public void testBlockingActivity_DoLaunchesNonDo_DoIsKilled_isBlocked()
232             throws Exception {
233         startDoActivity(DoActivity.INTENT_EXTRA_LAUNCH_NONDO_NEW_TASK);
234         assertBlockingActivityFound();
235 
236         for (TempActivity activity : sTestingActivities) {
237             if (activity instanceof DoActivity) {
238                 activity.finishCompletely();
239                 sTestingActivities.remove(activity);
240             }
241         }
242 
243         assertBlockingActivityFound();
244     }
245 
246     @Test
testBlockingActivity_nonDoActivity_translucentDoOnTopOfBlockingActivityRemoved_nonDoRemainsBlocked()247     public void testBlockingActivity_nonDoActivity_translucentDoOnTopOfBlockingActivityRemoved_nonDoRemainsBlocked() {
248         startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING);
249         waitForBlockingActivityFound();
250 
251         startDoActivity(DoTranslucentActivity.EXTRA_ONRESUME_FINISH_IMMEDIATELY,
252                 DoTranslucentActivity.class);
253 
254         /*
255             Expected task stack:
256             NonDoActivity (not visible) -> ActivityBlockingActivity (visible)
257         */
258         assertBlockingActivityFound();
259     }
260 
261     @Test
testBlockingActivity_nonDoFinishesOnCreate_noBlockingActivity()262     public void testBlockingActivity_nonDoFinishesOnCreate_noBlockingActivity()
263             throws Exception {
264         startNonDoActivity(NonDoActivity.EXTRA_ONCREATE_FINISH_IMMEDIATELY);
265 
266         assertBlockingActivityNotFound();
267     }
268 
269     @Test
testBlockingActivity_nonDoLaunchesDoOnCreate_noBlockingActivity()270     public void testBlockingActivity_nonDoLaunchesDoOnCreate_noBlockingActivity()
271             throws Exception {
272         startNonDoActivity(NonDoActivity.EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY);
273 
274         assertBlockingActivityNotFound();
275     }
276 
277     @Test
testBlockingActivity_nonDoFinishesOnResume_noBlockingActivity()278     public void testBlockingActivity_nonDoFinishesOnResume_noBlockingActivity()
279             throws Exception {
280         startNonDoActivity(NonDoActivity.EXTRA_ONRESUME_FINISH_IMMEDIATELY);
281 
282         assertBlockingActivityNotFound();
283     }
284 
285     @Test
testBlockingActivity_nonDoLaunchesDoOnResume_noBlockingActivity()286     public void testBlockingActivity_nonDoLaunchesDoOnResume_noBlockingActivity()
287             throws Exception {
288         startNonDoActivity(NonDoActivity.EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY);
289 
290         assertBlockingActivityNotFound();
291     }
292 
293     @Test
testIsActivityBackedBySafeActivity_notMoving_nonDoActivity_returnsTrue()294     public void testIsActivityBackedBySafeActivity_notMoving_nonDoActivity_returnsTrue()
295             throws Exception {
296         setDrivingStateParked();
297 
298         startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING);
299         assertActivityLaunched(NonDoActivity.class.getSimpleName());
300 
301         ComponentName nonDoActivity = toComponentName(getTestContext(), NonDoActivity.class);
302         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(nonDoActivity)).isTrue();
303     }
304 
305     @Test
testIsActivityBackedBySafeActivity_moving_rootNonDoActivity_returnsFalse()306     public void testIsActivityBackedBySafeActivity_moving_rootNonDoActivity_returnsFalse()
307             throws Exception {
308         startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING);
309         assertBlockingActivityFound();
310 
311         ComponentName nonDoActivity = toComponentName(getTestContext(), NonDoActivity.class);
312         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(nonDoActivity)).isFalse();
313     }
314 
315     @Test
testIsActivityBackedBySafeActivity_moving_nonDoActivityBackedByDo_returnsTrue()316     public void testIsActivityBackedBySafeActivity_moving_nonDoActivityBackedByDo_returnsTrue()
317             throws Exception {
318         startDoActivity(DoActivity.INTENT_EXTRA_LAUNCH_NONDO);
319         // DoActivity will launch NonDoActivity consecutively.
320         assertBlockingActivityFound();
321 
322         ComponentName nonDoActivity = toComponentName(getTestContext(), NonDoActivity.class);
323         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(nonDoActivity)).isTrue();
324     }
325 
326     @Test
testIsActivityBackedBySafeActivity_moving_doActivity_returnsFalse()327     public void testIsActivityBackedBySafeActivity_moving_doActivity_returnsFalse()
328             throws Exception {
329         startDoActivity(/* extra= */ null);
330         assertActivityLaunched(DoActivity.class.getSimpleName());
331 
332         ComponentName doActivity = toComponentName(getTestContext(), DoActivity.class);
333         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(doActivity)).isFalse();
334     }
335 
assertBlockingActivityNotFound()336     private void assertBlockingActivityNotFound() {
337         assertThat(mDevice.wait(Until.gone(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)),
338                 NOT_FOUND_UI_TIMEOUT_MS)).isNotNull();
339     }
340 
assertBlockingActivityFound()341     private void assertBlockingActivityFound() {
342         assertThat(waitForBlockingActivityFound()).isNotNull();
343     }
344 
waitForBlockingActivityFound()345     private UiObject2 waitForBlockingActivityFound() {
346         return mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)),
347                 UI_TIMEOUT_MS);
348     }
349 
assertBlockingActivityFoundAndExit(String exitLabel)350     private void assertBlockingActivityFoundAndExit(String exitLabel) {
351         assertBlockingActivityFound();
352         UiObject2 button = mDevice.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_EXIT_BUTTON_ID));
353         // TODO(b/200948830): Make the test not to compare the text directly.
354         assertThat(button.getText()).isEqualTo(exitLabel);
355 
356         button.click();
357     }
358 
assertActivityLaunched(String title)359     private void assertActivityLaunched(String title) {
360         assertThat(mDevice.wait(Until.findObject(By.text(title)), UI_TIMEOUT_MS)).isNotNull();
361     }
362 
startActivity(ComponentName name)363     private void startActivity(ComponentName name) {
364         Intent intent = new Intent();
365         intent.setComponent(name);
366         startActivity(intent);
367     }
368 
startActivity(Intent intent)369     private void startActivity(Intent intent) {
370         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
371         ActivityOptions options = ActivityOptions.makeBasic();
372         options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
373         getContext().startActivity(intent, options.toBundle());
374     }
375 
startNonDoActivity(int firstActionFlag)376     private void startNonDoActivity(int firstActionFlag) {
377         ComponentName activity = toComponentName(getTestContext(), NonDoActivity.class);
378         Intent intent = new Intent();
379         intent.setComponent(activity);
380         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
381         intent.putExtra(NonDoActivity.EXTRA_FIRST_ACTION, firstActionFlag);
382 
383         ActivityOptions options = ActivityOptions.makeBasic();
384         options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
385 
386         getContext().startActivity(intent, options.toBundle());
387     }
388 
startDoActivity(String extra)389     private void startDoActivity(String extra) {
390         startDoActivity(extra, DoActivity.class);
391     }
392 
startDoActivity(String extra, Class<?> cls)393     private void startDoActivity(String extra, Class<?> cls) {
394         Intent intent = new Intent()
395                 .setComponent(toComponentName(getTestContext(), cls))
396                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
397         if (extra != null) {
398             intent.putExtra(extra, true);
399         }
400         startActivity(intent);
401     }
402 
ensureHomeIsDisplayed()403     private void ensureHomeIsDisplayed() {
404         mDevice.pressHome();
405         final String launcherPackage = mDevice.getLauncherPackageName();
406         assertNotNull(launcherPackage);
407 
408         assumeTrue("Home is not displayed even after " + HOME_DISPLAYED_TIMEOUT_MS + "ms.",
409                 mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
410                         HOME_DISPLAYED_TIMEOUT_MS));
411     }
412 
setDrivingStateMoving()413     private void setDrivingStateMoving() {
414         mCarDrivingStateManager.injectDrivingState(CarDrivingStateEvent.DRIVING_STATE_MOVING);
415     }
416 
setDrivingStateIdling()417     private void setDrivingStateIdling() {
418         mCarDrivingStateManager.injectDrivingState(CarDrivingStateEvent.DRIVING_STATE_IDLING);
419     }
420 
setDrivingStateParked()421     private void setDrivingStateParked() {
422         mCarDrivingStateManager.injectDrivingState(CarDrivingStateEvent.DRIVING_STATE_PARKED);
423     }
424 
toComponentName(Context ctx, Class<?> cls)425     private static ComponentName toComponentName(Context ctx, Class<?> cls) {
426         return ComponentName.createRelative(ctx, cls.getName());
427     }
428 
429     public static class DoTranslucentActivity extends TempActivity {
430         public static final String EXTRA_ONRESUME_FINISH_IMMEDIATELY = "ONRESUME_FINISH";
431 
432         @Override
onResume()433         protected void onResume() {
434             super.onResume();
435             if (getIntent().getBooleanExtra(EXTRA_ONRESUME_FINISH_IMMEDIATELY, false)) {
436                 finish();
437             }
438         }
439     }
440 
441     public static class NonDoActivity extends TempActivity {
442 
443         static final String EXTRA_FIRST_ACTION = "first_action";
444 
445         static final int EXTRA_DO_NOTHING = 0;
446         static final int EXTRA_ONCREATE_FINISH_IMMEDIATELY = 1;
447         static final int EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY = 2;
448         static final int EXTRA_ONRESUME_FINISH_IMMEDIATELY = 3;
449         static final int EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY = 4;
450 
451         @Override
onCreate(Bundle savedInstanceState)452         protected void onCreate(Bundle savedInstanceState) {
453             super.onCreate(savedInstanceState);
454             Bundle extras = getIntent().getExtras();
455             if (extras != null) {
456                 switch (extras.getInt(EXTRA_FIRST_ACTION, EXTRA_DO_NOTHING)) {
457                     case EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY:
458                         startActivity(new Intent(this, DoActivity.class));
459                         finish();
460                         break;
461                     case EXTRA_ONCREATE_FINISH_IMMEDIATELY:
462                         finish();
463                         break;
464                     default:
465                         // do nothing
466                 }
467             }
468         }
469 
470         @Override
onResume()471         protected void onResume() {
472             super.onResume();
473             Bundle extras = getIntent().getExtras();
474             if (extras != null) {
475                 switch (extras.getInt(EXTRA_FIRST_ACTION, EXTRA_DO_NOTHING)) {
476                     case EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY:
477                         startActivity(new Intent(this, DoActivity.class));
478                         finish();
479                         break;
480                     case EXTRA_ONRESUME_FINISH_IMMEDIATELY:
481                         finish();
482                         break;
483                     default:
484                         // do nothing
485                 }
486             }
487         }
488     }
489 
490     public static class DoActivity extends TempActivity {
491         public static final String INTENT_EXTRA_SHOW_DIALOG = "SHOW_DIALOG";
492         public static final String DIALOG_TITLE = "Title";
493 
494         public static final String INTENT_EXTRA_LAUNCH_NONDO = "LAUNCH_NONDO";
495         public static final String INTENT_EXTRA_LAUNCH_NONDO_NEW_TASK = "LAUNCH_NONDO_NEW_TASK";
496 
497         @Override
onCreate(Bundle savedInstanceState)498         protected void onCreate(Bundle savedInstanceState) {
499             super.onCreate(savedInstanceState);
500             if (getIntent().getBooleanExtra(INTENT_EXTRA_LAUNCH_NONDO, false)) {
501                 startActivity(new Intent(this, NonDoActivity.class));
502             }
503             if (getIntent().getBooleanExtra(INTENT_EXTRA_LAUNCH_NONDO_NEW_TASK, false)) {
504                 startActivity(new Intent(this, NonDoActivity.class)
505                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
506             }
507             if (getIntent().getBooleanExtra(INTENT_EXTRA_SHOW_DIALOG, false)) {
508                 AlertDialog dialog = new AlertDialog.Builder(DoActivity.this)
509                         .setTitle(DIALOG_TITLE)
510                         .setMessage("Message")
511                         .create();
512                 dialog.show();
513             }
514         }
515     }
516 
517     /** Activity that closes itself after some timeout to clean up the screen. */
518     public static class TempActivity extends Activity {
519         private final CountDownLatch mDestroyed = new CountDownLatch(1);
520 
521         @Override
onCreate(Bundle savedInstanceState)522         protected void onCreate(Bundle savedInstanceState) {
523             super.onCreate(savedInstanceState);
524             setTitle(this.getClass().getSimpleName());
525             sTestingActivities.add(this);
526         }
527 
528         @Override
onDestroy()529         protected void onDestroy() {
530             sTestingActivities.remove(this);
531             super.onDestroy();
532             mDestroyed.countDown();
533         }
534 
finishCompletely()535         void finishCompletely() throws InterruptedException {
536             finish();
537             waitForDestroy();
538         }
539 
waitForDestroy()540         boolean waitForDestroy() throws InterruptedException {
541             return mDestroyed.await(ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS);
542         }
543     }
544 
getContext()545     private Context getContext() {
546         return InstrumentationRegistry.getInstrumentation().getTargetContext();
547     }
548 
getTestContext()549     private Context getTestContext() {
550         return InstrumentationRegistry.getInstrumentation().getContext();
551     }
552 }
553