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