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

Commit 7c687a41 authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Fix ApplicationInfo.hasGrantPermission()

When packageInfo.requestedPermissions is null.

Bug: 236346018
Test: Unit test
Change-Id: I3ee7f49fbaea58227dd67d8a7015d41f054533fc
parent cb7442ee
Loading
Loading
Loading
Loading
+45 −12
Original line number Diff line number Diff line
@@ -26,43 +26,76 @@ import com.android.settingslib.spa.framework.util.asyncFilter

private const val TAG = "PackageManagers"

object PackageManagers {
interface IPackageManagers {
    fun getPackageInfoAsUser(packageName: String, userId: Int): PackageInfo?
    fun getApplicationInfoAsUser(packageName: String, userId: Int): ApplicationInfo?

    /** Checks whether a package is installed for a given user. */
    fun isPackageInstalledAsUser(packageName: String, userId: Int): Boolean
    fun ApplicationInfo.hasRequestPermission(permission: String): Boolean

    /** Checks whether a permission is currently granted to the application. */
    fun ApplicationInfo.hasGrantPermission(permission: String): Boolean

    suspend fun getAppOpPermissionPackages(userId: Int, permission: String): Set<String>
    fun getPackageInfoAsUser(packageName: String, flags: Int, userId: Int): PackageInfo?
}

object PackageManagers : IPackageManagers by PackageManagersImpl(PackageManagerWrapperImpl)

internal interface PackageManagerWrapper {
    fun getPackageInfoAsUserCached(
        packageName: String,
        flags: Long,
        userId: Int,
    ): PackageInfo?
}

internal object PackageManagerWrapperImpl : PackageManagerWrapper {
    override fun getPackageInfoAsUserCached(
        packageName: String,
        flags: Long,
        userId: Int,
    ): PackageInfo? = PackageManager.getPackageInfoAsUserCached(packageName, flags, userId)
}

internal class PackageManagersImpl(
    private val packageManagerWrapper: PackageManagerWrapper,
) : IPackageManagers {
    private val iPackageManager by lazy { AppGlobals.getPackageManager() }

    fun getPackageInfoAsUser(packageName: String, userId: Int): PackageInfo? =
    override fun getPackageInfoAsUser(packageName: String, userId: Int): PackageInfo? =
        getPackageInfoAsUser(packageName, 0, userId)

    fun getApplicationInfoAsUser(packageName: String, userId: Int): ApplicationInfo? =
    override fun getApplicationInfoAsUser(packageName: String, userId: Int): ApplicationInfo? =
        PackageManager.getApplicationInfoAsUserCached(packageName, 0, userId)

    /** Checks whether a package is installed for a given user. */
    fun isPackageInstalledAsUser(packageName: String, userId: Int): Boolean =
    override fun isPackageInstalledAsUser(packageName: String, userId: Int): Boolean =
        getApplicationInfoAsUser(packageName, userId)?.hasFlag(ApplicationInfo.FLAG_INSTALLED)
            ?: false

    fun ApplicationInfo.hasRequestPermission(permission: String): Boolean {
    override fun ApplicationInfo.hasRequestPermission(permission: String): Boolean {
        val packageInfo = getPackageInfoAsUser(packageName, PackageManager.GET_PERMISSIONS, userId)
        return packageInfo?.requestedPermissions?.let {
            permission in it
        } ?: false
    }

    fun ApplicationInfo.hasGrantPermission(permission: String): Boolean {
    override fun ApplicationInfo.hasGrantPermission(permission: String): Boolean {
        val packageInfo = getPackageInfoAsUser(packageName, PackageManager.GET_PERMISSIONS, userId)
            ?: return false
        val index = packageInfo.requestedPermissions.indexOf(permission)
        val index = packageInfo?.requestedPermissions?.indexOf(permission) ?: return false
        return index >= 0 &&
            packageInfo.requestedPermissionsFlags[index].hasFlag(REQUESTED_PERMISSION_GRANTED)
    }

    suspend fun getAppOpPermissionPackages(userId: Int, permission: String): Set<String> =
    override suspend fun getAppOpPermissionPackages(userId: Int, permission: String): Set<String> =
        iPackageManager.getAppOpPermissionPackages(permission, userId).asIterable().asyncFilter {
            iPackageManager.isPackageAvailable(it, userId)
        }.toSet()

    fun getPackageInfoAsUser(packageName: String, flags: Int, userId: Int): PackageInfo? =
    override fun getPackageInfoAsUser(packageName: String, flags: Int, userId: Int): PackageInfo? =
        try {
            PackageManager.getPackageInfoAsUserCached(packageName, flags.toLong(), userId)
            packageManagerWrapper.getPackageInfoAsUserCached(packageName, flags.toLong(), userId)
        } catch (e: PackageManager.NameNotFoundException) {
            Log.w(TAG, "getPackageInfoAsUserCached() failed", e)
            null
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.settingslib.spaprivileged.model.app

import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class PackageManagersTest {

    private val fakePackageManagerWrapper = FakePackageManagerWrapper()

    private val packageManagersImpl = PackageManagersImpl(fakePackageManagerWrapper)

    @Test
    fun hasGrantPermission_packageInfoIsNull_returnFalse() {
        fakePackageManagerWrapper.fakePackageInfo = null

        val hasGrantPermission = with(packageManagersImpl) {
            APP.hasGrantPermission(PERMISSION_A)
        }

        assertThat(hasGrantPermission).isFalse()
    }

    @Test
    fun hasGrantPermission_requestedPermissionsIsNull_returnFalse() {
        fakePackageManagerWrapper.fakePackageInfo = PackageInfo()

        val hasGrantPermission = with(packageManagersImpl) {
            APP.hasGrantPermission(PERMISSION_A)
        }

        assertThat(hasGrantPermission).isFalse()
    }

    @Test
    fun hasGrantPermission_notRequested_returnFalse() {
        fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
            requestedPermissions = arrayOf(PERMISSION_B)
            requestedPermissionsFlags = intArrayOf(PackageInfo.REQUESTED_PERMISSION_GRANTED)
        }

        val hasGrantPermission = with(packageManagersImpl) {
            APP.hasGrantPermission(PERMISSION_A)
        }

        assertThat(hasGrantPermission).isFalse()
    }

    @Test
    fun hasGrantPermission_notGranted_returnFalse() {
        fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
            requestedPermissions = arrayOf(PERMISSION_A, PERMISSION_B)
            requestedPermissionsFlags = intArrayOf(0, PackageInfo.REQUESTED_PERMISSION_GRANTED)
        }

        val hasGrantPermission = with(packageManagersImpl) {
            APP.hasGrantPermission(PERMISSION_A)
        }

        assertThat(hasGrantPermission).isFalse()
    }

    @Test
    fun hasGrantPermission_granted_returnTrue() {
        fakePackageManagerWrapper.fakePackageInfo = PackageInfo().apply {
            requestedPermissions = arrayOf(PERMISSION_A, PERMISSION_B)
            requestedPermissionsFlags = intArrayOf(PackageInfo.REQUESTED_PERMISSION_GRANTED, 0)
        }

        val hasGrantPermission = with(packageManagersImpl) {
            APP.hasGrantPermission(PERMISSION_A)
        }

        assertThat(hasGrantPermission).isTrue()
    }

    private inner class FakePackageManagerWrapper : PackageManagerWrapper {
        var fakePackageInfo: PackageInfo? = null

        override fun getPackageInfoAsUserCached(
            packageName: String,
            flags: Long,
            userId: Int,
        ): PackageInfo? = fakePackageInfo
    }

    private companion object {
        const val PACKAGE_NAME = "packageName"
        const val PERMISSION_A = "permission.A"
        const val PERMISSION_B = "permission.B"
        const val UID = 123
        val APP = ApplicationInfo().apply {
            packageName = PACKAGE_NAME
            uid = UID
        }
    }
}