• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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