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

Commit 7024f9e0 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Merge "Fix app links verification resetting user state" into tm-dev am:...

Merge "Merge "Fix app links verification resetting user state" into tm-dev am: 4ad09b9b am: 2793e6f3 am: e41ec01a" into tm-qpr-dev-plus-aosp am: b2fec4ff

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18746485



Change-Id: I1ad9af427c0ae8c6496ae97cf5f809b2ac786407
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents f3911ef7 b2fec4ff
Loading
Loading
Loading
Loading
+51 −21
Original line number Diff line number Diff line
@@ -317,7 +317,7 @@ public class DomainVerificationService extends SystemService
        mEnforcer.assertApprovedVerifier(callingUid, mProxy);
        final Computer snapshot = mConnection.snapshot();
        synchronized (mLock) {
            List<String> verifiedDomains = new ArrayList<>();
            List<String> newlyVerifiedDomains = new ArrayList<>();

            GetAttachedResult result = getAndValidateAttachedLocked(domainSetId, domains,
                    true /* forAutoVerify */, callingUid, null /* userId */, snapshot);
@@ -329,21 +329,28 @@ public class DomainVerificationService extends SystemService
            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
            for (String domain : domains) {
                Integer previousState = stateMap.get(domain);
                if (previousState != null
                        && !DomainVerificationState.isModifiable(previousState)) {
                // Skip if the state hasn't changed or can't be changed
                if (previousState != null) {
                    if (previousState == state
                            || !DomainVerificationState.isModifiable(previousState)) {
                        continue;
                    }
                }

                if (DomainVerificationState.isVerified(state)) {
                    verifiedDomains.add(domain);
                    if (previousState == null
                            || !DomainVerificationState.isVerified(previousState)) {
                        newlyVerifiedDomains.add(domain);
                    }
                }

                stateMap.put(domain, state);
            }

            int size = verifiedDomains.size();
            // For newly verified domains, revoke their user states across other packages
            int size = newlyVerifiedDomains.size();
            for (int index = 0; index < size; index++) {
                removeUserStatesForDomain(verifiedDomains.get(index));
                removeUserStatesForDomain(pkgState, newlyVerifiedDomains.get(index));
            }
        }

@@ -367,7 +374,6 @@ public class DomainVerificationService extends SystemService
                        "State must be one of NO_RESPONSE, SUCCESS, APPROVED, or DENIED");
        }

        ArraySet<String> verifiedDomains = new ArraySet<>();
        if (packageName == null) {
            final Computer snapshot = mConnection.snapshot();
            synchronized (mLock) {
@@ -395,10 +401,6 @@ public class DomainVerificationService extends SystemService
                        validDomains.retainAll(autoVerifyDomains);
                    }

                    if (DomainVerificationState.isVerified(state)) {
                        verifiedDomains.addAll(validDomains);
                    }

                    setDomainVerificationStatusInternal(pkgState, state, validDomains);
                }
            }
@@ -424,19 +426,33 @@ public class DomainVerificationService extends SystemService
                    validDomains.retainAll(mCollector.collectValidAutoVerifyDomains(pkg));
                }

                ArraySet<String> newlyVerifiedDomains = null;
                if (DomainVerificationState.isVerified(state)) {
                    verifiedDomains.addAll(validDomains);
                    newlyVerifiedDomains = new ArraySet<>();
                    ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
                    int domainsSize = validDomains.size();
                    for (int domainIndex = 0; domainIndex < domainsSize; domainIndex++) {
                        String domain = validDomains.valueAt(domainIndex);
                        Integer oldState = stateMap.get(domain);
                        // Only remove if not previously verified
                        if (oldState == null || !DomainVerificationState.isVerified(oldState)) {
                            newlyVerifiedDomains.add(domain);
                        }

                setDomainVerificationStatusInternal(pkgState, state, validDomains);
                    }
                }

                setDomainVerificationStatusInternal(pkgState, state, validDomains);

                // Mirror SystemApi behavior of revoking user selection for approved domains.
        if (DomainVerificationState.isVerified(state)) {
            final int size = verifiedDomains.size();
            for (int index = 0; index < size; index++) {
                removeUserStatesForDomain(verifiedDomains.valueAt(index));
                // This is done in a second pass so that the previous state can be compared before
                // the previous method overwrites it with the new state.
                if (newlyVerifiedDomains != null) {
                    int domainsSize = newlyVerifiedDomains.size();
                    for (int domainIndex = 0; domainIndex < domainsSize; domainIndex++) {
                        String domain = newlyVerifiedDomains.valueAt(domainIndex);
                        removeUserStatesForDomain(pkgState, domain);
                    }
                }
            }
        }

