summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java82
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java65
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java57
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java247
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java83
5 files changed, 465 insertions, 69 deletions
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index a8a6a723ce74..a5ba82fe162a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -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;
@@ -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;
@@ -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;
@@ -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();
@@ -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;
@@ -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;
@@ -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;
}
/**
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index 1d0447842cac..adf8f0d3e486 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -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")
@@ -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) {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 0f99e1963f28..5aed3670e5c7 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -57,6 +57,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.
*/
@@ -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,
@@ -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 = {
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index a3e1a9cdc524..3a4b849bac3c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -741,62 +741,21 @@ 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 -> {
- // 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);
- if (pkgSetting == null) {
- continue;
- }
-
- int level = approvalLevelForDomain(pkgSetting, domain, userId, domain);
- if (level <= APPROVAL_LEVEL_NONE) {
- continue;
- }
- List<String> list = levelToPackages.get(level);
- if (list == null) {
- list = new ArrayList<>();
- levelToPackages.put(level, list);
- }
- list.add(packageName);
- }
- }
-
- final int size = levelToPackages.size();
- if (size == 0) {
+ SparseArray<List<String>> levelToPackages = getOwnersForDomainInternal(domain, false,
+ userId, pkgSettings);
+ if (levelToPackages.size() == 0) {
return emptyList();
}
- // 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);
-
- long firstInstallTime =
- firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
- long secondInstallTime =
- secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime();
-
- if (firstInstallTime != secondInstallTime) {
- return (int) (firstInstallTime - secondInstallTime);
- }
-
- return first.compareToIgnoreCase(second);
- });
- }
-
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;
@@ -811,6 +770,69 @@ public class DomainVerificationService extends SystemService
});
}
+ /**
+ * @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 = pkgSettingFunction.apply(packageName);
+ if (pkgSetting == null) {
+ continue;
+ }
+
+ int level = approvalLevelForDomain(pkgSetting, domain, includeNegative, userId,
+ domain);
+ if (!includeNegative && level <= APPROVAL_LEVEL_NONE) {
+ continue;
+ }
+ List<String> list = levelToPackages.get(level);
+ if (list == null) {
+ list = new ArrayList<>();
+ levelToPackages.put(level, list);
+ }
+ list.add(packageName);
+ }
+ }
+
+ final int size = levelToPackages.size();
+ if (size == 0) {
+ 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 = pkgSettingFunction.apply(first);
+ PackageSetting secondPkgSetting = pkgSettingFunction.apply(second);
+
+ long firstInstallTime =
+ firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
+ long secondInstallTime =
+ secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime();
+
+ if (firstInstallTime != secondInstallTime) {
+ return (int) (firstInstallTime - secondInstallTime);
+ }
+
+ return first.compareToIgnoreCase(second);
+ });
+ }
+
+ return levelToPackages;
+ }
+
@NonNull
@Override
public UUID generateNewId() {
@@ -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() {
@@ -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);
}
@@ -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);
}
@@ -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) {
@@ -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();
@@ -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;
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
index ea71b28c6acd..da2d162d46f9 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java
@@ -110,6 +110,13 @@ public class DomainVerificationShell {
pw.println(" packages will be reset if no one package is specified.");
pw.println(" <ALLOWED>: true to allow the package to open auto verified links, false");
pw.println(" to disable");
+ pw.println(" get-app-link-owners [--user <USER_ID>] [--package <PACKAGE>] [<DOMAINS>]");
+ pw.println(" Print the owners for a specific domain for a given user in low to high");
+ pw.println(" priority order.");
+ pw.println(" --user <USER_ID>: the user to query for");
+ pw.println(" --package <PACKAGE>: optionally also print for all web domains declared");
+ pw.println(" by a package, or \"all\" to print all packages");
+ pw.println(" --<DOMAINS>: space separated list of domains to query for");
}
/**
@@ -132,6 +139,8 @@ public class DomainVerificationShell {
return runSetAppLinksUserState(commandHandler);
case "set-app-links-allowed":
return runSetAppLinksAllowed(commandHandler);
+ case "get-app-link-owners":
+ return runGetAppLinkOwners(commandHandler);
}
return null;
@@ -420,6 +429,67 @@ public class DomainVerificationShell {
return true;
}
+ // pm get-app-link-owners [--user <USER_ID>] [--package <PACKAGE>] [<DOMAINS>]
+ private boolean runGetAppLinkOwners(@NonNull BasicShellCommandHandler commandHandler) {
+ String packageName = null;
+ Integer userId = null;
+ String option;
+ while ((option = commandHandler.getNextOption()) != null) {
+ switch (option) {
+ case "--user":
+ userId = UserHandle.parseUserArg(commandHandler.getNextArgRequired());
+ break;
+ case "--package":
+ packageName = commandHandler.getNextArgRequired();
+ if (TextUtils.isEmpty(packageName)) {
+ commandHandler.getErrPrintWriter().println("Error: no package specified");
+ return false;
+ }
+ break;
+ default:
+ commandHandler.getErrPrintWriter().println(
+ "Error: unexpected option: " + option);
+ return false;
+ }
+ }
+
+ ArrayList<String> domains = getRemainingArgs(commandHandler);
+ if (domains.isEmpty() && TextUtils.isEmpty(packageName)) {
+ commandHandler.getErrPrintWriter()
+ .println("Error: no package name or domain specified");
+ return false;
+ }
+
+ if (userId != null) {
+ userId = translateUserId(userId, "runSetAppLinksAllowed");
+ }
+
+ try (IndentingPrintWriter writer = new IndentingPrintWriter(
+ commandHandler.getOutPrintWriter(), /* singleIndent */ " ", /* wrapLength */
+ 120)) {
+ writer.increaseIndent();
+ if (packageName != null) {
+ if (packageName.equals("all")) {
+ packageName = null;
+ }
+
+ try {
+ mCallback.printOwnersForPackage(writer, packageName, userId);
+ } catch (NameNotFoundException e) {
+ commandHandler.getErrPrintWriter()
+ .println("Error: package not found: " + packageName);
+ return false;
+ }
+ }
+ if (!domains.isEmpty()) {
+ mCallback.printOwnersForDomains(writer, domains, userId);
+ }
+ writer.decreaseIndent();
+ return true;
+ }
+ }
+
+ @NonNull
private ArrayList<String> getRemainingArgs(@NonNull BasicShellCommandHandler commandHandler) {
ArrayList<String> args = new ArrayList<>();
String arg;
@@ -534,5 +604,18 @@ public class DomainVerificationShell {
*/
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+
+ /**
+ * Print the owners for all domains in a given package.
+ */
+ void printOwnersForPackage(@NonNull IndentingPrintWriter writer,
+ @Nullable String packageName, @Nullable @UserIdInt Integer userId)
+ throws NameNotFoundException;
+
+ /**
+ * Print the owners for the given domains.
+ */
+ void printOwnersForDomains(@NonNull IndentingPrintWriter writer,
+ @NonNull List<String> domains, @Nullable @UserIdInt Integer userId);
}
}