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

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

Merge "[Catalyst] Support metrics logger for Get/Set/Graph API" into main

parents 48b5a0e2 175cb578
Loading
Loading
Loading
Loading
+36 −10
Original line number Diff line number Diff line
@@ -18,16 +18,22 @@ package com.android.settingslib.graph

import android.app.Application
import android.os.Bundle
import android.os.SystemClock
import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.ipc.ApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.ipc.MessageCodec
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.preference.PreferenceScreenProvider
import java.util.Locale

/** API to get preference graph. */
abstract class GetPreferenceGraphApiHandler(
    private val preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>>
class GetPreferenceGraphApiHandler(
    override val id: Int,
    private val permissionChecker: ApiPermissionChecker<GetPreferenceGraphRequest>,
    private val metricsLogger: PreferenceRemoteOpMetricsLogger? = null,
    private val preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>> = emptySet(),
) : ApiHandler<GetPreferenceGraphRequest, PreferenceGraphProto> {

    override val requestCodec: MessageCodec<GetPreferenceGraphRequest>
@@ -36,12 +42,22 @@ abstract class GetPreferenceGraphApiHandler(
    override val responseCodec: MessageCodec<PreferenceGraphProto>
        get() = PreferenceGraphProtoCodec

    override fun hasPermission(
        application: Application,
        callingPid: Int,
        callingUid: Int,
        request: GetPreferenceGraphRequest,
    ) = permissionChecker.hasPermission(application, callingPid, callingUid, request)

    override suspend fun invoke(
        application: Application,
        callingPid: Int,
        callingUid: Int,
        request: GetPreferenceGraphRequest,
    ): PreferenceGraphProto {
        val elapsedRealtime = SystemClock.elapsedRealtime()
        var success = false
        try {
            val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request)
            if (request.screenKeys.isEmpty()) {
                PreferenceScreenRegistry.preferenceScreenMetadataFactories.forEachKeyAsync {
@@ -51,7 +67,17 @@ abstract class GetPreferenceGraphApiHandler(
                    builder.addPreferenceScreenProvider(provider)
                }
            }
        return builder.build()
            val result = builder.build()
            success = true
            return result
        } finally {
            metricsLogger?.logGraphApi(
                application,
                callingUid,
                success,
                SystemClock.elapsedRealtime() - elapsedRealtime,
            )
        }
    }
}

+55 −17
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.settingslib.graph

import android.app.Application
import android.os.SystemClock
import androidx.annotation.IntDef
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.ipc.ApiDescriptor
@@ -24,6 +25,7 @@ import com.android.settingslib.ipc.ApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.metadata.PreferenceCoordinate
import com.android.settingslib.metadata.PreferenceHierarchyNode
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
import com.android.settingslib.metadata.PreferenceScreenRegistry

/**
@@ -37,6 +39,7 @@ class PreferenceGetterRequest(val preferences: Array<PreferenceCoordinate>, val
/** Error code of preference getter request. */
@Target(AnnotationTarget.TYPE)
@IntDef(
    PreferenceGetterErrorCode.OK,
    PreferenceGetterErrorCode.NOT_FOUND,
    PreferenceGetterErrorCode.DISALLOW,
    PreferenceGetterErrorCode.INTERNAL_ERROR,
@@ -44,6 +47,8 @@ class PreferenceGetterRequest(val preferences: Array<PreferenceCoordinate>, val
@Retention(AnnotationRetention.SOURCE)
annotation class PreferenceGetterErrorCode {
    companion object {
        /** Preference value is returned. */
        const val OK = 0
        /** Preference is not found. */
        const val NOT_FOUND = 1
        /** Disallow to get preference value (e.g. uid not allowed). */
@@ -80,6 +85,7 @@ class PreferenceGetterApiDescriptor(override val id: Int) :
class PreferenceGetterApiHandler(
    override val id: Int,
    private val permissionChecker: ApiPermissionChecker<PreferenceGetterRequest>,
    private val metricsLogger: PreferenceRemoteOpMetricsLogger? = null,
) : ApiHandler<PreferenceGetterRequest, PreferenceGetterResponse> {

    override fun hasPermission(
@@ -95,14 +101,25 @@ class PreferenceGetterApiHandler(
        callingUid: Int,
        request: PreferenceGetterRequest,
    ): PreferenceGetterResponse {
        val elapsedRealtime = SystemClock.elapsedRealtime()
        val errors = mutableMapOf<PreferenceCoordinate, Int>()
        val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
        val flags = request.flags
        for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) {
            val screenMetadata = PreferenceScreenRegistry.create(application, screenKey)
            if (screenMetadata == null) {
                val latencyMs = SystemClock.elapsedRealtime() - elapsedRealtime
                for (coordinate in coordinates) {
                    errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND
                    metricsLogger?.logGetterApi(
                        application,
                        callingUid,
                        coordinate,
                        null,
                        null,
                        PreferenceGetterErrorCode.NOT_FOUND,
                        latencyMs,
                    )
                }
                continue
            }
@@ -117,9 +134,19 @@ class PreferenceGetterApiHandler(
                val node = nodes[coordinate.key]
                if (node == null) {
                    errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND
                    metricsLogger?.logGetterApi(
                        application,
                        callingUid,
                        coordinate,
                        null,
                        null,
                        PreferenceGetterErrorCode.NOT_FOUND,
                        SystemClock.elapsedRealtime() - elapsedRealtime,
                    )
                    continue
                }
                val metadata = node.metadata
                val errorCode =
                    try {
                        val preferenceProto =
                            metadata.toProto(
@@ -131,13 +158,24 @@ class PreferenceGetterApiHandler(
                                flags,
                            )
                        if (flags == PreferenceGetterFlags.VALUE && !preferenceProto.hasValue()) {
                        errors[coordinate] = PreferenceGetterErrorCode.DISALLOW
                            PreferenceGetterErrorCode.DISALLOW
                        } else {
                            preferences[coordinate] = preferenceProto
                            PreferenceGetterErrorCode.OK
                        }
                    } catch (e: Exception) {
                    errors[coordinate] = PreferenceGetterErrorCode.INTERNAL_ERROR
                        PreferenceGetterErrorCode.INTERNAL_ERROR
                    }
                if (errorCode != PreferenceGetterErrorCode.OK) errors[coordinate] = errorCode
                metricsLogger?.logGetterApi(
                    application,
                    callingUid,
                    coordinate,
                    screenMetadata,
                    metadata,
                    errorCode,
                    SystemClock.elapsedRealtime() - elapsedRealtime,
                )
            }
        }
        return PreferenceGetterResponse(errors, preferences)
+74 −44
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settingslib.graph
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.os.SystemClock
import androidx.annotation.IntDef
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.ipc.ApiDescriptor
@@ -29,7 +30,9 @@ import com.android.settingslib.ipc.MessageCodec
import com.android.settingslib.metadata.IntRangeValuePreference
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceCoordinate
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.PreferenceScreenRegistry
import com.android.settingslib.metadata.ReadWritePermit
@@ -97,6 +100,7 @@ class PreferenceSetterApiDescriptor(override val id: Int) :
class PreferenceSetterApiHandler(
    override val id: Int,
    private val permissionChecker: ApiPermissionChecker<PreferenceSetterRequest>,
    private val metricsLogger: PreferenceRemoteOpMetricsLogger? = null,
) : ApiHandler<PreferenceSetterRequest, Int> {

    override fun hasPermission(
@@ -112,21 +116,24 @@ class PreferenceSetterApiHandler(
        callingUid: Int,
        request: PreferenceSetterRequest,
    ): Int {
        val elapsedRealtime = SystemClock.elapsedRealtime()
        fun notFound(): Int {
            metricsLogger?.logSetterApi(
                application,
                callingUid,
                PreferenceCoordinate(request.screenKey, request.key),
                null,
                null,
                PreferenceSetterResult.UNSUPPORTED,
                SystemClock.elapsedRealtime() - elapsedRealtime,
            )
            return PreferenceSetterResult.UNSUPPORTED
        }
        val screenMetadata =
            PreferenceScreenRegistry.create(application, request.screenKey)
                ?: return PreferenceSetterResult.UNSUPPORTED
            PreferenceScreenRegistry.create(application, request.screenKey) ?: return notFound()
        val key = request.key
        val metadata =
            screenMetadata.getPreferenceHierarchy(application).find(key)
                ?: return PreferenceSetterResult.UNSUPPORTED
        if (metadata !is PersistentPreference<*>) return PreferenceSetterResult.UNSUPPORTED
        if (!metadata.isEnabled(application)) return PreferenceSetterResult.DISABLED
        if (metadata is PreferenceRestrictionProvider && metadata.isRestricted(application)) {
            return PreferenceSetterResult.RESTRICTED
        }
        if (metadata is PreferenceAvailabilityProvider && !metadata.isAvailable(application)) {
            return PreferenceSetterResult.UNAVAILABLE
        }
            screenMetadata.getPreferenceHierarchy(application).find(key) ?: return notFound()

        fun <T> PreferenceMetadata.checkWritePermit(value: T): Int {
            @Suppress("UNCHECKED_CAST") val preference = (this as PersistentPreference<T>)
@@ -141,6 +148,16 @@ class PreferenceSetterApiHandler(
            }
        }

        fun invoke(): Int {
            if (metadata !is PersistentPreference<*>) return PreferenceSetterResult.UNSUPPORTED
            if (!metadata.isEnabled(application)) return PreferenceSetterResult.DISABLED
            if (metadata is PreferenceRestrictionProvider && metadata.isRestricted(application)) {
                return PreferenceSetterResult.RESTRICTED
            }
            if (metadata is PreferenceAvailabilityProvider && !metadata.isAvailable(application)) {
                return PreferenceSetterResult.UNAVAILABLE
            }

            val storage = metadata.storage(application)
            val value = request.value
            try {
@@ -178,6 +195,19 @@ class PreferenceSetterApiHandler(
            return PreferenceSetterResult.INVALID_REQUEST
        }

        val result = invoke()
        metricsLogger?.logSetterApi(
            application,
            callingUid,
            PreferenceCoordinate(request.screenKey, request.key),
            screenMetadata,
            metadata,
            result,
            SystemClock.elapsedRealtime() - elapsedRealtime,
        )
        return result
    }

    override val requestCodec: MessageCodec<PreferenceSetterRequest>
        get() = PreferenceSetterRequestCodec

+30 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settingslib.metadata

import android.content.Context

/** Metrics logger for preference actions triggered by user interaction. */
interface PreferenceUiActionMetricsLogger {

@@ -28,8 +30,34 @@ interface PreferenceUiActionMetricsLogger {
        screen: PreferenceScreenMetadata,
        preference: PreferenceMetadata,
        value: Any?,
    ) {}
    )
}

/** Metrics logger for preference remote operations (e.g. external get/set). */
interface PreferenceRemoteOpMetricsLogger
interface PreferenceRemoteOpMetricsLogger {

    /** Logs get preference metadata operation. */
    fun logGetterApi(
        context: Context,
        callingUid: Int,
        preferenceCoordinate: PreferenceCoordinate,
        screen: PreferenceScreenMetadata?,
        preference: PreferenceMetadata?,
        errorCode: Int,
        latencyMs: Long,
    )

    /** Logs set preference value operation. */
    fun logSetterApi(
        context: Context,
        callingUid: Int,
        preferenceCoordinate: PreferenceCoordinate,
        screen: PreferenceScreenMetadata?,
        preference: PreferenceMetadata?,
        errorCode: Int,
        latencyMs: Long,
    )

    /** Logs get preference graph operation. */
    fun logGraphApi(context: Context, callingUid: Int, success: Boolean, latencyMs: Long)
}
+0 −40
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.service

import android.app.Application
import com.android.settingslib.graph.GetPreferenceGraphApiHandler
import com.android.settingslib.graph.GetPreferenceGraphRequest
import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.preference.PreferenceScreenProvider

/** Api to get preference graph. */
internal class PreferenceGraphApi(
    preferenceScreenProviders: Set<Class<out PreferenceScreenProvider>>,
    private val permissionChecker: ApiPermissionChecker<GetPreferenceGraphRequest>,
) : GetPreferenceGraphApiHandler(preferenceScreenProviders) {

    override val id: Int
        get() = API_GET_PREFERENCE_GRAPH

    override fun hasPermission(
        application: Application,
        callingPid: Int,
        callingUid: Int,
        request: GetPreferenceGraphRequest,
    ) = permissionChecker.hasPermission(application, callingPid, callingUid, request)
}
Loading