@@ -452,7 +468,10 @@ public class DomainVerificationService extends SystemService
        }
    }

    private void removeUserStatesForDomain(@NonNull String domain) {
    private void removeUserStatesForDomain(@NonNull DomainVerificationPkgState owningPkgState,
            @NonNull String domain) {
        SparseArray<DomainVerificationInternalUserState> owningUserStates =
                owningPkgState.getUserStates();
        synchronized (mLock) {
            final int size = mAttachedPkgStates.size();
            for (int index = 0; index < size; index++) {
@@ -460,6 +479,15 @@ public class DomainVerificationService extends SystemService
                SparseArray<DomainVerificationInternalUserState> array = pkgState.getUserStates();
                int arraySize = array.size();
                for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) {
                    int userId = array.keyAt(arrayIndex);
                    DomainVerificationInternalUserState owningUserState =
                            owningUserStates.get(userId);
                    if (owningUserState != null && !owningUserState.isLinkHandlingAllowed()) {
                        // Skip users where the owning package has their link handling disabled,
                        // since revoking those users would lead to no apps being able to handle
                        // the domain.
                        continue;
                    }
                    array.valueAt(arrayIndex).removeHost(domain);
                }
            }
@@ -527,6 +555,8 @@ public class DomainVerificationService extends SystemService
        mConnection.scheduleWriteSettings();
    }

    @CheckResult
    @DomainVerificationManager.Error
    public int setDomainVerificationUserSelection(@NonNull UUID domainSetId,
            @NonNull Set<String> domains, boolean enabled, @UserIdInt int userId)
            throws NameNotFoundException {
+169 −0
Original line number Diff line number Diff line
@@ -78,6 +78,8 @@ class DomainVerificationPackageTest {
        private val DOMAIN_4 = "four.$DOMAIN_BASE"

        private const val USER_ID = 0
        private const val USER_ID_SECONDARY = 10
        private val USER_IDS = listOf(USER_ID, USER_ID_SECONDARY)
    }

    private val pkg1 = mockPkgState(PKG_ONE, UUID_ONE, SIGNATURE_ONE)
@@ -813,6 +815,173 @@ class DomainVerificationPackageTest {
        assertExpectedState(serviceAfter)
    }

    @Test
    fun verifiedUnapproved_unverifiedSelected_approvalCausesUnselect_systemApi() {
        verifiedUnapproved_unverifiedSelected_approvalCausesUnselect {
            setDomainVerificationStatus(it.domainSetId, setOf(DOMAIN_1, DOMAIN_2), STATE_SUCCESS)
        }
    }

    @Test
    fun verifiedUnapproved_unverifiedSelected_approvalCausesUnselect_internalApi() {
        verifiedUnapproved_unverifiedSelected_approvalCausesUnselect {
            setDomainVerificationStatusInternal(it.packageName, STATE_SUCCESS,
                    ArraySet(setOf(DOMAIN_1, DOMAIN_2)))
        }
    }

    private fun verifiedUnapproved_unverifiedSelected_approvalCausesUnselect(
            setStatusBlock: DomainVerificationService.(PackageStateInternal) -> Unit
    ) {
        /*
            Domains tested:
                1: Becomes verified in package 1, but package 1 disabled in secondary user, only
                    disables selection for package 2 in main user
                2: Becomes verified in package 1, unselected by package 2, remains unselected
                3: Is autoVerify, but unverified, selected by package 2, remains selected
                4: Non-autoVerify, selected by package 2, remains selected
         */

        val pkg1 = mockPkgState(
            PKG_ONE,
            UUID_ONE,
            SIGNATURE_ONE,
            autoVerifyDomains = listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3),
            otherDomains = listOf(DOMAIN_4)
        )
        val pkg2 = mockPkgState(
            PKG_TWO,
            UUID_TWO,
            SIGNATURE_TWO,
            autoVerifyDomains = emptyList(),
            otherDomains = listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4)
        )

        val service = makeService(pkg1, pkg2)
        service.addPackage(pkg1)
        service.addPackage(pkg2)

        // Approve domain 1, 3, and 4 for package 2 for both users
        USER_IDS.forEach {
            assertThat(
                service.setDomainVerificationUserSelection(
                    UUID_TWO,
                    setOf(DOMAIN_1, DOMAIN_3, DOMAIN_4),
                    true,
                    it
                )
            ).isEqualTo(DomainVerificationManager.STATUS_OK)
        }

        // But disable the owner package link handling in the secondary user
        service.setDomainVerificationLinkHandlingAllowed(pkg1.packageName, false,
            USER_ID_SECONDARY
        )

        service.assertState(
            pkg1,
            verifyState = listOf(
                DOMAIN_1 to STATE_NO_RESPONSE,
                DOMAIN_2 to STATE_NO_RESPONSE,
                DOMAIN_3 to STATE_NO_RESPONSE,
            ),
            userState2LinkHandlingAllowed = false
        )

        service.assertState(
            pkg2,
            verifyState = null,
            userState1DomainState1 = DOMAIN_STATE_SELECTED,
            userState1DomainState3 = DOMAIN_STATE_SELECTED,
            userState1DomainState4 = DOMAIN_STATE_SELECTED,
            userState2DomainState1 = DOMAIN_STATE_SELECTED,
            userState2DomainState3 = DOMAIN_STATE_SELECTED,
            userState2DomainState4 = DOMAIN_STATE_SELECTED,
        )

        // Verify the owner package
        service.setStatusBlock(pkg1)

        // Assert that package 1 is now verified, but link handling disabled in secondary user
        service.assertState(
            pkg1,
            verifyState = listOf(
                DOMAIN_1 to STATE_SUCCESS,
                DOMAIN_2 to STATE_SUCCESS,
                DOMAIN_3 to STATE_NO_RESPONSE,
            ),
            userState1DomainState1 = DOMAIN_STATE_VERIFIED,
            userState1DomainState2 = DOMAIN_STATE_VERIFIED,
            userState1DomainState3 = DOMAIN_STATE_NONE,
            userState1DomainState4 = DOMAIN_STATE_NONE,
            userState2LinkHandlingAllowed = false,
            userState2DomainState1 = DOMAIN_STATE_VERIFIED,
            userState2DomainState2 = DOMAIN_STATE_VERIFIED,
            userState2DomainState3 = DOMAIN_STATE_NONE,
            userState2DomainState4 = DOMAIN_STATE_NONE,
        )

        // Assert package 2 maintains selected in user where package 1 had link handling disabled
        service.assertState(
            pkg2,
            verifyState = null,
            userState1DomainState1 = DOMAIN_STATE_NONE,
            userState1DomainState3 = DOMAIN_STATE_SELECTED,
            userState1DomainState4 = DOMAIN_STATE_SELECTED,
            userState2DomainState1 = DOMAIN_STATE_SELECTED,
            userState2DomainState3 = DOMAIN_STATE_SELECTED,
            userState2DomainState4 = DOMAIN_STATE_SELECTED,
        )
    }

    fun DomainVerificationService.assertState(
        pkg: PackageStateInternal,
        verifyState: List<Pair<String, Int>>?,
        userState1LinkHandlingAllowed: Boolean = true,
        userState1DomainState1: Int = DOMAIN_STATE_NONE,
        userState1DomainState2: Int = DOMAIN_STATE_NONE,
        userState1DomainState3: Int = DOMAIN_STATE_NONE,
        userState1DomainState4: Int = DOMAIN_STATE_NONE,
        userState2LinkHandlingAllowed: Boolean = true,
        userState2DomainState1: Int = DOMAIN_STATE_NONE,
        userState2DomainState2: Int = DOMAIN_STATE_NONE,
        userState2DomainState3: Int = DOMAIN_STATE_NONE,
        userState2DomainState4: Int = DOMAIN_STATE_NONE,
    ) {
        if (verifyState == null) {
            // If no auto verify domains, the info itself will be null
            assertThat(getDomainVerificationInfo(pkg.packageName)).isNull()
        } else {
            getInfo(pkg.packageName).run {
                assertThat(hostToStateMap).containsExactlyEntriesIn(verifyState.associate { it })
            }
        }

        getUserState(pkg.packageName, USER_ID).run {
            assertThat(isLinkHandlingAllowed).isEqualTo(userState1LinkHandlingAllowed)
            assertThat(hostToStateMap).containsExactlyEntriesIn(
                mapOf(
                    DOMAIN_1 to userState1DomainState1,
                    DOMAIN_2 to userState1DomainState2,
                    DOMAIN_3 to userState1DomainState3,
                    DOMAIN_4 to userState1DomainState4,
                )
            )
        }

        getUserState(pkg.packageName, USER_ID_SECONDARY).run {
            assertThat(isLinkHandlingAllowed).isEqualTo(userState2LinkHandlingAllowed)
            assertThat(hostToStateMap).containsExactlyEntriesIn(
                mapOf(
                    DOMAIN_1 to userState2DomainState1,
                    DOMAIN_2 to userState2DomainState2,
                    DOMAIN_3 to userState2DomainState3,
                    DOMAIN_4 to userState2DomainState4,
                )
            )
        }
    }

    private fun DomainVerificationService.getInfo(pkgName: String) =
            getDomainVerificationInfo(pkgName)
                    .also { assertThat(it).isNotNull() }!!