diff options
author | 2025-01-20 12:28:53 +0800 | |
---|---|---|
committer | 2025-01-22 07:30:36 +0800 | |
commit | 175cb57865165499e933a16b0c8e51f78ec4c48d (patch) | |
tree | 8beca08c11d84a6fb380dbef3eb3a3417063a768 | |
parent | 47fbb62f7caf630e9eb62e5e8f6908e13ec732db (diff) |
[Catalyst] Support metrics logger for Get/Set/Graph API
Bug: 372980186
Flag: com.android.settings.flags.catalyst
Test: statsd_testdrive
Change-Id: I4e914c9597222aa3fc987c9db1c5ffff3795d876
6 files changed, 210 insertions, 116 deletions
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt index 51813a1c9aab..adffd206d552 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt @@ -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,22 +42,42 @@ 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 builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request) - if (request.screenKeys.isEmpty()) { - PreferenceScreenRegistry.preferenceScreenMetadataFactories.forEachKeyAsync { - builder.addPreferenceScreenFromRegistry(it) - } - for (provider in preferenceScreenProviders) { - builder.addPreferenceScreenProvider(provider) + val elapsedRealtime = SystemClock.elapsedRealtime() + var success = false + try { + val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request) + if (request.screenKeys.isEmpty()) { + PreferenceScreenRegistry.preferenceScreenMetadataFactories.forEachKeyAsync { + builder.addPreferenceScreenFromRegistry(it) + } + for (provider in preferenceScreenProviders) { + builder.addPreferenceScreenProvider(provider) + } } + val result = builder.build() + success = true + return result + } finally { + metricsLogger?.logGraphApi( + application, + callingUid, + success, + SystemClock.elapsedRealtime() - elapsedRealtime, + ) } - return builder.build() } } diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt index 6fc6b5405eb2..a9958b975fc6 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt @@ -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,27 +134,48 @@ 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 - try { - val preferenceProto = - metadata.toProto( - application, - callingPid, - callingUid, - screenMetadata, - metadata.key == screenMetadata.key, - flags, - ) - if (flags == PreferenceGetterFlags.VALUE && !preferenceProto.hasValue()) { - errors[coordinate] = PreferenceGetterErrorCode.DISALLOW - } else { - preferences[coordinate] = preferenceProto + val errorCode = + try { + val preferenceProto = + metadata.toProto( + application, + callingPid, + callingUid, + screenMetadata, + metadata.key == screenMetadata.key, + flags, + ) + if (flags == PreferenceGetterFlags.VALUE && !preferenceProto.hasValue()) { + PreferenceGetterErrorCode.DISALLOW + } else { + preferences[coordinate] = preferenceProto + PreferenceGetterErrorCode.OK + } + } catch (e: Exception) { + PreferenceGetterErrorCode.INTERNAL_ERROR } - } catch (e: Exception) { - errors[coordinate] = 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) diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt index 728055c2f837..a595f42a573d 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt @@ -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,41 +148,64 @@ class PreferenceSetterApiHandler( } } - val storage = metadata.storage(application) - val value = request.value - try { - if (value.hasBooleanValue()) { - if (metadata.valueType != Boolean::class.javaObjectType) { - return PreferenceSetterResult.INVALID_REQUEST - } - val booleanValue = value.booleanValue - val resultCode = metadata.checkWritePermit(booleanValue) - if (resultCode != PreferenceSetterResult.OK) return resultCode - storage.setBoolean(key, booleanValue) - return PreferenceSetterResult.OK - } else if (value.hasIntValue()) { - val intValue = value.intValue - val resultCode = metadata.checkWritePermit(intValue) - if (resultCode != PreferenceSetterResult.OK) return resultCode - if ( - metadata is IntRangeValuePreference && - !metadata.isValidValue(application, intValue) - ) { - return PreferenceSetterResult.INVALID_REQUEST + 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 { + if (value.hasBooleanValue()) { + if (metadata.valueType != Boolean::class.javaObjectType) { + return PreferenceSetterResult.INVALID_REQUEST + } + val booleanValue = value.booleanValue + val resultCode = metadata.checkWritePermit(booleanValue) + if (resultCode != PreferenceSetterResult.OK) return resultCode + storage.setBoolean(key, booleanValue) + return PreferenceSetterResult.OK + } else if (value.hasIntValue()) { + val intValue = value.intValue + val resultCode = metadata.checkWritePermit(intValue) + if (resultCode != PreferenceSetterResult.OK) return resultCode + if ( + metadata is IntRangeValuePreference && + !metadata.isValidValue(application, intValue) + ) { + return PreferenceSetterResult.INVALID_REQUEST + } + storage.setInt(key, intValue) + return PreferenceSetterResult.OK + } else if (value.hasFloatValue()) { + val floatValue = value.floatValue + val resultCode = metadata.checkWritePermit(floatValue) + if (resultCode != PreferenceSetterResult.OK) return resultCode + storage.setFloat(key, floatValue) + return PreferenceSetterResult.OK } - storage.setInt(key, intValue) - return PreferenceSetterResult.OK - } else if (value.hasFloatValue()) { - val floatValue = value.floatValue - val resultCode = metadata.checkWritePermit(floatValue) - if (resultCode != PreferenceSetterResult.OK) return resultCode - storage.setFloat(key, floatValue) - return PreferenceSetterResult.OK + } catch (e: Exception) { + return PreferenceSetterResult.INTERNAL_ERROR } - } catch (e: Exception) { - return PreferenceSetterResult.INTERNAL_ERROR + return PreferenceSetterResult.INVALID_REQUEST } - 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> diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt index 7323488c5299..e532e545cc11 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt @@ -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) +} diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt deleted file mode 100644 index ae9642a38778..000000000000 --- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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) -} diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt index 7cb36db856eb..5def592a1afa 100644 --- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt +++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt @@ -16,6 +16,7 @@ package com.android.settingslib.service +import com.android.settingslib.graph.GetPreferenceGraphApiHandler import com.android.settingslib.graph.GetPreferenceGraphRequest import com.android.settingslib.graph.PreferenceGetterApiHandler import com.android.settingslib.graph.PreferenceGetterRequest @@ -25,6 +26,7 @@ import com.android.settingslib.ipc.ApiHandler import com.android.settingslib.ipc.ApiPermissionChecker import com.android.settingslib.ipc.MessengerService import com.android.settingslib.ipc.PermissionChecker +import com.android.settingslib.metadata.PreferenceRemoteOpMetricsLogger import com.android.settingslib.preference.PreferenceScreenProvider /** @@ -40,16 +42,26 @@ open class PreferenceService( graphPermissionChecker: ApiPermissionChecker<GetPreferenceGraphRequest>? = null, setterPermissionChecker: ApiPermissionChecker<PreferenceSetterRequest>? = null, getterPermissionChecker: ApiPermissionChecker<PreferenceGetterRequest>? = null, + metricsLogger: PreferenceRemoteOpMetricsLogger? = null, vararg apiHandlers: ApiHandler<*, *>, ) : MessengerService( mutableListOf<ApiHandler<*, *>>().apply { - graphPermissionChecker?.let { add(PreferenceGraphApi(preferenceScreenProviders, it)) } + graphPermissionChecker?.let { + add( + GetPreferenceGraphApiHandler( + API_GET_PREFERENCE_GRAPH, + it, + metricsLogger, + preferenceScreenProviders, + ) + ) + } setterPermissionChecker?.let { - add(PreferenceSetterApiHandler(API_PREFERENCE_SETTER, it)) + add(PreferenceSetterApiHandler(API_PREFERENCE_SETTER, it, metricsLogger)) } getterPermissionChecker?.let { - add(PreferenceGetterApiHandler(API_PREFERENCE_GETTER, it)) + add(PreferenceGetterApiHandler(API_PREFERENCE_GETTER, it, metricsLogger)) } addAll(apiHandlers) }, |