summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/tv/TvChannelInfo.java72
-rw-r--r--media/java/android/media/tv/TvInputManager.java16
-rwxr-xr-xservices/core/java/com/android/server/tv/TvInputManagerService.java64
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