1 /* 2 * Copyright (C) 2023 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 android.bugreport.cts_root; 18 19 import static android.app.admin.flags.Flags.FLAG_ONBOARDING_BUGREPORT_STORAGE_BUG_FIX; 20 import static android.app.admin.flags.Flags.FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS; 21 22 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.junit.Assert.fail; 27 28 import android.app.AlarmManager; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.os.BugreportManager; 34 import android.os.BugreportManager.BugreportCallback; 35 import android.os.BugreportParams; 36 import android.os.ParcelFileDescriptor; 37 import android.platform.test.annotations.RequiresFlagsEnabled; 38 import android.platform.test.flag.junit.CheckFlagsRule; 39 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 40 41 import androidx.annotation.NonNull; 42 import androidx.test.InstrumentationRegistry; 43 import androidx.test.filters.LargeTest; 44 import androidx.test.runner.AndroidJUnit4; 45 import androidx.test.uiautomator.By; 46 import androidx.test.uiautomator.BySelector; 47 import androidx.test.uiautomator.UiDevice; 48 import androidx.test.uiautomator.UiObject2; 49 import androidx.test.uiautomator.Until; 50 51 import com.android.compatibility.common.util.SystemUtil; 52 53 import org.junit.AfterClass; 54 import org.junit.Before; 55 import org.junit.BeforeClass; 56 import org.junit.Ignore; 57 import org.junit.Rule; 58 import org.junit.Test; 59 import org.junit.rules.TestName; 60 import org.junit.runner.RunWith; 61 62 import java.io.File; 63 import java.lang.reflect.Method; 64 import java.util.ArrayList; 65 import java.util.List; 66 import java.util.concurrent.CountDownLatch; 67 import java.util.concurrent.TimeUnit; 68 69 /** 70 * Device-side tests for Bugreport Manager API. 71 * 72 * <p>These tests require root to allowlist the test package to use the BugreportManager APIs. 73 */ 74 @RunWith(AndroidJUnit4.class) 75 public class BugreportManagerTest { 76 77 private Context mContext; 78 private BugreportManager mBugreportManager; 79 80 @Rule 81 public TestName name = new TestName(); 82 83 private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); 84 85 private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(4); 86 private static final int MAX_ALLOWED_BUGREPROTS = 8; 87 private static final String INTENT_BUGREPORT_FINISHED = 88 "com.android.internal.intent.action.BUGREPORT_FINISHED"; 89 90 @Rule 91 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 92 93 94 @Before setup()95 public void setup() throws Exception { 96 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 97 mBugreportManager = mContext.getSystemService(BugreportManager.class); 98 ensureNoConsentDialogShown(); 99 100 101 // Unlock before finding/clicking an object. 102 final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 103 device.wakeUp(); 104 device.executeShellCommand("wm dismiss-keyguard"); 105 } 106 107 @BeforeClass classSetup()108 public static void classSetup() { 109 runShellCommand("settings put global auto_time 0"); 110 runShellCommand("svc power stayon true"); 111 // Kill current bugreport, so that it does not interfere with future bugreports. 112 runShellCommand("setprop ctl.stop bugreportd"); 113 } 114 115 @AfterClass classTearDown()116 public static void classTearDown() { 117 // Restore auto time 118 runShellCommand("settings put global auto_time 1"); 119 runShellCommand("svc power stayon false"); 120 // Kill current bugreport, so that it does not interfere with future bugreports. 121 runShellCommand("setprop ctl.stop bugreportd"); 122 } 123 124 @LargeTest 125 @Test testRetrieveBugreportConsentGranted()126 public void testRetrieveBugreportConsentGranted() throws Exception { 127 try { 128 ensureNotConsentlessReport(); 129 File startBugreportFile = createTempFile("startbugreport", ".zip"); 130 CountDownLatch latch = new CountDownLatch(1); 131 BugreportCallbackImpl callback = new BugreportCallbackImpl(latch); 132 mBugreportManager.startBugreport(parcelFd(startBugreportFile), null, 133 new BugreportParams( 134 BugreportParams.BUGREPORT_MODE_ONBOARDING, 135 BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT), 136 mContext.getMainExecutor(), callback); 137 latch.await(4, TimeUnit.MINUTES); 138 assertThat(callback.isSuccess()).isTrue(); 139 // No data should be passed to the FD used to call startBugreport. 140 assertThat(startBugreportFile.length()).isEqualTo(0); 141 String bugreportFileLocation = callback.getBugreportFile(); 142 waitForDumpstateServiceToStop(); 143 144 // Trying to retrieve an unknown bugreport should fail 145 latch = new CountDownLatch(1); 146 callback = new BugreportCallbackImpl(latch); 147 File bugreportFile2 = createTempFile("bugreport2_" + name.getMethodName(), ".zip"); 148 mBugreportManager.retrieveBugreport( 149 "unknown/file.zip", parcelFd(bugreportFile2), 150 mContext.getMainExecutor(), callback); 151 assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); 152 assertThat(callback.getErrorCode()).isEqualTo( 153 BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE); 154 waitForDumpstateServiceToStop(); 155 156 File bugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip"); 157 // A bugreport was previously generated for this caller. When the consent dialog is invoked 158 // and accepted, the bugreport files should be passed to the calling package. 159 ParcelFileDescriptor bugreportFd = parcelFd(bugreportFile); 160 assertThat(bugreportFd).isNotNull(); 161 latch = new CountDownLatch(1); 162 mBugreportManager.retrieveBugreport(bugreportFileLocation, bugreportFd, 163 mContext.getMainExecutor(), new BugreportCallbackImpl(latch)); 164 shareConsentDialog(ConsentReply.ALLOW); 165 assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue(); 166 assertThat(bugreportFile.length()).isGreaterThan(0); 167 } finally { 168 waitForDumpstateServiceToStop(); 169 // Remove all bugreport files 170 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 171 } 172 } 173 174 175 @LargeTest 176 @Test testRetrieveBugreportConsentDenied()177 public void testRetrieveBugreportConsentDenied() throws Exception { 178 try { 179 // User denies consent, therefore no data should be passed back to the bugreport file. 180 ensureNotConsentlessReport(); 181 CountDownLatch latch = new CountDownLatch(1); 182 BugreportCallbackImpl callback = new BugreportCallbackImpl(latch); 183 mBugreportManager.startBugreport(parcelFd(new File("/dev/null")), 184 null, new BugreportParams(BugreportParams.BUGREPORT_MODE_ONBOARDING, 185 BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT), 186 mContext.getMainExecutor(), callback); 187 latch.await(4, TimeUnit.MINUTES); 188 assertThat(callback.isSuccess()).isTrue(); 189 String bugreportFileLocation = callback.getBugreportFile(); 190 waitForDumpstateServiceToStop(); 191 192 latch = new CountDownLatch(1); 193 callback = new BugreportCallbackImpl(latch); 194 File bugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip"); 195 ParcelFileDescriptor bugreportFd = parcelFd(bugreportFile); 196 assertThat(bugreportFd).isNotNull(); 197 mBugreportManager.retrieveBugreport( 198 bugreportFileLocation, 199 bugreportFd, 200 mContext.getMainExecutor(), 201 callback); 202 shareConsentDialog(ConsentReply.DENY); 203 latch.await(1, TimeUnit.MINUTES); 204 assertThat(callback.getErrorCode()).isEqualTo( 205 BugreportCallback.BUGREPORT_ERROR_USER_DENIED_CONSENT); 206 assertThat(bugreportFile.length()).isEqualTo(0); 207 waitForDumpstateServiceToStop(); 208 209 // Since consent has already been denied, this call should fail because consent cannot 210 // be requested twice for the same bugreport. 211 latch = new CountDownLatch(1); 212 callback = new BugreportCallbackImpl(latch); 213 mBugreportManager.retrieveBugreport(bugreportFileLocation, parcelFd(bugreportFile), 214 mContext.getMainExecutor(), callback); 215 latch.await(1, TimeUnit.MINUTES); 216 assertThat(callback.getErrorCode()).isEqualTo( 217 BugreportCallback.BUGREPORT_ERROR_NO_BUGREPORT_TO_RETRIEVE); 218 waitForDumpstateServiceToStop(); 219 } finally { 220 waitForDumpstateServiceToStop(); 221 // Remove all bugreport files 222 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 223 } 224 } 225 226 @LargeTest 227 @Test 228 @RequiresFlagsEnabled(FLAG_ONBOARDING_BUGREPORT_STORAGE_BUG_FIX) testBugreportsLimitReached()229 public void testBugreportsLimitReached() throws Exception { 230 try { 231 List<File> bugreportFiles = new ArrayList<>(); 232 List<String> bugreportFileLocations = new ArrayList<>(); 233 CountDownLatch latch = new CountDownLatch(1); 234 235 for (int i = 0; i < MAX_ALLOWED_BUGREPROTS + 1; i++) { 236 waitForDumpstateServiceToStop(); 237 File bugreportFile = createTempFile( 238 "bugreport_" + name.getMethodName() + "_" + i, ".zip"); 239 bugreportFiles.add(bugreportFile); 240 File startBugreportFile = createTempFile("startbugreport", ".zip"); 241 242 latch = new CountDownLatch(1); 243 BugreportCallbackImpl callback = new BugreportCallbackImpl(latch); 244 245 mBugreportManager.startBugreport(parcelFd(startBugreportFile), null, 246 new BugreportParams( 247 BugreportParams.BUGREPORT_MODE_ONBOARDING, 248 BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT), 249 mContext.getMainExecutor(), callback); 250 251 latch.await(BUGREPORT_TIMEOUT_MS, TimeUnit.MILLISECONDS); 252 assertThat(callback.isSuccess()).isTrue(); 253 bugreportFileLocations.add(callback.getBugreportFile()); 254 waitForDumpstateServiceToStop(); 255 } 256 257 final long newTime = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(10); 258 SystemUtil.runWithShellPermissionIdentity(() -> 259 mContext.getSystemService(AlarmManager.class).setTime(newTime)); 260 261 // Trigger a shell bugreport to trigger cleanup logic 262 triggerShellBugreport(BugreportParams.BUGREPORT_MODE_ONBOARDING); 263 264 // The retrieved first bugreport file should be empty. 265 latch = new CountDownLatch(1); 266 BugreportCallbackImpl callback = new BugreportCallbackImpl(latch); 267 mBugreportManager.retrieveBugreport( 268 bugreportFileLocations.getFirst(), parcelFd(bugreportFiles.getFirst()), 269 mContext.getMainExecutor(), callback); 270 ensureNotConsentlessReport(); 271 shareConsentDialog(ConsentReply.ALLOW); 272 assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue(); 273 assertThat(bugreportFiles.getFirst().length()).isEqualTo(0); 274 waitForDumpstateServiceToStop(); 275 276 // The retrieved last bugreport file should not be empty. 277 latch = new CountDownLatch(1); 278 callback = new BugreportCallbackImpl(latch); 279 mBugreportManager.retrieveBugreport( 280 bugreportFileLocations.getLast(), parcelFd(bugreportFiles.getLast()), 281 mContext.getMainExecutor(), callback); 282 ensureNotConsentlessReport(); 283 shareConsentDialog(ConsentReply.ALLOW); 284 assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue(); 285 assertThat(bugreportFiles.getLast().length()).isGreaterThan(0); 286 waitForDumpstateServiceToStop(); 287 } finally { 288 waitForDumpstateServiceToStop(); 289 // Remove all bugreport files 290 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 291 } 292 } 293 294 @LargeTest 295 @Test 296 @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS) testBugreport_skipsConsentForDeferredReportAfterFullReport()297 public void testBugreport_skipsConsentForDeferredReportAfterFullReport() throws Exception { 298 try { 299 ensureNotConsentlessReport(); 300 startFullReport(false); 301 302 startDeferredReport(true); 303 startDeferredReport(true); 304 305 } finally { 306 waitForDumpstateServiceToStop(); 307 // Remove all bugreport files 308 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 309 } 310 } 311 312 @LargeTest 313 @Test 314 @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS) testBugreport_skipConsentForDeferredReportAfterDeferredReport()315 public void testBugreport_skipConsentForDeferredReportAfterDeferredReport() throws Exception { 316 try { 317 ensureNotConsentlessReport(); 318 startDeferredReport(false); 319 320 startDeferredReport(true); 321 322 } finally { 323 waitForDumpstateServiceToStop(); 324 // Remove all bugreport files 325 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 326 } 327 } 328 329 @LargeTest 330 @Test 331 @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS) 332 @Ignore("b/344704922") testBugreport_doesNotSkipConsentForFullReportAfterFullReport()333 public void testBugreport_doesNotSkipConsentForFullReportAfterFullReport() throws Exception { 334 try { 335 ensureNotConsentlessReport(); 336 startFullReport(false); 337 338 startFullReport(false); 339 340 } finally { 341 waitForDumpstateServiceToStop(); 342 // Remove all bugreport files 343 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 344 } 345 } 346 347 @LargeTest 348 @Test 349 @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS) testBugreport_skipConsentForFullReportAfterDeferredReport()350 public void testBugreport_skipConsentForFullReportAfterDeferredReport() throws Exception { 351 try { 352 ensureNotConsentlessReport(); 353 startDeferredReport(false); 354 355 startFullReport(true); 356 357 } finally { 358 waitForDumpstateServiceToStop(); 359 // Remove all bugreport files 360 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 361 } 362 } 363 364 @LargeTest 365 @Test 366 @RequiresFlagsEnabled(FLAG_ONBOARDING_CONSENTLESS_BUGREPORTS) testBugreport_doesNotSkipConsentAfterTimeLimit()367 public void testBugreport_doesNotSkipConsentAfterTimeLimit() throws Exception { 368 try { 369 ensureNotConsentlessReport(); 370 startFullReport(false); 371 final long newTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3); 372 SystemUtil.runWithShellPermissionIdentity(() -> 373 mContext.getSystemService(AlarmManager.class).setTime(newTime)); 374 375 startDeferredReport(false); 376 377 } finally { 378 waitForDumpstateServiceToStop(); 379 // Remove all bugreport files 380 SystemUtil.runShellCommand("rm -f -rR -v /bugreports/"); 381 } 382 } 383 ensureNotConsentlessReport()384 private void ensureNotConsentlessReport() { 385 final long time = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(60); 386 SystemUtil.runWithShellPermissionIdentity(() -> 387 mContext.getSystemService(AlarmManager.class).setTime(time)); 388 assertThat(System.currentTimeMillis()).isGreaterThan(time); 389 } 390 startFullReport(boolean skipConsent)391 private void startFullReport(boolean skipConsent) throws Exception { 392 waitForDumpstateServiceToStop(); 393 File bugreportFile = createTempFile("startbugreport", ".zip"); 394 CountDownLatch latch = new CountDownLatch(1); 395 BugreportCallbackImpl callback = new BugreportCallbackImpl(latch); 396 mBugreportManager.startBugreport(parcelFd(bugreportFile), null, 397 new BugreportParams(BugreportParams.BUGREPORT_MODE_ONBOARDING, 0), 398 mContext.getMainExecutor(), callback); 399 if (!skipConsent) { 400 shareConsentDialog(ConsentReply.ALLOW); 401 } 402 403 latch.await(2, TimeUnit.MINUTES); 404 assertThat(callback.isSuccess()).isTrue(); 405 // No data should be passed to the FD used to call startBugreport. 406 assertThat(bugreportFile.length()).isGreaterThan(0); 407 waitForDumpstateServiceToStop(); 408 } 409 startDeferredReport(boolean skipConsent)410 private void startDeferredReport(boolean skipConsent) throws Exception { 411 waitForDumpstateServiceToStop(); 412 File bugreportFile = createTempFile("startbugreport", ".zip"); 413 CountDownLatch latch = new CountDownLatch(1); 414 BugreportCallbackImpl callback = new BugreportCallbackImpl(latch); 415 mBugreportManager.startBugreport(parcelFd(bugreportFile), null, 416 new BugreportParams( 417 BugreportParams.BUGREPORT_MODE_ONBOARDING, 418 BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT), 419 mContext.getMainExecutor(), callback); 420 421 latch.await(1, TimeUnit.MINUTES); 422 assertThat(callback.isSuccess()).isTrue(); 423 String location = callback.getBugreportFile(); 424 waitForDumpstateServiceToStop(); 425 426 427 // The retrieved bugreport file should not be empty. 428 latch = new CountDownLatch(1); 429 callback = new BugreportCallbackImpl(latch); 430 mBugreportManager.retrieveBugreport( 431 location, parcelFd(bugreportFile), 432 mContext.getMainExecutor(), callback); 433 if (!skipConsent) { 434 shareConsentDialog(ConsentReply.ALLOW); 435 } 436 assertThat(latch.await(1, TimeUnit.MINUTES)).isTrue(); 437 assertThat(bugreportFile.length()).isGreaterThan(0); 438 waitForDumpstateServiceToStop(); 439 } 440 triggerShellBugreport(int type)441 private void triggerShellBugreport(int type) throws Exception { 442 BugreportBroadcastReceiver br = new BugreportBroadcastReceiver(); 443 final IntentFilter intentFilter = new IntentFilter(INTENT_BUGREPORT_FINISHED); 444 mContext.registerReceiver(br, intentFilter, Context.RECEIVER_EXPORTED); 445 final BugreportParams params = new BugreportParams(type); 446 mBugreportManager.requestBugreport(params, "" /* shareTitle */, "" /* shareDescription */); 447 448 try { 449 br.waitForBugreportFinished(); 450 } finally { 451 // The latch may fail for a number of reasons but we still need to unregister the 452 // BroadcastReceiver. 453 mContext.unregisterReceiver(br); 454 } 455 456 Intent response = br.getBugreportFinishedIntent(); 457 assertThat(response.getAction()).isEqualTo(intentFilter.getAction(0)); 458 waitForDumpstateServiceToStop(); 459 } 460 461 private class BugreportBroadcastReceiver extends BroadcastReceiver { 462 Intent bugreportFinishedIntent = null; 463 final CountDownLatch latch; 464 BugreportBroadcastReceiver()465 BugreportBroadcastReceiver() { 466 latch = new CountDownLatch(1); 467 } 468 469 @Override onReceive(Context context, Intent intent)470 public void onReceive(Context context, Intent intent) { 471 setBugreportFinishedIntent(intent); 472 latch.countDown(); 473 } 474 setBugreportFinishedIntent(Intent intent)475 private void setBugreportFinishedIntent(Intent intent) { 476 bugreportFinishedIntent = intent; 477 } 478 getBugreportFinishedIntent()479 public Intent getBugreportFinishedIntent() { 480 return bugreportFinishedIntent; 481 } 482 waitForBugreportFinished()483 public void waitForBugreportFinished() throws Exception { 484 if (!latch.await(BUGREPORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 485 throw new Exception("Failed to receive BUGREPORT_FINISHED in " 486 + BUGREPORT_TIMEOUT_MS + " ms."); 487 } 488 } 489 } 490 parcelFd(File file)491 private ParcelFileDescriptor parcelFd(File file) throws Exception { 492 return ParcelFileDescriptor.open(file, 493 ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND); 494 } 495 createTempFile(String prefix, String extension)496 private static File createTempFile(String prefix, String extension) throws Exception { 497 final File f = File.createTempFile(prefix, extension); 498 f.setReadable(true, true); 499 f.setWritable(true, true); 500 501 f.deleteOnExit(); 502 return f; 503 } 504 505 private static final class BugreportCallbackImpl extends BugreportCallback { 506 private int mErrorCode = -1; 507 private boolean mSuccess = false; 508 private String mBugreportFile; 509 private final Object mLock = new Object(); 510 511 private final CountDownLatch mLatch; 512 BugreportCallbackImpl(CountDownLatch latch)513 BugreportCallbackImpl(CountDownLatch latch) { 514 mLatch = latch; 515 } 516 517 @Override onError(int errorCode)518 public void onError(int errorCode) { 519 synchronized (mLock) { 520 mErrorCode = errorCode; 521 mLatch.countDown(); 522 } 523 } 524 525 @Override onFinished(String bugreportFile)526 public void onFinished(String bugreportFile) { 527 synchronized (mLock) { 528 mBugreportFile = bugreportFile; 529 mLatch.countDown(); 530 mSuccess = true; 531 } 532 } 533 534 @Override onFinished()535 public void onFinished() { 536 synchronized (mLock) { 537 mLatch.countDown(); 538 mSuccess = true; 539 } 540 } 541 getErrorCode()542 public int getErrorCode() { 543 synchronized (mLock) { 544 return mErrorCode; 545 } 546 } 547 isSuccess()548 public boolean isSuccess() { 549 synchronized (mLock) { 550 return mSuccess; 551 } 552 } 553 getBugreportFile()554 public String getBugreportFile() { 555 synchronized (mLock) { 556 return mBugreportFile; 557 } 558 } 559 } 560 561 private enum ConsentReply { 562 ALLOW, 563 DENY, 564 TIMEOUT 565 } 566 567 /* 568 * Ensure the consent dialog is shown and take action according to <code>consentReply<code/>. 569 * It will fail if the dialog is not shown when <code>ignoreNotFound<code/> is false. 570 */ shareConsentDialog(@onNull ConsentReply consentReply)571 private void shareConsentDialog(@NonNull ConsentReply consentReply) throws Exception { 572 final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 573 574 final BySelector consentTitleObj = By.res("android", "alertTitle"); 575 if (!device.wait(Until.hasObject(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)) { 576 fail("The consent dialog is not found"); 577 } 578 if (consentReply.equals(ConsentReply.TIMEOUT)) { 579 return; 580 } 581 final BySelector selector; 582 if (consentReply.equals(ConsentReply.ALLOW)) { 583 selector = By.res("android", "button1"); 584 } else { // ConsentReply.DENY 585 selector = By.res("android", "button2"); 586 } 587 final UiObject2 btnObj = device.findObject(selector); 588 assertThat(btnObj).isNotNull(); 589 btnObj.click(); 590 591 assertThat(device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)).isTrue(); 592 } 593 594 /* 595 * Ensure the consent dialog is shown and take action according to <code>consentReply<code/>. 596 * It will fail if the dialog is not shown when <code>ignoreNotFound<code/> is false. 597 */ ensureNoConsentDialogShown()598 private void ensureNoConsentDialogShown() throws Exception { 599 final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 600 601 final BySelector consentTitleObj = By.res("android", "alertTitle"); 602 if (!device.wait(Until.hasObject(consentTitleObj), TimeUnit.SECONDS.toMillis(2))) { 603 return; 604 } 605 final BySelector selector = By.res("android", "button2"); 606 final UiObject2 btnObj = device.findObject(selector); 607 if (btnObj == null) { 608 return; 609 } 610 btnObj.click(); 611 612 device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS); 613 } 614 615 616 /** Waits for the dumpstate service to stop, for up to 5 seconds. */ waitForDumpstateServiceToStop()617 private void waitForDumpstateServiceToStop() throws Exception { 618 int pollingIntervalMillis = 100; 619 Method method = Class.forName("android.os.ServiceManager").getMethod( 620 "getService", String.class); 621 for (int i = 0; i < 10; i++) { 622 int numPolls = 50; 623 while (numPolls-- > 0) { 624 // If getService() returns null, the service has stopped. 625 if (method.invoke(null, "dumpstate") == null) { 626 break; 627 } 628 Thread.sleep(pollingIntervalMillis); 629 } 630 } 631 if (method.invoke(null, "dumpstate") == null) { 632 return; 633 } 634 fail("Dumpstate did not stop within 25 seconds"); 635 } 636 } 637