diff options
2 files changed, 276 insertions, 2 deletions
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 8f191580e2c9..e24e5bbfa4f6 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 @@ -542,8 +542,6 @@ public class DomainVerificationService extends SystemService userState.removeHosts(domains); } } - - mConnection.scheduleWriteSettings(); } @Nullable @@ -849,6 +847,7 @@ public class DomainVerificationService extends SystemService public void setLegacyUserState(@NonNull String packageName, @UserIdInt int userId, int state) { mEnforcer.callerIsLegacyUserSelector(mConnection.getCallingUid()); mLegacySettings.add(packageName, userId, state); + mConnection.scheduleWriteSettings(); } @Override @@ -1067,6 +1066,8 @@ public class DomainVerificationService extends SystemService } } } + + mConnection.scheduleWriteSettings(); } /** @@ -1124,6 +1125,8 @@ public class DomainVerificationService extends SystemService } } } + + mConnection.scheduleWriteSettings(); } @Override diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt new file mode 100644 index 000000000000..5792e02e37a2 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.test.verify.domain + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.pm.PackageUserState +import android.content.pm.verify.domain.DomainVerificationManager +import android.content.pm.parsing.component.ParsedActivity +import android.content.pm.parsing.component.ParsedIntentInfo +import android.os.Build +import android.os.Process +import android.util.ArraySet +import android.util.SparseArray +import com.android.server.pm.PackageSetting +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal +import com.android.server.pm.verify.domain.DomainVerificationService +import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.testutils.mockThrowOnUnmocked +import com.android.server.testutils.spyThrowOnUnmocked +import com.android.server.testutils.whenever +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.mockito.Mockito +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyLong +import org.mockito.Mockito.any +import org.mockito.Mockito.anyString +import org.mockito.Mockito.eq +import org.mockito.Mockito.verify +import java.io.File +import java.util.UUID + +@RunWith(Parameterized::class) +class DomainVerificationSettingsMutationTest { + + companion object { + private const val TEST_PKG = "com.test" + + // Pretend to be the system. This class doesn't verify any enforcement behavior. + private const val TEST_UID = Process.SYSTEM_UID + private const val TEST_USER_ID = 10 + private val TEST_UUID = UUID.fromString("5168e42e-327e-432b-b562-cfb553518a70") + + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun parameters(): Array<Any> { + val context: Context = mockThrowOnUnmocked { + whenever( + enforcePermission( + eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT), + anyInt(), anyInt(), anyString() + ) + ) + whenever( + enforcePermission( + eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION), + anyInt(), anyInt(), anyString() + ) + ) + whenever( + enforcePermission( + eq(android.Manifest.permission.INTERACT_ACROSS_USERS), + anyInt(), anyInt(), anyString() + ) + ) + whenever( + enforcePermission( + eq(android.Manifest.permission.SET_PREFERRED_APPLICATIONS), + anyInt(), anyInt(), anyString() + ) + ) + } + val proxy: DomainVerificationProxy = mockThrowOnUnmocked { + whenever(isCallerVerifier(anyInt())) { true } + whenever(sendBroadcastForPackages(any())) + } + + val makeService: (DomainVerificationManagerInternal.Connection) -> DomainVerificationService = + { connection -> + DomainVerificationService( + context, + mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } }, + mockThrowOnUnmocked { + whenever(isChangeEnabled(anyLong(),any())) { true } + }).apply { + setConnection(connection) + } + } + + fun service(name: String, block: DomainVerificationService.() -> Unit) = + Params(makeService, name) { service -> + service.proxy = proxy + service.addPackage(mockPkgSetting()) + service.block() + } + + return arrayOf( + service("clearPackage") { + clearPackage(TEST_PKG) + }, + service("clearUser") { + clearUser(TEST_USER_ID) + }, + service("clearState") { + clearDomainVerificationState(listOf(TEST_PKG)) + }, + service("clearUserSelections") { + clearUserSelections(listOf(TEST_PKG), TEST_USER_ID) + }, + service("setStatus") { + setDomainVerificationStatus( + TEST_UUID, + setOf("example.com"), + DomainVerificationManager.STATE_SUCCESS + ) + }, + service("setStatusInternalPackageName") { + setDomainVerificationStatusInternal( + TEST_PKG, + DomainVerificationManager.STATE_SUCCESS, + ArraySet(setOf("example.com")) + ) + }, + service("setStatusInternalUid") { + setDomainVerificationStatusInternal( + TEST_UID, + TEST_UUID, + setOf("example.com"), + DomainVerificationManager.STATE_SUCCESS + ) + }, + service("setLinkHandlingAllowed") { + setDomainVerificationLinkHandlingAllowed(TEST_PKG, true) + }, + service("setLinkHandlingAllowedUserId") { + setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID) + }, + service("setLinkHandlingAllowedInternal") { + setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID) + }, + service("setUserSelection") { + setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true) + }, + service("setUserSelectionUserId") { + setDomainVerificationUserSelection( + TEST_UUID, + setOf("example.com"), + true, + TEST_USER_ID + ) + }, + service("setUserSelectionInternal") { + setDomainVerificationUserSelectionInternal( + TEST_USER_ID, + TEST_PKG, + true, + ArraySet(setOf("example.com")), + ) + }, + service("setLegacyUserState") { + setLegacyUserState( + TEST_PKG, + TEST_USER_ID, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER + ) + }, + ) + } + + data class Params( + val construct: ( + DomainVerificationManagerInternal.Connection + ) -> DomainVerificationService, + val name: String, + val method: (DomainVerificationService) -> Unit + ) { + override fun toString() = name + } + + + fun mockPkg() = mockThrowOnUnmocked<AndroidPackage> { + whenever(packageName) { TEST_PKG } + whenever(targetSdkVersion) { Build.VERSION_CODES.S } + whenever(activities) { + listOf( + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + autoVerify = true + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("https") + addDataAuthority("example.com", null) + } + ) + } + ) + } + } + + // TODO: PackageSetting field encapsulation to move to whenever(name) + fun mockPkgSetting() = spyThrowOnUnmocked( + PackageSetting( + TEST_PKG, + TEST_PKG, + File("/test"), + null, + null, + null, + null, + 1, + 0, + 0, + 0, + null, + null, + null, + TEST_UUID + ) + ) { + whenever(getPkg()) { mockPkg() } + whenever(domainSetId) { TEST_UUID } + whenever(userState) { + SparseArray<PackageUserState>().apply { + this[0] = PackageUserState() + } + } + } + } + + @Parameterized.Parameter(0) + lateinit var params: Params + + @Test + fun writeScheduled() { + val connection = mockConnection() + val service = params.construct(connection) + params.method(service) + + verify(connection).scheduleWriteSettings() + } + + private fun mockConnection(): DomainVerificationManagerInternal.Connection = + mockThrowOnUnmocked { + whenever(callingUid) { TEST_UID } + whenever(callingUserId) { TEST_USER_ID } + whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting() } + whenever(getPackageLocked(TEST_PKG)) { mockPkg() } + whenever(schedule(anyInt(), any())) + whenever(scheduleWriteSettings()) + } +} |