1 /* 2 * Copyright (C) 2022 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.safetycenter.testing 18 19 import android.content.Context 20 import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES 21 import android.content.pm.PackageManager.PackageInfoFlags 22 import android.content.res.Resources 23 import android.os.Build 24 import android.os.Build.VERSION_CODES.TIRAMISU 25 import android.safetycenter.SafetySourceData 26 import android.safetycenter.config.SafetyCenterConfig 27 import android.safetycenter.config.SafetySource 28 import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_DYNAMIC 29 import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY 30 import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC 31 import android.safetycenter.config.SafetySourcesGroup 32 import androidx.annotation.RequiresApi 33 import com.android.modules.utils.build.SdkLevel 34 import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName 35 import java.security.MessageDigest 36 37 /** 38 * A class that provides [SafetyCenterConfig] objects and associated constants to facilitate setting 39 * up safety sources for testing. 40 */ 41 @RequiresApi(TIRAMISU) 42 class SafetyCenterTestConfigs(private val context: Context) { 43 /** The certificate hash signing the current package. */ 44 val packageCertHash = 45 MessageDigest.getInstance("SHA256") 46 .digest( 47 context.packageManager 48 .getPackageInfo( 49 context.packageName, 50 PackageInfoFlags.of(GET_SIGNING_CERTIFICATES.toLong()) 51 ) 52 .signingInfo 53 .apkContentsSigners[0] 54 .toByteArray() 55 ) <lambda>null56 .joinToString("") { "%02x".format(it) } 57 58 /** A simple [SafetyCenterConfig] for tests with a single source of id [SINGLE_SOURCE_ID]. */ 59 val singleSourceConfig = singleSourceConfig(dynamicSafetySource(SINGLE_SOURCE_ID)) 60 61 /** 62 * A simple [SafetyCenterConfig] with an invalid intent action for tests with a single source of 63 * id [SINGLE_SOURCE_ID]. 64 */ 65 val singleSourceInvalidIntentConfig = 66 singleSourceConfig( 67 dynamicSafetySourceBuilder(SINGLE_SOURCE_ID).setIntentAction("stub").build() 68 ) 69 70 /** 71 * Same as [singleSourceConfig] but with an `intentAction` that will resolve implicitly; i.e. 72 * the source's `packageName` does not own the activity resolved by the `intentAction`. 73 */ 74 val implicitIntentSingleSourceConfig = 75 singleSourceConfig( 76 dynamicSafetySourceBuilder(SINGLE_SOURCE_ID) 77 // A valid package name that is *not* CTS. 78 .setPackageName(context.packageManager.permissionControllerPackageName) 79 // Exported activity that lives in the CTS package. The PC package does 80 // implement this intent action so the activity has to resolve 81 // implicitly. 82 .setIntentAction(ACTION_TEST_ACTIVITY_EXPORTED) 83 .build() 84 ) 85 86 /** A simple [SafetyCenterConfig] for tests with a source max severity level of 0. */ 87 val severityZeroConfig = 88 singleSourceConfig( 89 dynamicSafetySourceBuilder(SINGLE_SOURCE_ID).setMaxSeverityLevel(0).build() 90 ) 91 92 /** A simple [SafetyCenterConfig] for tests with a fake/incorrect package cert hash. */ 93 val singleSourceWithFakeCert: SafetyCenterConfig 94 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 95 get() = 96 singleSourceConfig( 97 dynamicSafetySourceBuilder(SINGLE_SOURCE_ID) 98 .addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) 99 .build() 100 ) 101 102 /** 103 * A simple [SafetyCenterConfig] for tests with a invalid package cert hash (not a hex-formatted 104 * byte string). 105 */ 106 val singleSourceWithInvalidCert: SafetyCenterConfig 107 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 108 get() = 109 singleSourceConfig( 110 dynamicSafetySourceBuilder(SINGLE_SOURCE_ID) 111 .addPackageCertificateHash(PACKAGE_CERT_HASH_INVALID) 112 .build() 113 ) 114 115 /** 116 * A simple [SafetyCenterConfig] for tests with a source that does not support refresh on page 117 * open. 118 */ 119 val noPageOpenConfig = 120 singleSourceConfig( 121 dynamicSafetySourceBuilder(SINGLE_SOURCE_ID).setRefreshOnPageOpenAllowed(false).build() 122 ) 123 124 /** A Simple [SafetyCenterConfig] with an issue only source. */ 125 val issueOnlySourceConfig = 126 singleSourceConfig(issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID).build()) 127 128 /** A Simple [SafetyCenterConfig] with an issue only source supporting all profiles. */ 129 val issueOnlySourceAllProfileConfig = 130 singleSourceConfig( 131 issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_ALL_PROFILE_SOURCE_ID).build() 132 ) 133 134 /** 135 * A Simple [SafetyCenterConfig] with an issue only source inside a [SafetySourcesGroup] with 136 * null title. 137 */ 138 val issueOnlySourceNoGroupTitleConfig = 139 SafetyCenterConfig.Builder() 140 .addSafetySourcesGroup( 141 safetySourcesGroupBuilder(SINGLE_SOURCE_GROUP_ID) 142 .setTitleResId(Resources.ID_NULL) 143 .addSafetySource( 144 issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID).build() 145 ) 146 .build() 147 ) 148 .build() 149 150 /** A dynamic source with [OTHER_PACKAGE_NAME] */ 151 val dynamicOtherPackageSafetySource = 152 dynamicSafetySourceBuilder(DYNAMIC_OTHER_PACKAGE_ID) 153 .setRefreshOnPageOpenAllowed(false) 154 .setPackageName(OTHER_PACKAGE_NAME) 155 .build() 156 157 /** A [SafetyCenterConfig] with a dynamic source in a different, missing package. */ 158 val singleSourceOtherPackageConfig = singleSourceConfig(dynamicOtherPackageSafetySource) 159 160 /** A simple [SafetyCenterConfig] with a source supporting all profiles. */ 161 val singleSourceAllProfileConfig = 162 singleSourceConfig( 163 dynamicAllProfileSafetySourceBuilder(SINGLE_SOURCE_ALL_PROFILE_ID).build() 164 ) 165 166 /** A simple [SafetyCenterConfig] for tests with multiple sources. */ 167 val multipleSourcesConfig = 168 SafetyCenterConfig.Builder() 169 .addSafetySourcesGroup( 170 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1) 171 .addSafetySource(dynamicSafetySource(SOURCE_ID_1)) 172 .addSafetySource(dynamicSafetySource(SOURCE_ID_2)) 173 .build() 174 ) 175 .addSafetySourcesGroup( 176 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_2) 177 .setTitleResId(android.R.string.copy) 178 .setSummaryResId(android.R.string.cancel) 179 .addSafetySource( 180 dynamicSafetySourceBuilder(SOURCE_ID_3) 181 .setTitleResId(android.R.string.copy) 182 .setSummaryResId(android.R.string.cancel) 183 .setRefreshOnPageOpenAllowed(false) 184 .build() 185 ) 186 .build() 187 ) 188 .build() 189 190 /** A simple [SafetyCenterConfig] with multiple sources in a single [SafetySourcesGroup]. */ 191 val multipleSourcesInSingleGroupConfig = 192 SafetyCenterConfig.Builder() 193 .addSafetySourcesGroup( 194 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1) 195 .addSafetySource(dynamicSafetySource(SOURCE_ID_1)) 196 .addSafetySource(dynamicSafetySource(SOURCE_ID_2)) 197 .addSafetySource(dynamicSafetySource(SOURCE_ID_3)) 198 .build() 199 ) 200 .build() 201 202 /** A simple [SafetyCenterConfig] for tests with multiple sources with deduplication info. */ 203 val multipleSourcesWithDeduplicationInfoConfig: SafetyCenterConfig 204 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 205 get() = 206 SafetyCenterConfig.Builder() 207 .addSafetySourcesGroup( 208 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1) 209 .addSafetySource( 210 issueOnlySafetySourceWithDuplicationInfo( 211 SOURCE_ID_1, 212 DEDUPLICATION_GROUP_1 213 ) 214 ) 215 .addSafetySource( 216 issueOnlySafetySourceWithDuplicationInfo( 217 SOURCE_ID_2, 218 DEDUPLICATION_GROUP_1 219 ) 220 ) 221 .addSafetySource( 222 issueOnlySafetySourceWithDuplicationInfo( 223 SOURCE_ID_3, 224 DEDUPLICATION_GROUP_2 225 ) 226 ) 227 .addSafetySource( 228 issueOnlySafetySourceWithDuplicationInfo( 229 SOURCE_ID_4, 230 DEDUPLICATION_GROUP_3 231 ) 232 ) 233 .build() 234 ) 235 .addSafetySourcesGroup( 236 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_2) 237 .addSafetySource( 238 issueOnlySafetySourceWithDuplicationInfo( 239 SOURCE_ID_5, 240 DEDUPLICATION_GROUP_1 241 ) 242 ) 243 .addSafetySource( 244 issueOnlySafetySourceWithDuplicationInfo( 245 SOURCE_ID_6, 246 DEDUPLICATION_GROUP_3 247 ) 248 ) 249 .build() 250 ) 251 .addSafetySourcesGroup( 252 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_3) 253 .addSafetySource( 254 issueOnlySafetySourceWithDuplicationInfo( 255 SOURCE_ID_7, 256 DEDUPLICATION_GROUP_3 257 ) 258 ) 259 .build() 260 ) 261 .build() 262 263 /** 264 * A simple [SafetyCenterConfig] for testing the Privacy subpage. Note that this config contains 265 * the [PRIVACY_SOURCE_ID_1] source that is part of the generic category, and the [SOURCE_ID_1] 266 * that is part of the data category. 267 */ 268 val privacySubpageConfig: SafetyCenterConfig 269 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 270 get() = 271 SafetyCenterConfig.Builder() 272 .addSafetySourcesGroup( 273 safetySourcesGroupBuilder(ANDROID_PRIVACY_SOURCES_GROUP_ID) 274 .addSafetySource(dynamicSafetySource(PRIVACY_SOURCE_ID_1)) 275 .addSafetySource(dynamicSafetySource(SOURCE_ID_1)) 276 .build() 277 ) 278 .build() 279 280 /** 281 * A simple [SafetyCenterConfig] without data sources for testing the Privacy subpage. Note that 282 * this config contains only [PRIVACY_SOURCE_ID_1] source that is part of the generic category. 283 * Hence it doesn't have any data category sources. 284 */ 285 val privacySubpageWithoutDataSourcesConfig: SafetyCenterConfig 286 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 287 get() = 288 SafetyCenterConfig.Builder() 289 .addSafetySourcesGroup( 290 safetySourcesGroupBuilder(ANDROID_PRIVACY_SOURCES_GROUP_ID) 291 .addSafetySource(dynamicSafetySource(PRIVACY_SOURCE_ID_1)) 292 .build() 293 ) 294 .build() 295 296 /** Source included in [dynamicSourceGroup1]. */ 297 val dynamicSource1 = dynamicSafetySource(SOURCE_ID_1) 298 299 /** Source included in [dynamicSourceGroup1]. */ 300 val dynamicSource2 = dynamicSafetySource(SOURCE_ID_2) 301 302 /** Source included in [dynamicSourceGroup2]. */ 303 val dynamicSource3 = dynamicSafetySource(SOURCE_ID_3) 304 305 private val dynamicSource4 = dynamicSafetySource(SOURCE_ID_4) 306 private val dynamicSource5 = dynamicSafetySource(SOURCE_ID_5) 307 308 /** Source group provided by [multipleSourceGroupsConfig]. */ 309 val dynamicSourceGroup1 = 310 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1) 311 .addSafetySource(dynamicSource1) 312 .addSafetySource(dynamicSource2) 313 .setTitleResId(android.R.string.copy) 314 .setSummaryResId(android.R.string.cut) 315 .build() 316 317 /** Source group provided by [multipleSourceGroupsConfig]. */ 318 val dynamicSourceGroup2 = 319 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_2) 320 .addSafetySource(dynamicSource3) 321 .setTitleResId(android.R.string.paste) 322 .setSummaryResId(android.R.string.cancel) 323 .build() 324 325 /** Source group provided by [multipleSourceGroupsConfig]. */ 326 val dynamicSourceGroup3 = 327 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_3) 328 .addSafetySource(dynamicSource4) 329 .addSafetySource(dynamicSource5) 330 .setTitleResId(android.R.string.dialog_alert_title) 331 .setSummaryResId(android.R.string.selectAll) 332 .build() 333 334 /** A simple [SafetyCenterConfig] for tests with multiple groups of multiple sources. */ 335 val multipleSourceGroupsConfig = 336 SafetyCenterConfig.Builder() 337 .addSafetySourcesGroup(dynamicSourceGroup1) 338 .addSafetySourcesGroup(dynamicSourceGroup2) 339 .addSafetySourcesGroup(dynamicSourceGroup3) 340 .build() 341 342 /** 343 * A simple [SafetyCenterConfig] for tests with multiple sources with one source having an 344 * invalid default intent. 345 */ 346 val multipleSourcesConfigWithSourceWithInvalidIntent = 347 SafetyCenterConfig.Builder() 348 .addSafetySourcesGroup( 349 safetySourcesGroupBuilder(MULTIPLE_SOURCES_GROUP_ID_1) 350 .addSafetySource( 351 dynamicSafetySourceBuilder(SOURCE_ID_1).setIntentAction("stub").build() 352 ) 353 .addSafetySource(dynamicSafetySource(SOURCE_ID_2)) 354 .build() 355 ) 356 .build() 357 358 /** Source provided by [staticSourcesConfig]. */ 359 val staticSource1 = 360 staticSafetySourceBuilder("test_static_source_id_1") 361 .setTitleResId(android.R.string.dialog_alert_title) 362 .setSummaryResId(android.R.string.autofill) 363 .build() 364 365 /** Source provided by [staticSourcesConfig]. */ 366 val staticSource2 = 367 staticSafetySourceBuilder("test_static_source_id_2") 368 .setTitleResId(android.R.string.copyUrl) 369 .setSummaryResId(android.R.string.cut) 370 .build() 371 372 /** 373 * Source group provided by [staticSourcesConfig] containing a single source [staticSource1]. 374 */ 375 val staticSourceGroup1 = 376 SafetySourcesGroup.Builder() 377 .setId("test_static_sources_group_id_1") 378 .setTitleResId(android.R.string.paste) 379 .addSafetySource(staticSource1) 380 .build() 381 382 /** 383 * Source group provided by [staticSourcesConfig] containing a single source [staticSource2]. 384 */ 385 val staticSourceGroup2 = 386 SafetySourcesGroup.Builder() 387 .setId("test_static_sources_group_id_2") 388 .setTitleResId(android.R.string.copy) 389 .addSafetySource(staticSource2) 390 .build() 391 392 /** A simple [SafetyCenterConfig] for tests with static sources. */ 393 val staticSourcesConfig = 394 SafetyCenterConfig.Builder() 395 .addSafetySourcesGroup(staticSourceGroup1) 396 .addSafetySourcesGroup(staticSourceGroup2) 397 .build() 398 399 /** 400 * A [SafetyCenterConfig] with a single static source 401 * 402 * The particular source ID is configured in the same way as sources hosted by the Settings app, 403 * to launch as if it is part of the Settings app UI. 404 */ 405 val singleStaticSettingsSource = singleSourceConfig(staticSafetySource("TestSource")) 406 407 /** [SafetyCenterConfig] used in tests for Your Work Policy Info source. */ 408 val workPolicyInfoConfig = 409 SafetyCenterConfig.Builder() 410 .addSafetySourcesGroup( 411 SafetySourcesGroup.Builder() 412 .setId("AndroidAdvancedSources") 413 .setTitleResId(android.R.string.paste) 414 .addSafetySource( 415 SafetySource.Builder(SAFETY_SOURCE_TYPE_DYNAMIC) 416 .setId("AndroidWorkPolicyInfo") 417 .setPackageName(context.packageManager.permissionControllerPackageName) 418 .setProfile(SafetySource.PROFILE_PRIMARY) 419 .setRefreshOnPageOpenAllowed(true) 420 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) 421 .build() 422 ) 423 .build() 424 ) 425 .build() 426 427 /** [SafetyCenterConfig] used in tests to replicate the lock screen source. */ 428 val settingsLockScreenSourceConfig = 429 SafetyCenterConfig.Builder() 430 .addSafetySourcesGroup( 431 safetySourcesGroupBuilder(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID) 432 .addSafetySource( 433 dynamicSafetySourceBuilder("AndroidLockScreen") 434 .setPackageName(context.getSettingsPackageName()) 435 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) 436 .build() 437 ) 438 .build() 439 ) 440 .build() 441 442 /** [SafetyCenterConfig] used in tests to replicate the lock screen sources group. */ 443 val androidLockScreenSourcesConfig = 444 SafetyCenterConfig.Builder() 445 .addSafetySourcesGroup( 446 safetySourcesGroupBuilder(ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID) 447 // This is needed to have a stateful group with an empty summary 448 .setSummaryResId(Resources.ID_NULL) 449 .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) 450 .addSafetySource( 451 dynamicSafetySourceBuilder(DYNAMIC_BAREBONE_ID) 452 .setRefreshOnPageOpenAllowed(false) 453 .build() 454 ) 455 .addSafetySource( 456 dynamicSafetySourceBuilder(DYNAMIC_HIDDEN_ID) 457 .setTitleResId(Resources.ID_NULL) 458 .setSummaryResId(Resources.ID_NULL) 459 .setIntentAction(null) 460 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) 461 .build() 462 ) 463 .addSafetySource( 464 dynamicSafetySourceBuilder(DYNAMIC_DISABLED_ID) 465 .setIntentAction(null) 466 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) 467 .build() 468 ) 469 .build() 470 ) 471 .build() 472 473 /** A simple [SafetyCenterConfig] used in tests that stress the group summary logic. */ 474 val summaryTestConfig = 475 SafetyCenterConfig.Builder() 476 .addSafetySourcesGroup( 477 safetySourcesGroupBuilder(SUMMARY_TEST_GROUP_ID) 478 .addSafetySource(dynamicSafetySource(SOURCE_ID_1)) 479 .addSafetySource(dynamicSafetySource(SOURCE_ID_2)) 480 .addSafetySource(dynamicSafetySource(SOURCE_ID_3)) 481 .addSafetySource(dynamicSafetySource(SOURCE_ID_4)) 482 .addSafetySource(dynamicSafetySource(SOURCE_ID_5)) 483 .addSafetySource(dynamicSafetySource(SOURCE_ID_6)) 484 .addSafetySource(dynamicSafetySource(SOURCE_ID_7)) 485 .addSafetySource(staticSafetySource(STATIC_IN_STATEFUL_ID)) 486 .build() 487 ) 488 .build() 489 490 /** 491 * A complex [SafetyCenterConfig] exploring different combinations of valid sources and groups. 492 */ 493 val complexConfig = 494 SafetyCenterConfig.Builder() 495 .addSafetySourcesGroup( 496 safetySourcesGroupBuilder(DYNAMIC_GROUP_ID) 497 .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) 498 .addSafetySource( 499 dynamicSafetySourceBuilder(DYNAMIC_BAREBONE_ID) 500 .setRefreshOnPageOpenAllowed(false) 501 .build() 502 ) 503 .addSafetySource( 504 dynamicSafetySourceBuilder(DYNAMIC_ALL_OPTIONAL_ID) 505 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) 506 .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION) 507 .setSearchTermsResId(android.R.string.ok) 508 .setLoggingAllowed(false) <lambda>null509 .apply { 510 if (SdkLevel.isAtLeastU()) { 511 setNotificationsAllowed(true) 512 setDeduplicationGroup("group") 513 addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) 514 addPackageCertificateHash(packageCertHash) 515 } 516 } 517 .build() 518 ) 519 .addSafetySource( 520 dynamicSafetySourceBuilder(DYNAMIC_DISABLED_ID) 521 .setIntentAction(null) 522 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) 523 .build() 524 ) 525 .addSafetySource( 526 dynamicSafetySourceBuilder(DYNAMIC_HIDDEN_ID) 527 .setTitleResId(Resources.ID_NULL) 528 .setSummaryResId(Resources.ID_NULL) 529 .setIntentAction(null) 530 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) 531 .build() 532 ) 533 .addSafetySource( 534 dynamicSafetySourceBuilder(DYNAMIC_HIDDEN_WITH_SEARCH_ID) 535 .setSummaryResId(Resources.ID_NULL) 536 .setIntentAction(null) 537 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) 538 .setSearchTermsResId(android.R.string.ok) 539 .build() 540 ) 541 .addSafetySource(dynamicOtherPackageSafetySource) 542 .build() 543 ) 544 .addSafetySourcesGroup( 545 safetySourcesGroupBuilder(STATIC_GROUP_ID) <lambda>null546 .apply { 547 if (SdkLevel.isAtLeastU()) { 548 setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_STATELESS) 549 setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) 550 } else { 551 setSummaryResId(Resources.ID_NULL) 552 } 553 } 554 .addSafetySource( 555 staticSafetySourceBuilder(STATIC_BAREBONE_ID) 556 .setSummaryResId(Resources.ID_NULL) 557 .build() 558 ) 559 .addSafetySource( 560 staticSafetySourceBuilder(STATIC_ALL_OPTIONAL_ID) 561 .setSearchTermsResId(android.R.string.ok) <lambda>null562 .apply { 563 if (SdkLevel.isAtLeastU()) setPackageName(context.packageName) 564 } 565 .build() 566 ) 567 .build() 568 ) 569 .addSafetySourcesGroup( 570 SafetySourcesGroup.Builder() 571 .setId(ISSUE_ONLY_GROUP_ID) <lambda>null572 .apply { 573 if (SdkLevel.isAtLeastU()) { 574 setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) 575 setTitleResId(android.R.string.ok) 576 setSummaryResId(android.R.string.ok) 577 setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) 578 } 579 } 580 .addSafetySource( 581 issueOnlySafetySourceBuilder(ISSUE_ONLY_BAREBONE_ID) 582 .setRefreshOnPageOpenAllowed(false) 583 .build() 584 ) 585 .addSafetySource( 586 issueOnlySafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID) 587 .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION) 588 .setLoggingAllowed(false) <lambda>null589 .apply { 590 if (SdkLevel.isAtLeastU()) { 591 setNotificationsAllowed(true) 592 setDeduplicationGroup("group") 593 addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) 594 addPackageCertificateHash(packageCertHash) 595 } 596 } 597 .build() 598 ) 599 .build() 600 ) 601 .addSafetySourcesGroup( 602 safetySourcesGroupBuilder(MIXED_STATEFUL_GROUP_ID) 603 .addSafetySource(dynamicSafetySource(DYNAMIC_IN_STATEFUL_ID)) 604 .addSafetySource(staticSafetySource(STATIC_IN_STATEFUL_ID)) 605 .build() 606 ) 607 .addSafetySourcesGroup( 608 safetySourcesGroupBuilder(MIXED_STATELESS_GROUP_ID) 609 .setSummaryResId(Resources.ID_NULL) 610 .addSafetySource(dynamicSafetySource(DYNAMIC_IN_STATELESS_ID)) 611 .addSafetySource(staticSafetySource(STATIC_IN_STATELESS_ID)) 612 .addSafetySource(issueOnlySafetySource(ISSUE_ONLY_IN_STATELESS_ID)) 613 .build() 614 ) 615 .build() 616 617 /** 618 * A complex [SafetyCenterConfig] exploring different combinations of valid sources and groups. 619 * Including sources that support all profiles. 620 */ 621 val complexAllProfileConfig = 622 SafetyCenterConfig.Builder() 623 .addSafetySourcesGroup( 624 safetySourcesGroupBuilder(DYNAMIC_GROUP_ID) 625 .addSafetySource( 626 dynamicSafetySourceBuilder(DYNAMIC_BAREBONE_ID) 627 .setRefreshOnPageOpenAllowed(false) 628 .build() 629 ) 630 .addSafetySource( 631 dynamicAllProfileSafetySourceBuilder(DYNAMIC_DISABLED_ID) 632 .setIntentAction(null) 633 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_DISABLED) 634 .build() 635 ) 636 .addSafetySource( 637 dynamicAllProfileSafetySourceBuilder(DYNAMIC_HIDDEN_ID) 638 .setTitleResId(Resources.ID_NULL) 639 .setTitleForWorkResId(Resources.ID_NULL) 640 .setSummaryResId(Resources.ID_NULL) 641 .setIntentAction(null) 642 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) 643 .build() 644 ) 645 .build() 646 ) 647 .addSafetySourcesGroup( 648 safetySourcesGroupBuilder(STATIC_GROUP_ID) 649 .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) 650 .addSafetySource( 651 staticSafetySourceBuilder(STATIC_BAREBONE_ID) 652 .setSummaryResId(Resources.ID_NULL) 653 .build() 654 ) 655 .addSafetySource( 656 staticAllProfileSafetySourceBuilder(STATIC_ALL_OPTIONAL_ID) 657 .setSearchTermsResId(android.R.string.ok) <lambda>null658 .apply { 659 if (SdkLevel.isAtLeastU()) setPackageName(context.packageName) 660 } 661 .build() 662 ) 663 .build() 664 ) 665 .addSafetySourcesGroup( 666 SafetySourcesGroup.Builder() 667 .setId(ISSUE_ONLY_GROUP_ID) 668 .addSafetySource( 669 issueOnlySafetySourceBuilder(ISSUE_ONLY_BAREBONE_ID) 670 .setRefreshOnPageOpenAllowed(false) 671 .build() 672 ) 673 .addSafetySource( 674 issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_ALL_OPTIONAL_ID) 675 .setMaxSeverityLevel(SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION) 676 .setLoggingAllowed(false) <lambda>null677 .apply { 678 if (SdkLevel.isAtLeastU()) { 679 setNotificationsAllowed(true) 680 setDeduplicationGroup("group") 681 addPackageCertificateHash(PACKAGE_CERT_HASH_FAKE) 682 addPackageCertificateHash(packageCertHash) 683 } 684 } 685 .build() 686 ) 687 .build() 688 ) 689 .addSafetySourcesGroup( 690 safetySourcesGroupBuilder(MIXED_STATELESS_GROUP_ID) 691 .setSummaryResId(Resources.ID_NULL) 692 .addSafetySource( 693 dynamicAllProfileSafetySourceBuilder(DYNAMIC_IN_STATELESS_ID).build() 694 ) 695 .addSafetySource( 696 staticAllProfileSafetySourceBuilder(STATIC_IN_STATELESS_ID).build() 697 ) 698 .addSafetySource( 699 issueOnlyAllProfileSafetySourceBuilder(ISSUE_ONLY_IN_STATELESS_ID).build() 700 ) 701 .build() 702 ) 703 .build() 704 705 /** A [SafetyCenterConfig] containing only hidden sources. */ 706 val hiddenOnlyConfig = 707 SafetyCenterConfig.Builder() 708 .addSafetySourcesGroup( 709 safetySourcesGroupBuilder("some_collapsible_group") 710 .addSafetySource( 711 dynamicSafetySourceBuilder("some_hidden_source") 712 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) 713 .build() 714 ) 715 .build() 716 ) 717 .addSafetySourcesGroup( 718 safetySourcesGroupBuilder("some_rigid_group") 719 .setSummaryResId(Resources.ID_NULL) 720 .addSafetySource( 721 dynamicSafetySourceBuilder("another_hidden_source") 722 .setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN) 723 .build() 724 ) 725 .build() 726 ) 727 .build() 728 dynamicSafetySourcenull729 private fun dynamicSafetySource(id: String) = dynamicSafetySourceBuilder(id).build() 730 731 fun dynamicSafetySourceBuilder(id: String) = 732 SafetySource.Builder(SAFETY_SOURCE_TYPE_DYNAMIC) 733 .setId(id) 734 .setPackageName(context.packageName) 735 .setTitleResId(android.R.string.ok) 736 .setSummaryResId(android.R.string.ok) 737 .setIntentAction(ACTION_TEST_ACTIVITY) 738 .setProfile(SafetySource.PROFILE_PRIMARY) 739 .setRefreshOnPageOpenAllowed(true) 740 741 private fun dynamicAllProfileSafetySourceBuilder(id: String) = 742 dynamicSafetySourceBuilder(id) 743 .setProfile(SafetySource.PROFILE_ALL) 744 .setTitleForWorkResId(android.R.string.paste) 745 746 private fun staticSafetySource(id: String) = staticSafetySourceBuilder(id).build() 747 748 private fun staticSafetySourceBuilder(id: String) = 749 SafetySource.Builder(SAFETY_SOURCE_TYPE_STATIC) 750 .setId(id) 751 .setTitleResId(android.R.string.ok) 752 .setSummaryResId(android.R.string.ok) 753 .setIntentAction(ACTION_TEST_ACTIVITY) 754 .setProfile(SafetySource.PROFILE_PRIMARY) 755 756 private fun staticAllProfileSafetySourceBuilder(id: String) = 757 staticSafetySourceBuilder(id) 758 .setProfile(SafetySource.PROFILE_ALL) 759 .setTitleForWorkResId(android.R.string.paste) 760 761 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 762 private fun issueOnlySafetySourceWithDuplicationInfo(id: String, deduplicationGroup: String) = 763 issueOnlySafetySourceBuilder(id).setDeduplicationGroup(deduplicationGroup).build() 764 765 private fun issueOnlySafetySource(id: String) = issueOnlySafetySourceBuilder(id).build() 766 767 private fun issueOnlySafetySourceBuilder(id: String) = 768 SafetySource.Builder(SAFETY_SOURCE_TYPE_ISSUE_ONLY) 769 .setId(id) 770 .setPackageName(context.packageName) 771 .setProfile(SafetySource.PROFILE_PRIMARY) 772 .setRefreshOnPageOpenAllowed(true) 773 774 private fun issueOnlyAllProfileSafetySourceBuilder(id: String) = 775 issueOnlySafetySourceBuilder(id).setProfile(SafetySource.PROFILE_ALL) 776 777 private fun safetySourcesGroupBuilder(id: String) = 778 SafetySourcesGroup.Builder() 779 .setId(id) 780 .setTitleResId(android.R.string.ok) 781 .setSummaryResId(android.R.string.ok) 782 783 fun singleSourceConfig(safetySource: SafetySource) = 784 SafetyCenterConfig.Builder() 785 .addSafetySourcesGroup( 786 safetySourcesGroupBuilder(SINGLE_SOURCE_GROUP_ID) 787 .addSafetySource(safetySource) 788 .build() 789 ) 790 .build() 791 792 companion object { 793 /** ID of a source not used in any config. */ 794 const val SAMPLE_SOURCE_ID = "test_sample_source_id" 795 796 /** Activity action: Launches the [TestActivity] used to check redirects in tests. */ 797 const val ACTION_TEST_ACTIVITY = "com.android.safetycenter.testing.action.TEST_ACTIVITY" 798 799 /** 800 * Activity action: Launches the [TestActivity] used to check redirects in tests, but with 801 * an exported activity alias. 802 */ 803 const val ACTION_TEST_ACTIVITY_EXPORTED = 804 "com.android.safetycenter.testing.action.TEST_ACTIVITY_EXPORTED" 805 806 /** 807 * ID of the only source provided in [singleSourceConfig], [severityZeroConfig] and 808 * [noPageOpenConfig]. 809 */ 810 const val SINGLE_SOURCE_ID = "test_single_source_id" 811 812 /** ID of the only source provided in [singleSourceAllProfileConfig]. */ 813 const val SINGLE_SOURCE_ALL_PROFILE_ID = "test_single_source_all_profile_id" 814 815 /** ID of the only source provided in [issueOnlySourceAllProfileConfig]. */ 816 const val ISSUE_ONLY_ALL_PROFILE_SOURCE_ID = "test_issue_only_all_profile_id" 817 818 /** 819 * ID of the only [SafetySourcesGroup] provided by [singleSourceConfig], 820 * [severityZeroConfig] and [noPageOpenConfig]. 821 */ 822 const val SINGLE_SOURCE_GROUP_ID = "test_single_source_group_id" 823 824 /** 825 * SHA256 hash of a package certificate. 826 * 827 * <p>This is a fake certificate, and can be used to test failure cases, or to test a list 828 * of certificates when only one match is required. 829 */ 830 const val PACKAGE_CERT_HASH_FAKE = "feed12" 831 832 /** An invalid SHA256 hash (not a byte string, not even number of chars). */ 833 const val PACKAGE_CERT_HASH_INVALID = "0124ppl" 834 835 /** ID of a source provided by [multipleSourcesConfig] and [summaryTestConfig]. */ 836 const val SOURCE_ID_1 = "test_source_id_1" 837 838 /** ID of a source provided by [multipleSourcesConfig] and [summaryTestConfig]. */ 839 const val SOURCE_ID_2 = "test_source_id_2" 840 841 /** ID of a source provided by [multipleSourcesConfig] and [summaryTestConfig]. */ 842 const val SOURCE_ID_3 = "test_source_id_3" 843 844 /** ID of a source provided by [summaryTestConfig]. */ 845 const val SOURCE_ID_4 = "test_source_id_4" 846 847 /** ID of a source provided by [summaryTestConfig]. */ 848 const val SOURCE_ID_5 = "test_source_id_5" 849 850 /** ID of a source provided by [summaryTestConfig]. */ 851 const val SOURCE_ID_6 = "test_source_id_6" 852 853 /** ID of a source provided by [summaryTestConfig]. */ 854 const val SOURCE_ID_7 = "test_source_id_7" 855 856 /** 857 * ID of a source provided by [privacySubpageConfig] and 858 * [privacySubpageWithoutDataSourcesConfig]. 859 */ 860 const val PRIVACY_SOURCE_ID_1 = "AndroidPermissionUsage" 861 862 /** 863 * ID of a [SafetySourcesGroup] provided by [multipleSourcesConfig], containing two sources 864 * of ids [SOURCE_ID_1] and [SOURCE_ID_2]. 865 */ 866 const val MULTIPLE_SOURCES_GROUP_ID_1 = "test_multiple_sources_group_id_1" 867 868 /** 869 * ID of a [SafetySourcesGroup] provided by [multipleSourcesConfig], containing a single 870 * source of id [SOURCE_ID_3]. 871 */ 872 const val MULTIPLE_SOURCES_GROUP_ID_2 = "test_multiple_sources_group_id_2" 873 874 /** 875 * ID of a [SafetySourcesGroup] provided by [multipleSourcesConfig], containing two sources 876 * of ids [SOURCE_ID_4] and [SOURCE_ID_5]. 877 */ 878 const val MULTIPLE_SOURCES_GROUP_ID_3 = "test_multiple_sources_group_id_3" 879 880 /** 881 * ID of a [SafetySourcesGroup] provided by [summaryTestGroupConfig], containing sources: 882 * [SOURCE_ID_1], [SOURCE_ID_2], [SOURCE_ID_3], [SOURCE_ID_4], [SOURCE_ID_5], [SOURCE_ID_6], 883 * [SOURCE_ID_7], [STATIC_IN_STATEFUL_ID]. 884 */ 885 const val SUMMARY_TEST_GROUP_ID = "summary_test_group_id" 886 887 /** 888 * ID of a [SafetySourcesGroup] provided by [complexConfig], containing sources: 889 * [DYNAMIC_BAREBONE_ID], [DYNAMIC_ALL_OPTIONAL_ID], [DYNAMIC_DISABLED_ID], 890 * [DYNAMIC_HIDDEN_ID], [DYNAMIC_HIDDEN_WITH_SEARCH_ID], [DYNAMIC_OTHER_PACKAGE_ID]. And 891 * provided by [complexAllProfileConfig], containing sources: [DYNAMIC_BAREBONE_ID], 892 * [DYNAMIC_DISABLED_ID], [DYNAMIC_HIDDEN_ID]. 893 */ 894 const val DYNAMIC_GROUP_ID = "dynamic" 895 896 /** 897 * ID of a [SafetySourcesGroup] provided by [complexConfig] and [complexAllProfileConfig], 898 * containing sources: [STATIC_BAREBONE_ID], [STATIC_ALL_OPTIONAL_ID]. 899 */ 900 const val STATIC_GROUP_ID = "static" 901 902 /** 903 * ID of a [SafetySourcesGroup] provided by [complexConfig] and [complexAllProfileConfig], 904 * containing sources: [ISSUE_ONLY_BAREBONE_ID], [ISSUE_ONLY_ALL_OPTIONAL_ID]. 905 */ 906 const val ISSUE_ONLY_GROUP_ID = "issue_only" 907 908 /** 909 * ID of a [SafetySourcesGroup] provided by [complexConfig], containing sources: 910 * [DYNAMIC_IN_STATEFUL_ID], [STATIC_IN_STATEFUL_ID]. 911 */ 912 const val MIXED_STATEFUL_GROUP_ID = "mixed_stateful" 913 914 /** 915 * ID of a [SafetySourcesGroup] provided by [complexConfig] and [complexAllProfileConfig], 916 * containing sources: [DYNAMIC_IN_STATELESS_ID], [STATIC_IN_STATELESS_ID], 917 * [ISSUE_ONLY_IN_STATELESS_ID]. 918 */ 919 const val MIXED_STATELESS_GROUP_ID = "mixed_stateless" 920 921 /** 922 * ID of a source provided by [complexConfig], [complexAllProfileConfig], and 923 * [androidLockScreenSourcesConfig], this is a dynamic, primary profile only, visible source 924 * for which only the required fields are set. 925 */ 926 const val DYNAMIC_BAREBONE_ID = "dynamic_barebone" 927 928 /** 929 * ID of a source provided by [complexConfig] and [singleSourceOtherPackageConfig], this is 930 * a dynamic, primary profile only, visible source belonging to the [OTHER_PACKAGE_NAME] 931 * package for which only the required fields are set. 932 */ 933 const val DYNAMIC_OTHER_PACKAGE_ID = "dynamic_other_package" 934 935 /** 936 * ID of a source provided by [complexConfig], this is a dynamic, primary profile only, 937 * disabled by default source for which all the required and optional fields are set. 938 * Notably, this includes the refresh on page open flag and a max severity level of 939 * recommendation. 940 */ 941 const val DYNAMIC_ALL_OPTIONAL_ID = "dynamic_all_optional" 942 943 /** 944 * ID of a source provided by [complexConfig], [complexAllProfileConfig], and 945 * [androidLockScreenSourcesConfig], this is a dynamic, disabled by default source for which 946 * only the required fields are set. 947 */ 948 const val DYNAMIC_DISABLED_ID = "dynamic_disabled" 949 950 /** 951 * ID of a source provided by [complexConfig], [complexAllProfileConfig], and 952 * [androidLockScreenSourcesConfig], this ism a dynamic, hidden by default source for which 953 * only the required fields are set. 954 */ 955 const val DYNAMIC_HIDDEN_ID = "dynamic_hidden" 956 957 /** 958 * ID of a source provided by [complexConfig], this is a dynamic, primary profile only, 959 * hidden by default source for which all the required and optional fields are set. 960 */ 961 const val DYNAMIC_HIDDEN_WITH_SEARCH_ID = "dynamic_hidden_with_search" 962 963 /** 964 * ID of a source provided by [complexConfig] and [complexAllProfileConfig], this is a 965 * static, primary profile only source for which only the required fields are set. 966 */ 967 const val STATIC_BAREBONE_ID = "static_barebone" 968 969 /** 970 * ID of a source provided by [complexConfig] and [complexAllProfileConfig], this is a 971 * static source for which all the required and optional fields are set. 972 */ 973 const val STATIC_ALL_OPTIONAL_ID = "static_all_optional" 974 975 /** 976 * ID of a source provided by [complexConfig] and [complexAllProfileConfig], this is an 977 * issue-only, primary profile only source for which only the required fields are set. 978 */ 979 const val ISSUE_ONLY_BAREBONE_ID = "issue_only_barebone" 980 981 /** 982 * ID of a source provided by [complexConfig] and [complexAllProfileConfig], this is an 983 * issue-only source for which all the required and optional fields are set. Notably, this 984 * includes the refresh on page open flag and a max severity level of recommendation. 985 */ 986 const val ISSUE_ONLY_ALL_OPTIONAL_ID = "issue_only_all_optional" 987 988 /** 989 * ID of a source provided by [complexConfig], this is a generic, dynamic, primary profile 990 * only, visible source. 991 */ 992 const val DYNAMIC_IN_STATEFUL_ID = "dynamic_in_stateful" 993 994 /** 995 * ID of a source provided by [complexConfig] and [complexAllProfileConfig], this is a 996 * generic, dynamic, visible source. 997 */ 998 const val DYNAMIC_IN_STATELESS_ID = "dynamic_in_stateless" 999 1000 /** 1001 * ID of a source provided by [complexConfig] and [complexAllProfileConfig], this is an 1002 * issue-only source. 1003 */ 1004 const val ISSUE_ONLY_IN_STATELESS_ID = "issue_only_in_stateless" 1005 1006 /** 1007 * ID of a source provided by [complexConfig] and [summaryTestConfig], this is a generic, 1008 * static, primary profile only source. 1009 */ 1010 const val STATIC_IN_STATEFUL_ID = "static_in_stateful" 1011 1012 /** 1013 * ID of a source provided by [complexConfig] and [complexAllProfileConfig], this is a 1014 * generic, static source. 1015 */ 1016 const val STATIC_IN_STATELESS_ID = "static_in_stateless" 1017 1018 /** Package name for the [DYNAMIC_OTHER_PACKAGE_ID] source. */ 1019 const val OTHER_PACKAGE_NAME = "other_package_name" 1020 1021 private const val DEDUPLICATION_GROUP_1 = "deduplication_group_1" 1022 private const val DEDUPLICATION_GROUP_2 = "deduplication_group_2" 1023 private const val DEDUPLICATION_GROUP_3 = "deduplication_group_3" 1024 1025 /** 1026 * ID of a [SafetySourcesGroup] provided by [settingsLockScreenSourceConfig] and 1027 * [androidLockScreenSourcesConfig], to replicate the lock screen sources group. 1028 */ 1029 const val ANDROID_LOCK_SCREEN_SOURCES_GROUP_ID = "AndroidLockScreenSources" 1030 1031 /** 1032 * ID of a [SafetySourcesGroup] provided by [privacySubpageConfig] and 1033 * [privacySubpageWithoutDataSourcesConfig], to replicate the privacy sources group. 1034 */ 1035 const val ANDROID_PRIVACY_SOURCES_GROUP_ID = "AndroidPrivacySources" 1036 } 1037 } 1038