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

Commit d73c8c33 authored by Jacky Wang's avatar Jacky Wang Committed by Android (Google) Code Review
Browse files

Merge changes from topic "catalyst" into main

* changes:
  [Catalyst] Support parameterized screens for IPC
  [Catalyst] Fix no response for TransactionTooLargeException
parents 7230bd3f 2f65b396
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -28,11 +28,17 @@ message PreferenceScreenProto {
  optional bool complete_hierarchy = 3;
  // Parameterized screens (not recursive, provided on the top level only)
  repeated ParameterizedPreferenceScreenProto parameterized_screens = 4;
  // If the preference screen is parameterized.
  optional bool parameterized = 5;
  // Available parameters of the parameterized screen.
  repeated BundleProto parameters = 6;
}

// Proto of parameterized preference screen
message ParameterizedPreferenceScreenProto {
  // Arguments to materialize the parameterized screen.
  optional BundleProto args = 1;
  // Materialized screen.
  optional PreferenceScreenProto screen = 2;
}

@@ -108,6 +114,8 @@ message PreferenceProto {
      // app or activity belongs to other app.
      IntentProto intent = 2;
    }
    // Arguments to materialize the parameterized screen.
    optional BundleProto args = 3;
  }
}

+4 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.app.Application
import android.os.Bundle
import android.os.Parcelable
import android.os.SystemClock
import com.android.settingslib.graph.PreferenceGetterFlags.setEagerMode
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.ipc.ApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
@@ -63,7 +64,7 @@ class GetPreferenceGraphApiHandler(
                PreferenceGraphBuilder.of(application, callingPid, callingUid, request, this)
            if (request.screens.isEmpty()) {
                val factories = PreferenceScreenRegistry.preferenceScreenMetadataFactories
                factories.forEachAsync { _, factory -> builder.addPreferenceScreen(factory) }
                factories.forEachAsync { key, factory -> builder.addPreferenceScreen(key, factory) }
                for (provider in preferenceScreenProviders) {
                    builder.addPreferenceScreenProvider(provider)
                }
@@ -96,7 +97,8 @@ constructor(
    val screens: Set<PreferenceScreenCoordinate> = setOf(),
    val visitedScreens: Set<PreferenceScreenCoordinate> = setOf(),
    val locale: Locale? = null,
    val flags: Int = PreferenceGetterFlags.ALL,
    // To achieve backward compatibility, use eager mode to materialize hierarchy immediately
    val flags: Int = PreferenceGetterFlags.ALL.setEagerMode(),
)

class GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> {
+39 −1
Original line number Diff line number Diff line
@@ -26,7 +26,25 @@ object PreferenceGetterFlags {
    const val METADATA = 1 shl 2
    /** Flag to include all preference screens regardless of the `isFlagEnabled()` value. */
    const val FORCE_INCLUDE_ALL_SCREENS = 1 shl 3
    const val ALL = (1 shl 4) - 1
    /** Flag to include parameters for parameterized screens. */
    const val PARAMETERS = 1 shl 4
    /**
     * Flag to exclude preference hierarchy.
     *
     * If the bit is set, hierarchy will not be included to reduce the data size of preference graph
     * and the call site can retrieve the graph lazily.
     *
     * Leverage [withHierarchy] and [withoutHierarchy] to manipulate this bit.
     */
    const val EXCLUDE_HIERARCHY = 1 shl 5
    /**
     * Flag to shrink hierarchy (i.e. non-recursive).
     *
     * If the bit is set, only hierarchy of given screens are included, otherwise preference graph
     * will be built recursively to include all reachable screens.
     */
    const val SHRINK_HIERARCHY = 1 shl 6
    const val ALL = (1 shl 7) - 1

    fun Int.includeValue() = (this and VALUE) != 0

@@ -35,4 +53,24 @@ object PreferenceGetterFlags {
    fun Int.includeMetadata() = (this and METADATA) != 0

    fun Int.forceIncludeAllScreens() = (this and FORCE_INCLUDE_ALL_SCREENS) != 0

    /** Returns a new flag to exclude hierarchy. */
    fun Int.withoutHierarchy() = this or EXCLUDE_HIERARCHY

    /** Returns a new flag to include hierarchy. */
    fun Int.withHierarchy() = this and EXCLUDE_HIERARCHY.inv()

    /**
     * Returns a new flag with eager mode.
     *
     * In eager mode, all preference hierarchies are included and built recursively.
     */
    fun Int.setEagerMode() = this and (EXCLUDE_HIERARCHY or SHRINK_HIERARCHY).inv()

    /**
     * Returns a new flag with lazy mode.
     *
     * In lazy mode, preference hierarchy is excluded.
     */
    fun Int.setLazyMode() = (this or EXCLUDE_HIERARCHY or SHRINK_HIERARCHY) and PARAMETERS.inv()
}
+53 −20
Original line number Diff line number Diff line
@@ -86,12 +86,22 @@ private constructor(
    private val visitedScreens = request.visitedScreens.toMutableSet()
    private val screens = mutableMapOf<String, PreferenceScreenProto.Builder>()
    private val forceIncludeAllScreens = request.flags.forceIncludeAllScreens()
    private val includeParameters = (request.flags and PreferenceGetterFlags.PARAMETERS) != 0
    private val includeHierarchy = (request.flags and PreferenceGetterFlags.EXCLUDE_HIERARCHY) == 0
    private val shrinkHierarchy = (request.flags and PreferenceGetterFlags.SHRINK_HIERARCHY) != 0

    private suspend fun init() {
        val factories = PreferenceScreenRegistry.preferenceScreenMetadataFactories
        for (screen in request.screens) {
            val screenKey = screen.screenKey
            val factory = factories[screenKey] ?: continue
            if (screen.args == null && factory is PreferenceScreenMetadataParameterizedFactory) {
                addPreferenceScreen(screenKey, factory)
            } else {
                PreferenceScreenRegistry.create(context, screen)?.let { addPreferenceScreen(it) }
            }
        }
    }

    fun build(): PreferenceGraphProto {
        for ((key, screenBuilder) in screens) builder.putScreens(key, screenBuilder.build())
@@ -157,7 +167,7 @@ private constructor(
    private suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
        val factory =
            PreferenceScreenRegistry.preferenceScreenMetadataFactories[key] ?: return false
        return addPreferenceScreen(factory)
        return addPreferenceScreen(key, factory)
    }

    suspend fun addPreferenceScreenProvider(activityClass: Class<*>) {
@@ -208,25 +218,43 @@ private constructor(
        }
    }

    suspend fun addPreferenceScreen(factory: PreferenceScreenMetadataFactory): Boolean {
        if (factory is PreferenceScreenMetadataParameterizedFactory) {
    suspend fun addPreferenceScreen(
        screenKey: String,
        factory: PreferenceScreenMetadataFactory,
    ): Boolean {
        if (factory !is PreferenceScreenMetadataParameterizedFactory) {
            return addPreferenceScreen(factory.create(context))
        }
        if (visitedScreens.add(PreferenceScreenCoordinate(screenKey, null))) {
            val screen = screens.getOrPut(screenKey) { PreferenceScreenProto.newBuilder() }
            screen.root = preferenceGroupProto { preference = preferenceProto { key = screenKey } }
            screen.parameterized = true
            if (includeParameters) {
                factory.parameters(context).collect { screen.addParameters(it.toProto()) }
            }
        }
        if (includeHierarchy) {
            var flagEnabled: Boolean? = null
            factory.parameters(context).collect {
                if (flagEnabled == false) return@collect
                val screenMetadata = factory.create(context, it)
                if (flagEnabled == null) flagEnabled = checkScreenFlag(screenMetadata)
                if (flagEnabled == true) addPreferenceScreen(screenMetadata)
                if (flagEnabled) addPreferenceScreen(screenMetadata)
            }
            return true
        }
        return addPreferenceScreen(factory.create(context))
        return true
    }

    private suspend fun addPreferenceScreen(metadata: PreferenceScreenMetadata): Boolean {
        if (!checkScreenFlag(metadata)) return false
        return addPreferenceScreen(metadata.key, metadata.arguments) {
            completeHierarchy = metadata.hasCompleteHierarchy()
            root = metadata.getPreferenceHierarchy(context, coroutineScope).toProto(metadata, true)
            root =
                if (includeHierarchy) {
                    metadata.getPreferenceHierarchy(context, coroutineScope).toProto(metadata, true)
                } else {
                    preferenceGroupProto { preference = toProto(metadata, metadata, true) }
                }
        }
    }

@@ -246,20 +274,19 @@ private constructor(
        args: Bundle?,
        init: suspend PreferenceScreenProto.Builder.() -> Unit,
    ): Boolean {
        if (!visitedScreens.add(PreferenceScreenCoordinate(key, args))) {
            Log.w(TAG, "$key $args visited")
            return false
        }
        if (!visitedScreens.add(PreferenceScreenCoordinate(key, args))) return false
        fun newParameterizedScreenBuilder() =
            PreferenceScreenProto.newBuilder().also { it.parameterized = true }
        if (args == null) { // normal screen
            screens[key] = PreferenceScreenProto.newBuilder().also { init(it) }
        } else if (args.isEmpty) { // parameterized screen with backward compatibility
            val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
            val builder = screens.getOrPut(key) { newParameterizedScreenBuilder() }
            init(builder)
        } else { // parameterized screen with non-empty arguments
            val builder = screens.getOrPut(key) { PreferenceScreenProto.newBuilder() }
            val builder = screens.getOrPut(key) { newParameterizedScreenBuilder() }
            val parameterizedScreen = parameterizedPreferenceScreenProto {
                setArgs(args.toProto())
                setScreen(PreferenceScreenProto.newBuilder().also { init(it) })
                setScreen(newParameterizedScreenBuilder().also { init(it) })
            }
            builder.addParameterizedScreens(parameterizedScreen)
        }
@@ -328,6 +355,7 @@ private constructor(
            metadata
                .toProto(context, callingPid, callingUid, screenMetadata, isRoot, request.flags)
                .also {
                    if (!isRoot && shrinkHierarchy) return@also
                    if (metadata is PreferenceScreenMetadata) {
                        @Suppress("CheckReturnValue") addPreferenceScreen(metadata)
                    }
@@ -450,7 +478,14 @@ fun PreferenceMetadata.toProto(
        if (metadata is PreferenceRestrictionProvider) {
            restricted = metadata.isRestricted(context)
        }
        if (metadata is PreferenceScreenMetadata) {
            actionTarget = actionTargetProto {
                key = metadata.key
                metadata.arguments?.let { args = it.toProto() }
            }
        } else {
            metadata.intent(context)?.let { actionTarget = it.toActionTarget(context) }
        }
        val launchTarget = if (screenMetadata != metadata) metadata else null
        screenMetadata.getLaunchIntent(context, launchTarget)?.let { launchIntent = it.toProto() }
        for (tag in metadata.tags(context)) addTags(tag)
@@ -476,10 +511,8 @@ fun PreferenceMetadata.toProto(
            val key = metadata.bindingKey
            when (metadata.valueType) {
                Int::class.javaObjectType -> storage.getInt(key)?.let { intValue = it }
                Boolean::class.javaObjectType ->
                    storage.getBoolean(key)?.let { booleanValue = it }
                Float::class.javaObjectType ->
                    storage.getFloat(key)?.let { floatValue = it }
                Boolean::class.javaObjectType -> storage.getBoolean(key)?.let { booleanValue = it }
                Float::class.javaObjectType -> storage.getFloat(key)?.let { floatValue = it }
                Long::class.javaObjectType -> storage.getLong(key)?.let { longValue = it }
                else -> {}
            }
+41 −7
Original line number Diff line number Diff line
@@ -18,20 +18,27 @@ package com.android.settingslib.graph

import com.android.settingslib.graph.proto.BundleProto
import com.android.settingslib.graph.proto.BundleProto.BundleValue
import com.android.settingslib.graph.proto.BundleProtoOrBuilder
import com.android.settingslib.graph.proto.IntentProto
import com.android.settingslib.graph.proto.ParameterizedPreferenceScreenProto
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.graph.proto.PreferenceGroupProto
import com.android.settingslib.graph.proto.PreferenceGroupProtoOrBuilder
import com.android.settingslib.graph.proto.PreferenceOrGroupProto
import com.android.settingslib.graph.proto.PreferenceOrGroupProtoOrBuilder
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceProto.ActionTarget
import com.android.settingslib.graph.proto.PreferenceProto.ActionTargetOrBuilder
import com.android.settingslib.graph.proto.PreferenceProtoOrBuilder
import com.android.settingslib.graph.proto.PreferenceScreenProto
import com.android.settingslib.graph.proto.PreferenceScreenProtoOrBuilder
import com.android.settingslib.graph.proto.PreferenceValueDescriptorProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.proto.RangeValueProto
import com.android.settingslib.graph.proto.TextProto

/** Returns root or null. */
val PreferenceScreenProto.rootOrNull
val PreferenceScreenProtoOrBuilder.rootOrNull
    get() = if (hasRoot()) root else null

/** Kotlin DSL-style builder for [PreferenceScreenProto]. */
@@ -47,11 +54,11 @@ inline fun parameterizedPreferenceScreenProto(
    ParameterizedPreferenceScreenProto.newBuilder().also(init).build()

/** Returns preference or null. */
val PreferenceOrGroupProto.preferenceOrNull
val PreferenceOrGroupProtoOrBuilder.preferenceOrNull
    get() = if (hasPreference()) preference else null

/** Returns group or null. */
val PreferenceOrGroupProto.groupOrNull
val PreferenceOrGroupProtoOrBuilder.groupOrNull
    get() = if (hasGroup()) group else null

/** Kotlin DSL-style builder for [PreferenceOrGroupProto]. */
@@ -61,7 +68,7 @@ inline fun preferenceOrGroupProto(
): PreferenceOrGroupProto = PreferenceOrGroupProto.newBuilder().also(init).build()

/** Returns preference or null. */
val PreferenceGroupProto.preferenceOrNull
val PreferenceGroupProtoOrBuilder.preferenceOrNull
    get() = if (hasPreference()) preference else null

/** Kotlin DSL-style builder for [PreferenceGroupProto]. */
@@ -71,17 +78,25 @@ inline fun preferenceGroupProto(
): PreferenceGroupProto = PreferenceGroupProto.newBuilder().also(init).build()

/** Returns title or null. */
val PreferenceProto.titleOrNull
val PreferenceProtoOrBuilder.titleOrNull
    get() = if (hasTitle()) title else null

/** Returns summary or null. */
val PreferenceProto.summaryOrNull
val PreferenceProtoOrBuilder.summaryOrNull
    get() = if (hasSummary()) summary else null

/** Returns actionTarget or null. */
val PreferenceProto.actionTargetOrNull
val PreferenceProtoOrBuilder.actionTargetOrNull
    get() = if (hasActionTarget()) actionTarget else null

/** Returns key or null. */
val ActionTargetOrBuilder.keyOrNull
    get() = if (hasKey()) key else null

/** Returns args or null. */
val ActionTargetOrBuilder.argsOrNull
    get() = if (hasArgs()) args else null

/** Kotlin DSL-style builder for [PreferenceProto]. */
@JvmSynthetic
inline fun preferenceProto(init: PreferenceProto.Builder.() -> Unit): PreferenceProto =
@@ -132,3 +147,22 @@ inline fun bundleProto(init: BundleProto.Builder.() -> Unit): BundleProto =
@JvmSynthetic
inline fun bundleValueProto(init: BundleValue.Builder.() -> Unit): BundleValue =
    BundleValue.newBuilder().also(init).build()

fun PreferenceGraphProto.Builder.mergeForLazyMode(
    screen: PreferenceScreenProto,
    screenKey: String,
    args: BundleProtoOrBuilder?,
) {
    val oldScreen = getScreensOrDefault(screenKey, null)
    if (oldScreen == null) {
        putScreens(screenKey, screen)
        return
    }
    val screenBuilder = oldScreen.toBuilder()
    if (args == null) {
        screenBuilder.mergeFrom(screen)
    } else {
        screenBuilder.addAllParameterizedScreens(screen.parameterizedScreensList)
    }
    putScreens(screenKey, screenBuilder.build())
}
Loading