• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2021 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.server.pm.test.verify.domain
18 
19 import android.content.Intent
20 import android.content.pm.PackageManager
21 import android.content.pm.PackageUserState
22 import android.content.pm.Signature
23 import android.content.pm.parsing.component.ParsedActivity
24 import android.content.pm.parsing.component.ParsedIntentInfo
25 import android.content.pm.verify.domain.DomainOwner
26 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
27 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
28 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_SUCCESS
29 import android.content.pm.verify.domain.DomainVerificationInfo.STATE_UNMODIFIABLE
30 import android.content.pm.verify.domain.DomainVerificationManager
31 import android.content.pm.verify.domain.DomainVerificationState
32 import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE
33 import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_SELECTED
34 import android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_VERIFIED
35 import android.os.Build
36 import android.os.PatternMatcher
37 import android.os.Process
38 import android.util.ArraySet
39 import android.util.Xml
40 import com.android.server.pm.PackageSetting
41 import com.android.server.pm.parsing.pkg.AndroidPackage
42 import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
43 import com.android.server.pm.verify.domain.DomainVerificationService
44 import com.android.server.testutils.mockThrowOnUnmocked
45 import com.android.server.testutils.whenever
46 import com.google.common.truth.Truth.assertThat
47 import org.junit.Test
48 import org.mockito.ArgumentMatchers.any
49 import org.mockito.ArgumentMatchers.anyInt
50 import org.mockito.ArgumentMatchers.anyLong
51 import org.mockito.ArgumentMatchers.anyString
52 import java.io.ByteArrayInputStream
53 import java.io.ByteArrayOutputStream
54 import java.util.UUID
55 
56 class DomainVerificationPackageTest {
57 
58     companion object {
59         private const val PKG_ONE = "com.test.one"
60         private const val PKG_TWO = "com.test.two"
61         private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
62         private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
63         private const val SIGNATURE_ONE = "AA"
64         private const val DIGEST_ONE =
65             "BCEEF655B5A034911F1C3718CE056531B45EF03B4C7B1F15629E867294011A7D"
66         private const val SIGNATURE_TWO = "BB"
67 
68         private val DOMAIN_BASE = DomainVerificationPackageTest::class.java.packageName
69         private val DOMAIN_1 = "one.$DOMAIN_BASE"
70         private val DOMAIN_2 = "two.$DOMAIN_BASE"
71         private val DOMAIN_3 = "three.$DOMAIN_BASE"
72         private val DOMAIN_4 = "four.$DOMAIN_BASE"
73 
74         private const val USER_ID = 0
75     }
76 
77     private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE)
78     private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO)
79 
80     @Test
81     fun addPackageFirstTime() {
82         val service = makeService(pkg1, pkg2)
83         service.addPackage(pkg1)
84         val info = service.getInfo(pkg1.getName())
85         assertThat(info.packageName).isEqualTo(pkg1.getName())
86         assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
87         assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
88                 DOMAIN_1 to STATE_NO_RESPONSE,
89                 DOMAIN_2 to STATE_NO_RESPONSE,
90         ))
91 
92         val userState = service.getUserState(pkg1.getName())
93         assertThat(userState.packageName).isEqualTo(pkg1.getName())
94         assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
95         assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
96         assertThat(userState.user.identifier).isEqualTo(USER_ID)
97         assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
98                 DOMAIN_1 to DOMAIN_STATE_NONE,
99                 DOMAIN_2 to DOMAIN_STATE_NONE,
100         ))
101 
102         assertThat(service.queryValidVerificationPackageNames())
103                 .containsExactly(pkg1.getName())
104     }
105 
106     @Test
107     fun addPackageSystemConfigured() {
108         val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, isSystemApp = false)
109         val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO, isSystemApp = true)
110 
111         val service = makeService(
112             systemConfiguredPackageNames = ArraySet(setOf(pkg1.getName(), pkg2.getName())),
113             pkg1, pkg2
114         )
115         service.addPackage(pkg1)
116         service.addPackage(pkg2)
117 
118         service.getInfo(pkg1.getName()).apply {
119             assertThat(packageName).isEqualTo(pkg1.getName())
120             assertThat(identifier).isEqualTo(pkg1.domainSetId)
121             assertThat(hostToStateMap).containsExactlyEntriesIn(
122                 mapOf(
123                     DOMAIN_1 to STATE_NO_RESPONSE,
124                     DOMAIN_2 to STATE_NO_RESPONSE,
125                 )
126             )
127         }
128 
129         service.getUserState(pkg1.getName()).apply {
130             assertThat(packageName).isEqualTo(pkg1.getName())
131             assertThat(identifier).isEqualTo(pkg1.domainSetId)
132             assertThat(isLinkHandlingAllowed).isEqualTo(true)
133             assertThat(user.identifier).isEqualTo(USER_ID)
134             assertThat(hostToStateMap).containsExactlyEntriesIn(
135                 mapOf(
136                     DOMAIN_1 to DOMAIN_STATE_NONE,
137                     DOMAIN_2 to DOMAIN_STATE_NONE,
138                 )
139             )
140         }
141 
142         service.getInfo(pkg2.getName()).apply {
143             assertThat(packageName).isEqualTo(pkg2.getName())
144             assertThat(identifier).isEqualTo(pkg2.domainSetId)
145             assertThat(hostToStateMap).containsExactlyEntriesIn(
146                 mapOf(
147                     DOMAIN_1 to STATE_UNMODIFIABLE,
148                     DOMAIN_2 to STATE_UNMODIFIABLE,
149                 )
150             )
151         }
152 
153         service.getUserState(pkg2.getName()).apply {
154             assertThat(packageName).isEqualTo(pkg2.getName())
155             assertThat(identifier).isEqualTo(pkg2.domainSetId)
156             assertThat(isLinkHandlingAllowed).isEqualTo(true)
157             assertThat(user.identifier).isEqualTo(USER_ID)
158             assertThat(hostToStateMap).containsExactlyEntriesIn(
159                 mapOf(
160                     DOMAIN_1 to DOMAIN_STATE_VERIFIED,
161                     DOMAIN_2 to DOMAIN_STATE_VERIFIED,
162                 )
163             )
164         }
165 
166         assertThat(service.queryValidVerificationPackageNames())
167                 .containsExactly(pkg1.getName(), pkg2.getName())
168     }
169 
170     @Test
171     fun addPackageRestoredMatchingSignature() {
172         // language=XML
173         val xml = """
174             <?xml?>
175             <domain-verifications>
176                 <active>
177                     <package-state
178                         packageName="${pkg1.getName()}"
179                         id="${pkg1.domainSetId}"
180                         signature="$DIGEST_ONE"
181                         >
182                         <state>
183                             <domain name="$DOMAIN_1" state="1"/>
184                         </state>
185                     </package-state>
186                 </active>
187             </domain-verifications>
188         """
189 
190         val service = makeService(pkg1, pkg2)
191         service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
192         service.addPackage(pkg1)
193         val info = service.getInfo(pkg1.getName())
194         assertThat(info.packageName).isEqualTo(pkg1.getName())
195         assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
196         assertThat(info.hostToStateMap).containsExactlyEntriesIn(
197             mapOf(
198                 DOMAIN_1 to STATE_MODIFIABLE_VERIFIED,
199                 DOMAIN_2 to STATE_NO_RESPONSE,
200             )
201         )
202 
203         val userState = service.getUserState(pkg1.getName())
204         assertThat(userState.packageName).isEqualTo(pkg1.getName())
205         assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
206         assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
207         assertThat(userState.user.identifier).isEqualTo(USER_ID)
208         assertThat(userState.hostToStateMap).containsExactlyEntriesIn(
209             mapOf(
210                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
211                 DOMAIN_2 to DOMAIN_STATE_NONE,
212             )
213         )
214 
215         assertThat(service.queryValidVerificationPackageNames())
216             .containsExactly(pkg1.getName())
217     }
218 
219     @Test
220     fun addPackageRestoredMismatchSignature() {
221         // language=XML
222         val xml = """
223             <?xml?>
224             <domain-verifications>
225                 <active>
226                     <package-state
227                         packageName="${pkg1.getName()}"
228                         id="${pkg1.domainSetId}"
229                         signature="INVALID_SIGNATURE"
230                         >
231                         <state>
232                             <domain name="$DOMAIN_1" state="1"/>
233                         </state>
234                     </package-state>
235                 </active>
236             </domain-verifications>
237         """
238 
239         val service = makeService(pkg1, pkg2)
240         service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
241         service.addPackage(pkg1)
242         val info = service.getInfo(pkg1.getName())
243         assertThat(info.packageName).isEqualTo(pkg1.getName())
244         assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
245         assertThat(info.hostToStateMap).containsExactlyEntriesIn(
246             mapOf(
247                 DOMAIN_1 to STATE_NO_RESPONSE,
248                 DOMAIN_2 to STATE_NO_RESPONSE,
249             )
250         )
251 
252         val userState = service.getUserState(pkg1.getName())
253         assertThat(userState.packageName).isEqualTo(pkg1.getName())
254         assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
255         assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
256         assertThat(userState.user.identifier).isEqualTo(USER_ID)
257         assertThat(userState.hostToStateMap).containsExactlyEntriesIn(
258             mapOf(
259                 DOMAIN_1 to DOMAIN_STATE_NONE,
260                 DOMAIN_2 to DOMAIN_STATE_NONE,
261             )
262         )
263 
264         assertThat(service.queryValidVerificationPackageNames())
265             .containsExactly(pkg1.getName())
266     }
267 
268     @Test
269     fun addPackageActive() {
270         // language=XML
271         val xml = """
272             <?xml?>
273             <domain-verifications>
274                 <active>
275                     <package-state
276                         packageName="${pkg1.getName()}"
277                         id="${pkg1.domainSetId}"
278                         >
279                         <state>
280                             <domain name="$DOMAIN_1" state="$STATE_SUCCESS"/>
281                         </state>
282                         <user-states>
283                             <user-state userId="$USER_ID" allowLinkHandling="false">
284                                 <enabled-hosts>
285                                     <host name="$DOMAIN_2"/>
286                                 </enabled-hosts>
287                             </user-state>
288                         </user-states>
289                     </package-state>
290                 </active>
291             </domain-verifications>
292         """.trimIndent()
293 
294         val service = makeService(pkg1, pkg2)
295         xml.byteInputStream().use {
296             service.readSettings(Xml.resolvePullParser(it))
297         }
298 
299         service.addPackage(pkg1)
300 
301         assertAddPackageActivePendingRestoredState(service)
302     }
303 
304     @Test
305     fun addPackagePendingStripInvalidDomains() {
306         val xml = addPackagePendingOrRestoredWithInvalidDomains()
307         val service = makeService(pkg1, pkg2)
308         xml.byteInputStream().use {
309             service.readSettings(Xml.resolvePullParser(it))
310         }
311 
312         service.addPackage(pkg1)
313 
314         val userState = service.getUserState(pkg1.getName())
315         assertThat(userState.packageName).isEqualTo(pkg1.getName())
316         assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
317         assertThat(userState.isLinkHandlingAllowed).isEqualTo(false)
318         assertThat(userState.user.identifier).isEqualTo(USER_ID)
319         assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
320                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
321                 DOMAIN_2 to DOMAIN_STATE_SELECTED,
322         ))
323 
324         assertAddPackageActivePendingRestoredState(service)
325     }
326 
327     @Test
328     fun addPackageRestoredStripInvalidDomains() {
329         val xml = addPackagePendingOrRestoredWithInvalidDomains()
330         val service = makeService(pkg1, pkg2)
331         xml.byteInputStream().use {
332             service.restoreSettings(Xml.resolvePullParser(it))
333         }
334 
335         service.addPackage(pkg1)
336 
337         assertAddPackageActivePendingRestoredState(service, expectRestore = true)
338     }
339 
340     /**
341      * Shared string that contains invalid [DOMAIN_3] and [DOMAIN_4] which should be stripped from
342      * the final state.
343      */
344     private fun addPackagePendingOrRestoredWithInvalidDomains(): String =
345         // language=XML
346         """
347             <?xml?>
348             <domain-verifications>
349                 <active>
350                     <package-state
351                         packageName="${pkg1.getName()}"
352                         id="${pkg1.domainSetId}"
353                         signature="$DIGEST_ONE"
354                         >
355                         <state>
356                             <domain name="$DOMAIN_1" state="$STATE_SUCCESS"/>
357                             <domain name="$DOMAIN_3" state="$STATE_SUCCESS"/>
358                         </state>
359                         <user-states>
360                             <user-state userId="$USER_ID" allowLinkHandling="false">
361                                 <enabled-hosts>
362                                     <host name="$DOMAIN_2"/>
363                                     <host name="$DOMAIN_4"/>
364                                 </enabled-hosts>
365                             </user-state>
366                             <user-state userId="${USER_ID + 10}" allowLinkHandling="true">
367                                 <enabled-hosts>
368                                     <host name="$DOMAIN_4"/>
369                                 </enabled-hosts>
370                             </user-state>
371                         </user-states>
372                     </package-state>
373                 </active>
374             </domain-verifications>
375         """.trimIndent()
376 
377     /**
378      * Shared method to assert the same output when testing adding pkg1.
379      */
380     private fun assertAddPackageActivePendingRestoredState(
381             service: DomainVerificationService,
382             expectRestore: Boolean = false
383     ) {
384         val info = service.getInfo(pkg1.getName())
385         assertThat(info.packageName).isEqualTo(pkg1.getName())
386         assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
387         assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
388                 // To share the majority of code, special case restoration to check a different int
389                 DOMAIN_1 to if (expectRestore) STATE_MODIFIABLE_VERIFIED else STATE_SUCCESS,
390                 DOMAIN_2 to STATE_NO_RESPONSE,
391         ))
392 
393         val userState = service.getUserState(pkg1.getName())
394         assertThat(userState.packageName).isEqualTo(pkg1.getName())
395         assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
396         assertThat(userState.isLinkHandlingAllowed).isEqualTo(false)
397         assertThat(userState.user.identifier).isEqualTo(USER_ID)
398         assertThat(userState.hostToStateMap).containsExactlyEntriesIn(mapOf(
399                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
400                 DOMAIN_2 to DOMAIN_STATE_SELECTED,
401         ))
402 
403         assertThat(service.queryValidVerificationPackageNames())
404                 .containsExactly(pkg1.getName())
405 
406         // Re-enable link handling to check that the 3/4 domains were stripped
407         service.setDomainVerificationLinkHandlingAllowed(pkg1.getName(), true, USER_ID)
408 
409         assertThat(service.getOwnersForDomain(DOMAIN_1, USER_ID))
410                 .containsExactly(DomainOwner(PKG_ONE, false))
411 
412         assertThat(service.getOwnersForDomain(DOMAIN_2, USER_ID))
413                 .containsExactly(DomainOwner(PKG_ONE, true))
414 
415         assertThat(service.getOwnersForDomain(DOMAIN_2, USER_ID + 10)).isEmpty()
416 
417         listOf(DOMAIN_3, DOMAIN_4).forEach { domain ->
418             listOf(USER_ID, USER_ID + 10).forEach {  userId ->
419                 assertThat(service.getOwnersForDomain(domain, userId)).isEmpty()
420             }
421         }
422     }
423 
424     @Test
425     fun migratePackageDropDomain() {
426         val pkgName = PKG_ONE
427         val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE,
428             listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
429         val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
430 
431         // Test 4 domains:
432         // 1 will be approved and preserved, 2 will be selected and preserved,
433         // 3 will be denied and dropped, 4 will be selected and dropped
434 
435         val map = mutableMapOf<String, PackageSetting>()
436         val service = makeService { map[it] }
437         service.addPackage(pkgBefore)
438 
439         // Only insert the package after addPackage call to ensure the service doesn't access
440         // a live package inside the addPackage logic. It should only use the provided input.
441         map[pkgName] = pkgBefore
442 
443         // To test the approve/denial states, use the internal methods for this variant
444         service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_APPROVED,
445                 ArraySet(setOf(DOMAIN_1)))
446         service.setDomainVerificationStatusInternal(pkgName, DomainVerificationState.STATE_DENIED,
447                 ArraySet(setOf(DOMAIN_3)))
448         service.setUserSelection(
449                 UUID_ONE, setOf(DOMAIN_2, DOMAIN_4), true, USER_ID)
450 
451         // Check the verifier cannot change the shell approve/deny states
452         service.setStatus(UUID_ONE, setOf(DOMAIN_1, DOMAIN_3), STATE_SUCCESS)
453 
454         assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
455                 DOMAIN_1 to STATE_UNMODIFIABLE,
456                 DOMAIN_2 to STATE_NO_RESPONSE,
457                 DOMAIN_3 to STATE_UNMODIFIABLE,
458                 DOMAIN_4 to STATE_NO_RESPONSE,
459         ))
460         assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
461                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
462                 DOMAIN_2 to DOMAIN_STATE_SELECTED,
463                 DOMAIN_3 to DOMAIN_STATE_NONE,
464                 DOMAIN_4 to DOMAIN_STATE_SELECTED,
465         ))
466 
467         // Now remove the package because migrateState shouldn't use it either
468         map.remove(pkgName)
469 
470         map[pkgName] = pkgAfter
471 
472         service.migrateState(pkgBefore, pkgAfter)
473 
474         assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
475                 DOMAIN_1 to STATE_UNMODIFIABLE,
476                 DOMAIN_2 to STATE_NO_RESPONSE,
477         ))
478         assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
479                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
480                 DOMAIN_2 to DOMAIN_STATE_SELECTED,
481         ))
482         assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
483     }
484 
485     @Test
486     fun migratePackageDropAll() {
487         val pkgName = PKG_ONE
488         val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
489         val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, emptyList())
490 
491         val map = mutableMapOf<String, PackageSetting>()
492         val service = makeService { map[it] }
493         service.addPackage(pkgBefore)
494 
495         // Only insert the package after addPackage call to ensure the service doesn't access
496         // a live package inside the addPackage logic. It should only use the provided input.
497         map[pkgName] = pkgBefore
498 
499         assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
500                 DOMAIN_1 to STATE_NO_RESPONSE,
501                 DOMAIN_2 to STATE_NO_RESPONSE,
502         ))
503         assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
504                 DOMAIN_1 to DOMAIN_STATE_NONE,
505                 DOMAIN_2 to DOMAIN_STATE_NONE,
506         ))
507         assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
508 
509         // Now remove the package because migrateState shouldn't use it either
510         map.remove(pkgName)
511 
512         service.migrateState(pkgBefore, pkgAfter)
513 
514         map[pkgName] = pkgAfter
515 
516         assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
517             .isNotEqualTo(DomainVerificationManager.STATUS_OK)
518 
519         assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
520             .isNotEqualTo(DomainVerificationManager.STATUS_OK)
521 
522         assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
523         assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
524         assertThat(service.queryValidVerificationPackageNames()).isEmpty()
525     }
526 
527     @Test
528     fun migratePackageAddDomain() {
529         val pkgName = PKG_ONE
530         val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
531         val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO,
532             listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
533 
534         // Test 3 domains:
535         // 1 will be verified and preserved, 2 will be selected and preserved,
536         // 3 will be new and default
537 
538         val map = mutableMapOf<String, PackageSetting>()
539         val service = makeService { map[it] }
540         service.addPackage(pkgBefore)
541 
542         // Only insert the package after addPackage call to ensure the service doesn't access
543         // a live package inside the addPackage logic. It should only use the provided input.
544         map[pkgName] = pkgBefore
545 
546         service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS)
547         service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID)
548 
549         assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
550                 DOMAIN_1 to STATE_SUCCESS,
551                 DOMAIN_2 to STATE_NO_RESPONSE,
552         ))
553         assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
554                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
555                 DOMAIN_2 to DOMAIN_STATE_SELECTED,
556         ))
557 
558         // Now remove the package because migrateState shouldn't use it either
559         map.remove(pkgName)
560 
561         service.migrateState(pkgBefore, pkgAfter)
562 
563         map[pkgName] = pkgAfter
564 
565         assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
566                 DOMAIN_1 to STATE_SUCCESS,
567                 DOMAIN_2 to STATE_NO_RESPONSE,
568                 DOMAIN_3 to STATE_NO_RESPONSE,
569         ))
570         assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
571                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
572                 DOMAIN_2 to DOMAIN_STATE_SELECTED,
573                 DOMAIN_3 to DOMAIN_STATE_NONE,
574         ))
575         assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
576     }
577 
578     @Test
579     fun migratePackageAddAll() {
580         val pkgName = PKG_ONE
581         val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, emptyList())
582         val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
583 
584         val map = mutableMapOf<String, PackageSetting>()
585         val service = makeService { map[it] }
586         service.addPackage(pkgBefore)
587 
588         // Only insert the package after addPackage call to ensure the service doesn't access
589         // a live package inside the addPackage logic. It should only use the provided input.
590         map[pkgName] = pkgBefore
591 
592         assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
593             .isNotEqualTo(DomainVerificationManager.STATUS_OK)
594 
595         assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
596             .isNotEqualTo(DomainVerificationManager.STATUS_OK)
597 
598         assertThat(service.getDomainVerificationInfo(pkgName)).isNull()
599         assertThat(service.getUserState(pkgName).hostToStateMap).isEmpty()
600         assertThat(service.queryValidVerificationPackageNames()).isEmpty()
601 
602         // Now remove the package because migrateState shouldn't use it either
603         map.remove(pkgName)
604 
605         service.migrateState(pkgBefore, pkgAfter)
606 
607         map[pkgName] = pkgAfter
608 
609         assertThat(service.getInfo(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
610                 DOMAIN_1 to STATE_NO_RESPONSE,
611                 DOMAIN_2 to STATE_NO_RESPONSE,
612         ))
613         assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
614                 DOMAIN_1 to DOMAIN_STATE_NONE,
615                 DOMAIN_2 to DOMAIN_STATE_NONE,
616         ))
617         assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
618     }
619 
620     @Test
621     fun backupAndRestore() {
622         // This test acts as a proxy for true user restore through PackageManager,
623         // as that's much harder to test for real.
624 
625         val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
626         val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO,
627             listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
628         val serviceBefore = makeService(pkg1, pkg2)
629         serviceBefore.addPackage(pkg1)
630         serviceBefore.addPackage(pkg2)
631 
632         serviceBefore.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), STATE_SUCCESS)
633         serviceBefore.setDomainVerificationLinkHandlingAllowed(pkg1.getName(), false, 10)
634         serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0)
635         serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_3), true, 10)
636 
637         fun assertExpectedState(service: DomainVerificationService) {
638             service.assertState(
639                 pkg1, userId = 0, hostToStateMap = mapOf(
640                     DOMAIN_1 to DOMAIN_STATE_VERIFIED,
641                     DOMAIN_2 to DOMAIN_STATE_NONE,
642                 )
643             )
644 
645             service.assertState(
646                 pkg1, userId = 10, linkHandingAllowed = false, hostToStateMap = mapOf(
647                     DOMAIN_1 to DOMAIN_STATE_VERIFIED,
648                     DOMAIN_2 to DOMAIN_STATE_NONE,
649                 )
650             )
651 
652             service.assertState(
653                 pkg2, userId = 0, hostToStateMap = mapOf(
654                     DOMAIN_1 to DOMAIN_STATE_NONE,
655                     DOMAIN_2 to DOMAIN_STATE_SELECTED,
656                     DOMAIN_3 to DOMAIN_STATE_NONE
657                 )
658             )
659 
660             service.assertState(
661                 pkg2, userId = 10, hostToStateMap = mapOf(
662                     DOMAIN_1 to DOMAIN_STATE_NONE,
663                     DOMAIN_2 to DOMAIN_STATE_NONE,
664                     DOMAIN_3 to DOMAIN_STATE_SELECTED,
665                 )
666             )
667         }
668 
669         assertExpectedState(serviceBefore)
670 
671         val backupUser0 = ByteArrayOutputStream().use {
672             serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 0)
673             it.toByteArray()
674         }
675 
676         val backupUser1 = ByteArrayOutputStream().use {
677             serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 10)
678             it.toByteArray()
679         }
680 
681         val serviceAfter = makeService(pkg1, pkg2)
682         serviceAfter.addPackage(pkg1)
683         serviceAfter.addPackage(pkg2)
684 
685         // Check the state is default before the restoration applies
686         listOf(0, 10).forEach {
687             serviceAfter.assertState(
688                 pkg1, userId = it, hostToStateMap = mapOf(
689                     DOMAIN_1 to DOMAIN_STATE_NONE,
690                     DOMAIN_2 to DOMAIN_STATE_NONE,
691                 )
692             )
693         }
694 
695         listOf(0, 10).forEach {
696             serviceAfter.assertState(
697                 pkg2, userId = it, hostToStateMap = mapOf(
698                     DOMAIN_1 to DOMAIN_STATE_NONE,
699                     DOMAIN_2 to DOMAIN_STATE_NONE,
700                     DOMAIN_3 to DOMAIN_STATE_NONE,
701                 )
702             )
703         }
704 
705         ByteArrayInputStream(backupUser1).use {
706             serviceAfter.restoreSettings(Xml.resolvePullParser(it))
707         }
708 
709         // Assert user 1 was restored
710         serviceAfter.assertState(
711             pkg1, userId = 10, linkHandingAllowed = false, hostToStateMap = mapOf(
712                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
713                 DOMAIN_2 to DOMAIN_STATE_NONE,
714             )
715         )
716 
717         serviceAfter.assertState(
718             pkg2, userId = 10, hostToStateMap = mapOf(
719                 DOMAIN_1 to DOMAIN_STATE_NONE,
720                 DOMAIN_2 to DOMAIN_STATE_NONE,
721                 DOMAIN_3 to DOMAIN_STATE_SELECTED,
722             )
723         )
724 
725         // User 0 has domain verified (since that's not user-specific)
726         serviceAfter.assertState(
727             pkg1, userId = 0, hostToStateMap = mapOf(
728                 DOMAIN_1 to DOMAIN_STATE_VERIFIED,
729                 DOMAIN_2 to DOMAIN_STATE_NONE,
730             )
731         )
732 
733         // But user 0 is missing any user selected state
734         serviceAfter.assertState(
735             pkg2, userId = 0, hostToStateMap = mapOf(
736                 DOMAIN_1 to DOMAIN_STATE_NONE,
737                 DOMAIN_2 to DOMAIN_STATE_NONE,
738                 DOMAIN_3 to DOMAIN_STATE_NONE,
739             )
740         )
741 
742         ByteArrayInputStream(backupUser0).use {
743             serviceAfter.restoreSettings(Xml.resolvePullParser(it))
744         }
745 
746         assertExpectedState(serviceAfter)
747     }
748 
749     private fun DomainVerificationService.getInfo(pkgName: String) =
750             getDomainVerificationInfo(pkgName)
751                     .also { assertThat(it).isNotNull() }!!
752 
753     private fun DomainVerificationService.getUserState(pkgName: String, userId: Int = USER_ID) =
754             getDomainVerificationUserState(pkgName, userId)
755                     .also { assertThat(it).isNotNull() }!!
756 
757     private fun makeService(
758         systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
759         vararg pkgSettings: PackageSetting
760     ) = makeService(systemConfiguredPackageNames = systemConfiguredPackageNames) {
761         pkgName -> pkgSettings.find { pkgName == it.getName() }
762     }
763 
764     private fun makeService(vararg pkgSettings: PackageSetting) =
765         makeService { pkgName -> pkgSettings.find { pkgName == it.getName() } }
766 
767     private fun makeService(
768         systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
769         pkgSettingFunction: (String) -> PackageSetting? = { null }
770     ) = DomainVerificationService(mockThrowOnUnmocked {
771             // Assume the test has every permission necessary
772             whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
773             whenever(checkPermission(anyString(), anyInt(), anyInt())) {
774                 PackageManager.PERMISSION_GRANTED
775             }
776         }, mockThrowOnUnmocked {
777             whenever(this.linkedApps) { systemConfiguredPackageNames }
778         }, mockThrowOnUnmocked {
779             whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
780         }).apply {
781             setConnection(mockThrowOnUnmocked {
782                 whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
783                 whenever(doesUserExist(0)) { true }
784                 whenever(doesUserExist(10)) { true }
785                 whenever(scheduleWriteSettings())
786 
787                 // Need to provide an internal UID so some permission checks are ignored
788                 whenever(callingUid) { Process.ROOT_UID }
789                 whenever(callingUserId) { 0 }
790 
791                 mockPackageSettings {
792                     pkgSettingFunction(it)
793                 }
794             })
795         }
796 
797     private fun mockPkgSetting(
798         pkgName: String,
799         domainSetId: UUID,
800         signature: String,
801         domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
802         isSystemApp: Boolean = false
803     ) = mockThrowOnUnmocked<PackageSetting> {
804         val pkg = mockThrowOnUnmocked<AndroidPackage> {
805             whenever(packageName) { pkgName }
806             whenever(targetSdkVersion) { Build.VERSION_CODES.S }
807             whenever(isEnabled) { true }
808 
809             val activityList = listOf(
810                     ParsedActivity().apply {
811                         domains.forEach {
812                             addIntent(
813                                     ParsedIntentInfo().apply {
814                                         autoVerify = true
815                                         addAction(Intent.ACTION_VIEW)
816                                         addCategory(Intent.CATEGORY_BROWSABLE)
817                                         addCategory(Intent.CATEGORY_DEFAULT)
818                                         addDataScheme("http")
819                                         addDataScheme("https")
820                                         addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
821                                         addDataAuthority(it, null)
822                                     }
823                             )
824                         }
825                     },
826             )
827 
828             whenever(activities) { activityList }
829         }
830 
831         whenever(getPkg()) { pkg }
832         whenever(getName()) { pkgName }
833         whenever(this.domainSetId) { domainSetId }
834         whenever(getInstantApp(anyInt())) { false }
835         whenever(firstInstallTime) { 0L }
836         whenever(readUserState(0)) { PackageUserState() }
837         whenever(readUserState(10)) { PackageUserState() }
838         whenever(signatures) { arrayOf(Signature(signature)) }
839         whenever(isSystem) { isSystemApp }
840     }
841 
842     private fun DomainVerificationService.assertState(
843         pkg: PackageSetting,
844         userId: Int,
845         linkHandingAllowed: Boolean = true,
846         hostToStateMap: Map<String, Int>
847     ) {
848         getUserState(pkg.getName(), userId).apply {
849             assertThat(this.packageName).isEqualTo(pkg.getName())
850             assertThat(this.identifier).isEqualTo(pkg.domainSetId)
851             assertThat(this.isLinkHandlingAllowed).isEqualTo(linkHandingAllowed)
852             assertThat(this.user.identifier).isEqualTo(userId)
853             assertThat(this.hostToStateMap).containsExactlyEntriesIn(hostToStateMap)
854         }
855     }
856 }
857