Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 173ef942 authored by Winson's avatar Winson
Browse files

Expose error codes for domain verification setters

Removes the InvalidDomainSetException and exposes the reasons as API
error codes. Implementers can check for a non-0 error code to tell if
the failure is meant to indicate a cancelled request.

Bug: 180991102

Test: atest com.android.server.pm.test.verify.domain
Test: atest DomainVerificationManagerApiTest

Change-Id: I19b88fb582fc7f633497d036a0590350ec463f3e
parent f9e1c302
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -2848,9 +2848,16 @@ package android.content.pm.verify.domain {
    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
    method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> queryValidVerificationPackageNames();
    method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationLinkHandlingAllowed(@NonNull String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
    method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public void setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
    method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public void setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
    method @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public int setDomainVerificationStatus(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, int) throws android.content.pm.PackageManager.NameNotFoundException;
    method @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public int setDomainVerificationUserSelection(@NonNull java.util.UUID, @NonNull java.util.Set<java.lang.String>, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
    field public static final int ERROR_DOMAIN_SET_ID_INVALID = 1; // 0x1
    field public static final int ERROR_DOMAIN_SET_ID_NULL = 2; // 0x2
    field public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3; // 0x3
    field public static final int ERROR_INVALID_STATE_CODE = 6; // 0x6
    field public static final int ERROR_UNABLE_TO_APPROVE = 5; // 0x5
    field public static final int ERROR_UNKNOWN_DOMAIN = 4; // 0x4
    field public static final String EXTRA_VERIFICATION_REQUEST = "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";
    field public static final int STATUS_OK = 0; // 0x0
  }
  public final class DomainVerificationRequest implements android.os.Parcelable {
+91 −127
Original line number Diff line number Diff line
@@ -60,29 +60,97 @@ public final class DomainVerificationManager {
            "android.content.pm.verify.domain.extra.VERIFICATION_REQUEST";

    /**
     * Default return code for when a method has succeeded.
     *
     * @hide
     */
    @SystemApi
    public static final int STATUS_OK = 0;

    /**
     * The provided domain set ID was invalid, probably due to the package being updated between
     * the initial request that provided the ID and the method call that used it. This usually
     * means the work being processed by the verification agent is outdated and a new request
     * should be scheduled, which should already be in progress as part of the
     * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
     *
     * @hide
     */
    @SystemApi
    public static final int ERROR_DOMAIN_SET_ID_INVALID = 1;

    /**
     * The provided domain set ID was null. This is a developer error.
     *
     * @hide
     */
    @SystemApi
    public static final int ERROR_DOMAIN_SET_ID_NULL = 2;

    /**
     * The provided set of domains was null or empty. This is a developer error.
     *
     * @hide
     */
    @SystemApi
    public static final int ERROR_DOMAIN_SET_NULL_OR_EMPTY = 3;

    /**
     * The provided set of domains contains a domain not declared by the target package. This
     * usually means the work being processed by the verification agent is outdated and a new
     * request should be scheduled, which should already be in progress as part of the
     * {@link Intent#ACTION_DOMAINS_NEED_VERIFICATION} broadcast.
     *
     * @hide
     */
    @SystemApi
    public static final int ERROR_UNKNOWN_DOMAIN = 4;

    /**
     * The system was unable to select the domain for approval. This indicates another application
     * has been granted a higher approval, usually through domain verification, and the target
     * package is unable to override it.
     *
     * @hide
     */
    public static final int ERROR_INVALID_DOMAIN_SET = 1;
    @SystemApi
    public static final int ERROR_UNABLE_TO_APPROVE = 5;

    /**
     * The provided state code is incorrect. The domain verification agent is only allowed to
     * assign {@link DomainVerificationInfo#STATE_SUCCESS} or error codes equal to or greater than
     * {@link DomainVerificationInfo#STATE_FIRST_VERIFIER_DEFINED}.
     *
     * @hide
     */
    public static final int ERROR_NAME_NOT_FOUND = 2;
    @SystemApi
    public static final int ERROR_INVALID_STATE_CODE = 6;

    /**
     * Used to communicate through {@link ServiceSpecificException}. Should not be exposed as API.
     *
     * @hide
     */
    public static final int INTERNAL_ERROR_NAME_NOT_FOUND = 1;

    /**
     * @hide
     */
    @IntDef(prefix = {"ERROR_"}, value = {
            ERROR_INVALID_DOMAIN_SET,
            ERROR_NAME_NOT_FOUND,
            ERROR_DOMAIN_SET_ID_INVALID,
            ERROR_DOMAIN_SET_ID_NULL,
            ERROR_DOMAIN_SET_NULL_OR_EMPTY,
            ERROR_UNKNOWN_DOMAIN,
            ERROR_UNABLE_TO_APPROVE,
            ERROR_INVALID_STATE_CODE
    })
    private @interface Error {
    public @interface Error {
    }

    private final Context mContext;

    private final IDomainVerificationManager mDomainVerificationManager;


    /**
     * System service to access the domain verification APIs.
     * <p>
@@ -164,27 +232,24 @@ public final class DomainVerificationManager {
     * @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.
     * @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.
     * @return error code or {@link #STATUS_OK} if successful
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
    public void setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
    public int setDomainVerificationStatus(@NonNull UUID domainSetId, @NonNull Set<String> domains,
            int state) throws NameNotFoundException {
        try {
            mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
            return mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
                    new DomainSet(domains), state);
        } catch (Exception e) {
            Exception converted = rethrow(e, domainSetId);
            Exception converted = rethrow(e, null);
            if (converted instanceof NameNotFoundException) {
                throw (NameNotFoundException) converted;
            } else if (converted instanceof RuntimeException) {
@@ -213,7 +278,7 @@ public final class DomainVerificationManager {
            mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(packageName,
                    allowed, mContext.getUserId());
        } catch (Exception e) {
            Exception converted = rethrow(e, packageName);
            Exception converted = rethrow(e, null);
            if (converted instanceof NameNotFoundException) {
                throw (NameNotFoundException) converted;
            } else if (converted instanceof RuntimeException) {
@@ -247,24 +312,24 @@ public final class DomainVerificationManager {
     * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
     * @param domains     The domains to toggle the state of.
     * @param enabled     Whether or not the app should automatically open the domains specified.
     * @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.
     * @return error code or {@link #STATUS_OK} if successful
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
    public void setDomainVerificationUserSelection(@NonNull UUID domainSetId,
    public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
            @NonNull Set<String> domains, boolean enabled) throws NameNotFoundException {
        try {
            mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
                    new DomainSet(domains), enabled, mContext.getUserId());
            return mDomainVerificationManager.setDomainVerificationUserSelection(
                    domainSetId.toString(), new DomainSet(domains), enabled, mContext.getUserId());
        } catch (Exception e) {
            Exception converted = rethrow(e, domainSetId);
            Exception converted = rethrow(e, null);
            if (converted instanceof NameNotFoundException) {
                throw (NameNotFoundException) converted;
            } else if (converted instanceof RuntimeException) {
@@ -322,123 +387,22 @@ public final class DomainVerificationManager {
        }
    }

    private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
        return rethrow(exception, domainSetId, null);
    }

    private Exception rethrow(Exception exception, @Nullable String packageName) {
        return rethrow(exception, null, packageName);
    }

    private Exception rethrow(Exception exception, @Nullable UUID domainSetId,
            @Nullable String packageName) {
        if (exception instanceof ServiceSpecificException) {
            int packedErrorCode = ((ServiceSpecificException) exception).errorCode;
            int serviceSpecificErrorCode = ((ServiceSpecificException) exception).errorCode;
            if (packageName == null) {
                packageName = exception.getMessage();
            }

            @Error int managerErrorCode = packedErrorCode & 0xFFFF;
            switch (managerErrorCode) {
                case ERROR_INVALID_DOMAIN_SET:
                    int errorSpecificCode = packedErrorCode >> 16;
                    return new IllegalArgumentException(InvalidDomainSetException.buildMessage(
                            domainSetId, packageName, errorSpecificCode));
                case ERROR_NAME_NOT_FOUND:
            if (serviceSpecificErrorCode == INTERNAL_ERROR_NAME_NOT_FOUND) {
                return new NameNotFoundException(packageName);
                default:
                    return exception;
            }

            return exception;
        } else if (exception instanceof RemoteException) {
            return ((RemoteException) exception).rethrowFromSystemServer();
        } else {
            return exception;
        }
    }

    /**
     * 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.
     *
     * @hide
     */
    public static class InvalidDomainSetException extends IllegalArgumentException {

        public static final int REASON_ID_NULL = 1;
        public static final int REASON_ID_INVALID = 2;
        public static final int REASON_SET_NULL_OR_EMPTY = 3;
        public static final int REASON_UNKNOWN_DOMAIN = 4;
        public static final int REASON_UNABLE_TO_APPROVE = 5;

        /**
         * @hide
         */
        @IntDef({
                REASON_ID_NULL,
                REASON_ID_INVALID,
                REASON_SET_NULL_OR_EMPTY,
                REASON_UNKNOWN_DOMAIN,
                REASON_UNABLE_TO_APPROVE
        })
        public @interface Reason {
        }

        public static String buildMessage(@Nullable UUID domainSetId, @Nullable String packageName,
                @Reason int reason) {
            switch (reason) {
                case REASON_ID_NULL:
                    return "Domain set ID cannot be null";
                case REASON_ID_INVALID:
                    return "Domain set ID " + domainSetId + " has been invalidated";
                case REASON_SET_NULL_OR_EMPTY:
                    return "Domain set cannot be null or empty";
                case REASON_UNKNOWN_DOMAIN:
                    return "Domain set contains value that was not declared by the target package "
                            + packageName;
                case REASON_UNABLE_TO_APPROVE:
                    return "Domain set contains value that was owned by another package";
                default:
                    return "Unknown failure";
            }
        }

        @Reason
        private final int mReason;

        @Nullable
        private final UUID mDomainSetId;

        @Nullable
        private final String mPackageName;

        /**
         * @hide
         */
        public InvalidDomainSetException(@Nullable UUID domainSetId, @Nullable String packageName,
                @Reason int reason) {
            super(buildMessage(domainSetId, packageName, reason));
            mDomainSetId = domainSetId;
            mPackageName = packageName;
            mReason = reason;
        }

        @Nullable
        public UUID getDomainSetId() {
            return mDomainSetId;
        }

        @Nullable
        public String getPackageName() {
            return mPackageName;
        }

        @Reason
        public int getReason() {
            return mReason;
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -40,10 +40,10 @@ interface IDomainVerificationManager {
    @nullable
    List<DomainOwner> getOwnersForDomain(String domain, int userId);

    void setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
    int setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);

    void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);

    void setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
    int setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
            boolean enabled, int userId);
}
+3 −3
Original line number Diff line number Diff line
@@ -333,10 +333,10 @@ public interface DomainVerificationManagerInternal {
    @Nullable
    UUID getDomainVerificationInfoId(@NonNull String packageName);

    @DomainVerificationManager.Error
    @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT)
    void setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
            @NonNull Set<String> domains, int state)
            throws IllegalArgumentException, NameNotFoundException;
    int setDomainVerificationStatusInternal(int callingUid, @NonNull UUID domainSetId,
            @NonNull Set<String> domains, int state) throws NameNotFoundException;


    interface Connection extends DomainVerificationEnforcer.Callback {
+8 −12
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.content.pm.verify.domain.DomainOwner;
import android.content.pm.verify.domain.DomainSet;
import android.content.pm.verify.domain.DomainVerificationInfo;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.content.pm.verify.domain.IDomainVerificationManager;
import android.os.ServiceSpecificException;
@@ -61,11 +60,12 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
        }
    }

    @DomainVerificationManager.Error
    @Override
    public void setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
    public int setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
            int state) {
        try {
            mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
            return mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
                    domainSet.getDomains(), state);
        } catch (Exception e) {
            throw rethrow(e);
@@ -82,11 +82,12 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
        }
    }

    @DomainVerificationManager.Error
    @Override
    public void setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
    public int setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
            boolean enabled, @UserIdInt int userId) {
        try {
            mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
            return mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
                    domainSet.getDomains(), enabled, userId);
        } catch (Exception e) {
            throw rethrow(e);
@@ -116,14 +117,9 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St
    }

    private RuntimeException rethrow(Exception exception) throws RuntimeException {
        if (exception instanceof InvalidDomainSetException) {
            int packedErrorCode = DomainVerificationManager.ERROR_INVALID_DOMAIN_SET;
            packedErrorCode |= ((InvalidDomainSetException) exception).getReason() << 16;
            return new ServiceSpecificException(packedErrorCode,
                    ((InvalidDomainSetException) exception).getPackageName());
        } else if (exception instanceof NameNotFoundException) {
        if (exception instanceof NameNotFoundException) {
            return new ServiceSpecificException(
                    DomainVerificationManager.ERROR_NAME_NOT_FOUND);
                    DomainVerificationManager.INTERNAL_ERROR_NAME_NOT_FOUND);
        } else if (exception instanceof RuntimeException) {
            return (RuntimeException) exception;
        } else {
Loading