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

Commit 82100793 authored by Jacky Wang's avatar Jacky Wang
Browse files

[Catalyst] Implement metrics logger for Get/Set/Graph API

Bug: 372980186
Flag: com.android.settings.flags.catalyst
Test: statsd_testdrive
Change-Id: I358449612a09ef325e47d0d107e1d72339a9f741
parent 77737899
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -19,9 +19,9 @@ package com.android.settings
import android.content.Context
import com.android.settings.overlay.FeatureFactory
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider
import com.android.settingslib.metadata.PreferenceUiActionMetricsLogger
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceScreenMetadata
import com.android.settingslib.metadata.PreferenceUiActionMetricsLogger

/** Provides metrics for preference action. */
interface PreferenceActionMetricsProvider {
+4 −27
Original line number Diff line number Diff line
@@ -16,11 +16,9 @@

package com.android.settings

import android.app.Application
import android.content.Intent
import com.android.settings.flags.Flags
import com.android.settingslib.graph.PreferenceGetterRequest
import com.android.settingslib.graph.PreferenceSetterRequest
import com.android.settings.metrics.SettingsRemoteOpMetricsLogger
import com.android.settingslib.ipc.ApiPermissionChecker
import com.android.settingslib.service.PreferenceService

@@ -28,32 +26,11 @@ import com.android.settingslib.service.PreferenceService
class SettingsService :
    PreferenceService(
        graphPermissionChecker = ApiPermissionChecker.alwaysAllow(),
        setterPermissionChecker = SetterPermissionChecker(),
        getterPermissionChecker = GetterPermissionChecker(),
        setterPermissionChecker = ApiPermissionChecker.alwaysAllow(),
        getterPermissionChecker = ApiPermissionChecker.alwaysAllow(),
        metricsLogger = SettingsRemoteOpMetricsLogger(),
    ) {

    override fun onBind(intent: Intent) =
        if (Flags.catalystService()) super.onBind(intent) else null
}

/** Permission checker for external setter API. */
private class SetterPermissionChecker : ApiPermissionChecker<PreferenceSetterRequest> {

    override fun hasPermission(
        application: Application,
        callingPid: Int,
        callingUid: Int,
        request: PreferenceSetterRequest,
    ) = true
}

/** Permission checker for external getter API. */
private class GetterPermissionChecker : ApiPermissionChecker<PreferenceGetterRequest> {

    override fun hasPermission(
        application: Application,
        callingPid: Int,
        callingUid: Int,
        request: PreferenceGetterRequest,
    ) = true
}
+159 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.settings.metrics

import android.app.settings.SettingsEnums
import android.content.Context
import com.android.settings.PreferenceActionMetricsProvider
import com.android.settings.core.instrumentation.SettingsStatsLog
import com.android.settingslib.graph.PreferenceGetterErrorCode
import com.android.settingslib.graph.PreferenceSetterResult
import com.android.settingslib.metadata.PreferenceCoordinate
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger
import com.android.settingslib.metadata.PreferenceScreenMetadata

/** Metrics logger for settings remote operations. */
class SettingsRemoteOpMetricsLogger : PreferenceRemoteOpMetricsLogger {

    override fun logGetterApi(
        context: Context,
        callingUid: Int,
        preferenceCoordinate: PreferenceCoordinate,
        screen: PreferenceScreenMetadata?,
        preference: PreferenceMetadata?,
        errorCode: Int,
        latencyMs: Long,
    ) =
        SettingsStatsLog.SETTINGS_EXT_API_REPORTED__TYPE__ACTION_READ.log(
            context,
            callingUid,
            preferenceCoordinate,
            preference,
            errorCode,
            latencyMs,
            Int::convertGetterErrorCode,
        )

    override fun logSetterApi(
        context: Context,
        callingUid: Int,
        preferenceCoordinate: PreferenceCoordinate,
        screen: PreferenceScreenMetadata?,
        preference: PreferenceMetadata?,
        errorCode: Int,
        latencyMs: Long,
    ) =
        SettingsStatsLog.SETTINGS_EXT_API_REPORTED__TYPE__ACTION_WRITE.log(
            context,
            callingUid,
            preferenceCoordinate,
            preference,
            errorCode,
            latencyMs,
            Int::convertSetterErrorCode,
        )

    private fun Int.log(
        context: Context,
        callingUid: Int,
        preferenceCoordinate: PreferenceCoordinate,
        preference: PreferenceMetadata?,
        errorCode: Int,
        latencyMs: Long,
        errorCodeToMetricsResult: (Int) -> Int,
    ) {
        if (preference is PreferenceActionMetricsProvider) {
            SettingsStatsLog.write(
                SettingsStatsLog.SETTINGS_EXTAPI_REPORTED,
                context.packageNameOfUid(callingUid),
                "",
                this,
                errorCodeToMetricsResult(errorCode),
                latencyMs,
                preference.preferenceActionMetrics,
            )
        } else {
            SettingsStatsLog.write(
                SettingsStatsLog.SETTINGS_EXTAPI_REPORTED,
                context.packageNameOfUid(callingUid),
                preferenceCoordinate.settingsId,
                this,
                errorCodeToMetricsResult(errorCode),
                latencyMs,
                SettingsEnums.ACTION_UNKNOWN,
            )
        }
    }

