From 1fd8b5675e090a180406be8f6f3e48c310a2ef3f Mon Sep 17 00:00:00 2001 From: Nick Chalko Date: Wed, 12 Aug 2020 09:40:13 -0700 Subject: [PATCH 1/2] Add TifTuneStateChanged atom. This is the base atom for all TIF metrics Test: ./out/host/linux-x86/bin/statsd_testdrive Bug: 165369586 Change-Id: I2e5c3e7f51abd2700611fcc0eaef91ec029513c8 --- cmds/statsd/src/atoms.proto | 35 +++++++ core/proto/android/stats/tv/tif_enums.proto | 56 +++++++++++ .../server/tv/TvInputManagerService.java | 95 ++++++++++++++++++- 3 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 core/proto/android/stats/tv/tif_enums.proto diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 0f60eaebd59f6..d616d3d5f663c 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -60,6 +60,7 @@ import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto"; import "frameworks/base/core/proto/android/stats/tls/enums.proto"; +import "frameworks/base/core/proto/android/stats/tv/tif_enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; import "frameworks/base/core/proto/android/telephony/enums.proto"; import "frameworks/base/core/proto/android/view/enums.proto"; @@ -510,6 +511,7 @@ message Atom { MediaPlaybackTrackChanged media_playback_track_changed = 324; WifiScanReported wifi_scan_reported = 325 [(module) = "wifi"]; WifiPnoScanReported wifi_pno_scan_reported = 326 [(module) = "wifi"]; + TifTuneStateChanged tif_tune_changed = 327 [(module) = "framework"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -10351,6 +10353,39 @@ message CellBroadcastMessageError { optional string exception_message = 2; } +/** + * Logs when a TV Input Service Session changes tune state + * This is atom ID 327. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java + */ +message TifTuneStateChanged { + + // Tv Input Service uid, TV Player uid + repeated AttributionNode attribution_node = 1 [ + (state_field_option).primary_field_first_uid = true + ]; + optional android.stats.tv.TifTuneState state = 2 [ + (state_field_option).exclusive_state = true, + (state_field_option).default_state_value = 0, + (state_field_option).nested = false + ]; + // This a globally unique 128 bit random number created by TvInputManagerService when + // android.media.tv.TvInputManager#createSession is called. + // It is has no device or user association. + // See android.media.tv.TvInputService.onCreateSession(java.lang.String, java.lang.String) + // WARNING: Any changes to this field should be carefully reviewed for privacy. + // Inspect the code at + // framework/base/cmds/statsd/src/atoms.proto + // TifTuneState + // frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java + // logTuneStateChanged + // BinderService.createSession + // SessionState.sessionId + optional string tif_session_id = 3 [(state_field_option).primary_field = true]; +} + /** * Logs when a tune occurs through device's Frontend. * This is atom ID 276. diff --git a/core/proto/android/stats/tv/tif_enums.proto b/core/proto/android/stats/tv/tif_enums.proto new file mode 100644 index 0000000000000..a9028e5291860 --- /dev/null +++ b/core/proto/android/stats/tv/tif_enums.proto @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 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. + */ + +syntax = "proto2"; + +package android.stats.tv; +option java_multiple_files = true; + +// Enums for TV Input Framework +option java_outer_classname = "TifStatsEnums"; + +// Tune State of a TV Input Service Framework +enum TifTuneState { + TIF_TUNE_STATE_UNKNOWN = 0; + CREATED = 1; + SURFACE_ATTACHED = 2; + SURFACE_DETACHED = 3; + RELEASED = 4; + TUNE_STARTED = 5; + VIDEO_AVAILABLE = 6; + + // Keep in sync with TvInputManager + // Use the TvInputManager value + 100 + VIDEO_UNAVAILABLE_REASON_UNKNOWN = 100; + VIDEO_UNAVAILABLE_REASON_TUNING = 101; + VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 102; + VIDEO_UNAVAILABLE_REASON_BUFFERING = 103; + VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 104; + VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 105; + VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 106; + VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION=107; + VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED=108; + VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE=109; + VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED=110; + VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION=111; + VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING=112; + VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD=113; + VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE=114; + VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID=115; + VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT=116; + VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING=117; + VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN=118; +} diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index afbe5527a5cbc..4e35e577ccfea 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -87,6 +87,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.SystemService; @@ -715,7 +716,8 @@ public final class TvInputManagerService extends SystemService { } @GuardedBy("mLock") - private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { + @Nullable + private SessionState releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { SessionState sessionState = null; try { sessionState = getSessionStateLocked(sessionToken, callingUid, userId); @@ -738,6 +740,7 @@ public final class TvInputManagerService extends SystemService { } } removeSessionStateLocked(sessionToken, userId); + return sessionState; } @GuardedBy("mLock") @@ -1248,7 +1251,22 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "createSession"); final long identity = Binder.clearCallingIdentity(); - // Generate a unique session id with a random UUID. + /** + * A randomly generated id for this this session. + * + *

This field contains no user or device reference and is large enough to be + * effectively globally unique. + * + *

WARNING Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + *

+ */ String uniqueSessionId = UUID.randomUUID().toString(); try { synchronized (mLock) { @@ -1301,6 +1319,8 @@ public final class TvInputManagerService extends SystemService { } else { updateServiceConnectionLocked(info.getComponent(), resolvedUserId); } + logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED, + sessionState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1317,8 +1337,13 @@ public final class TvInputManagerService extends SystemService { userId, "releaseSession"); final long identity = Binder.clearCallingIdentity(); try { + SessionState sessionState = null; synchronized (mLock) { - releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + } + if (sessionState != null) { + logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED, + sessionState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1372,10 +1397,11 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, userId, "setSurface"); final long identity = Binder.clearCallingIdentity(); + SessionState sessionState = null; try { synchronized (mLock) { try { - SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + sessionState = getSessionStateLocked(sessionToken, callingUid, resolvedUserId); if (sessionState.hardwareSessionToken == null) { getSessionLocked(sessionState).setSurface(surface); @@ -1392,6 +1418,13 @@ public final class TvInputManagerService extends SystemService { // surface is not used in TvInputManagerService. surface.release(); } + if (sessionState != null) { + int state = surface == null + ? + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED + : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED; + logTuneStateChanged(state, sessionState); + } Binder.restoreCallingIdentity(identity); } } @@ -1479,6 +1512,9 @@ public final class TvInputManagerService extends SystemService { return; } + logTuneStateChanged( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED, + sessionState); // Log the start of watch. SomeArgs args = SomeArgs.obtain(); args.arg1 = sessionState.componentName.getPackageName(); @@ -2367,6 +2403,27 @@ public final class TvInputManagerService extends SystemService { } } + /** + * Log Tune state changes to {@link FrameworkStatsLog}. + * + *

WARNING Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + *

+ */ + private void logTuneStateChanged(int state, SessionState sessionState) { + // TODO(b/165372105): log the tis uid. + // TODO(b/173536904): log input type and id + FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED, + new int[]{sessionState.callingUid}, + new String[]{"tif_player"}, state, sessionState.sessionId); + } + private static final class UserState { // A mapping from the TV input id to its TvInputState. private Map inputMap = new HashMap<>(); @@ -2478,6 +2535,23 @@ public final class TvInputManagerService extends SystemService { private final class SessionState implements IBinder.DeathRecipient { private final String inputId; + + /** + * A randomly generated id for this this session. + * + *

This field contains no user or device reference and is large enough to be + * effectively globally unique. + * + *

WARNING Any changes to this field should be carefully reviewed for privacy. + * Inspect the code at: + * + *

+ */ private final String sessionId; private final ComponentName componentName; private final boolean isRecordingSession; @@ -2817,6 +2891,9 @@ public final class TvInputManagerService extends SystemService { } try { mSessionState.client.onVideoAvailable(mSessionState.seq); + logTuneStateChanged( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE, + mSessionState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoAvailable", e); } @@ -2834,6 +2911,16 @@ public final class TvInputManagerService extends SystemService { } try { mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); + int loggedReason = reason + FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + if (loggedReason < FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN + || loggedReason > FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN) { + loggedReason = FrameworkStatsLog + .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; + } + logTuneStateChanged(loggedReason, mSessionState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoUnavailable", e); } From d2df703638c88160cc2e1656e770d10806af4382 Mon Sep 17 00:00:00 2001 From: Nick Chalko Date: Tue, 10 Nov 2020 15:58:03 -0800 Subject: [PATCH 2/2] Include the TV Input Service uid in metrics logging. Change-Id: I0a8801248e120e60df368273b91b9d87d00e2496 Bug: 165372105 Test: ./out/host/linux-x86/bin/statsd_testdrive --- .../server/tv/TvInputManagerService.java | 88 +++++++++++++++---- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 4e35e577ccfea..510893b2940b3 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -338,6 +338,7 @@ public final class TvInputManagerService extends SystemService { inputState = new TvInputState(); } inputState.info = info; + inputState.uid = getInputUid(info); inputMap.put(info.getId(), inputState); } @@ -372,6 +373,16 @@ public final class TvInputManagerService extends SystemService { userState.inputMap = inputMap; } + private int getInputUid(TvInputInfo info) { + try { + return getContext().getPackageManager().getApplicationInfo( + info.getServiceInfo().packageName, 0).uid; + } catch (NameNotFoundException e) { + Slog.w(TAG, "Unable to get UID for " + info, e); + return Process.INVALID_UID; + } + } + @GuardedBy("mLock") private void buildTvContentRatingSystemListLocked(int userId) { UserState userState = getOrCreateUserStateLocked(userId); @@ -406,7 +417,7 @@ public final class TvInputManagerService extends SystemService { return; } if (mUserStates.contains(mCurrentUserId)) { - UserState userState = mUserStates.get(mCurrentUserId); + UserState userState = getUserStateLocked(mCurrentUserId); List sessionStatesToRelease = new ArrayList<>(); for (SessionState sessionState : userState.sessionStateMap.values()) { if (sessionState.session != null && !sessionState.isRecordingSession) { @@ -475,7 +486,7 @@ public final class TvInputManagerService extends SystemService { private void removeUser(int userId) { synchronized (mLock) { - UserState userState = mUserStates.get(userId); + UserState userState = getUserStateLocked(userId); if (userState == null) { return; } @@ -536,7 +547,7 @@ public final class TvInputManagerService extends SystemService { @GuardedBy("mLock") private UserState getOrCreateUserStateLocked(int userId) { - UserState userState = mUserStates.get(userId); + UserState userState = getUserStateLocked(userId); if (userState == null) { userState = new UserState(mContext, userId); mUserStates.put(userId, userState); @@ -911,6 +922,7 @@ public final class TvInputManagerService extends SystemService { return; } inputState.info = inputInfo; + inputState.uid = getInputUid(inputInfo); int n = userState.mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { @@ -1287,6 +1299,8 @@ public final class TvInputManagerService extends SystemService { TvInputInfo info = inputState.info; ServiceState serviceState = userState.serviceStateMap.get(info.getComponent()); if (serviceState == null) { + int tisUid = PackageManager.getApplicationInfoAsUserCached( + info.getComponent().getPackageName(), 0, resolvedUserId).uid; serviceState = new ServiceState(info.getComponent(), resolvedUserId); userState.serviceStateMap.put(info.getComponent(), serviceState); } @@ -1320,7 +1334,7 @@ public final class TvInputManagerService extends SystemService { updateServiceConnectionLocked(info.getComponent(), resolvedUserId); } logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED, - sessionState); + sessionState, inputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1338,12 +1352,16 @@ public final class TvInputManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { SessionState sessionState = null; + UserState userState = null; synchronized (mLock) { sessionState = releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + userState = getUserStateLocked(userId); } if (sessionState != null) { + TvInputState tvInputState = TvInputManagerService.getTvInputState(sessionState, + userState); logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__RELEASED, - sessionState); + sessionState, tvInputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1398,9 +1416,11 @@ public final class TvInputManagerService extends SystemService { userId, "setSurface"); final long identity = Binder.clearCallingIdentity(); SessionState sessionState = null; + UserState userState = null; try { synchronized (mLock) { try { + userState = getUserStateLocked(userId); sessionState = getSessionStateLocked(sessionToken, callingUid, resolvedUserId); if (sessionState.hardwareSessionToken == null) { @@ -1423,7 +1443,8 @@ public final class TvInputManagerService extends SystemService { ? FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_ATTACHED : FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__SURFACE_DETACHED; - logTuneStateChanged(state, sessionState); + logTuneStateChanged(state, sessionState, + TvInputManagerService.getTvInputState(sessionState, userState)); } Binder.restoreCallingIdentity(identity); } @@ -1514,7 +1535,8 @@ public final class TvInputManagerService extends SystemService { logTuneStateChanged( FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED, - sessionState); + sessionState, + TvInputManagerService.getTvInputState(sessionState, userState)); // Log the start of watch. SomeArgs args = SomeArgs.obtain(); args.arg1 = sessionState.componentName.getPackageName(); @@ -2338,6 +2360,16 @@ public final class TvInputManagerService extends SystemService { } } + @Nullable + private static TvInputState getTvInputState( + SessionState sessionState, + @Nullable UserState userState) { + if (userState != null) { + return userState.inputMap.get(sessionState.inputId); + } + return null; + } + @GuardedBy("mLock") private List getCurrentTunedInfosInternalLocked( UserState userState, int callingPid, int callingUid) { @@ -2416,12 +2448,13 @@ public final class TvInputManagerService extends SystemService { *
  • {@link SessionState#sessionId} * */ - private void logTuneStateChanged(int state, SessionState sessionState) { - // TODO(b/165372105): log the tis uid. + private void logTuneStateChanged(int state, SessionState sessionState, + @Nullable TvInputState inputState) { // TODO(b/173536904): log input type and id FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED, - new int[]{sessionState.callingUid}, - new String[]{"tif_player"}, state, sessionState.sessionId); + new int[]{sessionState.callingUid, + inputState == null ? Process.INVALID_UID : inputState.uid}, + new String[]{"tif_player", "tv_input_service"}, state, sessionState.sessionId); } private static final class UserState { @@ -2521,10 +2554,25 @@ public final class TvInputManagerService extends SystemService { } private static final class TvInputState { - // A TvInputInfo object which represents the TV input. + + /** A TvInputInfo object which represents the TV input. */ private TvInputInfo info; - // The state of TV input. Connected by default. + /** + * The kernel user-ID that has been assigned to the application the TvInput is a part of. + * + *

    + * Currently this is not a unique ID (multiple applications can have + * the same uid). + */ + private int uid; + + /** + * The state of TV input. + * + *

    + * Connected by default + */ private int state = INPUT_STATE_CONNECTED; @Override @@ -2619,7 +2667,7 @@ public final class TvInputManagerService extends SystemService { Slog.d(TAG, "onServiceConnected(component=" + component + ")"); } synchronized (mLock) { - UserState userState = mUserStates.get(mUserId); + UserState userState = getUserStateLocked(mUserId); if (userState == null) { // The user was removed while connecting. mContext.unbindService(this); @@ -2889,11 +2937,13 @@ public final class TvInputManagerService extends SystemService { if (mSessionState.session == null || mSessionState.client == null) { return; } + TvInputState tvInputState = getTvInputState(mSessionState, + getUserStateLocked(mCurrentUserId)); try { mSessionState.client.onVideoAvailable(mSessionState.seq); logTuneStateChanged( FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__VIDEO_AVAILABLE, - mSessionState); + mSessionState, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoAvailable", e); } @@ -2909,6 +2959,8 @@ public final class TvInputManagerService extends SystemService { if (mSessionState.session == null || mSessionState.client == null) { return; } + TvInputState tvInputState = getTvInputState(mSessionState, + getUserStateLocked(mCurrentUserId)); try { mSessionState.client.onVideoUnavailable(reason, mSessionState.seq); int loggedReason = reason + FrameworkStatsLog @@ -2920,7 +2972,7 @@ public final class TvInputManagerService extends SystemService { loggedReason = FrameworkStatsLog .TIF_TUNE_STATE_CHANGED__STATE__VIDEO_UNAVAILABLE_REASON_UNKNOWN; } - logTuneStateChanged(loggedReason, mSessionState); + logTuneStateChanged(loggedReason, mSessionState, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoUnavailable", e); } @@ -3106,6 +3158,10 @@ public final class TvInputManagerService extends SystemService { } } + private UserState getUserStateLocked(int userId) { + return mUserStates.get(userId); + } + private static final class WatchLogHandler extends Handler { // There are only two kinds of watch events that can happen on the system: // 1. The current TV input session is tuned to a new channel.