• 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.InstrumentationRegistry;
49 import androidx.test.ext.junit.runners.AndroidJUnit4;
50 import androidx.test.filters.MediumTest;
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         }
106     }
107 
108     @Test
testBlockingActivity_doActivity_isNotBlocked()109     public void testBlockingActivity_doActivity_isNotBlocked() throws Exception {
110         startDoActivity(/* extra= */ null);
111 
112         assertThat(mDevice.wait(Until.findObject(By.text(
113                 DoActivity.class.getSimpleName())),
114                 UI_TIMEOUT_MS)).isNotNull();
115         assertBlockingActivityNotFound();
116     }
117 
118     @Test
testBlockingActivity_doActivity_showingDialog_isNotBlocked()119     public void testBlockingActivity_doActivity_showingDialog_isNotBlocked() throws Exception {
120         startDoActivity(DoActivity.INTENT_EXTRA_SHOW_DIALOG);
121 
122         assertThat(mDevice.wait(Until.findObject(By.text(
123                 DoActivity.DIALOG_TITLE)),
124                 UI_TIMEOUT_MS)).isNotNull();
125         assertBlockingActivityNotFound();
126     }
127 
128     @Test
testBlockingActivity_doTemplateActivity_isNotBlocked()129     public void testBlockingActivity_doTemplateActivity_isNotBlocked() throws Exception {
130         startActivity(toComponentName(getTestContext(), CarAppActivity.class));
131 
132         assertThat(mDevice.wait(Until.findObject(By.text(
133                 CarAppActivity.class.getSimpleName())),
134                 UI_TIMEOUT_MS)).isNotNull();
135         assertBlockingActivityNotFound();
136     }
137 
138     @Test
testBlockingActivity_multipleDoTemplateActivity_notBlocked()139     public void testBlockingActivity_multipleDoTemplateActivity_notBlocked() throws Exception {
140         startActivity(toComponentName(getTestContext(), CarAppActivity.class));
141         assertThat(mDevice.wait(Until.findObject(By.text(
142                 CarAppActivity.class.getSimpleName())),
143                 UI_TIMEOUT_MS)).isNotNull();
144         getContext().sendBroadcast(new Intent().setAction(ACTION_START_SECOND_INSTANCE)
145                 .setPackage(getTestContext().getPackageName()));
146         assertThat(mDevice.wait(Until.findObject(By.text(
147                 SECOND_INSTANCE_TITLE)),
148                 UI_TIMEOUT_MS)).isNotNull();
149         assertBlockingActivityNotFound();
150     }
151 
152     @Test
testBlockingActivity_doTemplateActivity_showingDialog_isBlocked()153     public void testBlockingActivity_doTemplateActivity_showingDialog_isBlocked() throws Exception {
154         startActivity(toComponentName(getTestContext(), CarAppActivity.class));
155         assertThat(mDevice.wait(Until.findObject(By.text(
156                 CarAppActivity.class.getSimpleName())),
157                 UI_TIMEOUT_MS)).isNotNull();
158         assertBlockingActivityNotFound();
159         assertThat(mCarPackageManager.isActivityDistractionOptimized(
160                 getTestContext().getPackageName(),
161                 CarAppActivity.class.getName()
162         )).isTrue();
163 
164         getContext().sendBroadcast(new Intent().setAction(ACTION_SHOW_DIALOG)
165                 .setPackage(getTestContext().getPackageName()));
166 
167         assertThat(mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)),
168                 UI_TIMEOUT_MS)).isNotNull();
169         assertThat(mCarPackageManager.isActivityDistractionOptimized(
170                 getTestContext().getPackageName(),
171                 CarAppActivity.class.getName()
172         )).isFalse();
173     }
174 
175     @Test
testBlockingActivity_nonDoActivity_isBlocked()176     public void testBlockingActivity_nonDoActivity_isBlocked() throws Exception {
177         startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING);
178 
179         // The label should be 'Close app' since NonDoActivity is the root task.
180         assertBlockingActivityFoundAndExit("Close app");
181 
182         // To exit ABA will close nonDoActivity.
183         assertBlockingActivityNotFound();
184         assertThat(mDevice.wait(Until.findObject(By.text(NonDoActivity.class.getSimpleName())),
185                 UI_TIMEOUT_MS)).isNull();
186     }
187 
188     @Test
testBlockingActivity_DoLaunchesNonDoOnCreate_isBlocked()189     public void testBlockingActivity_DoLaunchesNonDoOnCreate_isBlocked() throws Exception {
190         startDoActivity(DoActivity.INTENT_EXTRA_LAUNCH_NONDO);
191 
192         // The label should be 'Back' since NonDo's root task is DO.
193         assertBlockingActivityFoundAndExit("Back");
194 
195         // To exit ABA will show the root task, DoActivity.
196         assertBlockingActivityNotFound();
197         assertThat(mDevice.wait(Until.findObject(By.text(DoActivity.class.getSimpleName())),
198                 UI_TIMEOUT_MS)).isNotNull();
199     }
200 
201     @Test
testBlockingActivity_DoLaunchesNonDo_nonDoIsKilled_noBlockingActivity()202     public void testBlockingActivity_DoLaunchesNonDo_nonDoIsKilled_noBlockingActivity()
203             throws Exception {
204         startDoActivity(DoActivity.INTENT_EXTRA_LAUNCH_NONDO_NEW_TASK);
205         assertBlockingActivityFound();
206 
207         for (TempActivity activity : sTestingActivities) {
208             if (activity instanceof NonDoActivity) {
209                 activity.finishCompletely();
210                 sTestingActivities.remove(activity);
211             }
212         }
213 
214         assertBlockingActivityNotFound();
215         // After NonDo & ABA finishes, DoActivity will come to the top.
216         assertActivityLaunched(DoActivity.class.getSimpleName());
217     }
218 
219     @Test
testBlockingActivity_nonDoFinishesOnCreate_noBlockingActivity()220     public void testBlockingActivity_nonDoFinishesOnCreate_noBlockingActivity()
221             throws Exception {
222         startNonDoActivity(NonDoActivity.EXTRA_ONCREATE_FINISH_IMMEDIATELY);
223 
224         assertBlockingActivityNotFound();
225     }
226 
227     @Test
testBlockingActivity_nonDoLaunchesDoOnCreate_noBlockingActivity()228     public void testBlockingActivity_nonDoLaunchesDoOnCreate_noBlockingActivity()
229             throws Exception {
230         startNonDoActivity(NonDoActivity.EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY);
231 
232         assertBlockingActivityNotFound();
233     }
234 
235     @Test
testBlockingActivity_nonDoFinishesOnResume_noBlockingActivity()236     public void testBlockingActivity_nonDoFinishesOnResume_noBlockingActivity()
237             throws Exception {
238         startNonDoActivity(NonDoActivity.EXTRA_ONRESUME_FINISH_IMMEDIATELY);
239 
240         assertBlockingActivityNotFound();
241     }
242 
243     @Test
testBlockingActivity_nonDoLaunchesDoOnResume_noBlockingActivity()244     public void testBlockingActivity_nonDoLaunchesDoOnResume_noBlockingActivity()
245             throws Exception {
246         startNonDoActivity(NonDoActivity.EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY);
247 
248         assertBlockingActivityNotFound();
249     }
250 
251     @Test
testBlockingActivity_nonDoNoHistory_isBlocked()252     public void testBlockingActivity_nonDoNoHistory_isBlocked() throws Exception {
253         startActivity(toComponentName(getTestContext(), NonDoNoHistoryActivity.class));
254 
255         assertThat(mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)),
256                 UI_TIMEOUT_MS)).isNotNull();
257     }
258 
259     @Test
testIsActivityBackedBySafeActivity_notMoving_nonDoActivity_returnsTrue()260     public void testIsActivityBackedBySafeActivity_notMoving_nonDoActivity_returnsTrue()
261             throws Exception {
262         setDrivingStateParked();
263 
264         startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING);
265         assertActivityLaunched(NonDoActivity.class.getSimpleName());
266 
267         ComponentName nonDoActivity = toComponentName(getTestContext(), NonDoActivity.class);
268         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(nonDoActivity)).isTrue();
269     }
270 
271     @Test
testIsActivityBackedBySafeActivity_moving_rootNonDoActivity_returnsFalse()272     public void testIsActivityBackedBySafeActivity_moving_rootNonDoActivity_returnsFalse()
273             throws Exception {
274         startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING);
275         assertBlockingActivityFound();
276 
277         ComponentName nonDoActivity = toComponentName(getTestContext(), NonDoActivity.class);
278         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(nonDoActivity)).isFalse();
279     }
280 
281     @Test
testIsActivityBackedBySafeActivity_moving_nonDoActivityBackedByDo_returnsTrue()282     public void testIsActivityBackedBySafeActivity_moving_nonDoActivityBackedByDo_returnsTrue()
283             throws Exception {
284         startDoActivity(DoActivity.INTENT_EXTRA_LAUNCH_NONDO);
285         // DoActivity will launch NonDoActivity consecutively.
286         assertBlockingActivityFound();
287 
288         ComponentName nonDoActivity = toComponentName(getTestContext(), NonDoActivity.class);
289         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(nonDoActivity)).isTrue();
290     }
291 
292     @Test
testIsActivityBackedBySafeActivity_moving_doActivity_returnsFalse()293     public void testIsActivityBackedBySafeActivity_moving_doActivity_returnsFalse()
294             throws Exception {
295         startDoActivity(/* extra= */ null);
296         assertActivityLaunched(DoActivity.class.getSimpleName());
297 
298         ComponentName doActivity = toComponentName(getTestContext(), DoActivity.class);
299         assertThat(mCarPackageManager.isActivityBackedBySafeActivity(doActivity)).isFalse();
300     }
301 
assertBlockingActivityNotFound()302     private void assertBlockingActivityNotFound() {
303         assertThat(mDevice.wait(Until.gone(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)),
304                 NOT_FOUND_UI_TIMEOUT_MS)).isNotNull();
305     }
306 
assertBlockingActivityFound()307     private void assertBlockingActivityFound() {
308         assertThat(mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)),
309                 UI_TIMEOUT_MS)).isNotNull();
310     }
311 
assertBlockingActivityFoundAndExit(String exitLabel)312     private void assertBlockingActivityFoundAndExit(String exitLabel) {
313         assertBlockingActivityFound();
314         UiObject2 button = mDevice.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_EXIT_BUTTON_ID));
315         // TODO(b/200948830): Make the test not to compare the text directly.
316         assertThat(button.getText()).isEqualTo(exitLabel);
317 
318         button.click();
319     }
320 
assertActivityLaunched(String title)321     private void assertActivityLaunched(String title) {
322         assertThat(mDevice.wait(Until.findObject(By.text(title)), UI_TIMEOUT_MS)).isNotNull();
323     }
324 
startActivity(ComponentName name)325     private void startActivity(ComponentName name) {
326         Intent intent = new Intent();
327         intent.setComponent(name);
328         startActivity(intent);
329     }
330 
startActivity(Intent intent)331     private void startActivity(Intent intent) {
332         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
333         ActivityOptions options = ActivityOptions.makeBasic();
334         options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
335         getContext().startActivity(intent, options.toBundle());
336     }
337 
startNonDoActivity(int firstActionFlag)338     private void startNonDoActivity(int firstActionFlag) {
339         ComponentName activity = toComponentName(getTestContext(), NonDoActivity.class);
340         Intent intent = new Intent();
341         intent.setComponent(activity);
342         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
343         intent.putExtra(NonDoActivity.EXTRA_FIRST_ACTION, firstActionFlag);
344 
345         ActivityOptions options = ActivityOptions.makeBasic();
346         options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
347 
348         getContext().startActivity(intent, options.toBundle());
349     }
350 
startDoActivity(String extra)351     private void startDoActivity(String extra) {
352         Intent intent = new Intent()
353                 .setComponent(toComponentName(getTestContext(), DoActivity.class))
354                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
355         if (extra != null) {
356             intent.putExtra(extra, true);
357         }
358         startActivity(intent);
359     }
360 
ensureHomeIsDisplayed()361     private void ensureHomeIsDisplayed() {
362         mDevice.pressHome();
363         final String launcherPackage = mDevice.getLauncherPackageName();
364         assertNotNull(launcherPackage);
365 
366         assumeTrue("Home is not displayed even after " + HOME_DISPLAYED_TIMEOUT_MS + "ms.",
367                 mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
368                         HOME_DISPLAYED_TIMEOUT_MS));
369     }
370 
setDrivingStateMoving()371     private void setDrivingStateMoving() {
372         mCarDrivingStateManager.injectDrivingState(CarDrivingStateEvent.DRIVING_STATE_MOVING);
373     }
374 
setDrivingStateParked()375     private void setDrivingStateParked() {
376         mCarDrivingStateManager.injectDrivingState(CarDrivingStateEvent.DRIVING_STATE_PARKED);
377     }
378 
toComponentName(Context ctx, Class<?> cls)379     private static ComponentName toComponentName(Context ctx, Class<?> cls) {
380         return ComponentName.createRelative(ctx, cls.getName());
381     }
382 
383     public static class NonDoActivity extends TempActivity {
384 
385         static final String EXTRA_FIRST_ACTION = "first_action";
386 
387         static final int EXTRA_DO_NOTHING = 0;
388         static final int EXTRA_ONCREATE_FINISH_IMMEDIATELY = 1;
389         static final int EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY = 2;
390         static final int EXTRA_ONRESUME_FINISH_IMMEDIATELY = 3;
391         static final int EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY = 4;
392 
393         @Override
onCreate(Bundle savedInstanceState)394         protected void onCreate(Bundle savedInstanceState) {
395             super.onCreate(savedInstanceState);
396             Bundle extras = getIntent().getExtras();
397             if (extras != null) {
398                 switch (extras.getInt(EXTRA_FIRST_ACTION, EXTRA_DO_NOTHING)) {
399                     case EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY:
400                         startActivity(new Intent(this, DoActivity.class));
401                         finish();
402                         break;
403                     case EXTRA_ONCREATE_FINISH_IMMEDIATELY:
404                         finish();
405                         break;
406                     default:
407                         // do nothing
408                 }
409             }
410         }
411 
412         @Override
onResume()413         protected void onResume() {
414             super.onResume();
415             Bundle extras = getIntent().getExtras();
416             if (extras != null) {
417                 switch (extras.getInt(EXTRA_FIRST_ACTION, EXTRA_DO_NOTHING)) {
418                     case EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY:
419                         startActivity(new Intent(this, DoActivity.class));
420                         finish();
421                         break;
422                     case EXTRA_ONRESUME_FINISH_IMMEDIATELY:
423                         finish();
424                         break;
425                     default:
426                         // do nothing
427                 }
428             }
429         }
430     }
431 
432     public static class NonDoNoHistoryActivity extends TempActivity {
433     }
434 
435     public static class DoActivity extends TempActivity {
436         public static final String INTENT_EXTRA_SHOW_DIALOG = "SHOW_DIALOG";
437         public static final String DIALOG_TITLE = "Title";
438 
439         public static final String INTENT_EXTRA_LAUNCH_NONDO = "LAUNCH_NONDO";
440         public static final String INTENT_EXTRA_LAUNCH_NONDO_NEW_TASK = "LAUNCH_NONDO_NEW_TASK";
441 
442         @Override
onCreate(Bundle savedInstanceState)443         protected void onCreate(Bundle savedInstanceState) {
444             super.onCreate(savedInstanceState);
445             if (getIntent().getBooleanExtra(INTENT_EXTRA_LAUNCH_NONDO, false)) {
446                 startActivity(new Intent(this, NonDoActivity.class));
447             }
448             if (getIntent().getBooleanExtra(INTENT_EXTRA_LAUNCH_NONDO_NEW_TASK, false)) {
449                 startActivity(new Intent(this, NonDoActivity.class)
450                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
451             }
452             if (getIntent().getBooleanExtra(INTENT_EXTRA_SHOW_DIALOG, false)) {
453                 AlertDialog dialog = new AlertDialog.Builder(DoActivity.this)
454                         .setTitle(DIALOG_TITLE)
455                         .setMessage("Message")
456                         .create();
457                 dialog.show();
458             }
459         }
460     }
461 
462     /** Activity that closes itself after some timeout to clean up the screen. */
463     public static class TempActivity extends Activity {
464         private final CountDownLatch mDestroyed = new CountDownLatch(1);
465 
466         @Override
onCreate(Bundle savedInstanceState)467         protected void onCreate(Bundle savedInstanceState) {
468             super.onCreate(savedInstanceState);
469             setTitle(this.getClass().getSimpleName());
470             sTestingActivities.add(this);
471         }
472 
473         @Override
onDestroy()474         protected void onDestroy() {
475             sTestingActivities.remove(this);
476             super.onDestroy();
477             mDestroyed.countDown();
478         }
479 
finishCompletely()480         void finishCompletely() throws InterruptedException {
481             finish();
482             waitForDestroy();
483         }
484 
waitForDestroy()485         boolean waitForDestroy() throws InterruptedException {
486             return mDestroyed.await(ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS);
487         }
488     }
489 
getContext()490     private Context getContext() {
491         return InstrumentationRegistry.getInstrumentation().getTargetContext();
492     }
493 
getTestContext()494     private Context getTestContext() {
495         return InstrumentationRegistry.getInstrumentation().getContext();
496     }
497 }
498