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

Commit 6b9593c3 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add unit tests for AppIdPermissionPolicy" into main

parents b638f56d 6da851cc
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -43,8 +43,7 @@ class AppIdPermissionPolicyPermissionStatesTest : BaseAppIdPermissionPolicyTest(
    @Parameterized.Parameter(0) lateinit var action: Action

    @Before
    override fun setUp() {
        super.setUp()
    fun setUp() {
        if (action == Action.ON_USER_ADDED) {
            createUserState(USER_ID_NEW)
        }
+430 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.permission.test

import android.content.pm.PermissionGroupInfo
import android.content.pm.PermissionInfo
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.permission.AppIdPermissionPolicy
import com.android.server.permission.access.permission.Permission
import com.android.server.permission.access.permission.PermissionFlags
import com.android.server.testutils.mock
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Test
import org.mockito.Mockito.times
import org.mockito.Mockito.verify

class AppIdPermissionPolicyTest : BaseAppIdPermissionPolicyTest() {
    @Test
    fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
        val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
        val permissionOwnerPackageState = mockPackageState(
            APP_ID_0,
            mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
        )
        val requestingPackageState = mockPackageState(
            APP_ID_1,
            mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
        )
        addPackageState(permissionOwnerPackageState)
        addPackageState(requestingPackageState)
        addPermission(parsedPermission)
        setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED)

        mutateState {
            with(appIdPermissionPolicy) {
                onAppIdRemoved(APP_ID_1)
            }
        }

        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = 0
        assertWithMessage(
            "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" +
                " owns by appId $APP_ID_0 with existing permission flags. The actual permission" +
                " flags $actualFlags should be null"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    @Test
    fun testOnStorageVolumeMounted_nonSystemAppAfterNonSystemUpdate_remainsRevoked() {
        val permissionOwnerPackageState = mockPackageState(APP_ID_0, mockSimpleAndroidPackage())
        val installedPackageState = mockPackageState(
            APP_ID_1,
            mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
        )
        addPackageState(permissionOwnerPackageState)
        addPackageState(installedPackageState)
        addPermission(defaultPermission)
        val oldFlags = PermissionFlags.INSTALL_REVOKED
        setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)

        mutateState {
            with(appIdPermissionPolicy) {
                onStorageVolumeMounted(null, listOf(installedPackageState.packageName), false)
            }
        }

        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = oldFlags
        assertWithMessage(
            "After onStorageVolumeMounted() is called for a non-system app that requests a normal" +
                " permission with existing INSTALL_REVOKED flag after a non-system-update" +
                " (such as an OTA update), the actual permission flags should remain revoked." +
                " The actual permission flags $actualFlags should match the expected flags" +
                " $expectedNewFlags"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    @Test
    fun testOnPackageRemoved_packageIsRemoved_permissionDefinitionsAndStatesAreUpdated() {
        val permissionOwnerPackageState = mockPackageState(
            APP_ID_0,
            mockAndroidPackage(
                PACKAGE_NAME_0,
                requestedPermissions = setOf(PERMISSION_NAME_0),
                permissions = listOf(defaultPermission)
            )
        )
        val requestingPackageState = mockPackageState(
            APP_ID_1,
            mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
        )
        addPackageState(permissionOwnerPackageState)
        addPackageState(requestingPackageState)
        addPermission(defaultPermission)
        val oldFlags = PermissionFlags.INSTALL_GRANTED
        setPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0, oldFlags)
        setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags)

        mutateState {
            removePackageState(permissionOwnerPackageState)
            with(appIdPermissionPolicy) {
                onPackageRemoved(PACKAGE_NAME_0, APP_ID_0)
            }
        }

        assertWithMessage(
            "After onPackageRemoved() is called for a permission owner, the permission" +
                " definitions owned by this package should be removed"
        )
            .that(getPermission(PERMISSION_NAME_0))
            .isNull()

        val app0ActualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
        val app0ExpectedNewFlags = 0
        assertWithMessage(
            "After onPackageRemoved() is called for a permission owner, the permission states of" +
                " this app should be trimmed. The actual permission flags $app0ActualFlags should" +
                " match the expected flags $app0ExpectedNewFlags"
        )
            .that(app0ActualFlags)
            .isEqualTo(app0ExpectedNewFlags)

        val app1ActualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val app1ExpectedNewFlags = PermissionFlags.INSTALL_REVOKED
        assertWithMessage(
            "After onPackageRemoved() is called for a permission owner, the permission states of" +
                " the permission requester should remain unchanged. The actual permission flags" +
                " $app1ActualFlags should match the expected flags $app1ExpectedNewFlags"
        )
            .that(app1ActualFlags)
            .isEqualTo(app1ExpectedNewFlags)
    }

    @Test
    fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() {
        val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
        testOnPackageInstalled(
            oldFlags,
            permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
        ) {}
        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = PermissionFlags.SOFT_RESTRICTED
        assertWithMessage(
            "After onPackageInstalled() is called for a non-system app that requests a runtime" +
                " soft restricted permission, UPGRADE_EXEMPT flag should be removed. The actual" +
                " permission flags $actualFlags should match the expected flags $expectedNewFlags"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    @Test
    fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() {
        val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
        testOnPackageInstalled(
            oldFlags,
            permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED,
            isInstalledPackageSystem = true
        ) {}
        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = oldFlags
        assertWithMessage(
            "After onPackageInstalled() is called for a system app that requests a runtime" +
                " soft restricted permission, UPGRADE_EXEMPT flag should be retained. The actual" +
                " permission flags $actualFlags should match the expected flags $expectedNewFlags"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    @Test
    fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() {
        val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.UPGRADE_EXEMPT
        testOnPackageInstalled(
            oldFlags,
            permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
        ) {
            val systemAppPackageState = mockPackageState(
                APP_ID_1,
                mockAndroidPackage(PACKAGE_NAME_2, requestedPermissions = setOf(PERMISSION_NAME_0)),
                isSystem = true
            )
            addPackageState(systemAppPackageState)
        }
        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = oldFlags
        assertWithMessage(
            "After onPackageInstalled() is called for a non-system app that requests a runtime" +
                " soft restricted permission, and that permission is also requested by a system" +
                " app in the same appId, UPGRADE_EXEMPT flag should be retained. The actual" +
                " permission flags $actualFlags should match the expected flags $expectedNewFlags"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    @Test
    fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() {
        val oldFlags = PermissionFlags.RESTRICTION_REVOKED
        testOnPackageInstalled(
            oldFlags,
            permissionInfoFlags = PermissionInfo.FLAG_HARD_RESTRICTED
        ) {}
        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = oldFlags
        assertWithMessage(
            "After onPackageInstalled() is called for a non-system app that requests a runtime" +
                " hard restricted permission that is not exempted. The actual permission flags" +
                " $actualFlags should match the expected flags $expectedNewFlags"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    @Test
    fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
        val oldFlags = PermissionFlags.SOFT_RESTRICTED or PermissionFlags.INSTALLER_EXEMPT
        testOnPackageInstalled(
            oldFlags,
            permissionInfoFlags = PermissionInfo.FLAG_SOFT_RESTRICTED
        ) {}
        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = PermissionFlags.INSTALLER_EXEMPT
        assertWithMessage(
            "After onPackageInstalled() is called for a non-system app that requests a runtime" +
                " soft restricted permission that is exempted. The actual permission flags" +
                " $actualFlags should match the expected flags $expectedNewFlags"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    private fun testOnPackageInstalled(
        oldFlags: Int,
        permissionInfoFlags: Int = 0,
        isInstalledPackageSystem: Boolean = false,
        additionalSetup: () -> Unit
    ) {
        val parsedPermission = mockParsedPermission(
            PERMISSION_NAME_0,
            PACKAGE_NAME_0,
            protectionLevel = PermissionInfo.PROTECTION_DANGEROUS,
            flags = permissionInfoFlags
        )
        val permissionOwnerPackageState = mockPackageState(
            APP_ID_0,
            mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
        )
        addPackageState(permissionOwnerPackageState)
        addPermission(parsedPermission)

        additionalSetup()

        mutateState {
            val installedPackageState = mockPackageState(
                APP_ID_1,
                mockAndroidPackage(
                    PACKAGE_NAME_1,
                    requestedPermissions = setOf(PERMISSION_NAME_0),
                ),
                isSystem = isInstalledPackageSystem,
            )
            addPackageState(installedPackageState, newState)
            setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, oldFlags, newState)
            with(appIdPermissionPolicy) {
                onPackageInstalled(installedPackageState, USER_ID_0)
            }
        }
    }

    @Test
    fun testOnStateMutated_notEmpty_isCalledForEachListener() {
        val mockListener = mock<AppIdPermissionPolicy.OnPermissionFlagsChangedListener> {}
        appIdPermissionPolicy.addOnPermissionFlagsChangedListener(mockListener)

        GetStateScope(oldState).apply {
            with(appIdPermissionPolicy) {
                onStateMutated()
            }
        }

        verify(mockListener, times(1)).onStateMutated()
    }

    @Test
    fun testGetPermissionTrees() {
        val permissionTrees: IndexedMap<String, Permission>
        GetStateScope(oldState).apply {
            with(appIdPermissionPolicy) {
                permissionTrees = getPermissionTrees()
            }
        }

        assertThat(oldState.systemState.permissionTrees).isEqualTo(permissionTrees)
    }

    @Test
    fun testFindPermissionTree() {
        val permissionTree = createSimplePermission(isTree = true)
        val actualPermissionTree: Permission?
        oldState.mutateSystemState().mutatePermissionTrees()[PERMISSION_TREE_NAME] = permissionTree

        GetStateScope(oldState).apply {
            with(appIdPermissionPolicy) {
                actualPermissionTree = findPermissionTree(PERMISSION_BELONGS_TO_A_TREE)
            }
        }

        assertThat(actualPermissionTree).isEqualTo(permissionTree)
    }

    @Test
    fun testAddPermissionTree() {
        val permissionTree = createSimplePermission(isTree = true)

        mutateState {
            with(appIdPermissionPolicy) {
                addPermissionTree(permissionTree)
            }
        }

        assertThat(newState.systemState.permissionTrees[PERMISSION_TREE_NAME])
            .isEqualTo(permissionTree)
    }

    @Test
    fun testGetPermissionGroups() {
        val permissionGroups: IndexedMap<String, PermissionGroupInfo>
        GetStateScope(oldState).apply {
            with(appIdPermissionPolicy) {
                permissionGroups = getPermissionGroups()
            }
        }

        assertThat(oldState.systemState.permissionGroups).isEqualTo(permissionGroups)
    }

    @Test
    fun testGetPermissions() {
        val permissions: IndexedMap<String, Permission>
        GetStateScope(oldState).apply {
            with(appIdPermissionPolicy) {
                permissions = getPermissions()
            }
        }

        assertThat(oldState.systemState.permissions).isEqualTo(permissions)
    }

    @Test
    fun testAddPermission() {
        val permission = createSimplePermission()

        mutateState {
            with(appIdPermissionPolicy) {
                addPermission(permission)
            }
        }

        assertThat(newState.systemState.permissions[PERMISSION_NAME_0]).isEqualTo(permission)
    }

    @Test
    fun testRemovePermission() {
        val permission = createSimplePermission()

        mutateState {
            with(appIdPermissionPolicy) {
                addPermission(permission)
                removePermission(permission)
            }
        }

        assertThat(newState.systemState.permissions[PERMISSION_NAME_0]).isNull()
    }

    @Test
    fun testGetUidPermissionFlags() {
        val uidPermissionFlags: IndexedMap<String, Int>?
        GetStateScope(oldState).apply {
            with(appIdPermissionPolicy) {
                uidPermissionFlags = getUidPermissionFlags(APP_ID_0, USER_ID_0)
            }
        }

        assertThat(oldState.userStates[USER_ID_0]!!.appIdPermissionFlags[APP_ID_0])
            .isEqualTo(uidPermissionFlags)
    }

    @Test
    fun testUpdateAndGetPermissionFlags() {
        val flags = PermissionFlags.INSTALL_GRANTED
        var actualFlags = 0
        mutateState {
            with(appIdPermissionPolicy) {
                updatePermissionFlags(
                    APP_ID_0,
                    USER_ID_0,
                    PERMISSION_NAME_0,
                    PermissionFlags.MASK_ALL,
                    flags
                )
                actualFlags = getPermissionFlags(APP_ID_0, USER_ID_0, PERMISSION_NAME_0)
            }
        }

        assertThat(actualFlags).isEqualTo(flags)
    }
}
+26 −80
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.permission.AppIdPermissionPolicy
import com.android.server.permission.access.permission.Permission
import com.android.server.permission.access.permission.PermissionFlags
import com.android.server.permission.access.util.hasBits
import com.android.server.pm.parsing.PackageInfoUtils
import com.android.server.pm.pkg.AndroidPackage
@@ -45,10 +44,8 @@ import com.android.server.pm.pkg.component.ParsedPermissionGroup
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyLong

@@ -56,7 +53,7 @@ import org.mockito.ArgumentMatchers.anyLong
 * Mocking unit test for AppIdPermissionPolicy.
 */
@RunWith(AndroidJUnit4::class)
open class BaseAppIdPermissionPolicyTest {
abstract class BaseAppIdPermissionPolicyTest {
    protected lateinit var oldState: MutableAccessState
    protected lateinit var newState: MutableAccessState

@@ -80,7 +77,7 @@ open class BaseAppIdPermissionPolicyTest {
        .build()

    @Before
    open fun setUp() {
    fun baseSetUp() {
        oldState = MutableAccessState()
        createUserState(USER_ID_0)
        oldState.mutateExternalState().setPackageStates(ArrayMap())
@@ -139,78 +136,6 @@ open class BaseAppIdPermissionPolicyTest {
        }
    }

    @Test
    fun testOnAppIdRemoved_appIdIsRemoved_permissionFlagsCleared() {
        val parsedPermission = mockParsedPermission(PERMISSION_NAME_0, PACKAGE_NAME_0)
        val permissionOwnerPackageState = mockPackageState(
            APP_ID_0,
            mockAndroidPackage(PACKAGE_NAME_0, permissions = listOf(parsedPermission))
        )
        val requestingPackageState = mockPackageState(
            APP_ID_1,
            mockAndroidPackage(PACKAGE_NAME_1, requestedPermissions = setOf(PERMISSION_NAME_0))
        )
        addPackageState(permissionOwnerPackageState)
        addPackageState(requestingPackageState)
        addPermission(parsedPermission)
        setPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0, PermissionFlags.INSTALL_GRANTED)

        mutateState {
            with(appIdPermissionPolicy) {
                onAppIdRemoved(APP_ID_1)
            }
        }

        val actualFlags = getPermissionFlags(APP_ID_1, USER_ID_0, PERMISSION_NAME_0)
        val expectedNewFlags = 0
        assertWithMessage(
            "After onAppIdRemoved() is called for appId $APP_ID_1 that requests a permission" +
                " owns by appId $APP_ID_0 with existing permission flags. The actual permission" +
                " flags $actualFlags should be null"
        )
            .that(actualFlags)
            .isEqualTo(expectedNewFlags)
    }

    @Test
    fun testOnPackageRemoved_packageIsRemoved_permissionsAreTrimmedAndStatesAreEvaluated() {
        // TODO
        // shouldn't reuse test cases because it's really different despite it's also for
        // trim permission states. It's different because it's package removal
    }

    @Test
    fun testOnPackageInstalled_nonSystemAppIsInstalled_upgradeExemptFlagIsCleared() {
        // TODO
        // should be fine for it to be its own test cases and not to re-use
        // clearRestrictedPermissionImplicitExemption
    }

    @Test
    fun testOnPackageInstalled_systemAppIsInstalled_upgradeExemptFlagIsRetained() {
        // TODO
    }

    @Test
    fun testOnPackageInstalled_requestedPermissionAlsoRequestedBySystemApp_exemptFlagIsRetained() {
        // TODO
    }

    @Test
    fun testOnPackageInstalled_restrictedPermissionsNotExempt_getsRestrictionFlags() {
        // TODO
    }

    @Test
    fun testOnPackageInstalled_restrictedPermissionsIsExempted_clearsRestrictionFlags() {
        // TODO
    }

    @Test
    fun testOnStateMutated_notEmpty_isCalledForEachListener() {
        // TODO
    }

    /**
     * Mock an AndroidPackage with PACKAGE_NAME_0, PERMISSION_NAME_0 and PERMISSION_GROUP_NAME_0
     */
@@ -221,6 +146,15 @@ open class BaseAppIdPermissionPolicyTest {
            permissions = listOf(defaultPermissionTree, defaultPermission)
        )

    protected fun createSimplePermission(isTree: Boolean = false): Permission {
        val parsedPermission = if (isTree) { defaultPermissionTree } else { defaultPermission }
        val permissionInfo = PackageInfoUtils.generatePermissionInfo(
            parsedPermission,
            PackageManager.GET_META_DATA.toLong()
        )!!
        return Permission(permissionInfo, true, Permission.TYPE_MANIFEST, APP_ID_0)
    }

    protected inline fun mutateState(action: MutateStateScope.() -> Unit) {
        newState = oldState.toMutable()
        MutateStateScope(oldState, newState).action()
@@ -330,15 +264,26 @@ open class BaseAppIdPermissionPolicyTest {
    ) {
        state.mutateExternalState().apply {
            setPackageStates(
                packageStates.toMutableMap().apply {
                    put(packageState.packageName, packageState)
                }
                packageStates.toMutableMap().apply { put(packageState.packageName, packageState) }
            )
            mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
                .add(packageState.packageName)
        }
    }

    protected fun removePackageState(
        packageState: PackageState,
        state: MutableAccessState = oldState
    ) {
        state.mutateExternalState().apply {
            setPackageStates(
                packageStates.toMutableMap().apply { remove(packageState.packageName) }
            )
            mutateAppIdPackageNames().mutateOrPut(packageState.appId) { MutableIndexedListSet() }
                .remove(packageState.packageName)
        }
    }

    protected fun addDisabledSystemPackageState(
        packageState: PackageState,
        state: MutableAccessState = oldState
@@ -429,6 +374,7 @@ open class BaseAppIdPermissionPolicyTest {
        @JvmStatic protected val PERMISSION_NAME_0 = "permissionName0"
        @JvmStatic protected val PERMISSION_NAME_1 = "permissionName1"
        @JvmStatic protected val PERMISSION_NAME_2 = "permissionName2"
        @JvmStatic protected val PERMISSION_BELONGS_TO_A_TREE = "permissionTree.permission"
        @JvmStatic protected val PERMISSION_READ_EXTERNAL_STORAGE =
            Manifest.permission.READ_EXTERNAL_STORAGE
        @JvmStatic protected val PERMISSION_POST_NOTIFICATIONS =
+1 −1

File changed.

Contains only whitespace changes.