diff --git a/core/api/current.txt b/core/api/current.txt index 61d30de25147b..8ce2d5d0da05d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12947,6 +12947,27 @@ package android.content.pm { } +package android.content.pm.verify.domain { + + public final class DomainVerificationManager { + method @Nullable public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + } + + public final class DomainVerificationUserSelection implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Map getHostToStateMap(); + method @NonNull public String getPackageName(); + method @NonNull public android.os.UserHandle getUser(); + method @NonNull public boolean isLinkHandlingAllowed(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int DOMAIN_STATE_NONE = 0; // 0x0 + field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1 + field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2 + } + +} + package android.content.res { public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c85f566344258..af16151d5e7b4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2771,8 +2771,7 @@ package android.content.pm.verify.domain { public final class DomainVerificationManager { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; - method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; - method @NonNull public java.util.List getOwnersForDomain(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List getOwnersForDomain(@NonNull String); method public static boolean isStateModifiable(int); method public static boolean isStateVerified(int); method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List queryValidVerificationPackageNames(); @@ -2793,17 +2792,7 @@ package android.content.pm.verify.domain { } public final class DomainVerificationUserSelection implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.Map getHostToStateMap(); method @NonNull public java.util.UUID getIdentifier(); - method @NonNull public String getPackageName(); - method @NonNull public android.os.UserHandle getUser(); - method @NonNull public boolean isLinkHandlingAllowed(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - field public static final int DOMAIN_STATE_NONE = 0; // 0x0 - field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1 - field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2 } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index a9b5a6da98843..43c14a99b2212 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1421,7 +1421,6 @@ public final class SystemServiceRegistry { } }); - // TODO(b/159952358): Only register this service for the domain verification agent? registerService(Context.DOMAIN_VERIFICATION_SERVICE, DomainVerificationManager.class, new CachedServiceFetcher() { @Override diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java index 4df8d167e7d16..e18d7a0248623 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java @@ -29,60 +29,66 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; -import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; /** - * System service to access the domain verification APIs. + * System service to access domain verification APIs. * - * Allows the approved domain verification - * agent on the device (the sole holder of - * {@link android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status - * of domains declared by applications in their AndroidManifest.xml, to allow them to open those - * links inside the app when selected by the user. This is done through querying - * {@link #getDomainVerificationInfo(String)} and calling - * {@link #setDomainVerificationStatus(UUID, Set, int)}. - * - * Also allows the domain preference settings (holder of - * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) to update the - * preferences of the user, when they have chosen to explicitly allow an application to open links. - * This is done through querying {@link #getDomainVerificationUserSelection(String)} and calling - * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and - * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}. - * - * @hide + * Applications should use {@link #getDomainVerificationUserSelection(String)} if necessary to + * check if/how they are verified for a domain, which is required starting from platform + * {@link android.os.Build.VERSION_CODES#S} in order to open {@link Intent}s which declare + * {@link Intent#CATEGORY_BROWSABLE} or no category and also match against + * {@link Intent#CATEGORY_DEFAULT} {@link android.content.IntentFilter}s, either through an + * explicit declaration of {@link Intent#CATEGORY_DEFAULT} or through the use of + * {@link android.content.pm.PackageManager#MATCH_DEFAULT_ONLY}, which is usually added for the + * caller when using {@link Context#startActivity(Intent)} and similar. */ -@SystemApi @SystemService(Context.DOMAIN_VERIFICATION_SERVICE) public final class DomainVerificationManager { /** - * Extra field name for a {@link DomainVerificationRequest} for the requested packages. - * Passed to an the domain verification agent that handles + * Extra field name for a {@link DomainVerificationRequest} for the requested packages. Passed + * to an the domain verification agent that handles * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION}. + * + * @hide */ + @SystemApi public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST"; /** * No response has been recorded by either the system or any verification agent. + * + * @hide */ + @SystemApi public static final int STATE_NO_RESPONSE = DomainVerificationState.STATE_NO_RESPONSE; - /** The verification agent has explicitly verified the domain at some point. */ + /** + * The verification agent has explicitly verified the domain at some point. + * + * @hide + */ + @SystemApi public static final int STATE_SUCCESS = DomainVerificationState.STATE_SUCCESS; /** - * The first available custom response code. This and any greater integer, along with - * {@link #STATE_SUCCESS} are the only values settable by the verification agent. All values - * will be treated as if the domain is unverified. + * The first available custom response code. This and any greater integer, along with {@link + * #STATE_SUCCESS} are the only values settable by the verification agent. All values will be + * treated as if the domain is unverified. + * + * @hide */ + @SystemApi public static final int STATE_FIRST_VERIFIER_DEFINED = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED; - /** @hide */ + /** + * @hide + */ @NonNull public static String stateToDebugString(@DomainVerificationState.State int state) { switch (state) { @@ -108,9 +114,12 @@ public final class DomainVerificationManager { } /** - * Checks if a state considers the corresponding domain to be successfully verified. The - * domain verification agent may use this to determine whether or not to re-verify a domain. + * Checks if a state considers the corresponding domain to be successfully verified. The domain + * verification agent may use this to determine whether or not to re-verify a domain. + * + * @hide */ + @SystemApi public static boolean isStateVerified(@DomainVerificationState.State int state) { switch (state) { case DomainVerificationState.STATE_SUCCESS: @@ -130,9 +139,12 @@ public final class DomainVerificationManager { /** * Checks if a state is modifiable by the domain verification agent. This is useful as the * platform may add new state codes in newer versions, and older verification agents can use - * this method to determine if a state can be changed without having to be aware of what the - * new state means. + * this method to determine if a state can be changed without having to be aware of what the new + * state means. + * + * @hide */ + @SystemApi public static boolean isStateModifiable(@DomainVerificationState.State int state) { switch (state) { case DomainVerificationState.STATE_NO_RESPONSE: @@ -151,8 +163,9 @@ public final class DomainVerificationManager { } /** - * For determine re-verify policy. This is hidden from the domain verification agent so that - * no behavior is made based on the result. + * For determine re-verify policy. This is hidden from the domain verification agent so that no + * behavior is made based on the result. + * * @hide */ public static boolean isStateDefault(@DomainVerificationState.State int state) { @@ -171,13 +184,19 @@ public final class DomainVerificationManager { } } - /** @hide */ + /** + * @hide + */ public static final int ERROR_INVALID_DOMAIN_SET = 1; - /** @hide */ + /** + * @hide + */ public static final int ERROR_NAME_NOT_FOUND = 2; - /** @hide */ - @IntDef(prefix = { "ERROR_" }, value = { + /** + * @hide + */ + @IntDef(prefix = {"ERROR_"}, value = { ERROR_INVALID_DOMAIN_SET, ERROR_NAME_NOT_FOUND, }) @@ -188,7 +207,27 @@ public final class DomainVerificationManager { private final IDomainVerificationManager mDomainVerificationManager; - /** @hide */ + + /** + * System service to access the domain verification APIs. + *

+ * Allows the approved domain verification agent on the device (the sole holder of {@link + * android.Manifest.permission#DOMAIN_VERIFICATION_AGENT}) to update the approval status of + * domains declared by applications in their AndroidManifest.xml, to allow them to open those + * links inside the app when selected by the user. This is done through querying {@link + * #getDomainVerificationInfo(String)} and calling {@link #setDomainVerificationStatus(UUID, + * Set, int)}. + *

+ * Also allows the domain preference settings (holder of + * {@link android.Manifest.permission#UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) + * to update the preferences of the user, when they have chosen to explicitly allow an + * application to open links. This is done through querying + * {@link #getDomainVerificationUserSelection(String)} and calling + * {@link #setDomainVerificationUserSelection(UUID, Set, boolean)} and + * {@link #setDomainVerificationLinkHandlingAllowed(String, boolean)}. + * + * @hide + */ public DomainVerificationManager(Context context, IDomainVerificationManager domainVerificationManager) { mContext = context; @@ -200,7 +239,9 @@ public final class DomainVerificationManager { * usually a heavy workload and should be done infrequently. * * @return the current snapshot of package names with valid autoVerify URLs. + * @hide */ + @SystemApi @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public List queryValidVerificationPackageNames() { @@ -217,7 +258,9 @@ public final class DomainVerificationManager { * @return the data for the package, or null if it does not declare any autoVerify domains * @throws NameNotFoundException If the package is unavailable. This is an unrecoverable error * and should not be re-tried except on a time scheduled basis. + * @hide */ + @SystemApi @Nullable @RequiresPermission(anyOf = { android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, @@ -240,25 +283,25 @@ public final class DomainVerificationManager { } /** - * Change the verification status of the {@param domains} of the package associated with - * {@param domainSetId}. + * Change the verification status of the {@param domains} of the package associated with {@param + * domainSetId}. * * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}. * @param domains List of host names to change the state of. * @param state See {@link DomainVerificationInfo#getHostToStateMap()}. * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are * invalid. This usually means the work being processed by the - * verification agent is outdated and a new request should - * be scheduled, if one has not already been done as part of - * the {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} - * broadcast. + * verification agent is outdated and a new request should be + * scheduled, if one has not already been done as part of the + * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast. * @throws NameNotFoundException If the ID is known to be good, but the package is - * unavailable. This may be because the package is - * installed on a volume that is no longer mounted. This - * error is unrecoverable until the package is available - * again, and should not be re-tried except on a time - * scheduled basis. + * unavailable. This may be because the package is installed on + * a volume that is no longer mounted. This error is + * unrecoverable until the package is available again, and + * should not be re-tried except on a time scheduled basis. + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set domains, @DomainVerificationState.State int state) throws NameNotFoundException { @@ -284,7 +327,10 @@ public final class DomainVerificationManager { * with this enabled is not guaranteed to be the sole link handler for its domains. *

* By default, all apps are allowed to open links. Users must disable them explicitly. + * + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed) throws NameNotFoundException { @@ -307,11 +353,11 @@ public final class DomainVerificationManager { * Update the recorded user selection for the given {@param domains} for the given {@param * domainSetId}. This state is recorded for the lifetime of a domain for a package on device, * and will never be reset by the system short of an app data clear. - * + *

* This state is stored per device user. If another user needs to be changed, the appropriate - * permissions must be acquired and - * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used. - * + * permissions must be acquired and {@link Context#createContextAsUser(UserHandle, int)} should + * be used. + *

* Enabling an unverified domain will allow an application to open it, but this can only occur * if no other app on the device is approved for a higher approval level. This can queried * using {@link #getOwnersForDomain(String)}. @@ -329,12 +375,13 @@ public final class DomainVerificationManager { * @throws IllegalArgumentException If the ID is invalidated or the {@param domains} are * invalid. * @throws NameNotFoundException If the ID is known to be good, but the package is - * unavailable. This may be because the package is - * installed on a volume that is no longer mounted. This - * error is unrecoverable until the package is available - * again, and should not be re-tried except on a time - * scheduled basis. + * unavailable. This may be because the package is installed on + * a volume that is no longer mounted. This error is + * unrecoverable until the package is available again, and + * should not be re-tried except on a time scheduled basis. + * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull UUID domainSetId, @NonNull Set domains, boolean enabled) throws NameNotFoundException { @@ -355,19 +402,12 @@ public final class DomainVerificationManager { /** * Retrieve the user selection data for the given {@param packageName} and the current user. - * It is the responsibility of the caller to ensure that the - * {@link DomainVerificationUserSelection#getIdentifier()} matches any prior API calls. - * - * This state is stored per device user. If another user needs to be accessed, the appropriate - * permissions must be acquired and - * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used. * * @param packageName The app to query state for. - * @return the user selection verification data for the given package for the current user, - * or null if the package does not declare any HTTP/HTTPS domains. + * @return the user selection verification data for the given package for the current user, or + * null if the package does not declare any HTTP/HTTPS domains. */ @Nullable - @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName) throws NameNotFoundException { try { @@ -393,7 +433,10 @@ public final class DomainVerificationManager { * * By default the list will be returned ordered from lowest to highest * priority. + * + * @hide */ + @SystemApi @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public List getOwnersForDomain(@NonNull String domain) { @@ -442,8 +485,9 @@ public final class DomainVerificationManager { * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains * provided by the caller is no longer valid. This may be recoverable, and the caller should * re-query the package name associated with the ID using - * {@link #getDomainVerificationInfo(String)} in order to check. If that also fails, then the - * package is no longer known to the device and thus all pending work for it should be dropped. + * {@link #getDomainVerificationInfo(String)} + * in order to check. If that also fails, then the package is no longer known to the device and + * thus all pending work for it should be dropped. * * @hide */ @@ -455,7 +499,9 @@ public final class DomainVerificationManager { public static final int REASON_UNKNOWN_DOMAIN = 4; public static final int REASON_UNABLE_TO_APPROVE = 5; - /** @hide */ + /** + * @hide + */ @IntDef({ REASON_ID_NULL, REASON_ID_INVALID, @@ -494,7 +540,9 @@ public final class DomainVerificationManager { @Nullable private final String mPackageName; - /** @hide */ + /** + * @hide + */ public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName, @Reason int reason) { super(buildMessage(domainSetId, packageName, reason)); diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java index d23f5f133841e..8b7f1467a0cb4 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; +import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; @@ -36,28 +37,26 @@ import java.util.UUID; * Contains the user selection state for a package. This means all web HTTP(S) domains declared by a * package in its manifest, whether or not they were marked for auto verification. *

- * By default, all apps are allowed to automatically open links with domains that they've - * successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}. The user - * can decide to disable this, disallowing the application from opening all links. Note that the - * toggle affects all links and is not based on the verification state of the domains. + * Applications should use {@link #getHostToStateMap()} if necessary to + * check if/how they are verified for a domain, which is required starting from platform + * {@link android.os.Build.VERSION_CODES#S} in order to open {@link Intent}s which declare + * {@link Intent#CATEGORY_BROWSABLE} or no category and also match against + * {@link Intent#CATEGORY_DEFAULT} {@link android.content.IntentFilter}s, either through an + * explicit declaration of {@link Intent#CATEGORY_DEFAULT} or through the use of + * {@link android.content.pm.PackageManager#MATCH_DEFAULT_ONLY}, which is usually added for the + * caller when using {@link Context#startActivity(Intent)} and similar. + *

+ * By default, all apps are allowed to automatically open links for the above case for domains that + * they've successfully verified against. This is reflected by {@link #isLinkHandlingAllowed()}. + * The user can decide to disable this, disallowing the application from opening all links. Note + * that the toggle affects all links and is not based on the verification state of the + * domains. *

* Assuming the toggle is enabled, the user can also select additional unverified domains to grant * to the application to open, which is reflected in {@link #getHostToStateMap()}. But only a single * application can be approved for a domain unless the applications are both approved. If another * application is approved, the user will not be allowed to enable the domain. - *

- * These values can be changed through the - * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, - * boolean)} and {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, - * boolean)} APIs. - *

- * Note that because state is per user, if a different user needs to be changed, one will need to - * use {@link Context#createContextAsUser(UserHandle, int)} and hold the {@link - * android.Manifest.permission#INTERACT_ACROSS_USERS} permission. - * - * @hide */ -@SystemApi @SuppressWarnings("DefaultAnnotationParam") @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, genEqualsHashCode = true, genHiddenConstDefs = true) @@ -70,9 +69,8 @@ public final class DomainVerificationUserSelection implements Parcelable { public static final int DOMAIN_STATE_NONE = 0; /** - * The domain has been selected through the - * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)} - * API, under the assumption it has not been reset by the system. + * The domain has been selected by the user. This may be reset to {@link #DOMAIN_STATE_NONE} if + * another application is selected or verified for the same domain. */ public static final int DOMAIN_STATE_SELECTED = 1; @@ -122,6 +120,15 @@ public final class DomainVerificationUserSelection implements Parcelable { DomainVerificationUserSelection.class.getClassLoader()); } + /** + * @see DomainVerificationInfo#getIdentifier + * @hide + */ + @SystemApi + public @NonNull UUID getIdentifier() { + return mIdentifier; + } + // Code below generated by codegen v1.0.22. @@ -200,14 +207,6 @@ public final class DomainVerificationUserSelection implements Parcelable { // onConstructed(); // You can define this method to get a callback } - /** - * @see DomainVerificationInfo#getIdentifier - */ - @DataClass.Generated.Member - public @NonNull UUID getIdentifier() { - return mIdentifier; - } - /** * The package name that this data corresponds to. */ @@ -368,10 +367,10 @@ public final class DomainVerificationUserSelection implements Parcelable { }; @DataClass.Generated( - time = 1613683603297L, + time = 1614649708937L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java", - inputSignatures = "public static final int DOMAIN_STATE_NONE\npublic static final int DOMAIN_STATE_SELECTED\npublic static final int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)") + inputSignatures = "public static final int DOMAIN_STATE_NONE\npublic static final int DOMAIN_STATE_SELECTED\npublic static final int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map unparcelHostToStateMap(android.os.Parcel)\npublic @android.annotation.SystemApi @android.annotation.NonNull java.util.UUID getIdentifier()\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java index ed37fa0da01f9..712ed6c19152a 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java @@ -129,6 +129,21 @@ public class DomainVerificationEnforcer { } } + /** + * Enforced when mutating user selection state inside an exposed API method. + */ + public boolean assertApprovedUserSelectionQuerent(int callingUid, @UserIdInt int callingUserId, + @NonNull String packageName, @UserIdInt int targetUserId) throws SecurityException { + if (callingUserId != targetUserId) { + mContext.enforcePermission( + Manifest.permission.INTERACT_ACROSS_USERS, + Binder.getCallingPid(), callingUid, + "Caller is not allowed to edit other users"); + } + + return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); + } + /** * Enforced when mutating user selection state inside an exposed API method. */ 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 f5fc58ea83297..3b25c7a24eba6 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 @@ -629,7 +629,7 @@ public class DomainVerificationService extends SystemService @Override public DomainVerificationUserSelection getDomainVerificationUserSelection( @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { - if (!mEnforcer.assertApprovedUserSelector(mConnection.getCallingUid(), + if (!mEnforcer.assertApprovedUserSelectionQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(), packageName, userId)) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt index fa700b9a7a207..2616a59c7edba 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt @@ -154,6 +154,15 @@ class DomainVerificationEnforcerTest { enforcer(Type.VERIFIER, "approvedVerifier") { assertApprovedVerifier(it.callingUid, it.proxy) }, + enforcer( + Type.SELECTION_QUERENT, + "approvedUserSelectionQuerent" + ) { + assertApprovedUserSelectionQuerent( + it.callingUid, it.callingUserId, + it.targetPackageName, it.userId + ) + }, enforcer( Type.SELECTOR, "approvedUserSelector" @@ -211,7 +220,7 @@ class DomainVerificationEnforcerTest { service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") { setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId) }, - service(Type.SELECTOR_USER, "getUserSelectionUserId") { + service(Type.SELECTION_QUERENT, "getUserSelectionUserId") { getDomainVerificationUserSelection(it.targetPackageName, it.userId) }, service(Type.SELECTOR_USER, "setUserSelectionUserId") { @@ -345,6 +354,7 @@ class DomainVerificationEnforcerTest { Type.INTERNAL -> internal() Type.QUERENT -> approvedQuerent() Type.VERIFIER -> approvedVerifier() + Type.SELECTION_QUERENT -> approvedUserSelectionQuerent(verifyCrossUser = true) Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false) Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true) Type.LEGACY_QUERENT -> legacyQuerent() @@ -354,7 +364,7 @@ class DomainVerificationEnforcerTest { }.run { /*exhaust*/ } } - fun internal() { + private fun internal() { val context: Context = mockThrowOnUnmocked() val target = params.construct(context) @@ -368,7 +378,7 @@ class DomainVerificationEnforcerTest { } } - fun approvedQuerent() { + private fun approvedQuerent() { val allowUserSelection = AtomicBoolean(false) val allowPreferredApps = AtomicBoolean(false) val allowQueryAll = AtomicBoolean(false) @@ -410,7 +420,7 @@ class DomainVerificationEnforcerTest { runMethod(target, NON_VERIFIER_UID) } - fun approvedVerifier() { + private fun approvedVerifier() { val allowDomainVerificationAgent = AtomicBoolean(false) val allowIntentVerificationAgent = AtomicBoolean(false) val allowQueryAll = AtomicBoolean(false) @@ -452,7 +462,56 @@ class DomainVerificationEnforcerTest { assertFails { runMethod(target, NON_VERIFIER_UID) } } - fun approvedUserSelector(verifyCrossUser: Boolean) { + private fun approvedUserSelectionQuerent(verifyCrossUser: Boolean) { + val allowInteractAcrossUsers = AtomicBoolean(false) + val context: Context = mockThrowOnUnmocked { + initPermission( + allowInteractAcrossUsers, + android.Manifest.permission.INTERACT_ACROSS_USERS + ) + } + val target = params.construct(context) + + fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { + // User selector makes no distinction by UID + val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID + if (throws) { + allUids.forEach { + assertFails { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + } else { + allUids.forEach { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + + // User selector doesn't use QUERY_ALL, so the invisible package should always fail + allUids.forEach { + assertFails { + runMethod(target, it, visible = false, callingUserId, targetUserId) + } + } + } + + val callingUserId = 0 + val notCallingUserId = 1 + + runTestCases(callingUserId, callingUserId, throws = false) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = true) + } + + allowInteractAcrossUsers.set(true) + + runTestCases(callingUserId, callingUserId, throws = false) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = false) + } + } + + private fun approvedUserSelector(verifyCrossUser: Boolean) { val allowUserSelection = AtomicBoolean(false) val allowInteractAcrossUsers = AtomicBoolean(false) val context: Context = mockThrowOnUnmocked { @@ -752,6 +811,9 @@ class DomainVerificationEnforcerTest { // INTERNAL || domain verification agent VERIFIER, + // No permissions, allows all apps to view domain state for visible packages + SELECTION_QUERENT, + // Holding the user setting permission SELECTOR,