Loading media/java/android/media/tv/TvChannelInfo.java +70 −2 Original line number Diff line number Diff line Loading @@ -22,19 +22,44 @@ import android.annotation.Nullable; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** * This class is used to specify information of a TV channel. * @hide */ public final class TvChannelInfo implements Parcelable { static final String TAG = "TvChannelInfo"; /** * App tag for {@link #getAppTag()}: the corresponding application of the channel is the same as * the caller. * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is * {@link #APP_TAG_SELF}. */ public static final int APP_TAG_SELF = 0; /** * App tag for {@link #getAppType()}: the corresponding application of the channel is the same * as the caller. * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is * {@link #APP_TAG_SELF}. */ public static final int APP_TYPE_SELF = 1; /** * App tag for {@link #getAppType()}: the corresponding app of the channel is a system * application. */ public static final int APP_TYPE_SYSTEM = 2; /** * App tag for {@link #getAppType()}: the corresponding app of the channel is not a system * application. */ public static final int APP_TYPE_NON_SYSTEM = 3; /** @hide */ Loading Loading @@ -68,6 +93,7 @@ public final class TvChannelInfo implements Parcelable { @AppType private final int mAppType; private final int mAppTag; /** @hide */ public TvChannelInfo( String inputId, @Nullable Uri channelUri, boolean isRecordingSession, boolean isForeground, @AppType int appType, int appTag) { Loading @@ -90,24 +116,41 @@ public final class TvChannelInfo implements Parcelable { mAppTag = source.readInt(); } /** * Returns the TV input ID of the channel. */ @NonNull public String getInputId() { return mInputId; } /** * Returns the channel URI of the channel. * <p>Returns {@code null} if it's a passthrough input or the permission is not granted. */ @Nullable public Uri getChannelUri() { return mChannelUri; } /** * Returns {@code true} if the channel session is a recording session. * @see TvInputService.RecordingSession */ public boolean isRecordingSession() { return mIsRecordingSession; } /** * Returns {@code true} if the application is a foreground application. * @see android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND */ public boolean isForeground() { return mIsForeground; } /** * Gets app tag. * Returns the app tag. * <p>App tag is used to differentiate one app from another. * {@link #APP_TAG_SELF} is for current app. */ Loading @@ -115,6 +158,9 @@ public final class TvChannelInfo implements Parcelable { return mAppTag; } /** * Returns the app type. */ @AppType public int getAppType() { return mAppType; Loading @@ -126,7 +172,7 @@ public final class TvChannelInfo implements Parcelable { } @Override public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mInputId); String uriString = mChannelUri == null ? null : mChannelUri.toString(); dest.writeString(uriString); Loading @@ -145,4 +191,26 @@ public final class TvChannelInfo implements Parcelable { + ";appType=" + mAppType + ";appTag=" + mAppTag; } @Override public boolean equals(Object o) { if (!(o instanceof TvChannelInfo)) { return false; } TvChannelInfo other = (TvChannelInfo) o; return TextUtils.equals(mInputId, other.getInputId()) && Objects.equals(mChannelUri, other.mChannelUri) && mIsRecordingSession == other.mIsRecordingSession && mIsForeground == other.mIsForeground && mAppType == other.mAppType && mAppTag == other.mAppTag; } @Override public int hashCode() { return Objects.hash( mInputId, mChannelUri, mIsRecordingSession, mIsForeground, mAppType, mAppTag); } } media/java/android/media/tv/TvInputManager.java +14 −2 Original line number Diff line number Diff line Loading @@ -900,8 +900,13 @@ public final class TvInputManager { public void onTvInputInfoUpdated(TvInputInfo inputInfo) { } /** @hide */ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> tvChannelInfos) { /** * This is called when the information about current TV channels has been updated. * * @param tvChannelInfos a list of {@link TvChannelInfo} objects of new current channels. * @hide */ public void onCurrentTvChannelInfosUpdated(@NonNull List<TvChannelInfo> tvChannelInfos) { } } Loading Loading @@ -1976,8 +1981,15 @@ public final class TvInputManager { } /** * Returns the list of TV channel information for {@link TvInputService.Session} that are * currently in use. * <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get * the channel URIs. If the permission is not granted, {@link TvChannelInfo#getChannelUri()} * returns {@code null}. * @hide */ @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") @NonNull public List<TvChannelInfo> getCurrentTvChannelInfos() { try { return mService.getCurrentTvChannelInfos(mUserId); Loading services/core/java/com/android/server/tv/TvInputManagerService.java +36 −28 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.InputChannel; Loading Loading @@ -709,8 +710,7 @@ public final class TvInputManagerService extends SystemService { } sessionState.isCurrent = false; sessionState.currentChannel = null; notifyCurrentChannelInfosUpdatedLocked( userState, getCurrentTvChannelInfosInternalLocked(userState)); notifyCurrentChannelInfosUpdatedLocked(userState); } catch (RemoteException | SessionNotFoundException e) { Slog.e(TAG, "error in releaseSession", e); } finally { Loading Loading @@ -851,15 +851,18 @@ public final class TvInputManagerService extends SystemService { } } private void notifyCurrentChannelInfosUpdatedLocked( UserState userState, List<TvChannelInfo> infos) { private void notifyCurrentChannelInfosUpdatedLocked(UserState userState) { if (DEBUG) { Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked"); } int n = userState.mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { userState.mCallbacks.getBroadcastItem(i).onCurrentTvChannelInfosUpdated(infos); ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i); Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback); List<TvChannelInfo> infos = getCurrentTvChannelInfosInternalLocked( userState, pidUid.first, pidUid.second); callback.onCurrentTvChannelInfosUpdated(infos); } catch (RemoteException e) { Slog.e(TAG, "failed to report updated current channel infos to callback", e); } Loading Loading @@ -1063,14 +1066,19 @@ public final class TvInputManagerService extends SystemService { @Override public void registerCallback(final ITvInputManagerCallback callback, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "registerCallback"); int callingPid = Binder.getCallingPid(); int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "registerCallback"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { final UserState userState = getOrCreateUserStateLocked(resolvedUserId); if (!userState.mCallbacks.register(callback)) { Slog.e(TAG, "client process has already died"); } else { userState.callbackPidUidMap.put( callback, Pair.create(callingPid, callingUid)); } } } finally { Loading @@ -1087,6 +1095,7 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(resolvedUserId); userState.mCallbacks.unregister(callback); userState.callbackPidUidMap.remove(callback); } } finally { Binder.restoreCallingIdentity(identity); Loading Loading @@ -1419,8 +1428,8 @@ public final class TvInputManagerService extends SystemService { @Override public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, userId, "tune"); final int callingPid = Binder.getCallingPid(); final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "tune"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Loading @@ -1432,8 +1441,7 @@ public final class TvInputManagerService extends SystemService { if (sessionState != null) { sessionState.isCurrent = true; sessionState.currentChannel = channelUri; notifyCurrentChannelInfosUpdatedLocked( userState, getCurrentTvChannelInfosInternalLocked(userState)); notifyCurrentChannelInfosUpdatedLocked(userState); } if (TvContract.isChannelUriForPassthroughInput(channelUri)) { // Do not log the watch history for passthrough inputs. Loading Loading @@ -2090,16 +2098,13 @@ public final class TvInputManagerService extends SystemService { @Override public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "getTvCurrentChannelInfos"); final long identity = Binder.clearCallingIdentity(); try { int callingPid = Binder.getCallingPid(); int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "getTvCurrentChannelInfos"); synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(resolvedUserId); return getCurrentTvChannelInfosInternalLocked(userState); } } finally { Binder.restoreCallingIdentity(identity); return getCurrentTvChannelInfosInternalLocked(userState, callingPid, callingUid); } } Loading Loading @@ -2273,14 +2278,15 @@ public final class TvInputManagerService extends SystemService { } } private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(UserState userState) { private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked( UserState userState, int callingPid, int callingUid) { List<TvChannelInfo> channelInfos = new ArrayList<>(); boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(); boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid); for (SessionState state : userState.sessionStateMap.values()) { if (state.isCurrent) { Integer appTag; int appType; if (state.callingUid == Binder.getCallingUid()) { if (state.callingUid == callingUid) { appTag = APP_TAG_SELF; appType = TvChannelInfo.APP_TYPE_SELF; } else { Loading Loading @@ -2322,8 +2328,8 @@ public final class TvInputManagerService extends SystemService { return false; } private boolean hasAccessWatchedProgramsPermission() { return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS) private boolean hasAccessWatchedProgramsPermission(int callingPid, int callingUid) { return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; } Loading Loading @@ -2360,6 +2366,9 @@ public final class TvInputManagerService extends SystemService { private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks = new RemoteCallbackList<ITvInputManagerCallback>(); private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap = new HashMap<>(); // The token of a "main" TV input session. private IBinder mainSessionToken = null; Loading Loading @@ -2712,8 +2721,7 @@ public final class TvInputManagerService extends SystemService { mSessionState.isCurrent = true; mSessionState.currentChannel = channelUri; UserState userState = getOrCreateUserStateLocked(mSessionState.userId); notifyCurrentChannelInfosUpdatedLocked( userState, getCurrentTvChannelInfosInternalLocked(userState)); notifyCurrentChannelInfosUpdatedLocked(userState); try { // TODO: Consider adding this channel change in the watch log. When we do // that, how we can protect the watch log from malicious tv inputs should Loading Loading
media/java/android/media/tv/TvChannelInfo.java +70 −2 Original line number Diff line number Diff line Loading @@ -22,19 +22,44 @@ import android.annotation.Nullable; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** * This class is used to specify information of a TV channel. * @hide */ public final class TvChannelInfo implements Parcelable { static final String TAG = "TvChannelInfo"; /** * App tag for {@link #getAppTag()}: the corresponding application of the channel is the same as * the caller. * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is * {@link #APP_TAG_SELF}. */ public static final int APP_TAG_SELF = 0; /** * App tag for {@link #getAppType()}: the corresponding application of the channel is the same * as the caller. * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is * {@link #APP_TAG_SELF}. */ public static final int APP_TYPE_SELF = 1; /** * App tag for {@link #getAppType()}: the corresponding app of the channel is a system * application. */ public static final int APP_TYPE_SYSTEM = 2; /** * App tag for {@link #getAppType()}: the corresponding app of the channel is not a system * application. */ public static final int APP_TYPE_NON_SYSTEM = 3; /** @hide */ Loading Loading @@ -68,6 +93,7 @@ public final class TvChannelInfo implements Parcelable { @AppType private final int mAppType; private final int mAppTag; /** @hide */ public TvChannelInfo( String inputId, @Nullable Uri channelUri, boolean isRecordingSession, boolean isForeground, @AppType int appType, int appTag) { Loading @@ -90,24 +116,41 @@ public final class TvChannelInfo implements Parcelable { mAppTag = source.readInt(); } /** * Returns the TV input ID of the channel. */ @NonNull public String getInputId() { return mInputId; } /** * Returns the channel URI of the channel. * <p>Returns {@code null} if it's a passthrough input or the permission is not granted. */ @Nullable public Uri getChannelUri() { return mChannelUri; } /** * Returns {@code true} if the channel session is a recording session. * @see TvInputService.RecordingSession */ public boolean isRecordingSession() { return mIsRecordingSession; } /** * Returns {@code true} if the application is a foreground application. * @see android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND */ public boolean isForeground() { return mIsForeground; } /** * Gets app tag. * Returns the app tag. * <p>App tag is used to differentiate one app from another. * {@link #APP_TAG_SELF} is for current app. */ Loading @@ -115,6 +158,9 @@ public final class TvChannelInfo implements Parcelable { return mAppTag; } /** * Returns the app type. */ @AppType public int getAppType() { return mAppType; Loading @@ -126,7 +172,7 @@ public final class TvChannelInfo implements Parcelable { } @Override public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mInputId); String uriString = mChannelUri == null ? null : mChannelUri.toString(); dest.writeString(uriString); Loading @@ -145,4 +191,26 @@ public final class TvChannelInfo implements Parcelable { + ";appType=" + mAppType + ";appTag=" + mAppTag; } @Override public boolean equals(Object o) { if (!(o instanceof TvChannelInfo)) { return false; } TvChannelInfo other = (TvChannelInfo) o; return TextUtils.equals(mInputId, other.getInputId()) && Objects.equals(mChannelUri, other.mChannelUri) && mIsRecordingSession == other.mIsRecordingSession && mIsForeground == other.mIsForeground && mAppType == other.mAppType && mAppTag == other.mAppTag; } @Override public int hashCode() { return Objects.hash( mInputId, mChannelUri, mIsRecordingSession, mIsForeground, mAppType, mAppTag); } }
media/java/android/media/tv/TvInputManager.java +14 −2 Original line number Diff line number Diff line Loading @@ -900,8 +900,13 @@ public final class TvInputManager { public void onTvInputInfoUpdated(TvInputInfo inputInfo) { } /** @hide */ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> tvChannelInfos) { /** * This is called when the information about current TV channels has been updated. * * @param tvChannelInfos a list of {@link TvChannelInfo} objects of new current channels. * @hide */ public void onCurrentTvChannelInfosUpdated(@NonNull List<TvChannelInfo> tvChannelInfos) { } } Loading Loading @@ -1976,8 +1981,15 @@ public final class TvInputManager { } /** * Returns the list of TV channel information for {@link TvInputService.Session} that are * currently in use. * <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get * the channel URIs. If the permission is not granted, {@link TvChannelInfo#getChannelUri()} * returns {@code null}. * @hide */ @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") @NonNull public List<TvChannelInfo> getCurrentTvChannelInfos() { try { return mService.getCurrentTvChannelInfos(mUserId); Loading
services/core/java/com/android/server/tv/TvInputManagerService.java +36 −28 Original line number Diff line number Diff line Loading @@ -77,6 +77,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.InputChannel; Loading Loading @@ -709,8 +710,7 @@ public final class TvInputManagerService extends SystemService { } sessionState.isCurrent = false; sessionState.currentChannel = null; notifyCurrentChannelInfosUpdatedLocked( userState, getCurrentTvChannelInfosInternalLocked(userState)); notifyCurrentChannelInfosUpdatedLocked(userState); } catch (RemoteException | SessionNotFoundException e) { Slog.e(TAG, "error in releaseSession", e); } finally { Loading Loading @@ -851,15 +851,18 @@ public final class TvInputManagerService extends SystemService { } } private void notifyCurrentChannelInfosUpdatedLocked( UserState userState, List<TvChannelInfo> infos) { private void notifyCurrentChannelInfosUpdatedLocked(UserState userState) { if (DEBUG) { Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked"); } int n = userState.mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { userState.mCallbacks.getBroadcastItem(i).onCurrentTvChannelInfosUpdated(infos); ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i); Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback); List<TvChannelInfo> infos = getCurrentTvChannelInfosInternalLocked( userState, pidUid.first, pidUid.second); callback.onCurrentTvChannelInfosUpdated(infos); } catch (RemoteException e) { Slog.e(TAG, "failed to report updated current channel infos to callback", e); } Loading Loading @@ -1063,14 +1066,19 @@ public final class TvInputManagerService extends SystemService { @Override public void registerCallback(final ITvInputManagerCallback callback, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "registerCallback"); int callingPid = Binder.getCallingPid(); int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "registerCallback"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { final UserState userState = getOrCreateUserStateLocked(resolvedUserId); if (!userState.mCallbacks.register(callback)) { Slog.e(TAG, "client process has already died"); } else { userState.callbackPidUidMap.put( callback, Pair.create(callingPid, callingUid)); } } } finally { Loading @@ -1087,6 +1095,7 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(resolvedUserId); userState.mCallbacks.unregister(callback); userState.callbackPidUidMap.remove(callback); } } finally { Binder.restoreCallingIdentity(identity); Loading Loading @@ -1419,8 +1428,8 @@ public final class TvInputManagerService extends SystemService { @Override public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, userId, "tune"); final int callingPid = Binder.getCallingPid(); final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "tune"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Loading @@ -1432,8 +1441,7 @@ public final class TvInputManagerService extends SystemService { if (sessionState != null) { sessionState.isCurrent = true; sessionState.currentChannel = channelUri; notifyCurrentChannelInfosUpdatedLocked( userState, getCurrentTvChannelInfosInternalLocked(userState)); notifyCurrentChannelInfosUpdatedLocked(userState); } if (TvContract.isChannelUriForPassthroughInput(channelUri)) { // Do not log the watch history for passthrough inputs. Loading Loading @@ -2090,16 +2098,13 @@ public final class TvInputManagerService extends SystemService { @Override public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "getTvCurrentChannelInfos"); final long identity = Binder.clearCallingIdentity(); try { int callingPid = Binder.getCallingPid(); int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "getTvCurrentChannelInfos"); synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(resolvedUserId); return getCurrentTvChannelInfosInternalLocked(userState); } } finally { Binder.restoreCallingIdentity(identity); return getCurrentTvChannelInfosInternalLocked(userState, callingPid, callingUid); } } Loading Loading @@ -2273,14 +2278,15 @@ public final class TvInputManagerService extends SystemService { } } private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(UserState userState) { private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked( UserState userState, int callingPid, int callingUid) { List<TvChannelInfo> channelInfos = new ArrayList<>(); boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(); boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid); for (SessionState state : userState.sessionStateMap.values()) { if (state.isCurrent) { Integer appTag; int appType; if (state.callingUid == Binder.getCallingUid()) { if (state.callingUid == callingUid) { appTag = APP_TAG_SELF; appType = TvChannelInfo.APP_TYPE_SELF; } else { Loading Loading @@ -2322,8 +2328,8 @@ public final class TvInputManagerService extends SystemService { return false; } private boolean hasAccessWatchedProgramsPermission() { return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS) private boolean hasAccessWatchedProgramsPermission(int callingPid, int callingUid) { return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; } Loading Loading @@ -2360,6 +2366,9 @@ public final class TvInputManagerService extends SystemService { private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks = new RemoteCallbackList<ITvInputManagerCallback>(); private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap = new HashMap<>(); // The token of a "main" TV input session. private IBinder mainSessionToken = null; Loading Loading @@ -2712,8 +2721,7 @@ public final class TvInputManagerService extends SystemService { mSessionState.isCurrent = true; mSessionState.currentChannel = channelUri; UserState userState = getOrCreateUserStateLocked(mSessionState.userId); notifyCurrentChannelInfosUpdatedLocked( userState, getCurrentTvChannelInfosInternalLocked(userState)); notifyCurrentChannelInfosUpdatedLocked(userState); try { // TODO: Consider adding this channel change in the watch log. When we do // that, how we can protect the watch log from malicious tv inputs should Loading