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

Bug: 372980186
Flag: com.android.settings.flags.catalyst
Test: statsd_testdrive
Change-Id: I358449612a09ef325e47d0d107e1d72339a9f741
This commit is contained in:
Jacky Wang
2025-01-20 12:30:15 +08:00
parent 777378996f
commit 82100793fd
4 changed files with 179 additions and 45 deletions

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}
}