    override fun logGraphApi(context: Context, callingUid: Int, success: Boolean, latencyMs: Long) {
        val result =
            if (success) {
                SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_OK
            } else {
                SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_INTERNAL_ERROR
            }
        SettingsStatsLog.write(
            SettingsStatsLog.SETTINGS_EXTAPI_REPORTED,
            context.packageNameOfUid(callingUid),
            "",
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__TYPE__ACTION_GET_METADATA,
            result,
            latencyMs,
            SettingsEnums.ACTION_UNKNOWN,
        )
    }
}

private fun Context.packageNameOfUid(uid: Int) = packageManager.getNameForUid(uid) ?: ""

private val PreferenceCoordinate.settingsId: String
    get() = "$screenKey/$key"

private fun Int.convertGetterErrorCode() =
    when (this) {
        PreferenceGetterErrorCode.OK ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_OK
        PreferenceGetterErrorCode.NOT_FOUND ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_UNSUPPORTED
        PreferenceGetterErrorCode.DISALLOW ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_DISALLOW
        else -> SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_INTERNAL_ERROR
    }

private fun Int.convertSetterErrorCode() =
    when (this) {
        PreferenceSetterResult.OK -> SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_OK
        PreferenceSetterResult.UNSUPPORTED ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_UNSUPPORTED
        PreferenceSetterResult.DISABLED ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_DISABLED
        PreferenceSetterResult.RESTRICTED ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_RESTRICTED
        PreferenceSetterResult.UNAVAILABLE ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_UNAVAILABLE
        PreferenceSetterResult.REQUIRE_APP_PERMISSION ->
            SettingsStatsLog
                .SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_REQUIRE_APP_PERMISSION
        PreferenceSetterResult.REQUIRE_USER_AGREEMENT ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_REQUIRE_USER_CONSENT
        PreferenceSetterResult.DISALLOW ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_DISALLOW
        PreferenceSetterResult.INVALID_REQUEST ->
            SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_INVALID_REQUEST
        else -> SettingsStatsLog.SETTINGS_EXT_API_REPORTED__RESULT__RESULT_FAILURE_INTERNAL_ERROR
    }
+15 −17
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.settings.service

import android.app.Application
import android.os.Binder
import android.os.OutcomeReceiver
import android.service.settings.preferences.GetValueRequest
@@ -26,6 +25,7 @@ import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceService
import com.android.settings.metrics.SettingsRemoteOpMetricsLogger
import com.android.settingslib.graph.GetPreferenceGraphApiHandler
import com.android.settingslib.graph.GetPreferenceGraphRequest
import com.android.settingslib.graph.PreferenceGetterApiHandler
@@ -41,9 +41,19 @@ class PreferenceService : SettingsPreferenceService() {

    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

    private val getApiHandler = PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow())
    private val setApiHandler = PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow())
    private val graphApi = GraphProvider(3)
    private val getApiHandler: PreferenceGetterApiHandler
    private val setApiHandler: PreferenceSetterApiHandler
    private val graphApi: GetPreferenceGraphApiHandler

    init {
        val metricsLogger = SettingsRemoteOpMetricsLogger()
        getApiHandler =
            PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow(), metricsLogger)
        setApiHandler =
            PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow(), metricsLogger)
        graphApi =
            GetPreferenceGraphApiHandler(3, ApiPermissionChecker.alwaysAllow(), metricsLogger)
    }

    override fun onGetAllPreferenceMetadata(
        request: MetadataRequest,
@@ -58,9 +68,7 @@ class PreferenceService : SettingsPreferenceService() {
                    application,
                    callingPid,
                    callingUid,
                    GetPreferenceGraphRequest(
                        flags = PreferenceGetterFlags.METADATA,
                    ),
                    GetPreferenceGraphRequest(flags = PreferenceGetterFlags.METADATA),
                )
            val result = transformCatalystGetMetadataResponse(this@PreferenceService, graphProto)
            callback.onResult(result)
@@ -107,14 +115,4 @@ class PreferenceService : SettingsPreferenceService() {
            }
        }
    }

    // Basic implementation - we already have permission to access Graph for Metadata via superclass
    private class GraphProvider(override val id: Int) : GetPreferenceGraphApiHandler(emptySet()) {
        override fun hasPermission(
            application: Application,
            callingPid: Int,
            callingUid: Int,
            request: GetPreferenceGraphRequest,
        ) = true
    }
}