1package com.peachpasswords.autofill23import android.content.Context4import android.content.pm.verify.domain.DomainVerificationManager5import android.content.pm.verify.domain.DomainVerificationUserState6import android.os.Build7import com.peachpasswords.shared.extractDomain8import com.peachpasswords.passkeys.PasskeyCallerOrigin9import com.peachpasswords.vault.VaultEntry101112131415161718192021222324252627282930internal fun resolveAutofillOrigin(context: Context, parsed: ParsedStructure): ParsedStructure {31 if (parsed.webDomain != null) {32 return applyWebOriginVerification(33 parsed,34 PasskeyCallerOrigin.isVerifiedBrowser(context, parsed.packageName),35 )36 }37 return parsed.copy(38 nativeSignerDigests = parsed.packageName?.let { PasskeyCallerOrigin.signerFingerprints(context, it) }.orEmpty(),39 originDomains = appLinkDomains(context, parsed.packageName),40 )41}424344internal fun applyWebOriginVerification(45 parsed: ParsedStructure,46 verifiedBrowser: Boolean,47): ParsedStructure {48 val webDomain = parsed.webDomain ?: return parsed49 return if (verifiedBrowser) {50 parsed.copy(51 originDomains = listOfNotNull(extractDomain(webDomain)),52 credentialReleaseAllowed = true,53 )54 } else {55 parsed.copy(56 webDomain = null,57 originDomains = emptyList(),58 credentialReleaseAllowed = false,59 )60 }61}626364internal fun appLinkDomains(context: Context, packageName: String?): List<String> {65 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || packageName.isNullOrBlank()) {66 return emptyList()67 }68 return runCatching {69 val state = context.getSystemService(DomainVerificationManager::class.java)70 ?.getDomainVerificationUserState(packageName)71 state?.hostToStateMap.orEmpty()72 .filterValues { it == DomainVerificationUserState.DOMAIN_STATE_VERIFIED }73 .keys74 .mapNotNull { extractDomain(it.removePrefix("*.")) }75 .distinct()76 .sorted()77 }.getOrDefault(emptyList())78}7980818283848586internal fun rememberOfferDomains(entry: VaultEntry, parsed: ParsedStructure): List<String> {87 val existing = sequenceOf(entry.url)88 .plus(entry.additionalUrls.orEmpty().asSequence())89 .filterNotNull()90 .mapNotNull(::extractDomain)91 .toSet()92 if (existing.isEmpty() || parsed.originDomains.isEmpty()) return emptyList()93 return parsed.originDomains.filter { it !in existing }94}