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

Commit 039a7cee authored by Jacky Wang's avatar Jacky Wang
Browse files

[Catalyst] Allow specifying read/write permissions

Bug: 374115149
Flag: EXEMPT library
Test: devtool & pm grant/revoke
Change-Id: I13002bf6d2d6a9038240b34d27f04e56f3466e99
parent 3fb8581f
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
@@ -396,7 +397,7 @@ fun PreferenceMetadata.toProto(
                (!hasAvailable() || available) &&
                (!hasRestricted() || !restricted) &&
                metadata is PersistentPreference<*> &&
                metadata.getReadPermit(context, callingPid, callingUid) == ReadWritePermit.ALLOW
                metadata.evalReadPermit(context, callingPid, callingUid) == ReadWritePermit.ALLOW
        ) {
            value = preferenceValueProto {
                when (metadata) {
@@ -427,6 +428,20 @@ fun PreferenceMetadata.toProto(
    }
}

/** Evaluates the read permit of a persistent preference. */
fun <T> PersistentPreference<T>.evalReadPermit(
    context: Context,
    callingPid: Int,
    callingUid: Int,
): Int {
    for (permission in getReadPermissions(context)) {
        if (context.checkPermission(permission, callingPid, callingUid) != PERMISSION_GRANTED) {
            return ReadWritePermit.REQUIRE_APP_PERMISSION
        }
    }
    return getReadPermit(context, callingPid, callingUid)
}

private fun PreferenceMetadata.getTitleTextProto(context: Context, isRoot: Boolean): TextProto? {
    if (isRoot && this is PreferenceScreenMetadata) {
        val titleRes = screenTitle
+18 −1
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.settingslib.graph

import android.app.Application
import android.content.Context
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Bundle
import androidx.annotation.IntDef
import com.android.settingslib.graph.proto.PreferenceValueProto
@@ -127,7 +129,7 @@ class PreferenceSetterApiHandler(

        fun <T> PreferenceMetadata.checkWritePermit(value: T): Int {
            @Suppress("UNCHECKED_CAST") val preference = (this as PersistentPreference<T>)
            return when (preference.getWritePermit(application, value, callingPid, callingUid)) {
            return when (preference.evalWritePermit(application, value, callingPid, callingUid)) {
                ReadWritePermit.ALLOW -> PreferenceSetterResult.OK
                ReadWritePermit.DISALLOW -> PreferenceSetterResult.DISALLOW
                ReadWritePermit.REQUIRE_APP_PERMISSION ->
@@ -171,6 +173,21 @@ class PreferenceSetterApiHandler(
        get() = IntMessageCodec
}

/** Evaluates the write permit of a persistent preference. */
fun <T> PersistentPreference<T>.evalWritePermit(
    context: Context,
    value: T?,
    callingPid: Int,
    callingUid: Int,
): Int {
    for (permission in getWritePermissions(context)) {
        if (context.checkPermission(permission, callingPid, callingUid) != PERMISSION_GRANTED) {
            return ReadWritePermit.REQUIRE_APP_PERMISSION
        }
    }
    return getWritePermit(context, value, callingPid, callingUid)
}

/** Message codec for [PreferenceSetterRequest]. */
object PreferenceSetterRequestCodec : MessageCodec<PreferenceSetterRequest> {
    override fun encode(data: PreferenceSetterRequest) =
+22 −10
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.settingslib.datastore.KeyValueStore
    ReadWritePermit.REQUIRE_USER_AGREEMENT,
)
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.TYPE)
annotation class ReadWritePermit {
    companion object {
        /** Allow to read/write value. */
@@ -67,15 +68,18 @@ interface PersistentPreference<T> {
    fun storage(context: Context): KeyValueStore =
        PreferenceScreenRegistry.getKeyValueStore(context, this as PreferenceMetadata)!!

    /** Returns the required permissions to read preference value. */
    fun getReadPermissions(context: Context): Array<String> = arrayOf()

    /**
     * Returns if the external application (identified by [callingPid] and [callingUid]) has
     * permission to read preference value.
     * Returns if the external application (identified by [callingPid] and [callingUid]) is
     * permitted to read preference value.
     *
     * The underlying implementation does NOT need to check common states like isEnabled,
     * isRestricted or isAvailable.
     * isRestricted, isAvailable or permissions in [getReadPermissions]. The framework will do it
     * behind the scene.
     */
    @ReadWritePermit
    fun getReadPermit(context: Context, callingPid: Int, callingUid: Int): Int =
    fun getReadPermit(context: Context, callingPid: Int, callingUid: Int): @ReadWritePermit Int =
        PreferenceScreenRegistry.getReadPermit(
            context,
            callingPid,
@@ -83,15 +87,23 @@ interface PersistentPreference<T> {
            this as PreferenceMetadata,
        )

    /** Returns the required permissions to write preference value. */
    fun getWritePermissions(context: Context): Array<String> = arrayOf()

    /**
     * Returns if the external application (identified by [callingPid] and [callingUid]) has
     * permission to write preference with given [value].
     * Returns if the external application (identified by [callingPid] and [callingUid]) is
     * permitted to write preference with given [value].
     *
     * The underlying implementation does NOT need to check common states like isEnabled,
     * isRestricted or isAvailable.
     * isRestricted, isAvailable or permissions in [getWritePermissions]. The framework will do it
     * behind the scene.
     */
    @ReadWritePermit
    fun getWritePermit(context: Context, value: T?, callingPid: Int, callingUid: Int): Int =
    fun getWritePermit(
        context: Context,
        value: T?,
        callingPid: Int,
        callingUid: Int,
    ): @ReadWritePermit Int =
        PreferenceScreenRegistry.getWritePermit(
            context,
            value,
+10 −33
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
    val preferenceScreens: PreferenceScreenMap
        get() = preferenceScreensSupplier.get()

    private var readWritePermitProvider: ReadWritePermitProvider? = null
    private var readWritePermitProvider: ReadWritePermitProvider =
        object : ReadWritePermitProvider {}

    /** Sets the [KeyValueStoreProvider]. */
    fun setKeyValueStoreProvider(keyValueStoreProvider: KeyValueStoreProvider) {
@@ -77,7 +78,7 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
    /**
     * Sets the provider to check read write permit. Read and write requests are denied by default.
     */
    fun setReadWritePermitProvider(readWritePermitProvider: ReadWritePermitProvider?) {
    fun setReadWritePermitProvider(readWritePermitProvider: ReadWritePermitProvider) {
        this.readWritePermitProvider = readWritePermitProvider
    }

@@ -86,9 +87,7 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
        callingPid: Int,
        callingUid: Int,
        preference: PreferenceMetadata,
    ) =
        readWritePermitProvider?.getReadPermit(context, callingPid, callingUid, preference)
            ?: ReadWritePermit.DISALLOW
    ) = readWritePermitProvider.getReadPermit(context, callingPid, callingUid, preference)

    override fun getWritePermit(
        context: Context,
@@ -96,9 +95,7 @@ object PreferenceScreenRegistry : ReadWritePermitProvider {
        callingPid: Int,
        callingUid: Int,
        preference: PreferenceMetadata,
    ) =
        readWritePermitProvider?.getWritePermit(context, value, callingPid, callingUid, preference)
            ?: ReadWritePermit.DISALLOW
    ) = readWritePermitProvider.getWritePermit(context, value, callingPid, callingUid, preference)
}

/** Provider of [KeyValueStore]. */
@@ -117,41 +114,21 @@ fun interface KeyValueStoreProvider {
/** Provider of read and write permit. */
interface ReadWritePermitProvider {

    @ReadWritePermit
    val defaultReadWritePermit: @ReadWritePermit Int
        get() = ReadWritePermit.DISALLOW

    fun getReadPermit(
        context: Context,
        callingPid: Int,
        callingUid: Int,
        preference: PreferenceMetadata,
    ): Int
    ): @ReadWritePermit Int = defaultReadWritePermit

    @ReadWritePermit
    fun getWritePermit(
        context: Context,
        value: Any?,
        callingPid: Int,
        callingUid: Int,
        preference: PreferenceMetadata,
    ): Int

    companion object {
        @JvmField
        val ALLOW_ALL_READ_WRITE =
            object : ReadWritePermitProvider {
                override fun getReadPermit(
                    context: Context,
                    callingPid: Int,
                    callingUid: Int,
                    preference: PreferenceMetadata,
                ) = ReadWritePermit.ALLOW

                override fun getWritePermit(
                    context: Context,
                    value: Any?,
                    callingPid: Int,
                    callingUid: Int,
                    preference: PreferenceMetadata,
                ) = ReadWritePermit.ALLOW
            }
    }
    ): @ReadWritePermit Int = defaultReadWritePermit
}