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..510893b2940b3 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; @@ -337,6 +338,7 @@ public final class TvInputManagerService extends SystemService { inputState = new TvInputState(); } inputState.info = info; + inputState.uid = getInputUid(info); inputMap.put(info.getId(), inputState); } @@ -371,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); @@ -405,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) { @@ -474,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; } @@ -535,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); @@ -715,7 +727,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 +751,7 @@ public final class TvInputManagerService extends SystemService { } } removeSessionStateLocked(sessionToken, userId); + return sessionState; } @GuardedBy("mLock") @@ -908,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) { @@ -1248,7 +1263,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) { @@ -1269,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); } @@ -1301,6 +1333,8 @@ public final class TvInputManagerService extends SystemService { } else { updateServiceConnectionLocked(info.getComponent(), resolvedUserId); } + logTuneStateChanged(FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__CREATED, + sessionState, inputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1317,8 +1351,17 @@ public final class TvInputManagerService extends SystemService { userId, "releaseSession"); final long identity = Binder.clearCallingIdentity(); try { + SessionState sessionState = null; + UserState userState = null; synchronized (mLock) { - releaseSessionLocked(sessionToken, callingUid, resolvedUserId); + 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, tvInputState); } } finally { Binder.restoreCallingIdentity(identity); @@ -1372,10 +1415,13 @@ public final class TvInputManagerService extends SystemService { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, userId, "setSurface"); final long identity = Binder.clearCallingIdentity(); + SessionState sessionState = null; + UserState userState = null; try { synchronized (mLock) { try { - SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + userState = getUserStateLocked(userId); + sessionState = getSessionStateLocked(sessionToken, callingUid, resolvedUserId); if (sessionState.hardwareSessionToken == null) { getSessionLocked(sessionState).setSurface(surface); @@ -1392,6 +1438,14 @@ 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, + TvInputManagerService.getTvInputState(sessionState, userState)); + } Binder.restoreCallingIdentity(identity); } } @@ -1479,6 +1533,10 @@ public final class TvInputManagerService extends SystemService { return; } + logTuneStateChanged( + FrameworkStatsLog.TIF_TUNE_STATE_CHANGED__STATE__TUNE_STARTED, + sessionState, + TvInputManagerService.getTvInputState(sessionState, userState)); // Log the start of watch. SomeArgs args = SomeArgs.obtain(); args.arg1 = sessionState.componentName.getPackageName(); @@ -2302,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) { @@ -2367,6 +2435,28 @@ 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, + @Nullable TvInputState inputState) { + // TODO(b/173536904): log input type and id + FrameworkStatsLog.write(FrameworkStatsLog.TIF_TUNE_CHANGED, + 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 { // A mapping from the TV input id to its TvInputState. private Map inputMap = new HashMap<>(); @@ -2464,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 @@ -2478,6 +2583,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: + * + *

    + *
  • framework/base/cmds/statsd/src/atoms.proto#TifTuneState + *
  • {@link #logTuneStateChanged} + *
  • {@link TvInputManagerService.BinderService#createSession} + *
  • {@link SessionState#sessionId} + *
+ */ private final String sessionId; private final ComponentName componentName; private final boolean isRecordingSession; @@ -2545,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); @@ -2815,8 +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, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoAvailable", e); } @@ -2832,8 +2959,20 @@ 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 + .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, tvInputState); } catch (RemoteException e) { Slog.e(TAG, "error in onVideoUnavailable", e); } @@ -3019,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.