diff options
| -rw-r--r-- | media/java/android/media/tv/TvChannelInfo.java | 72 | ||||
| -rw-r--r-- | media/java/android/media/tv/TvInputManager.java | 16 | ||||
| -rwxr-xr-x | services/core/java/com/android/server/tv/TvInputManagerService.java | 64 |
3 files changed, 120 insertions, 32 deletions
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TvChannelInfo.java index 635b13045921..11cb1f7cd0bd 100644 --- a/media/java/android/media/tv/TvChannelInfo.java +++ b/media/java/android/media/tv/TvChannelInfo.java @@ -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 */ @@ -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) { @@ -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. */ @@ -115,6 +158,9 @@ public final class TvChannelInfo implements Parcelable { return mAppTag; } + /** + * Returns the app type. + */ @AppType public int getAppType() { return mAppType; @@ -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); @@ -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); + } } diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index d38369fea2b8..c80f3c6a0b39 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -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) { } } @@ -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); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index cfceaabfc229..7b044edfeea6 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -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; @@ -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 { @@ -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); } @@ -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 { @@ -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); @@ -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) { @@ -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. @@ -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 { - synchronized (mLock) { - UserState userState = getOrCreateUserStateLocked(resolvedUserId); - return getCurrentTvChannelInfosInternalLocked(userState); - } - } finally { - Binder.restoreCallingIdentity(identity); + 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, callingPid, callingUid); } } @@ -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 { @@ -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; } @@ -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; @@ -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 |