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