summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jacky Wang <jiannan@google.com> 2025-01-20 12:28:53 +0800
committer Jacky Wang <jiannan@google.com> 2025-01-22 07:30:36 +0800
commit175cb57865165499e933a16b0c8e51f78ec4c48d (patch)
tree8beca08c11d84a6fb380dbef3eb3a3417063a768
parent47fbb62f7caf630e9eb62e5e8f6908e13ec732db (diff)
[Catalyst] Support metrics logger for Get/Set/Graph API
Bug: 372980186 Flag: com.android.settings.flags.catalyst Test: statsd_testdrive Change-Id: I4e914c9597222aa3fc987c9db1c5ffff3795d876
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt46
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt72
-rw-r--r--packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt118
-rw-r--r--packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Metrics.kt32
-rw-r--r--packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceGraphApi.kt40
-rw-r--r--packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt18
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)
},