1 /* 2 * 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 migratePackageSelected() { 622 val pkgName = PKG_ONE 623 val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, 624 listOf(DOMAIN_1), listOf(DOMAIN_2)) 625 val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, 626 listOf(DOMAIN_1), listOf(DOMAIN_2)) 627 628 val map = mutableMapOf<String, PackageSetting>() 629 val service = makeService { map[it] } 630 service.addPackage(pkgBefore) 631 632 // Only insert the package after addPackage call to ensure the service doesn't access 633 // a live package inside the addPackage logic. It should only use the provided input. 634 map[pkgName] = pkgBefore 635 636 assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS)) 637 .isEqualTo(DomainVerificationManager.STATUS_OK) 638 639 assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID)) 640 .isEqualTo(DomainVerificationManager.STATUS_OK) 641 642 service.getInfo(pkgName).run { 643 assertThat(identifier).isEqualTo(UUID_ONE) 644 assertThat(hostToStateMap).containsExactlyEntriesIn(mapOf( 645 DOMAIN_1 to STATE_SUCCESS, 646 )) 647 } 648 assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf( 649 DOMAIN_1 to DOMAIN_STATE_VERIFIED, 650 DOMAIN_2 to DOMAIN_STATE_SELECTED, 651 )) 652 assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName) 653 654 // Now remove the package because migrateState shouldn't use it either 655 map.remove(pkgName) 656 657 service.migrateState(pkgBefore, pkgAfter) 658 659 map[pkgName] = pkgAfter 660 661 service.getInfo(pkgName).run { 662 assertThat(identifier).isEqualTo(UUID_TWO) 663 assertThat(hostToStateMap).containsExactlyEntriesIn(mapOf( 664 DOMAIN_1 to STATE_SUCCESS, 665 )) 666 } 667 assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf( 668 DOMAIN_1 to DOMAIN_STATE_VERIFIED, 669 DOMAIN_2 to DOMAIN_STATE_SELECTED, 670 )) 671 assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName) 672 } 673 674 @Test 675 fun backupAndRestore() { 676 // This test acts as a proxy for true user restore through PackageManager, 677 // as that's much harder to test for real. 678 679 val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2)) 680 val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO, 681 listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3)) 682 val serviceBefore = makeService(pkg1, pkg2) 683 serviceBefore.addPackage(pkg1) 684 serviceBefore.addPackage(pkg2) 685 686 serviceBefore.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), STATE_SUCCESS) 687 serviceBefore.setDomainVerificationLinkHandlingAllowed(pkg1.getName(), false, 10) 688 serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0) 689 serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_3), true, 10) 690 691 fun assertExpectedState(service: DomainVerificationService) { 692 service.assertState( 693 pkg1, userId = 0, hostToStateMap = mapOf( 694 DOMAIN_1 to DOMAIN_STATE_VERIFIED, 695 DOMAIN_2 to DOMAIN_STATE_NONE, 696 ) 697 ) 698 699 service.assertState( 700 pkg1, userId = 10, linkHandingAllowed = false, hostToStateMap = mapOf( 701 DOMAIN_1 to DOMAIN_STATE_VERIFIED, 702 DOMAIN_2 to DOMAIN_STATE_NONE, 703 ) 704 ) 705 706 service.assertState( 707 pkg2, userId = 0, hostToStateMap = mapOf( 708 DOMAIN_1 to DOMAIN_STATE_NONE, 709 DOMAIN_2 to DOMAIN_STATE_SELECTED, 710 DOMAIN_3 to DOMAIN_STATE_NONE 711 ) 712 ) 713 714 service.assertState( 715 pkg2, userId = 10, hostToStateMap = mapOf( 716 DOMAIN_1 to DOMAIN_STATE_NONE, 717 DOMAIN_2 to DOMAIN_STATE_NONE, 718 DOMAIN_3 to DOMAIN_STATE_SELECTED, 719 ) 720 ) 721 } 722 723 assertExpectedState(serviceBefore) 724 725 val backupUser0 = ByteArrayOutputStream().use { 726 serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 0) 727 it.toByteArray() 728 } 729 730 val backupUser1 = ByteArrayOutputStream().use { 731 serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 10) 732 it.toByteArray() 733 } 734 735 val serviceAfter = makeService(pkg1, pkg2) 736 serviceAfter.addPackage(pkg1) 737 serviceAfter.addPackage(pkg2) 738 739 // Check the state is default before the restoration applies 740 listOf(0, 10).forEach { 741 serviceAfter.assertState( 742 pkg1, userId = it, hostToStateMap = mapOf( 743 DOMAIN_1 to DOMAIN_STATE_NONE, 744 DOMAIN_2 to DOMAIN_STATE_NONE, 745 ) 746 ) 747 } 748 749 listOf(0, 10).forEach { 750 serviceAfter.assertState( 751 pkg2, userId = it, hostToStateMap = mapOf( 752 DOMAIN_1 to DOMAIN_STATE_NONE, 753 DOMAIN_2 to DOMAIN_STATE_NONE, 754 DOMAIN_3 to DOMAIN_STATE_NONE, 755 ) 756 ) 757 } 758 759 ByteArrayInputStream(backupUser1).use { 760 serviceAfter.restoreSettings(Xml.resolvePullParser(it)) 761 } 762 763 // Assert user 1 was restored 764 serviceAfter.assertState( 765 pkg1, userId = 10, linkHandingAllowed = false, hostToStateMap = mapOf( 766 DOMAIN_1 to DOMAIN_STATE_VERIFIED, 767 DOMAIN_2 to DOMAIN_STATE_NONE, 768 ) 769 ) 770 771 serviceAfter.assertState( 772 pkg2, userId = 10, hostToStateMap = mapOf( 773 DOMAIN_1 to DOMAIN_STATE_NONE, 774 DOMAIN_2 to DOMAIN_STATE_NONE, 775 DOMAIN_3 to DOMAIN_STATE_SELECTED, 776 ) 777 ) 778 779 // User 0 has domain verified (since that's not user-specific) 780 serviceAfter.assertState( 781 pkg1, userId = 0, hostToStateMap = mapOf( 782 DOMAIN_1 to DOMAIN_STATE_VERIFIED, 783 DOMAIN_2 to DOMAIN_STATE_NONE, 784 ) 785 ) 786 787 // But user 0 is missing any user selected state 788 serviceAfter.assertState( 789 pkg2, userId = 0, hostToStateMap = mapOf( 790 DOMAIN_1 to DOMAIN_STATE_NONE, 791 DOMAIN_2 to DOMAIN_STATE_NONE, 792 DOMAIN_3 to DOMAIN_STATE_NONE, 793 ) 794 ) 795 796 ByteArrayInputStream(backupUser0).use { 797 serviceAfter.restoreSettings(Xml.resolvePullParser(it)) 798 } 799 800 assertExpectedState(serviceAfter) 801 } 802 803 private fun DomainVerificationService.getInfo(pkgName: String) = 804 getDomainVerificationInfo(pkgName) 805 .also { assertThat(it).isNotNull() }!! 806 807 private fun DomainVerificationService.getUserState(pkgName: String, userId: Int = USER_ID) = 808 getDomainVerificationUserState(pkgName, userId) 809 .also { assertThat(it).isNotNull() }!! 810 811 private fun makeService( 812 systemConfiguredPackageNames: ArraySet<String> = ArraySet(), 813 vararg pkgSettings: PackageSetting 814 ) = makeService(systemConfiguredPackageNames = systemConfiguredPackageNames) { 815 pkgName -> pkgSettings.find { pkgName == it.getName() } 816 } 817 818 private fun makeService(vararg pkgSettings: PackageSetting) = 819 makeService { pkgName -> pkgSettings.find { pkgName == it.getName() } } 820 821 private fun makeService( 822 systemConfiguredPackageNames: ArraySet<String> = ArraySet(), 823 pkgSettingFunction: (String) -> PackageSetting? = { null } 824 ) = DomainVerificationService(mockThrowOnUnmocked { 825 // Assume the test has every permission necessary 826 whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString())) 827 whenever(checkPermission(anyString(), anyInt(), anyInt())) { 828 PackageManager.PERMISSION_GRANTED 829 } 830 }, mockThrowOnUnmocked { 831 whenever(this.linkedApps) { systemConfiguredPackageNames } 832 }, mockThrowOnUnmocked { 833 whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true } 834 }).apply { 835 setConnection(mockThrowOnUnmocked { 836 whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false } 837 whenever(doesUserExist(0)) { true } 838 whenever(doesUserExist(10)) { true } 839 whenever(scheduleWriteSettings()) 840 841 // Need to provide an internal UID so some permission checks are ignored 842 whenever(callingUid) { Process.ROOT_UID } 843 whenever(callingUserId) { 0 } 844 845 mockPackageSettings { 846 pkgSettingFunction(it) 847 } 848 }) 849 } 850 851 private fun mockPkgSetting( 852 pkgName: String, 853 domainSetId: UUID, 854 signature: String, 855 autoVerifyDomains: List<String> = listOf(DOMAIN_1, DOMAIN_2), 856 otherDomains: List<String> = listOf(), 857 isSystemApp: Boolean = false 858 ) = mockThrowOnUnmocked<PackageSetting> { 859 val pkg = mockThrowOnUnmocked<AndroidPackage> { 860 whenever(packageName) { pkgName } 861 whenever(targetSdkVersion) { Build.VERSION_CODES.S } 862 whenever(isEnabled) { true } 863 864 fun baseIntent(domain: String) = ParsedIntentInfo().apply { 865 addAction(Intent.ACTION_VIEW) 866 addCategory(Intent.CATEGORY_BROWSABLE) 867 addCategory(Intent.CATEGORY_DEFAULT) 868 addDataScheme("http") 869 addDataScheme("https") 870 addDataPath("/sub", PatternMatcher.PATTERN_LITERAL) 871 addDataAuthority(domain, null) 872 } 873 874 val activityList = listOf( 875 ParsedActivity().apply { 876 autoVerifyDomains.forEach { 877 addIntent(baseIntent(it).apply { autoVerify = true }) 878 } 879 otherDomains.forEach { 880 addIntent(baseIntent(it).apply { autoVerify = false }) 881 } 882 }, 883 ) 884 885 whenever(activities) { activityList } 886 } 887 888 whenever(getPkg()) { pkg } 889 whenever(getName()) { pkgName } 890 whenever(this.domainSetId) { domainSetId } 891 whenever(getInstantApp(anyInt())) { false } 892 whenever(firstInstallTime) { 0L } 893 whenever(readUserState(0)) { PackageUserState() } 894 whenever(readUserState(10)) { PackageUserState() } 895 whenever(signatures) { arrayOf(Signature(signature)) } 896 whenever(isSystem) { isSystemApp } 897 } 898 899 private fun DomainVerificationService.assertState( 900 pkg: PackageSetting, 901 userId: Int, 902 linkHandingAllowed: Boolean = true, 903 hostToStateMap: Map<String, Int> 904 ) { 905 getUserState(pkg.getName(), userId).apply { 906 assertThat(this.packageName).isEqualTo(pkg.getName()) 907 assertThat(this.identifier).isEqualTo(pkg.domainSetId) 908 assertThat(this.isLinkHandlingAllowed).isEqualTo(linkHandingAllowed) 909 assertThat(this.user.identifier).isEqualTo(userId) 910 assertThat(this.hostToStateMap).containsExactlyEntriesIn(hostToStateMap) 911 } 912 } 913 } 914