Loading services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java +68 −14 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Intent; Loading @@ -33,6 +34,8 @@ import com.android.server.compat.PlatformCompat; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.List; import java.util.Objects; import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; Loading @@ -44,6 +47,12 @@ public class DomainVerificationCollector { private static final int MAX_DOMAINS_BYTE_SIZE = 1024 * 1024; private static final BiFunction<ArraySet<String>, String, Boolean> ARRAY_SET_COLLECTOR = (set, domain) -> { set.add(domain); return null; }; @NonNull private final PlatformCompat mPlatformCompat; Loading Loading @@ -105,27 +114,62 @@ public class DomainVerificationCollector { return collectDomains(pkg, true /* checkAutoVerify */, false /* valid */); } public boolean containsWebDomain(@NonNull AndroidPackage pkg, @NonNull String targetDomain) { return collectDomains(pkg, false /* checkAutoVerify */, true /* valid */, null, (BiFunction<Void, String, Boolean>) (unused, domain) -> { if (Objects.equals(targetDomain, domain)) { return true; } return null; }) != null; } public boolean containsAutoVerifyDomain(@NonNull AndroidPackage pkg, @NonNull String targetDomain) { return collectDomains(pkg, true /* checkAutoVerify */, true /* valid */, null, (BiFunction<Void, String, Boolean>) (unused, domain) -> { if (Objects.equals(targetDomain, domain)) { return true; } return null; }) != null; } @NonNull private ArraySet<String> collectDomains(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid) { ArraySet<String> domains = new ArraySet<>(); collectDomains(pkg, checkAutoVerify, valid, domains, ARRAY_SET_COLLECTOR); return domains; } @NonNull private <InitialValue, ReturnValue> ReturnValue collectDomains(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue, @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { boolean restrictDomains = DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS); if (restrictDomains) { return collectDomainsInternal(pkg, checkAutoVerify, valid); return collectDomainsInternal(pkg, checkAutoVerify, valid, initialValue, domainCollector); } else { return collectDomainsLegacy(pkg, checkAutoVerify, valid); return collectDomainsLegacy(pkg, checkAutoVerify, valid, initialValue, domainCollector); } } /** * @see #RESTRICT_DOMAINS */ private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid) { @Nullable private <InitialValue, ReturnValue> ReturnValue collectDomainsLegacy( @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue, @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { if (!checkAutoVerify) { // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2 return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */); return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */, initialValue, domainCollector); } List<ParsedActivity> activities = pkg.getActivities(); Loading @@ -148,11 +192,10 @@ public class DomainVerificationCollector { } if (!needsAutoVerify) { return new ArraySet<>(); return null; } } ArraySet<String> domains = new ArraySet<>(); int totalSize = 0; boolean underMaxSize = true; for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize; Loading @@ -169,22 +212,30 @@ public class DomainVerificationCollector { if (isValidHost(host) == valid) { totalSize += byteSizeOf(host); underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; domains.add(host); ReturnValue returnValue = domainCollector.apply(initialValue, host); if (returnValue != null) { return returnValue; } } } } } } return domains; return null; } /** * @see #RESTRICT_DOMAINS * @param domainCollector Function to call with initialValue and a valid host. Should return * a non-null value if the function should return immediately * after the currently processed host. */ private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid) { ArraySet<String> domains = new ArraySet<>(); @Nullable private <InitialValue, ReturnValue> ReturnValue collectDomainsInternal( @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue, @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { int totalSize = 0; boolean underMaxSize = true; Loading Loading @@ -226,13 +277,16 @@ public class DomainVerificationCollector { if (isValidHost(host) == valid) { totalSize += byteSizeOf(host); underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; domains.add(host); ReturnValue returnValue = domainCollector.apply(initialValue, host); if (returnValue != null) { return returnValue; } } } } } return domains; return null; } /** Loading services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java +65 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; import java.util.Arrays; import java.util.List; import java.util.function.Function; @SuppressWarnings("PointlessBooleanExpression") Loading Loading @@ -100,6 +101,70 @@ public class DomainVerificationDebug { } } /** * @param userIdToApprovalLevelToOwners Mapping of user ID to approval level to domain owners. */ public void printOwners(@NonNull IndentingPrintWriter writer, @NonNull String domain, SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners) { writer.println(domain + ":"); writer.increaseIndent(); if (userIdToApprovalLevelToOwners.size() == 0) { writer.println("none"); writer.decreaseIndent(); return; } int usersSize = userIdToApprovalLevelToOwners.size(); for (int userIndex = 0; userIndex < usersSize; userIndex++) { int userId = userIdToApprovalLevelToOwners.keyAt(userIndex); SparseArray<List<String>> approvalLevelToOwners = userIdToApprovalLevelToOwners.valueAt(userIndex); if (approvalLevelToOwners.size() == 0) { continue; } boolean printedUserHeader = false; int approvalsSize = approvalLevelToOwners.size(); for (int approvalIndex = 0; approvalIndex < approvalsSize; approvalIndex++) { int approvalLevel = approvalLevelToOwners.keyAt(approvalIndex); if (approvalLevel < DomainVerificationManagerInternal.APPROVAL_LEVEL_UNVERIFIED) { continue; } if (!printedUserHeader) { writer.println("User " + userId + ":"); writer.increaseIndent(); printedUserHeader = true; } String approvalString = DomainVerificationManagerInternal.approvalLevelToDebugString(approvalLevel); List<String> owners = approvalLevelToOwners.valueAt(approvalIndex); writer.println(approvalString + "[" + approvalLevel + "]" + ":"); writer.increaseIndent(); if (owners.size() == 0) { writer.println("none"); writer.decreaseIndent(); continue; } int ownersSize = owners.size(); for (int ownersIndex = 0; ownersIndex < ownersSize; ownersIndex++) { writer.println(owners.get(ownersIndex)); } writer.decreaseIndent(); } if (printedUserHeader) { writer.decreaseIndent(); } } writer.decreaseIndent(); } boolean printState(@NonNull IndentingPrintWriter writer, @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg, @NonNull ArrayMap<String, Integer> reusedMap, boolean wasHeaderPrinted) { Loading services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +55 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,28 @@ public interface DomainVerificationManagerInternal { UUID DISABLED_ID = new UUID(0, 0); /** * The app was not installed for the user. */ int APPROVAL_LEVEL_NOT_INSTALLED = -4; /** * The app was not enabled for the user. */ int APPROVAL_LEVEL_DISABLED = -3; /** * The app has not declared this domain in a valid web intent-filter in their manifest, and so * would never be able to be approved for this domain. */ int APPROVAL_LEVEL_UNDECLARED = -2; /** * The app has declared this domain as a valid autoVerify domain, but it failed or has not * succeeded verification. */ int APPROVAL_LEVEL_UNVERIFIED = -1; /** * The app has not been approved for this domain and should never be able to open it through * an implicit web intent. Loading Loading @@ -117,10 +139,14 @@ public interface DomainVerificationManagerInternal { * by approval priority. A higher numerical value means the package should override all lower * values. This means that comparison using less/greater than IS valid. * * Negative values are possible, although not implemented, reserved if explicit disable of a * package for a domain needs to be tracked. * Negative values are possible, used for tracking specific reasons for why an app doesn't have * approval. */ @IntDef({ APPROVAL_LEVEL_NOT_INSTALLED, APPROVAL_LEVEL_DISABLED, APPROVAL_LEVEL_UNDECLARED, APPROVAL_LEVEL_UNVERIFIED, APPROVAL_LEVEL_NONE, APPROVAL_LEVEL_LEGACY_ASK, APPROVAL_LEVEL_LEGACY_ALWAYS, Loading @@ -131,6 +157,33 @@ public interface DomainVerificationManagerInternal { @interface ApprovalLevel { } static String approvalLevelToDebugString(@ApprovalLevel int level) { switch (level) { case APPROVAL_LEVEL_NOT_INSTALLED: return "NOT_INSTALLED"; case APPROVAL_LEVEL_DISABLED: return "DISABLED"; case APPROVAL_LEVEL_UNDECLARED: return "UNDECLARED"; case APPROVAL_LEVEL_UNVERIFIED: return "UNVERIFIED"; case APPROVAL_LEVEL_NONE: return "NONE"; case APPROVAL_LEVEL_LEGACY_ASK: return "LEGACY_ASK"; case APPROVAL_LEVEL_LEGACY_ALWAYS: return "LEGACY_ALWAYS"; case APPROVAL_LEVEL_SELECTION: return "USER_SELECTION"; case APPROVAL_LEVEL_VERIFIED: return "VERIFIED"; case APPROVAL_LEVEL_INSTANT_APP: return "INSTANT_APP"; default: return "UNKNOWN"; } } /** @see DomainVerificationManager#getDomainVerificationInfo(String) */ @Nullable @RequiresPermission(anyOf = { Loading services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +194 −53 Original line number Diff line number Diff line Loading @@ -741,26 +741,60 @@ public class DomainVerificationService extends SystemService }); } @NonNull public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) { Objects.requireNonNull(domain); mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(), userId); SparseArray<List<String>> levelToPackages = new SparseArray<>(); return mConnection.withPackageSettingsReturningThrowing(pkgSettings -> { SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false, userId, pkgSettings); if (levelToPackages.size() == 0) { return emptyList(); } List<DomainOwner> owners = new ArrayList<>(); int size = levelToPackages.size(); for (int index = 0; index < size; index++) { int level = levelToPackages.keyAt(index); boolean overrideable = level <= APPROVAL_LEVEL_SELECTION; List<String> packages = levelToPackages.valueAt(index); int packagesSize = packages.size(); for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) { owners.add(new DomainOwner(packages.get(packageIndex), overrideable)); } } return owners; }); } /** * @param includeNegative See {@link #approvalLevelForDomain(PackageSetting, String, boolean, * int, Object)}. * @return Mapping of approval level to packages; packages are sorted by firstInstallTime. Null * if no owners were found. */ @NonNull private SparseArray<List<String>> getOwnersForDomainInternal(@NonNull String domain, boolean includeNegative, @UserIdInt int userId, @NonNull Function<String, PackageSetting> pkgSettingFunction) { SparseArray<List<String>> levelToPackages = new SparseArray<>(); // First, collect the raw approval level values synchronized (mLock) { final int size = mAttachedPkgStates.size(); for (int index = 0; index < size; index++) { DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); String packageName = pkgState.getPackageName(); PackageSetting pkgSetting = pkgSettings.apply(packageName); PackageSetting pkgSetting = pkgSettingFunction.apply(packageName); if (pkgSetting == null) { continue; } int level = approvalLevelForDomain(pkgSetting, domain, userId, domain); if (level <= APPROVAL_LEVEL_NONE) { int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId, domain); if (!includeNegative && level <= APPROVAL_LEVEL_NONE) { continue; } List<String> list = levelToPackages.get(level); Loading @@ -774,14 +808,14 @@ public class DomainVerificationService extends SystemService final int size = levelToPackages.size(); if (size == 0) { return emptyList(); return levelToPackages; } // Then sort them ascending by first installed time, with package name as tie breaker for (int index = 0; index < size; index++) { levelToPackages.valueAt(index).sort((first, second) -> { PackageSetting firstPkgSetting = pkgSettings.apply(first); PackageSetting secondPkgSetting = pkgSettings.apply(second); PackageSetting firstPkgSetting = pkgSettingFunction.apply(first); PackageSetting secondPkgSetting = pkgSettingFunction.apply(second); long firstInstallTime = firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime(); Loading @@ -796,19 +830,7 @@ public class DomainVerificationService extends SystemService }); } List<DomainOwner> owners = new ArrayList<>(); for (int index = 0; index < size; index++) { int level = levelToPackages.keyAt(index); boolean overrideable = level <= APPROVAL_LEVEL_SELECTION; List<String> packages = levelToPackages.valueAt(index); int packagesSize = packages.size(); for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) { owners.add(new DomainOwner(packages.get(packageIndex), overrideable)); } } return owners; }); return levelToPackages; } @NonNull Loading Loading @@ -1153,6 +1175,88 @@ public class DomainVerificationService extends SystemService } } @Override public void printOwnersForPackage(@NonNull IndentingPrintWriter writer, @Nullable String packageName, @Nullable @UserIdInt Integer userId) throws NameNotFoundException { mConnection.withPackageSettingsThrowing(pkgSettings -> { synchronized (mLock) { if (packageName == null) { int size = mAttachedPkgStates.size(); for (int index = 0; index < size; index++) { try { printOwnersForPackage(writer, mAttachedPkgStates.valueAt(index).getPackageName(), userId, pkgSettings); } catch (NameNotFoundException ignored) { // When iterating packages, if one doesn't exist somehow, ignore } } } else { printOwnersForPackage(writer, packageName, userId, pkgSettings); } } }); } private void printOwnersForPackage(@NonNull IndentingPrintWriter writer, @NonNull String packageName, @Nullable @UserIdInt Integer userId, @NonNull Function<String, PackageSetting> pkgSettingFunction) throws NameNotFoundException { PackageSetting pkgSetting = pkgSettingFunction.apply(packageName); AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg(); if (pkg == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } ArraySet<String> domains = mCollector.collectAllWebDomains(pkg); int size = domains.size(); if (size == 0) { return; } writer.println(packageName + ":"); writer.increaseIndent(); for (int index = 0; index < size; index++) { printOwnersForDomain(writer, domains.valueAt(index), userId, pkgSettingFunction); } writer.decreaseIndent(); } @Override public void printOwnersForDomains(@NonNull IndentingPrintWriter writer, @NonNull List<String> domains, @Nullable @UserIdInt Integer userId) { mConnection.withPackageSettings(pkgSettings -> { synchronized (mLock) { int size = domains.size(); for (int index = 0; index < size; index++) { printOwnersForDomain(writer, domains.get(index), userId, pkgSettings); } } }); } private void printOwnersForDomain(@NonNull IndentingPrintWriter writer, @NonNull String domain, @Nullable @UserIdInt Integer userId, @NonNull Function<String, PackageSetting> pkgSettingFunction) { SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners = new SparseArray<>(); if (userId == null || userId == UserHandle.USER_ALL) { for (int aUserId : mConnection.getAllUserIds()) { userIdToApprovalLevelToOwners.put(aUserId, getOwnersForDomainInternal(domain, true, aUserId, pkgSettingFunction)); } } else { userIdToApprovalLevelToOwners.put(userId, getOwnersForDomainInternal(domain, true, userId, pkgSettingFunction)); } mDebug.printOwners(writer, domain, userIdToApprovalLevelToOwners); } @NonNull @Override public DomainVerificationShell getShell() { Loading Loading @@ -1427,7 +1531,7 @@ public class DomainVerificationService extends SystemService // Find all approval levels int highestApproval = fillMapWithApprovalLevels(infoApprovals, domain, userId, pkgSettingFunction); if (highestApproval == APPROVAL_LEVEL_NONE) { if (highestApproval <= APPROVAL_LEVEL_NONE) { return Pair.create(emptyList(), highestApproval); } Loading Loading @@ -1484,7 +1588,7 @@ public class DomainVerificationService extends SystemService fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE); continue; } int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain); int approval = approvalLevelForDomain(pkgSetting, domain, false, userId, domain); highestApproval = Math.max(highestApproval, approval); fillInfoMapForSamePackage(inputMap, packageName, approval); } Loading Loading @@ -1600,18 +1704,53 @@ public class DomainVerificationService extends SystemService return APPROVAL_LEVEL_NONE; } return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), userId, intent); return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), false, userId, intent); } /** * @param debugObject Should be an {@link Intent} if checking for resolution or a {@link String} * otherwise. * @param includeNegative Whether to include negative values, which requires an expensive * domain comparison operation. * @param debugObject Should be an {@link Intent} if checking for resolution or a * {@link String} otherwise. */ private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host, @UserIdInt int userId, @NonNull Object debugObject) { boolean includeNegative, @UserIdInt int userId, @NonNull Object debugObject) { int approvalLevel = approvalLevelForDomainInternal(pkgSetting, host, includeNegative, userId, debugObject); if (includeNegative && approvalLevel == APPROVAL_LEVEL_NONE) { PackageUserState pkgUserState = pkgSetting.readUserState(userId); if (!pkgUserState.installed) { return APPROVAL_LEVEL_NOT_INSTALLED; } AndroidPackage pkg = pkgSetting.getPkg(); if (pkg != null) { if (!pkgUserState.isPackageEnabled(pkg)) { return APPROVAL_LEVEL_DISABLED; } else if (mCollector.containsAutoVerifyDomain(pkgSetting.getPkg(), host)) { return APPROVAL_LEVEL_UNVERIFIED; } } } return approvalLevel; } private int approvalLevelForDomainInternal(@NonNull PackageSetting pkgSetting, @NonNull String host, boolean includeNegative, @UserIdInt int userId, @NonNull Object debugObject) { String packageName = pkgSetting.getName(); final AndroidPackage pkg = pkgSetting.getPkg(); if (pkg != null && includeNegative && !mCollector.containsWebDomain(pkg, host)) { if (DEBUG_APPROVAL) { debugApproval(packageName, debugObject, userId, false, "domain not declared"); } return APPROVAL_LEVEL_UNDECLARED; } final PackageUserState pkgUserState = pkgSetting.readUserState(userId); if (pkgUserState == null) { if (DEBUG_APPROVAL) { Loading Loading @@ -1749,6 +1888,7 @@ public class DomainVerificationService extends SystemService private Pair<List<String>, Integer> getApprovedPackagesLocked(@NonNull String domain, @UserIdInt int userId, int minimumApproval, @NonNull Function<String, PackageSetting> pkgSettingFunction) { boolean includeNegative = minimumApproval < APPROVAL_LEVEL_NONE; int highestApproval = minimumApproval; List<String> approvedPackages = emptyList(); Loading @@ -1762,7 +1902,8 @@ public class DomainVerificationService extends SystemService continue; } int level = approvalLevelForDomain(pkgSetting, domain, userId, domain); int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId, domain); if (level < minimumApproval) { continue; } Loading services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java +83 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java +68 −14 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Intent; Loading @@ -33,6 +34,8 @@ import com.android.server.compat.PlatformCompat; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.List; import java.util.Objects; import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; Loading @@ -44,6 +47,12 @@ public class DomainVerificationCollector { private static final int MAX_DOMAINS_BYTE_SIZE = 1024 * 1024; private static final BiFunction<ArraySet<String>, String, Boolean> ARRAY_SET_COLLECTOR = (set, domain) -> { set.add(domain); return null; }; @NonNull private final PlatformCompat mPlatformCompat; Loading Loading @@ -105,27 +114,62 @@ public class DomainVerificationCollector { return collectDomains(pkg, true /* checkAutoVerify */, false /* valid */); } public boolean containsWebDomain(@NonNull AndroidPackage pkg, @NonNull String targetDomain) { return collectDomains(pkg, false /* checkAutoVerify */, true /* valid */, null, (BiFunction<Void, String, Boolean>) (unused, domain) -> { if (Objects.equals(targetDomain, domain)) { return true; } return null; }) != null; } public boolean containsAutoVerifyDomain(@NonNull AndroidPackage pkg, @NonNull String targetDomain) { return collectDomains(pkg, true /* checkAutoVerify */, true /* valid */, null, (BiFunction<Void, String, Boolean>) (unused, domain) -> { if (Objects.equals(targetDomain, domain)) { return true; } return null; }) != null; } @NonNull private ArraySet<String> collectDomains(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid) { ArraySet<String> domains = new ArraySet<>(); collectDomains(pkg, checkAutoVerify, valid, domains, ARRAY_SET_COLLECTOR); return domains; } @NonNull private <InitialValue, ReturnValue> ReturnValue collectDomains(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue, @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { boolean restrictDomains = DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS); if (restrictDomains) { return collectDomainsInternal(pkg, checkAutoVerify, valid); return collectDomainsInternal(pkg, checkAutoVerify, valid, initialValue, domainCollector); } else { return collectDomainsLegacy(pkg, checkAutoVerify, valid); return collectDomainsLegacy(pkg, checkAutoVerify, valid, initialValue, domainCollector); } } /** * @see #RESTRICT_DOMAINS */ private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid) { @Nullable private <InitialValue, ReturnValue> ReturnValue collectDomainsLegacy( @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue, @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { if (!checkAutoVerify) { // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2 return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */); return collectDomainsInternal(pkg, false /* checkAutoVerify */, true /* valid */, initialValue, domainCollector); } List<ParsedActivity> activities = pkg.getActivities(); Loading @@ -148,11 +192,10 @@ public class DomainVerificationCollector { } if (!needsAutoVerify) { return new ArraySet<>(); return null; } } ArraySet<String> domains = new ArraySet<>(); int totalSize = 0; boolean underMaxSize = true; for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize; Loading @@ -169,22 +212,30 @@ public class DomainVerificationCollector { if (isValidHost(host) == valid) { totalSize += byteSizeOf(host); underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; domains.add(host); ReturnValue returnValue = domainCollector.apply(initialValue, host); if (returnValue != null) { return returnValue; } } } } } } return domains; return null; } /** * @see #RESTRICT_DOMAINS * @param domainCollector Function to call with initialValue and a valid host. Should return * a non-null value if the function should return immediately * after the currently processed host. */ private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid) { ArraySet<String> domains = new ArraySet<>(); @Nullable private <InitialValue, ReturnValue> ReturnValue collectDomainsInternal( @NonNull AndroidPackage pkg, boolean checkAutoVerify, boolean valid, @Nullable InitialValue initialValue, @NonNull BiFunction<InitialValue, String, ReturnValue> domainCollector) { int totalSize = 0; boolean underMaxSize = true; Loading Loading @@ -226,13 +277,16 @@ public class DomainVerificationCollector { if (isValidHost(host) == valid) { totalSize += byteSizeOf(host); underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; domains.add(host); ReturnValue returnValue = domainCollector.apply(initialValue, host); if (returnValue != null) { return returnValue; } } } } } return domains; return null; } /** Loading
services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java +65 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.server.pm.verify.domain.models.DomainVerificationPkgState; import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; import java.util.Arrays; import java.util.List; import java.util.function.Function; @SuppressWarnings("PointlessBooleanExpression") Loading Loading @@ -100,6 +101,70 @@ public class DomainVerificationDebug { } } /** * @param userIdToApprovalLevelToOwners Mapping of user ID to approval level to domain owners. */ public void printOwners(@NonNull IndentingPrintWriter writer, @NonNull String domain, SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners) { writer.println(domain + ":"); writer.increaseIndent(); if (userIdToApprovalLevelToOwners.size() == 0) { writer.println("none"); writer.decreaseIndent(); return; } int usersSize = userIdToApprovalLevelToOwners.size(); for (int userIndex = 0; userIndex < usersSize; userIndex++) { int userId = userIdToApprovalLevelToOwners.keyAt(userIndex); SparseArray<List<String>> approvalLevelToOwners = userIdToApprovalLevelToOwners.valueAt(userIndex); if (approvalLevelToOwners.size() == 0) { continue; } boolean printedUserHeader = false; int approvalsSize = approvalLevelToOwners.size(); for (int approvalIndex = 0; approvalIndex < approvalsSize; approvalIndex++) { int approvalLevel = approvalLevelToOwners.keyAt(approvalIndex); if (approvalLevel < DomainVerificationManagerInternal.APPROVAL_LEVEL_UNVERIFIED) { continue; } if (!printedUserHeader) { writer.println("User " + userId + ":"); writer.increaseIndent(); printedUserHeader = true; } String approvalString = DomainVerificationManagerInternal.approvalLevelToDebugString(approvalLevel); List<String> owners = approvalLevelToOwners.valueAt(approvalIndex); writer.println(approvalString + "[" + approvalLevel + "]" + ":"); writer.increaseIndent(); if (owners.size() == 0) { writer.println("none"); writer.decreaseIndent(); continue; } int ownersSize = owners.size(); for (int ownersIndex = 0; ownersIndex < ownersSize; ownersIndex++) { writer.println(owners.get(ownersIndex)); } writer.decreaseIndent(); } if (printedUserHeader) { writer.decreaseIndent(); } } writer.decreaseIndent(); } boolean printState(@NonNull IndentingPrintWriter writer, @NonNull DomainVerificationPkgState pkgState, @NonNull AndroidPackage pkg, @NonNull ArrayMap<String, Integer> reusedMap, boolean wasHeaderPrinted) { Loading
services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +55 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,28 @@ public interface DomainVerificationManagerInternal { UUID DISABLED_ID = new UUID(0, 0); /** * The app was not installed for the user. */ int APPROVAL_LEVEL_NOT_INSTALLED = -4; /** * The app was not enabled for the user. */ int APPROVAL_LEVEL_DISABLED = -3; /** * The app has not declared this domain in a valid web intent-filter in their manifest, and so * would never be able to be approved for this domain. */ int APPROVAL_LEVEL_UNDECLARED = -2; /** * The app has declared this domain as a valid autoVerify domain, but it failed or has not * succeeded verification. */ int APPROVAL_LEVEL_UNVERIFIED = -1; /** * The app has not been approved for this domain and should never be able to open it through * an implicit web intent. Loading Loading @@ -117,10 +139,14 @@ public interface DomainVerificationManagerInternal { * by approval priority. A higher numerical value means the package should override all lower * values. This means that comparison using less/greater than IS valid. * * Negative values are possible, although not implemented, reserved if explicit disable of a * package for a domain needs to be tracked. * Negative values are possible, used for tracking specific reasons for why an app doesn't have * approval. */ @IntDef({ APPROVAL_LEVEL_NOT_INSTALLED, APPROVAL_LEVEL_DISABLED, APPROVAL_LEVEL_UNDECLARED, APPROVAL_LEVEL_UNVERIFIED, APPROVAL_LEVEL_NONE, APPROVAL_LEVEL_LEGACY_ASK, APPROVAL_LEVEL_LEGACY_ALWAYS, Loading @@ -131,6 +157,33 @@ public interface DomainVerificationManagerInternal { @interface ApprovalLevel { } static String approvalLevelToDebugString(@ApprovalLevel int level) { switch (level) { case APPROVAL_LEVEL_NOT_INSTALLED: return "NOT_INSTALLED"; case APPROVAL_LEVEL_DISABLED: return "DISABLED"; case APPROVAL_LEVEL_UNDECLARED: return "UNDECLARED"; case APPROVAL_LEVEL_UNVERIFIED: return "UNVERIFIED"; case APPROVAL_LEVEL_NONE: return "NONE"; case APPROVAL_LEVEL_LEGACY_ASK: return "LEGACY_ASK"; case APPROVAL_LEVEL_LEGACY_ALWAYS: return "LEGACY_ALWAYS"; case APPROVAL_LEVEL_SELECTION: return "USER_SELECTION"; case APPROVAL_LEVEL_VERIFIED: return "VERIFIED"; case APPROVAL_LEVEL_INSTANT_APP: return "INSTANT_APP"; default: return "UNKNOWN"; } } /** @see DomainVerificationManager#getDomainVerificationInfo(String) */ @Nullable @RequiresPermission(anyOf = { Loading
services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +194 −53 Original line number Diff line number Diff line Loading @@ -741,26 +741,60 @@ public class DomainVerificationService extends SystemService }); } @NonNull public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) { Objects.requireNonNull(domain); mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(), userId); SparseArray<List<String>> levelToPackages = new SparseArray<>(); return mConnection.withPackageSettingsReturningThrowing(pkgSettings -> { SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false, userId, pkgSettings); if (levelToPackages.size() == 0) { return emptyList(); } List<DomainOwner> owners = new ArrayList<>(); int size = levelToPackages.size(); for (int index = 0; index < size; index++) { int level = levelToPackages.keyAt(index); boolean overrideable = level <= APPROVAL_LEVEL_SELECTION; List<String> packages = levelToPackages.valueAt(index); int packagesSize = packages.size(); for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) { owners.add(new DomainOwner(packages.get(packageIndex), overrideable)); } } return owners; }); } /** * @param includeNegative See {@link #approvalLevelForDomain(PackageSetting, String, boolean, * int, Object)}. * @return Mapping of approval level to packages; packages are sorted by firstInstallTime. Null * if no owners were found. */ @NonNull private SparseArray<List<String>> getOwnersForDomainInternal(@NonNull String domain, boolean includeNegative, @UserIdInt int userId, @NonNull Function<String, PackageSetting> pkgSettingFunction) { SparseArray<List<String>> levelToPackages = new SparseArray<>(); // First, collect the raw approval level values synchronized (mLock) { final int size = mAttachedPkgStates.size(); for (int index = 0; index < size; index++) { DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); String packageName = pkgState.getPackageName(); PackageSetting pkgSetting = pkgSettings.apply(packageName); PackageSetting pkgSetting = pkgSettingFunction.apply(packageName); if (pkgSetting == null) { continue; } int level = approvalLevelForDomain(pkgSetting, domain, userId, domain); if (level <= APPROVAL_LEVEL_NONE) { int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId, domain); if (!includeNegative && level <= APPROVAL_LEVEL_NONE) { continue; } List<String> list = levelToPackages.get(level); Loading @@ -774,14 +808,14 @@ public class DomainVerificationService extends SystemService final int size = levelToPackages.size(); if (size == 0) { return emptyList(); return levelToPackages; } // Then sort them ascending by first installed time, with package name as tie breaker for (int index = 0; index < size; index++) { levelToPackages.valueAt(index).sort((first, second) -> { PackageSetting firstPkgSetting = pkgSettings.apply(first); PackageSetting secondPkgSetting = pkgSettings.apply(second); PackageSetting firstPkgSetting = pkgSettingFunction.apply(first); PackageSetting secondPkgSetting = pkgSettingFunction.apply(second); long firstInstallTime = firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime(); Loading @@ -796,19 +830,7 @@ public class DomainVerificationService extends SystemService }); } List<DomainOwner> owners = new ArrayList<>(); for (int index = 0; index < size; index++) { int level = levelToPackages.keyAt(index); boolean overrideable = level <= APPROVAL_LEVEL_SELECTION; List<String> packages = levelToPackages.valueAt(index); int packagesSize = packages.size(); for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) { owners.add(new DomainOwner(packages.get(packageIndex), overrideable)); } } return owners; }); return levelToPackages; } @NonNull Loading Loading @@ -1153,6 +1175,88 @@ public class DomainVerificationService extends SystemService } } @Override public void printOwnersForPackage(@NonNull IndentingPrintWriter writer, @Nullable String packageName, @Nullable @UserIdInt Integer userId) throws NameNotFoundException { mConnection.withPackageSettingsThrowing(pkgSettings -> { synchronized (mLock) { if (packageName == null) { int size = mAttachedPkgStates.size(); for (int index = 0; index < size; index++) { try { printOwnersForPackage(writer, mAttachedPkgStates.valueAt(index).getPackageName(), userId, pkgSettings); } catch (NameNotFoundException ignored) { // When iterating packages, if one doesn't exist somehow, ignore } } } else { printOwnersForPackage(writer, packageName, userId, pkgSettings); } } }); } private void printOwnersForPackage(@NonNull IndentingPrintWriter writer, @NonNull String packageName, @Nullable @UserIdInt Integer userId, @NonNull Function<String, PackageSetting> pkgSettingFunction) throws NameNotFoundException { PackageSetting pkgSetting = pkgSettingFunction.apply(packageName); AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg(); if (pkg == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } ArraySet<String> domains = mCollector.collectAllWebDomains(pkg); int size = domains.size(); if (size == 0) { return; } writer.println(packageName + ":"); writer.increaseIndent(); for (int index = 0; index < size; index++) { printOwnersForDomain(writer, domains.valueAt(index), userId, pkgSettingFunction); } writer.decreaseIndent(); } @Override public void printOwnersForDomains(@NonNull IndentingPrintWriter writer, @NonNull List<String> domains, @Nullable @UserIdInt Integer userId) { mConnection.withPackageSettings(pkgSettings -> { synchronized (mLock) { int size = domains.size(); for (int index = 0; index < size; index++) { printOwnersForDomain(writer, domains.get(index), userId, pkgSettings); } } }); } private void printOwnersForDomain(@NonNull IndentingPrintWriter writer, @NonNull String domain, @Nullable @UserIdInt Integer userId, @NonNull Function<String, PackageSetting> pkgSettingFunction) { SparseArray<SparseArray<List<String>>> userIdToApprovalLevelToOwners = new SparseArray<>(); if (userId == null || userId == UserHandle.USER_ALL) { for (int aUserId : mConnection.getAllUserIds()) { userIdToApprovalLevelToOwners.put(aUserId, getOwnersForDomainInternal(domain, true, aUserId, pkgSettingFunction)); } } else { userIdToApprovalLevelToOwners.put(userId, getOwnersForDomainInternal(domain, true, userId, pkgSettingFunction)); } mDebug.printOwners(writer, domain, userIdToApprovalLevelToOwners); } @NonNull @Override public DomainVerificationShell getShell() { Loading Loading @@ -1427,7 +1531,7 @@ public class DomainVerificationService extends SystemService // Find all approval levels int highestApproval = fillMapWithApprovalLevels(infoApprovals, domain, userId, pkgSettingFunction); if (highestApproval == APPROVAL_LEVEL_NONE) { if (highestApproval <= APPROVAL_LEVEL_NONE) { return Pair.create(emptyList(), highestApproval); } Loading Loading @@ -1484,7 +1588,7 @@ public class DomainVerificationService extends SystemService fillInfoMapForSamePackage(inputMap, packageName, APPROVAL_LEVEL_NONE); continue; } int approval = approvalLevelForDomain(pkgSetting, domain, userId, domain); int approval = approvalLevelForDomain(pkgSetting, domain, false, userId, domain); highestApproval = Math.max(highestApproval, approval); fillInfoMapForSamePackage(inputMap, packageName, approval); } Loading Loading @@ -1600,18 +1704,53 @@ public class DomainVerificationService extends SystemService return APPROVAL_LEVEL_NONE; } return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), userId, intent); return approvalLevelForDomain(pkgSetting, intent.getData().getHost(), false, userId, intent); } /** * @param debugObject Should be an {@link Intent} if checking for resolution or a {@link String} * otherwise. * @param includeNegative Whether to include negative values, which requires an expensive * domain comparison operation. * @param debugObject Should be an {@link Intent} if checking for resolution or a * {@link String} otherwise. */ private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host, @UserIdInt int userId, @NonNull Object debugObject) { boolean includeNegative, @UserIdInt int userId, @NonNull Object debugObject) { int approvalLevel = approvalLevelForDomainInternal(pkgSetting, host, includeNegative, userId, debugObject); if (includeNegative && approvalLevel == APPROVAL_LEVEL_NONE) { PackageUserState pkgUserState = pkgSetting.readUserState(userId); if (!pkgUserState.installed) { return APPROVAL_LEVEL_NOT_INSTALLED; } AndroidPackage pkg = pkgSetting.getPkg(); if (pkg != null) { if (!pkgUserState.isPackageEnabled(pkg)) { return APPROVAL_LEVEL_DISABLED; } else if (mCollector.containsAutoVerifyDomain(pkgSetting.getPkg(), host)) { return APPROVAL_LEVEL_UNVERIFIED; } } } return approvalLevel; } private int approvalLevelForDomainInternal(@NonNull PackageSetting pkgSetting, @NonNull String host, boolean includeNegative, @UserIdInt int userId, @NonNull Object debugObject) { String packageName = pkgSetting.getName(); final AndroidPackage pkg = pkgSetting.getPkg(); if (pkg != null && includeNegative && !mCollector.containsWebDomain(pkg, host)) { if (DEBUG_APPROVAL) { debugApproval(packageName, debugObject, userId, false, "domain not declared"); } return APPROVAL_LEVEL_UNDECLARED; } final PackageUserState pkgUserState = pkgSetting.readUserState(userId); if (pkgUserState == null) { if (DEBUG_APPROVAL) { Loading Loading @@ -1749,6 +1888,7 @@ public class DomainVerificationService extends SystemService private Pair<List<String>, Integer> getApprovedPackagesLocked(@NonNull String domain, @UserIdInt int userId, int minimumApproval, @NonNull Function<String, PackageSetting> pkgSettingFunction) { boolean includeNegative = minimumApproval < APPROVAL_LEVEL_NONE; int highestApproval = minimumApproval; List<String> approvedPackages = emptyList(); Loading @@ -1762,7 +1902,8 @@ public class DomainVerificationService extends SystemService continue; } int level = approvalLevelForDomain(pkgSetting, domain, userId, domain); int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId, domain); if (level < minimumApproval) { continue; } Loading
services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java +83 −0 File changed.Preview size limit exceeded, changes collapsed. Show changes