AutofillOrigin.kt

2.7 KB · raw

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.extractDomain89/**10 * Origin resolution for autofill matching.11 *12 * Origin *verification* (browser certificate pinning + caller-trust gating) was13 * removed (queue #3, June 2026-08-02) — it flagged nearly every request as14 * "unverified" and withheld matches, adding friction without value. Matches are15 * now ranked purely by content: the page domain for web origins, and a native16 * app's app-link domains + display label for app origins. The caller package is17 * still the framework-authenticated identity (never a caller-supplied extra).18 */1920/**21 * Populate [ParsedStructure.originDomains] and [ParsedStructure.appTitle] from22 * the authenticated origin. Web origins match on the page domain; native apps23 * match on their app-link domains and their display label. No trust24 * classification — every origin is eligible to see its matching entries.25 */26internal fun resolveAutofillOrigin(context: Context, parsed: ParsedStructure): ParsedStructure {27    val webDomain = parsed.webDomain28    if (webDomain != null) {29        return parsed.copy(originDomains = listOfNotNull(extractDomain(webDomain)), appTitle = null)30    }31    return parsed.copy(32        originDomains = appLinkDomains(context, parsed.packageName),33        appTitle = appLabel(context, parsed.packageName),34    )35}3637/** The display label of the calling app, for matching entries by app title. */38internal fun appLabel(context: Context, packageName: String?): String? {39    if (packageName.isNullOrBlank()) return null40    return runCatching {41        context.packageManager.getApplicationLabel(42            context.packageManager.getApplicationInfo(packageName, 0),43        ).toString().takeIf { it.isNotBlank() }44    }.getOrNull()45}4647/** A native app's verified app-link domains (Android's cached Digital Asset Links verdict). */48internal fun appLinkDomains(context: Context, packageName: String?): List<String> {49    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || packageName.isNullOrBlank()) {50        return emptyList()51    }52    return runCatching {53        val state = context.getSystemService(DomainVerificationManager::class.java)54            ?.getDomainVerificationUserState(packageName)55        state?.hostToStateMap.orEmpty()56            .filterValues { it == DomainVerificationUserState.DOMAIN_STATE_VERIFIED }57            .keys58            .mapNotNull { extractDomain(it.removePrefix("*.")) }59            .distinct()60            .sorted()61    }.getOrDefault(emptyList())62}