From 1a8a5e7ac0ad1c38376d4ba5230011688d84faad Mon Sep 17 00:00:00 2001 From: shubang Date: Fri, 18 Jun 2021 11:53:28 -0700 Subject: [PATCH] Use DeviceConfig in MediaMetricsManagerService Bug: 186764459 Test: atest MediaMetricsAtomTests Change-Id: I1becb0ef3eb8b889719c3aafa8578ff7f9af8a26 --- .../metrics/MediaMetricsManagerService.java | 248 +++++++++++++++++- 1 file changed, 247 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java index 2cc2ebf616c58..981e75988d56b 100644 --- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java @@ -17,6 +17,7 @@ package com.android.server.media.metrics; import android.content.Context; +import android.content.pm.PackageManager; import android.media.metrics.IMediaMetricsManager; import android.media.metrics.NetworkEvent; import android.media.metrics.PlaybackErrorEvent; @@ -24,19 +25,60 @@ import android.media.metrics.PlaybackMetrics; import android.media.metrics.PlaybackStateEvent; import android.media.metrics.TrackChangeEvent; import android.os.Binder; +import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; import android.util.Base64; +import android.util.Slog; import android.util.StatsEvent; import android.util.StatsLog; +import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; import java.security.SecureRandom; +import java.util.Arrays; +import java.util.List; /** * System service manages media metrics. */ public final class MediaMetricsManagerService extends SystemService { + private static final String TAG = "MediaMetricsManagerService"; + + private static final String MEDIA_METRICS_MODE = "media_metrics_mode"; + private static final String PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST = + "player_metrics_per_app_attribution_allowlist"; + private static final String PLAYER_METRICS_APP_ALLOWLIST = "player_metrics_app_allowlist"; + + private static final String PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST = + "player_metrics_per_app_attribution_blocklist"; + private static final String PLAYER_METRICS_APP_BLOCKLIST = "player_metrics_app_blocklist"; + + private static final int MEDIA_METRICS_MODE_OFF = 0; + private static final int MEDIA_METRICS_MODE_ON = 1; + private static final int MEDIA_METRICS_MODE_BLOCKLIST = 2; + private static final int MEDIA_METRICS_MODE_ALLOWLIST = 3; + + // Cascading logging levels. The higher value, the more constrains (less logging data). + // The unused values between 2 consecutive levels are reserved for potential extra levels. + private static final int LOGGING_LEVEL_EVERYTHING = 0; + private static final int LOGGING_LEVEL_NO_UID = 1000; + private static final int LOGGING_LEVEL_BLOCKED = 99999; + + private static final String FAILED_TO_GET = "failed_to_get"; private final SecureRandom mSecureRandom; + @GuardedBy("mLock") + private Integer mMode = null; + @GuardedBy("mLock") + private List mAllowlist = null; + @GuardedBy("mLock") + private List mNoUidAllowlist = null; + @GuardedBy("mLock") + private List mBlockList = null; + @GuardedBy("mLock") + private List mNoUidBlocklist = null; + private final Object mLock = new Object(); + private final Context mContext; /** * Initializes the playback metrics manager service. @@ -45,20 +87,73 @@ public final class MediaMetricsManagerService extends SystemService { */ public MediaMetricsManagerService(Context context) { super(context); + mContext = context; mSecureRandom = new SecureRandom(); } @Override public void onStart() { publishBinderService(Context.MEDIA_METRICS_SERVICE, new BinderService()); + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_MEDIA, + mContext.getMainExecutor(), + this::updateConfigs); + } + + private void updateConfigs(Properties properties) { + synchronized (mLock) { + mMode = properties.getInt( + MEDIA_METRICS_MODE, + MEDIA_METRICS_MODE_BLOCKLIST); + List newList = getListLocked(PLAYER_METRICS_APP_ALLOWLIST); + if (newList != null || mMode != MEDIA_METRICS_MODE_ALLOWLIST) { + // don't overwrite the list if the mode IS MEDIA_METRICS_MODE_ALLOWLIST + // but failed to get + mAllowlist = newList; + } + newList = getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST); + if (newList != null || mMode != MEDIA_METRICS_MODE_ALLOWLIST) { + mNoUidAllowlist = newList; + } + newList = getListLocked(PLAYER_METRICS_APP_BLOCKLIST); + if (newList != null || mMode != MEDIA_METRICS_MODE_BLOCKLIST) { + mBlockList = newList; + } + newList = getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST); + if (newList != null || mMode != MEDIA_METRICS_MODE_BLOCKLIST) { + mNoUidBlocklist = newList; + } + } + } + + @GuardedBy("mLock") + private List getListLocked(String listName) { + final long identity = Binder.clearCallingIdentity(); + String listString = FAILED_TO_GET; + try { + listString = DeviceConfig.getString( + DeviceConfig.NAMESPACE_MEDIA, listName, FAILED_TO_GET); + } finally { + Binder.restoreCallingIdentity(identity); + } + if (listString.equals(FAILED_TO_GET)) { + Slog.d(TAG, "failed to get " + listName + " from DeviceConfig"); + return null; + } + String[] pkgArr = listString.split(","); + return Arrays.asList(pkgArr); } private final class BinderService extends IMediaMetricsManager.Stub { @Override public void reportPlaybackMetrics(String sessionId, PlaybackMetrics metrics, int userId) { + int level = loggingLevel(); + if (level == LOGGING_LEVEL_BLOCKED) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(320) - .writeInt(Binder.getCallingUid()) + .writeInt(level == LOGGING_LEVEL_EVERYTHING ? Binder.getCallingUid() : 0) .writeString(sessionId) .writeLong(metrics.getMediaDurationMillis()) .writeInt(metrics.getStreamSource()) @@ -85,6 +180,10 @@ public final class MediaMetricsManagerService extends SystemService { @Override public void reportPlaybackStateEvent( String sessionId, PlaybackStateEvent event, int userId) { + int level = loggingLevel(); + if (level == LOGGING_LEVEL_BLOCKED) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(322) .writeString(sessionId) @@ -116,6 +215,10 @@ public final class MediaMetricsManagerService extends SystemService { @Override public void reportPlaybackErrorEvent( String sessionId, PlaybackErrorEvent event, int userId) { + int level = loggingLevel(); + if (level == LOGGING_LEVEL_BLOCKED) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(323) .writeString(sessionId) @@ -130,6 +233,10 @@ public final class MediaMetricsManagerService extends SystemService { public void reportNetworkEvent( String sessionId, NetworkEvent event, int userId) { + int level = loggingLevel(); + if (level == LOGGING_LEVEL_BLOCKED) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(321) .writeString(sessionId) @@ -143,6 +250,10 @@ public final class MediaMetricsManagerService extends SystemService { @Override public void reportTrackChangeEvent( String sessionId, TrackChangeEvent event, int userId) { + int level = loggingLevel(); + if (level == LOGGING_LEVEL_BLOCKED) { + return; + } StatsEvent statsEvent = StatsEvent.newBuilder() .setAtomId(324) .writeString(sessionId) @@ -165,5 +276,140 @@ public final class MediaMetricsManagerService extends SystemService { .build(); StatsLog.write(statsEvent); } + + private int loggingLevel() { + synchronized (mLock) { + int uid = Binder.getCallingUid(); + + if (mMode == null) { + final long identity = Binder.clearCallingIdentity(); + try { + mMode = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_MEDIA, + MEDIA_METRICS_MODE, + MEDIA_METRICS_MODE_BLOCKLIST); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + if (mMode == MEDIA_METRICS_MODE_ON) { + return LOGGING_LEVEL_EVERYTHING; + } + if (mMode == MEDIA_METRICS_MODE_OFF) { + return LOGGING_LEVEL_BLOCKED; + } + + PackageManager pm = getContext().getPackageManager(); + String[] packages = pm.getPackagesForUid(uid); + if (packages == null || packages.length == 0) { + // The valid application UID range is from + // android.os.Process.FIRST_APPLICATION_UID to + // android.os.Process.LAST_APPLICATION_UID. + // UIDs outside this range will not have a package. + Slog.d(TAG, "empty package from uid " + uid); + // block the data if the mode is MEDIA_METRICS_MODE_ALLOWLIST + return mMode == MEDIA_METRICS_MODE_BLOCKLIST + ? LOGGING_LEVEL_NO_UID : LOGGING_LEVEL_BLOCKED; + } + if (mMode == MEDIA_METRICS_MODE_BLOCKLIST) { + if (mBlockList == null) { + mBlockList = getListLocked(PLAYER_METRICS_APP_BLOCKLIST); + if (mBlockList == null) { + // failed to get the blocklist. Block it. + return LOGGING_LEVEL_BLOCKED; + } + } + Integer level = loggingLevelInternal( + packages, mBlockList, PLAYER_METRICS_APP_BLOCKLIST); + if (level != null) { + return level; + } + if (mNoUidBlocklist == null) { + mNoUidBlocklist = + getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST); + if (mNoUidBlocklist == null) { + // failed to get the blocklist. Block it. + return LOGGING_LEVEL_BLOCKED; + } + } + level = loggingLevelInternal( + packages, + mNoUidBlocklist, + PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST); + if (level != null) { + return level; + } + // Not detected in any blocklist. Log everything. + return LOGGING_LEVEL_EVERYTHING; + } + if (mMode == MEDIA_METRICS_MODE_ALLOWLIST) { + if (mNoUidAllowlist == null) { + mNoUidAllowlist = + getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST); + if (mNoUidAllowlist == null) { + // failed to get the allowlist. Block it. + return LOGGING_LEVEL_BLOCKED; + } + } + Integer level = loggingLevelInternal( + packages, + mNoUidAllowlist, + PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST); + if (level != null) { + return level; + } + if (mAllowlist == null) { + mAllowlist = getListLocked(PLAYER_METRICS_APP_ALLOWLIST); + if (mAllowlist == null) { + // failed to get the allowlist. Block it. + return LOGGING_LEVEL_BLOCKED; + } + } + level = loggingLevelInternal( + packages, mAllowlist, PLAYER_METRICS_APP_ALLOWLIST); + if (level != null) { + return level; + } + // Not detected in any allowlist. Block. + return LOGGING_LEVEL_BLOCKED; + } + } + // Blocked by default. + return LOGGING_LEVEL_BLOCKED; + } + + private Integer loggingLevelInternal( + String[] packages, List cached, String listName) { + if (inList(packages, cached)) { + return listNameToLoggingLevel(listName); + } + return null; + } + + private boolean inList(String[] packages, List arr) { + for (String p : packages) { + for (String element : arr) { + if (p.equals(element)) { + return true; + } + } + } + return false; + } + + private int listNameToLoggingLevel(String listName) { + switch (listName) { + case PLAYER_METRICS_APP_BLOCKLIST: + return LOGGING_LEVEL_BLOCKED; + case PLAYER_METRICS_APP_ALLOWLIST: + return LOGGING_LEVEL_EVERYTHING; + case PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST: + case PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST: + return LOGGING_LEVEL_NO_UID; + default: + return LOGGING_LEVEL_BLOCKED; + } + } } }