From 82100793fdbcf246bd62294f8214f221f666b34d Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Mon, 20 Jan 2025 12:30:15 +0800 Subject: [PATCH] [Catalyst] Implement metrics logger for Get/Set/Graph API Bug: 372980186 Flag: com.android.settings.flags.catalyst Test: statsd_testdrive Change-Id: I358449612a09ef325e47d0d107e1d72339a9f741 --- src/com/android/settings/Metrics.kt | 2 +- src/com/android/settings/SettingsService.kt | 31 +--- .../metrics/SettingsRemoteOpMetricsLogger.kt | 159 ++++++++++++++++++ .../settings/service/PreferenceService.kt | 32 ++-- 4 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 src/com/android/settings/metrics/SettingsRemoteOpMetricsLogger.kt diff --git a/src/com/android/settings/Metrics.kt b/src/com/android/settings/Metrics.kt index 0d5ea56aff3..8dde9d5a60f 100644 --- a/src/com/android/settings/Metrics.kt +++ b/src/com/android/settings/Metrics.kt @@ -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 { diff --git a/src/com/android/settings/SettingsService.kt b/src/com/android/settings/SettingsService.kt index 1783a7d423e..b7c6220bd23 100644 --- a/src/com/android/settings/SettingsService.kt +++ b/src/com/android/settings/SettingsService.kt @@ -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 { - - override fun hasPermission( - application: Application, - callingPid: Int, - callingUid: Int, - request: PreferenceSetterRequest, - ) = true -} - -/** Permission checker for external getter API. */ -private class GetterPermissionChecker : ApiPermissionChecker { - - override fun hasPermission( - application: Application, - callingPid: Int, - callingUid: Int, - request: PreferenceGetterRequest, - ) = true -} diff --git a/src/com/android/settings/metrics/SettingsRemoteOpMetricsLogger.kt b/src/com/android/settings/metrics/SettingsRemoteOpMetricsLogger.kt new file mode 100644 index 00000000000..8653ef9ff0e --- /dev/null +++ b/src/com/android/settings/metrics/SettingsRemoteOpMetricsLogger.kt @@ -0,0 +1,159 @@ +/* + * 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 + } diff --git a/src/com/android/settings/service/PreferenceService.kt b/src/com/android/settings/service/PreferenceService.kt index 2e5191486c0..9843847304e 100644 --- a/src/com/android/settings/service/PreferenceService.kt +++ b/src/com/android/settings/service/PreferenceService.kt @@ -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 - } }