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.permissionui.cts 18 19 import android.app.Instrumentation 20 import android.app.UiAutomation 21 import android.content.Context 22 import android.content.pm.PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE 23 import android.content.pm.PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE 24 import android.content.pm.PackageInstaller.PACKAGE_SOURCE_OTHER 25 import android.content.pm.PackageInstaller.PACKAGE_SOURCE_STORE 26 import android.content.pm.PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED 27 import android.content.pm.PackageManager 28 import android.os.Build 29 import android.os.PersistableBundle 30 import android.os.Process 31 import android.permission.cts.CtsNotificationListenerHelperRule 32 import android.permission.cts.CtsNotificationListenerServiceUtils 33 import android.permission.cts.CtsNotificationListenerServiceUtils.getNotification 34 import android.permission.cts.CtsNotificationListenerServiceUtils.getNotificationForPackageAndId 35 import android.permission.cts.PermissionUtils 36 import android.permission.cts.TestUtils 37 import android.permissionui.cts.AppMetadata.createAppMetadataWithLocationSharingNoAds 38 import android.permissionui.cts.AppMetadata.createAppMetadataWithNoSharing 39 import android.platform.test.annotations.RequiresFlagsEnabled 40 import android.platform.test.flag.junit.DeviceFlagsValueProvider 41 import android.provider.DeviceConfig 42 import android.safetylabel.SafetyLabelConstants 43 import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED 44 import androidx.test.InstrumentationRegistry 45 import androidx.test.filters.FlakyTest 46 import androidx.test.filters.SdkSuppress 47 import androidx.test.uiautomator.By 48 import com.android.compatibility.common.util.DeviceConfigStateChangerRule 49 import com.android.compatibility.common.util.SystemUtil 50 import com.android.compatibility.common.util.SystemUtil.eventually 51 import com.android.compatibility.common.util.SystemUtil.waitForBroadcasts 52 import com.google.common.truth.Truth.assertThat 53 import org.junit.After 54 import org.junit.Assume 55 import org.junit.Before 56 import org.junit.ClassRule 57 import org.junit.Rule 58 import org.junit.Test 59 60 /** End-to-end test for SafetyLabelChangesJobService. */ 61 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") 62 @FlakyTest 63 class SafetyLabelChangesJobServiceTest : BaseUsePermissionTest() { 64 65 @get:Rule 66 val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() 67 68 @get:Rule 69 val safetyLabelChangeNotificationsEnabledConfig = 70 DeviceConfigStateChangerRule( 71 context, 72 DeviceConfig.NAMESPACE_PRIVACY, 73 SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, 74 true.toString() 75 ) 76 77 /** 78 * This rule serves to limit the max number of safety labels that can be persisted, so that 79 * repeated tests don't overwhelm the disk storage on the device. 80 */ 81 @get:Rule 82 val deviceConfigMaxSafetyLabelsPersistedPerApp = 83 DeviceConfigStateChangerRule( 84 context, 85 DeviceConfig.NAMESPACE_PRIVACY, 86 PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP, 87 "2" 88 ) 89 90 @get:Rule 91 val deviceConfigDataSharingUpdatesPeriod = 92 DeviceConfigStateChangerRule( 93 BasePermissionTest.context, 94 DeviceConfig.NAMESPACE_PRIVACY, 95 PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS, 96 "600000" 97 ) 98 99 @Before setupnull100 fun setup() { 101 val packageManager = context.packageManager 102 Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) 103 Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) 104 Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) 105 106 SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP") 107 SystemUtil.runShellCommand("wm dismiss-keyguard") 108 109 CtsNotificationListenerServiceUtils.cancelNotifications(permissionControllerPackageName) 110 resetPermissionControllerAndSimulateReboot() 111 } 112 113 @After cancelJobsAndNotificationsnull114 fun cancelJobsAndNotifications() { 115 cancelJob(SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID) 116 cancelJob(SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID) 117 CtsNotificationListenerServiceUtils.cancelNotifications(permissionControllerPackageName) 118 } 119 120 @Test runDetectUpdatesJob_initializesSafetyLabelsHistoryForAppsnull121 fun runDetectUpdatesJob_initializesSafetyLabelsHistoryForApps() { 122 installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithNoSharing()) 123 grantLocationPermission(APP_PACKAGE_NAME) 124 125 // Run the job to check whether the missing safety label for the above app install is 126 // identified and recorded. 127 runDetectUpdatesJob() 128 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds()) 129 waitForBroadcasts() 130 131 assertNotificationNotShown() 132 assertDataSharingScreenHasUpdates() 133 } 134 135 @Test runNotificationJob_initializesSafetyLabelsHistoryForAppsnull136 fun runNotificationJob_initializesSafetyLabelsHistoryForApps() { 137 installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithNoSharing()) 138 grantLocationPermission(APP_PACKAGE_NAME) 139 140 // Run the job to check whether the missing safety label for the above app install is 141 // identified and recorded. 142 runNotificationJob() 143 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds()) 144 waitForBroadcasts() 145 146 assertDataSharingScreenHasUpdates() 147 } 148 149 @Test runDetectUpdatesJob_updatesSafetyLabelHistoryForAppsnull150 fun runDetectUpdatesJob_updatesSafetyLabelHistoryForApps() { 151 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing()) 152 waitForBroadcastReceiverFinished() 153 installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds()) 154 grantLocationPermission(APP_PACKAGE_NAME) 155 156 // Run the job to check whether the missing safety label for the above app update is 157 // identified and recorded. 158 runDetectUpdatesJob() 159 160 assertNotificationNotShown() 161 assertDataSharingScreenHasUpdates() 162 } 163 164 @Test runNotificationJob_updatesSafetyLabelHistoryForAppsnull165 fun runNotificationJob_updatesSafetyLabelHistoryForApps() { 166 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing()) 167 waitForBroadcastReceiverFinished() 168 installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds()) 169 grantLocationPermission(APP_PACKAGE_NAME) 170 171 // Run the job to check whether the missing safety label for the above app update is 172 // identified and recorded. 173 runNotificationJob() 174 175 assertDataSharingScreenHasUpdates() 176 } 177 178 @Test runNotificationJob_whenLocationSharingUpdatesForLocationGrantedApps_showsNotificationnull179 fun runNotificationJob_whenLocationSharingUpdatesForLocationGrantedApps_showsNotification() { 180 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing()) 181 waitForBroadcasts() 182 // TODO(b/279455955): Investigate why this is necessary and remove if possible. 183 Thread.sleep(500) 184 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds()) 185 waitForBroadcasts() 186 grantLocationPermission(APP_PACKAGE_NAME) 187 188 runNotificationJob() 189 190 waitForNotificationShown() 191 192 val statusBarNotification = 193 getNotification(permissionControllerPackageName, SAFETY_LABEL_CHANGES_NOTIFICATION_ID) 194 val contentIntent = statusBarNotification!!.notification.contentIntent 195 contentIntent.send() 196 197 assertDataSharingScreenHasUpdates() 198 } 199 200 @Test runNotificationJob_whenNoLocationGrantedApps_doesNotShowNotificationnull201 fun runNotificationJob_whenNoLocationGrantedApps_doesNotShowNotification() { 202 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing()) 203 waitForBroadcasts() 204 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds()) 205 waitForBroadcasts() 206 207 runNotificationJob() 208 209 assertNotificationNotShown() 210 } 211 212 @Test runNotificationJob_whenNoLocationSharingUpdates_doesNotShowNotificationnull213 fun runNotificationJob_whenNoLocationSharingUpdates_doesNotShowNotification() { 214 installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing()) 215 waitForBroadcasts() 216 grantLocationPermission(APP_PACKAGE_NAME) 217 218 runNotificationJob() 219 220 assertNotificationNotShown() 221 } 222 223 @Test runNotificationJob_packageSourceUnspecified_updatesSafetyLabelHistoryForAppsnull224 fun runNotificationJob_packageSourceUnspecified_updatesSafetyLabelHistoryForApps() { 225 installPackageViaSession( 226 APP_APK_NAME_31, 227 createAppMetadataWithNoSharing(), 228 PACKAGE_SOURCE_UNSPECIFIED 229 ) 230 waitForBroadcastReceiverFinished() 231 installPackageNoBroadcast( 232 APP_APK_NAME_31, 233 createAppMetadataWithLocationSharingNoAds(), 234 PACKAGE_SOURCE_UNSPECIFIED 235 ) 236 grantLocationPermission(APP_PACKAGE_NAME) 237 238 // Run the job to check whether the missing safety label for the above app update is 239 // identified and recorded. 240 runNotificationJob() 241 242 assertDataSharingScreenHasUpdates() 243 } 244 245 @Test runNotificationJob_packageSourceOther_doesNotShowNotificationnull246 fun runNotificationJob_packageSourceOther_doesNotShowNotification() { 247 installPackageViaSession( 248 APP_APK_NAME_31, 249 createAppMetadataWithNoSharing(), 250 PACKAGE_SOURCE_OTHER 251 ) 252 waitForBroadcastReceiverFinished() 253 installPackageNoBroadcast( 254 APP_APK_NAME_31, 255 createAppMetadataWithLocationSharingNoAds(), 256 PACKAGE_SOURCE_OTHER 257 ) 258 grantLocationPermission(APP_PACKAGE_NAME) 259 260 // Run the job to check whether the missing safety label for the above app update is 261 // identified and recorded. 262 runNotificationJob() 263 264 assertNotificationNotShown() 265 } 266 267 @Test runNotificationJob_packageSourceStore_updatesSafetyLabelHistoryForAppsnull268 fun runNotificationJob_packageSourceStore_updatesSafetyLabelHistoryForApps() { 269 installPackageViaSession( 270 APP_APK_NAME_31, 271 createAppMetadataWithNoSharing(), 272 PACKAGE_SOURCE_STORE 273 ) 274 waitForBroadcastReceiverFinished() 275 installPackageNoBroadcast( 276 APP_APK_NAME_31, 277 createAppMetadataWithLocationSharingNoAds(), 278 PACKAGE_SOURCE_STORE 279 ) 280 grantLocationPermission(APP_PACKAGE_NAME) 281 282 // Run the job to check whether the missing safety label for the above app update is 283 // identified and recorded. 284 runNotificationJob() 285 286 assertDataSharingScreenHasUpdates() 287 } 288 289 @Test runNotificationJob_packageSourceLocalFile_doesNotShowNotificationnull290 fun runNotificationJob_packageSourceLocalFile_doesNotShowNotification() { 291 installPackageViaSession( 292 APP_APK_NAME_31, 293 createAppMetadataWithNoSharing(), 294 PACKAGE_SOURCE_LOCAL_FILE 295 ) 296 waitForBroadcastReceiverFinished() 297 installPackageNoBroadcast( 298 APP_APK_NAME_31, 299 createAppMetadataWithLocationSharingNoAds(), 300 PACKAGE_SOURCE_LOCAL_FILE 301 ) 302 grantLocationPermission(APP_PACKAGE_NAME) 303 304 // Run the job to check whether the missing safety label for the above app update is 305 // identified and recorded. 306 runNotificationJob() 307 308 assertNotificationNotShown() 309 } 310 311 @Test runNotificationJob_packageSourceDownloadedFile_doesNotShowNotificationnull312 fun runNotificationJob_packageSourceDownloadedFile_doesNotShowNotification() { 313 installPackageViaSession( 314 APP_APK_NAME_31, 315 createAppMetadataWithNoSharing(), 316 PACKAGE_SOURCE_DOWNLOADED_FILE 317 ) 318 waitForBroadcastReceiverFinished() 319 installPackageNoBroadcast( 320 APP_APK_NAME_31, 321 createAppMetadataWithLocationSharingNoAds(), 322 PACKAGE_SOURCE_DOWNLOADED_FILE 323 ) 324 grantLocationPermission(APP_PACKAGE_NAME) 325 326 // Run the job to check whether the missing safety label for the above app update is 327 // identified and recorded. 328 runNotificationJob() 329 330 assertNotificationNotShown() 331 } 332 333 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = 334 "VanillaIceCream") 335 @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) 336 @Test runNotificationJob_packageSourceUnspecified_aslInApk_doesNotShowNotificationnull337 fun runNotificationJob_packageSourceUnspecified_aslInApk_doesNotShowNotification() { 338 installPackageViaSession( 339 APP_APK_NAME_31, 340 createAppMetadataWithNoSharing(), 341 PACKAGE_SOURCE_UNSPECIFIED 342 ) 343 waitForBroadcastReceiverFinished() 344 installPackageNoBroadcast( 345 APP_APK_NAME_31_WITH_ASL, 346 packageSource = PACKAGE_SOURCE_UNSPECIFIED 347 ) 348 grantLocationPermission(APP_PACKAGE_NAME) 349 350 // Run the job to check whether the missing safety label for the above app update is 351 // identified and recorded. 352 runNotificationJob() 353 354 assertNotificationNotShown() 355 } 356 357 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = 358 "VanillaIceCream") 359 @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) 360 @Test runNotificationJob_packageSourceOther_aslInApk_doesNotShowNotificationnull361 fun runNotificationJob_packageSourceOther_aslInApk_doesNotShowNotification() { 362 installPackageViaSession( 363 APP_APK_NAME_31, 364 createAppMetadataWithNoSharing(), 365 PACKAGE_SOURCE_OTHER 366 ) 367 waitForBroadcastReceiverFinished() 368 installPackageNoBroadcast( 369 APP_APK_NAME_31_WITH_ASL, 370 packageSource = PACKAGE_SOURCE_OTHER 371 ) 372 grantLocationPermission(APP_PACKAGE_NAME) 373 374 // Run the job to check whether the missing safety label for the above app update is 375 // identified and recorded. 376 runNotificationJob() 377 378 assertNotificationNotShown() 379 } 380 381 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = 382 "VanillaIceCream") 383 @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) 384 @Test runNotificationJob_packageSourceStore_aslInApk_doesNotShowNotificationnull385 fun runNotificationJob_packageSourceStore_aslInApk_doesNotShowNotification() { 386 installPackageViaSession( 387 APP_APK_NAME_31, 388 createAppMetadataWithNoSharing(), 389 PACKAGE_SOURCE_STORE 390 ) 391 waitForBroadcastReceiverFinished() 392 installPackageNoBroadcast( 393 APP_APK_NAME_31_WITH_ASL, 394 packageSource = PACKAGE_SOURCE_STORE 395 ) 396 grantLocationPermission(APP_PACKAGE_NAME) 397 398 // Run the job to check whether the missing safety label for the above app update is 399 // identified and recorded. 400 runNotificationJob() 401 402 assertNotificationNotShown() 403 } 404 405 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = 406 "VanillaIceCream") 407 @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) 408 @Test runNotificationJob_packageSourceLocalFile_aslInApk_doesNotShowNotificationnull409 fun runNotificationJob_packageSourceLocalFile_aslInApk_doesNotShowNotification() { 410 installPackageViaSession( 411 APP_APK_NAME_31, 412 createAppMetadataWithNoSharing(), 413 PACKAGE_SOURCE_LOCAL_FILE 414 ) 415 waitForBroadcastReceiverFinished() 416 installPackageNoBroadcast( 417 APP_APK_NAME_31_WITH_ASL, 418 packageSource = PACKAGE_SOURCE_LOCAL_FILE 419 ) 420 grantLocationPermission(APP_PACKAGE_NAME) 421 422 // Run the job to check whether the missing safety label for the above app update is 423 // identified and recorded. 424 runNotificationJob() 425 426 assertNotificationNotShown() 427 } 428 429 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = 430 "VanillaIceCream") 431 @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE) 432 @Test runNotificationJob_packageSourceDownloadedFile_aslInApk_doesNotShowNotificationnull433 fun runNotificationJob_packageSourceDownloadedFile_aslInApk_doesNotShowNotification() { 434 installPackageViaSession( 435 APP_APK_NAME_31, 436 createAppMetadataWithNoSharing(), 437 PACKAGE_SOURCE_DOWNLOADED_FILE 438 ) 439 waitForBroadcastReceiverFinished() 440 installPackageNoBroadcast( 441 APP_APK_NAME_31_WITH_ASL, 442 packageSource = PACKAGE_SOURCE_DOWNLOADED_FILE 443 ) 444 grantLocationPermission(APP_PACKAGE_NAME) 445 446 // Run the job to check whether the missing safety label for the above app update is 447 // identified and recorded. 448 runNotificationJob() 449 450 assertNotificationNotShown() 451 } 452 grantLocationPermissionnull453 private fun grantLocationPermission(packageName: String) { 454 uiAutomation.grantRuntimePermission( 455 packageName, 456 android.Manifest.permission.ACCESS_FINE_LOCATION 457 ) 458 } 459 installPackageNoBroadcastnull460 private fun installPackageNoBroadcast( 461 apkName: String, 462 appMetadata: PersistableBundle? = null, 463 packageSource: Int? = null 464 ) { 465 // Disable the safety labels feature during install to simulate installing an app without 466 // receiving an update about the change to its safety label. 467 setDeviceConfigPrivacyProperty(SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false.toString()) 468 installPackageViaSession(apkName, appMetadata, packageSource) 469 waitForBroadcastReceiverFinished() 470 setDeviceConfigPrivacyProperty(SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true.toString()) 471 } 472 assertDataSharingScreenHasUpdatesnull473 private fun assertDataSharingScreenHasUpdates() { 474 startAppDataSharingUpdatesActivity() 475 try { 476 findView(By.descContains(DATA_SHARING_UPDATES), true) 477 findView(By.textContains(DATA_SHARING_UPDATES_SUBTITLE), true) 478 findView(By.textContains(UPDATES_IN_LAST_30_DAYS), true) 479 findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true) 480 findView(By.textContains(DATA_SHARING_UPDATES_FOOTER_MESSAGE), true) 481 } finally { 482 pressBack() 483 } 484 } 485 486 companion object { 487 private const val TIMEOUT_TIME_MS = 60_000L 488 private const val SHORT_SLEEP_MS = 2000L 489 490 private const val SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID = 8 491 private const val SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID = 9 492 private const val SET_UP_SAFETY_LABEL_CHANGES_JOB = 493 "com.android.permissioncontroller.action.SET_UP_SAFETY_LABEL_CHANGES_JOB" 494 private const val SAFETY_LABEL_CHANGES_JOB_SERVICE_RECEIVER_CLASS = 495 "com.android.permissioncontroller.permission.service.v34" + 496 ".SafetyLabelChangesJobService\$Receiver" 497 private const val SAFETY_LABEL_CHANGES_NOTIFICATION_ID = 5 498 private const val JOB_STATUS_UNKNOWN = "unknown" 499 private const val JOB_STATUS_ACTIVE = "active" 500 private const val JOB_STATUS_WAITING = "waiting" 501 502 private val context: Context = InstrumentationRegistry.getTargetContext() 503 private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() uiAutomationnull504 private fun uiAutomation(): UiAutomation = instrumentation.uiAutomation 505 private val permissionControllerPackageName = 506 context.packageManager.permissionControllerPackageName 507 private val userId = Process.myUserHandle().identifier 508 509 @get:ClassRule 510 @JvmStatic 511 val ctsNotificationListenerHelper = 512 CtsNotificationListenerHelperRule( 513 InstrumentationRegistry.getInstrumentation().targetContext 514 ) 515 516 private fun waitForNotificationShown() { 517 eventually { 518 val notification = getNotification(false) 519 assertThat(notification).isNotNull() 520 } 521 } 522 assertNotificationNotShownnull523 private fun assertNotificationNotShown() { 524 eventually { 525 val notification = getNotification(false) 526 assertThat(notification).isNull() 527 } 528 } 529 getNotificationnull530 private fun getNotification(cancelNotification: Boolean) = 531 getNotificationForPackageAndId( 532 permissionControllerPackageName, 533 SAFETY_LABEL_CHANGES_NOTIFICATION_ID, 534 cancelNotification 535 ) 536 ?.notification 537 538 private fun cancelJob(jobId: Int) { 539 SystemUtil.runShellCommandOrThrow( 540 "cmd jobscheduler cancel -u $userId $permissionControllerPackageName $jobId" 541 ) 542 TestUtils.awaitJobUntilRequestedState( 543 permissionControllerPackageName, 544 jobId, 545 TIMEOUT_TIME_MS, 546 uiAutomation(), 547 JOB_STATUS_UNKNOWN 548 ) 549 } 550 runDetectUpdatesJobnull551 private fun runDetectUpdatesJob() { 552 startJob(SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID) 553 TestUtils.awaitJobUntilRequestedState( 554 permissionControllerPackageName, 555 SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID, 556 TIMEOUT_TIME_MS, 557 uiAutomation(), 558 JOB_STATUS_ACTIVE 559 ) 560 TestUtils.awaitJobUntilRequestedState( 561 permissionControllerPackageName, 562 SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID, 563 TIMEOUT_TIME_MS, 564 uiAutomation(), 565 JOB_STATUS_UNKNOWN 566 ) 567 } 568 runNotificationJobnull569 private fun runNotificationJob() { 570 startJob(SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID) 571 TestUtils.awaitJobUntilRequestedState( 572 permissionControllerPackageName, 573 SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID, 574 TIMEOUT_TIME_MS, 575 uiAutomation(), 576 JOB_STATUS_ACTIVE 577 ) 578 // TODO(b/266449833): In theory we should only have to wait for "waiting" here, but 579 // sometimes jobscheduler returns "unknown". 580 TestUtils.awaitJobUntilRequestedState( 581 permissionControllerPackageName, 582 SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID, 583 TIMEOUT_TIME_MS, 584 uiAutomation(), 585 JOB_STATUS_WAITING, 586 JOB_STATUS_UNKNOWN 587 ) 588 } 589 startJobnull590 private fun startJob(jobId: Int) { 591 val runJobCmd = 592 "cmd jobscheduler run -u $userId -f " + "$permissionControllerPackageName $jobId" 593 try { 594 SystemUtil.runShellCommandOrThrow(runJobCmd) 595 } catch (e: Throwable) { 596 throw RuntimeException(e) 597 } 598 } 599 resetPermissionControllerAndSimulateRebootnull600 private fun resetPermissionControllerAndSimulateReboot() { 601 PermissionUtils.resetPermissionControllerJob( 602 uiAutomation(), 603 permissionControllerPackageName, 604 SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID, 605 TIMEOUT_TIME_MS, 606 SET_UP_SAFETY_LABEL_CHANGES_JOB, 607 SAFETY_LABEL_CHANGES_JOB_SERVICE_RECEIVER_CLASS 608 ) 609 } 610 waitForBroadcastReceiverFinishednull611 private fun waitForBroadcastReceiverFinished() { 612 waitForBroadcasts() 613 // Add a short sleep to ensure that the SafetyLabelChangedBroadcastReceiver finishes its 614 // work based according to the current feature flag value before changing the flag 615 // value. 616 // While `waitForBroadcasts()` waits for broadcasts to be dispatched, it will not wait 617 // for 618 // the receivers' `onReceive` to finish. 619 Thread.sleep(SHORT_SLEEP_MS) 620 } 621 } 622 } 623