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 27 import android.app.Activity; 28 import android.app.ActivityOptions; 29 import android.app.AlertDialog; 30 import android.app.UiAutomation; 31 import android.car.Car; 32 import android.car.content.pm.CarPackageManager; 33 import android.car.drivingstate.CarDrivingStateEvent; 34 import android.car.drivingstate.CarDrivingStateManager; 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.os.Bundle; 39 import android.support.test.uiautomator.By; 40 import android.support.test.uiautomator.Configurator; 41 import android.support.test.uiautomator.UiDevice; 42 import android.support.test.uiautomator.Until; 43 import android.view.Display; 44 45 import androidx.car.app.activity.CarAppActivity; 46 import androidx.test.InstrumentationRegistry; 47 import androidx.test.ext.junit.runners.AndroidJUnit4; 48 import androidx.test.filters.MediumTest; 49 50 import org.junit.After; 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 55 import java.util.concurrent.CountDownLatch; 56 import java.util.concurrent.TimeUnit; 57 import java.util.concurrent.atomic.AtomicReference; 58 59 @RunWith(AndroidJUnit4.class) 60 @MediumTest 61 public class ActivityBlockingActivityTest { 62 private static final String ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID = 63 "com.android.car:id/blocking_text"; 64 65 // cf_x86_auto is very slow, so uses very long timeout. 66 private static final int UI_TIMEOUT_MS = 20_000; 67 private static final int NOT_FOUND_UI_TIMEOUT_MS = 10_000; 68 private static final long ACTIVITY_TIMEOUT_MS = 5000; 69 70 private CarDrivingStateManager mCarDrivingStateManager; 71 private CarPackageManager mCarPackageManager; 72 73 private UiDevice mDevice; 74 75 // NOTE: Assume there is only one testing Activity. 76 private static final AtomicReference<TempActivity> sTestingActivity = new AtomicReference<>(); 77 78 @Before setUp()79 public void setUp() throws Exception { 80 Car car = Car.createCar(getContext()); 81 mCarDrivingStateManager = (CarDrivingStateManager) 82 car.getCarManager(Car.CAR_DRIVING_STATE_SERVICE); 83 mCarPackageManager = (CarPackageManager) 84 car.getCarManager(Car.PACKAGE_SERVICE); 85 assertNotNull(mCarDrivingStateManager); 86 87 Configurator.getInstance() 88 .setUiAutomationFlags(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 89 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 90 91 setDrivingStateMoving(); 92 } 93 94 @After tearDown()95 public void tearDown() throws Exception { 96 setDrivingStateParked(); 97 98 TempActivity testingActivity = sTestingActivity.get(); 99 if (testingActivity != null) { 100 testingActivity.finishCompletely(); 101 } 102 } 103 104 @Test testBlockingActivity_doActivity_isNotBlocked()105 public void testBlockingActivity_doActivity_isNotBlocked() throws Exception { 106 startActivity(toComponentName(getTestContext(), DoActivity.class)); 107 108 assertThat(mDevice.wait(Until.findObject(By.text( 109 DoActivity.class.getSimpleName())), 110 UI_TIMEOUT_MS)).isNotNull(); 111 assertBlockingActivityNotFound(); 112 } 113 114 @Test testBlockingActivity_doActivity_showingDialog_isNotBlocked()115 public void testBlockingActivity_doActivity_showingDialog_isNotBlocked() throws Exception { 116 Intent intent = new Intent(); 117 intent.putExtra(DoActivity.INTENT_EXTRA_SHOW_DIALOG, true); 118 intent.setComponent(toComponentName(getTestContext(), DoActivity.class)); 119 startActivity(intent); 120 121 assertThat(mDevice.wait(Until.findObject(By.text( 122 DoActivity.DIALOG_TITLE)), 123 UI_TIMEOUT_MS)).isNotNull(); 124 assertBlockingActivityNotFound(); 125 } 126 127 @Test testBlockingActivity_doTemplateActivity_isNotBlocked()128 public void testBlockingActivity_doTemplateActivity_isNotBlocked() throws Exception { 129 startActivity(toComponentName(getTestContext(), CarAppActivity.class)); 130 131 assertThat(mDevice.wait(Until.findObject(By.text( 132 CarAppActivity.class.getSimpleName())), 133 UI_TIMEOUT_MS)).isNotNull(); 134 assertBlockingActivityNotFound(); 135 } 136 137 @Test testBlockingActivity_multipleDoTemplateActivity_notBlocked()138 public void testBlockingActivity_multipleDoTemplateActivity_notBlocked() throws Exception { 139 startActivity(toComponentName(getTestContext(), CarAppActivity.class)); 140 assertThat(mDevice.wait(Until.findObject(By.text( 141 CarAppActivity.class.getSimpleName())), 142 UI_TIMEOUT_MS)).isNotNull(); 143 getContext().sendBroadcast(new Intent().setAction(ACTION_START_SECOND_INSTANCE)); 144 assertThat(mDevice.wait(Until.findObject(By.text( 145 SECOND_INSTANCE_TITLE)), 146 UI_TIMEOUT_MS)).isNotNull(); 147 assertBlockingActivityNotFound(); 148 } 149 150 @Test testBlockingActivity_doTemplateActivity_showingDialog_isBlocked()151 public void testBlockingActivity_doTemplateActivity_showingDialog_isBlocked() throws Exception { 152 startActivity(toComponentName(getTestContext(), CarAppActivity.class)); 153 assertThat(mDevice.wait(Until.findObject(By.text( 154 CarAppActivity.class.getSimpleName())), 155 UI_TIMEOUT_MS)).isNotNull(); 156 assertBlockingActivityNotFound(); 157 assertThat(mCarPackageManager.isActivityDistractionOptimized( 158 getTestContext().getPackageName(), 159 CarAppActivity.class.getName() 160 )).isTrue(); 161 162 getContext().sendBroadcast(new Intent().setAction(ACTION_SHOW_DIALOG)); 163 assertThat(mDevice.wait(Until.findObject(By.text(DoActivity.DIALOG_TITLE)), 164 UI_TIMEOUT_MS)).isNotNull(); 165 166 assertThat(mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)), 167 UI_TIMEOUT_MS)).isNotNull(); 168 assertThat(mCarPackageManager.isActivityDistractionOptimized( 169 getTestContext().getPackageName(), 170 CarAppActivity.class.getName() 171 )).isFalse(); 172 } 173 174 @Test testBlockingActivity_nonDoActivity_isBlocked()175 public void testBlockingActivity_nonDoActivity_isBlocked() throws Exception { 176 startNonDoActivity(NonDoActivity.EXTRA_DO_NOTHING); 177 178 assertThat(mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)), 179 UI_TIMEOUT_MS)).isNotNull(); 180 } 181 182 @Test testBlockingActivity_nonDoFinishesOnCreate_noBlockingActivity()183 public void testBlockingActivity_nonDoFinishesOnCreate_noBlockingActivity() 184 throws Exception { 185 startNonDoActivity(NonDoActivity.EXTRA_ONCREATE_FINISH_IMMEDIATELY); 186 187 assertBlockingActivityNotFound(); 188 } 189 190 @Test testBlockingActivity_nonDoLaunchesDoOnCreate_noBlockingActivity()191 public void testBlockingActivity_nonDoLaunchesDoOnCreate_noBlockingActivity() 192 throws Exception { 193 startNonDoActivity(NonDoActivity.EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY); 194 195 assertBlockingActivityNotFound(); 196 } 197 198 @Test testBlockingActivity_nonDoFinishesOnResume_noBlockingActivity()199 public void testBlockingActivity_nonDoFinishesOnResume_noBlockingActivity() 200 throws Exception { 201 startNonDoActivity(NonDoActivity.EXTRA_ONRESUME_FINISH_IMMEDIATELY); 202 203 assertBlockingActivityNotFound(); 204 } 205 206 @Test testBlockingActivity_nonDoLaunchesDoOnResume_noBlockingActivity()207 public void testBlockingActivity_nonDoLaunchesDoOnResume_noBlockingActivity() 208 throws Exception { 209 startNonDoActivity(NonDoActivity.EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY); 210 211 assertBlockingActivityNotFound(); 212 } 213 214 @Test testBlockingActivity_nonDoNoHistory_isBlocked()215 public void testBlockingActivity_nonDoNoHistory_isBlocked() throws Exception { 216 startActivity(toComponentName(getTestContext(), NonDoNoHistoryActivity.class)); 217 218 assertThat(mDevice.wait(Until.findObject(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)), 219 UI_TIMEOUT_MS)).isNotNull(); 220 } 221 assertBlockingActivityNotFound()222 private void assertBlockingActivityNotFound() { 223 assertThat(mDevice.wait(Until.gone(By.res(ACTIVITY_BLOCKING_ACTIVITY_TEXTVIEW_ID)), 224 NOT_FOUND_UI_TIMEOUT_MS)).isNotNull(); 225 } 226 startActivity(ComponentName name)227 private void startActivity(ComponentName name) { 228 Intent intent = new Intent(); 229 intent.setComponent(name); 230 startActivity(intent); 231 } 232 startActivity(Intent intent)233 private void startActivity(Intent intent) { 234 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 235 ActivityOptions options = ActivityOptions.makeBasic(); 236 options.setLaunchDisplayId(Display.DEFAULT_DISPLAY); 237 getContext().startActivity(intent, options.toBundle()); 238 } 239 startNonDoActivity(int firstActionFlag)240 private void startNonDoActivity(int firstActionFlag) { 241 ComponentName activity = toComponentName(getTestContext(), NonDoActivity.class); 242 Intent intent = new Intent(); 243 intent.setComponent(activity); 244 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 245 intent.putExtra(NonDoActivity.EXTRA_FIRST_ACTION, firstActionFlag); 246 247 ActivityOptions options = ActivityOptions.makeBasic(); 248 options.setLaunchDisplayId(Display.DEFAULT_DISPLAY); 249 250 getContext().startActivity(intent, options.toBundle()); 251 } 252 253 setDrivingStateMoving()254 private void setDrivingStateMoving() { 255 mCarDrivingStateManager.injectDrivingState(CarDrivingStateEvent.DRIVING_STATE_MOVING); 256 } 257 setDrivingStateParked()258 private void setDrivingStateParked() { 259 mCarDrivingStateManager.injectDrivingState(CarDrivingStateEvent.DRIVING_STATE_PARKED); 260 } 261 toComponentName(Context ctx, Class<?> cls)262 private static ComponentName toComponentName(Context ctx, Class<?> cls) { 263 return ComponentName.createRelative(ctx, cls.getName()); 264 } 265 266 public static class NonDoActivity extends TempActivity { 267 268 static final String EXTRA_FIRST_ACTION = "first_action"; 269 270 static final int EXTRA_DO_NOTHING = 0; 271 static final int EXTRA_ONCREATE_FINISH_IMMEDIATELY = 1; 272 static final int EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY = 2; 273 static final int EXTRA_ONRESUME_FINISH_IMMEDIATELY = 3; 274 static final int EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY = 4; 275 276 @Override onCreate(Bundle savedInstanceState)277 protected void onCreate(Bundle savedInstanceState) { 278 super.onCreate(savedInstanceState); 279 Bundle extras = getIntent().getExtras(); 280 if (extras != null) { 281 switch (extras.getInt(EXTRA_FIRST_ACTION, EXTRA_DO_NOTHING)) { 282 case EXTRA_ONCREATE_LAUNCH_DO_IMMEDIATELY: 283 startActivity(new Intent(this, DoActivity.class)); 284 finish(); 285 break; 286 case EXTRA_ONCREATE_FINISH_IMMEDIATELY: 287 finish(); 288 break; 289 default: 290 // do nothing 291 } 292 } 293 } 294 295 @Override onResume()296 protected void onResume() { 297 super.onResume(); 298 Bundle extras = getIntent().getExtras(); 299 if (extras != null) { 300 switch (extras.getInt(EXTRA_FIRST_ACTION, EXTRA_DO_NOTHING)) { 301 case EXTRA_ONRESUME_LAUNCH_DO_IMMEDIATELY: 302 startActivity(new Intent(this, DoActivity.class)); 303 finish(); 304 break; 305 case EXTRA_ONRESUME_FINISH_IMMEDIATELY: 306 finish(); 307 break; 308 default: 309 // do nothing 310 } 311 } 312 } 313 } 314 315 public static class NonDoNoHistoryActivity extends TempActivity { 316 } 317 318 public static class DoActivity extends TempActivity { 319 public static final String INTENT_EXTRA_SHOW_DIALOG = "SHOW_DIALOG"; 320 public static final String DIALOG_TITLE = "Title"; 321 322 @Override onCreate(Bundle savedInstanceState)323 protected void onCreate(Bundle savedInstanceState) { 324 super.onCreate(savedInstanceState); 325 if (getIntent().getBooleanExtra(INTENT_EXTRA_SHOW_DIALOG, false)) { 326 AlertDialog dialog = new AlertDialog.Builder(DoActivity.this) 327 .setTitle(DIALOG_TITLE) 328 .setMessage("Message") 329 .create(); 330 dialog.show(); 331 } 332 } 333 } 334 335 /** Activity that closes itself after some timeout to clean up the screen. */ 336 public static class TempActivity extends Activity { 337 private final CountDownLatch mDestroyed = new CountDownLatch(1); 338 @Override onCreate(Bundle savedInstanceState)339 protected void onCreate(Bundle savedInstanceState) { 340 super.onCreate(savedInstanceState); 341 sTestingActivity.set(this); 342 } 343 344 @Override onDestroy()345 protected void onDestroy() { 346 sTestingActivity.set(null); 347 super.onDestroy(); 348 mDestroyed.countDown(); 349 } 350 finishCompletely()351 void finishCompletely() throws InterruptedException { 352 finish(); 353 mDestroyed.await(ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS); 354 } 355 } 356 getContext()357 private Context getContext() { 358 return InstrumentationRegistry.getInstrumentation().getTargetContext(); 359 } 360 getTestContext()361 private Context getTestContext() { 362 return InstrumentationRegistry.getInstrumentation().getContext(); 363 } 364 } 365