From d931e912b40ce3941010d72edd6b207859b59306 Mon Sep 17 00:00:00 2001 From: shubang Date: Mon, 9 Jan 2023 15:41:19 -0800 Subject: [PATCH] Extend TIAF APIs 1. get current video bounds 2. timeline selector 3. playback command stop modes 4. extend table request / response Bug: 264934231 Test: mmm Change-Id: Id1f8b18e264495c37ebf841326e0e5b4ff534287 --- media/java/android/media/tv/TableRequest.java | 45 +++++++++++- .../java/android/media/tv/TableResponse.java | 68 ++++++++++++++++++- .../android/media/tv/TimelineRequest.java | 24 +++++++ .../interactive/ITvInteractiveAppClient.aidl | 1 + .../interactive/ITvInteractiveAppManager.aidl | 1 + .../interactive/ITvInteractiveAppSession.aidl | 1 + .../ITvInteractiveAppSessionCallback.aidl | 1 + .../ITvInteractiveAppSessionWrapper.java | 11 +++ .../interactive/TvInteractiveAppManager.java | 42 ++++++++++++ .../interactive/TvInteractiveAppService.java | 68 +++++++++++++++++++ .../tv/interactive/TvInteractiveAppView.java | 45 ++++++++++++ .../TvInteractiveAppManagerService.java | 42 ++++++++++++ 12 files changed, 347 insertions(+), 2 deletions(-) diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java index a1a6b516859cc..a9ea6d3999d0c 100644 --- a/media/java/android/media/tv/TableRequest.java +++ b/media/java/android/media/tv/TableRequest.java @@ -33,11 +33,54 @@ public final class TableRequest extends BroadcastInfoRequest implements Parcelab /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef({TABLE_NAME_PAT, TABLE_NAME_PMT}) + @IntDef({TABLE_NAME_PAT, TABLE_NAME_PMT, TABLE_NAME_CAT}) public @interface TableName {} + /** Program Association Table */ public static final int TABLE_NAME_PAT = 0; + /** Program Mapping Table */ public static final int TABLE_NAME_PMT = 1; + /** + * Conditional Access Table + * @hide + */ + public static final int TABLE_NAME_CAT = 2; + /** + * Network Information Table + * @hide + */ + public static final int TABLE_NAME_NIT = 3; + /** + * Bouquet Association Table + * @hide + */ + public static final int TABLE_NAME_BAT = 4; + /** + * Service Description Table + * @hide + */ + public static final int TABLE_NAME_SDT = 5; + /** + * Event Information Table + * @hide + */ + public static final int TABLE_NAME_EIT = 6; + /** + * Time and Date Table + * @hide + */ + public static final int TABLE_NAME_TDT = 7; + /** + * Time Offset Table + * @hide + */ + public static final int TABLE_NAME_TOT = 8; + /** + * Selection Information Table + * @hide + */ + public static final int TABLE_NAME_SIT = 9; + public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java index afc9bee5fb858..1c314b09db9a2 100644 --- a/media/java/android/media/tv/TableResponse.java +++ b/media/java/android/media/tv/TableResponse.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.os.SharedMemory; /** * A response for Table from broadcast signal. @@ -46,6 +47,8 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel private final Uri mTableUri; private final int mVersion; private final int mSize; + private final byte[] mTableByteArray; + private final SharedMemory mTableSharedMemory; static TableResponse createFromParcelBody(Parcel in) { return new TableResponse(in); @@ -54,9 +57,33 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel public TableResponse(int requestId, int sequence, @ResponseResult int responseResult, @Nullable Uri tableUri, int version, int size) { super(RESPONSE_TYPE, requestId, sequence, responseResult); - mTableUri = tableUri; mVersion = version; mSize = size; + mTableUri = tableUri; + mTableByteArray = null; + mTableSharedMemory = null; + } + + /** @hide */ + public TableResponse(int requestId, int sequence, @ResponseResult int responseResult, + @NonNull byte[] tableByteArray, int version, int size) { + super(RESPONSE_TYPE, requestId, sequence, responseResult); + mVersion = version; + mSize = size; + mTableUri = null; + mTableByteArray = tableByteArray; + mTableSharedMemory = null; + } + + /** @hide */ + public TableResponse(int requestId, int sequence, @ResponseResult int responseResult, + @NonNull SharedMemory tableSharedMemory, int version, int size) { + super(RESPONSE_TYPE, requestId, sequence, responseResult); + mVersion = version; + mSize = size; + mTableUri = null; + mTableByteArray = null; + mTableSharedMemory = tableSharedMemory; } TableResponse(Parcel source) { @@ -65,6 +92,14 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel mTableUri = uriString == null ? null : Uri.parse(uriString); mVersion = source.readInt(); mSize = source.readInt(); + int arrayLength = source.readInt(); + if (arrayLength >= 0) { + mTableByteArray = new byte[arrayLength]; + source.readByteArray(mTableByteArray); + } else { + mTableByteArray = null; + } + mTableSharedMemory = (SharedMemory) source.readTypedObject(SharedMemory.CREATOR); } /** @@ -75,6 +110,30 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel return mTableUri; } + /** + * Gets the data of the table as a byte array. + * + * @return the table data as a byte array, or {@code null} if the data is not stored as a byte + * array. + * @hide + */ + @Nullable + public byte[] getTableByteArray() { + return mTableByteArray; + } + + /** + * Gets the data of the table as a {@link SharedMemory} object. + * + * @return the table data as a {@link SharedMemory} object, or {@code null} if the data is not + * stored in shared memory. + * @hide + */ + @Nullable + public SharedMemory getTableSharedMemory() { + return mTableSharedMemory; + } + /** * Gets the version number of requested table. If it is null, value will be -1. *

The consistency of version numbers between request and response depends on @@ -106,5 +165,12 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel dest.writeString(uriString); dest.writeInt(mVersion); dest.writeInt(mSize); + if (mTableByteArray != null) { + dest.writeInt(mTableByteArray.length); + dest.writeByteArray(mTableByteArray); + } else { + dest.writeInt(-1); + } + dest.writeTypedObject(mTableSharedMemory, flags); } } diff --git a/media/java/android/media/tv/TimelineRequest.java b/media/java/android/media/tv/TimelineRequest.java index 03c62f0824f30..d04c58a9b869e 100644 --- a/media/java/android/media/tv/TimelineRequest.java +++ b/media/java/android/media/tv/TimelineRequest.java @@ -42,6 +42,7 @@ public final class TimelineRequest extends BroadcastInfoRequest implements Parce }; private final int mIntervalMillis; + private final String mSelector; static TimelineRequest createFromParcelBody(Parcel in) { return new TimelineRequest(in); @@ -50,11 +51,21 @@ public final class TimelineRequest extends BroadcastInfoRequest implements Parce public TimelineRequest(int requestId, @RequestOption int option, int intervalMillis) { super(REQUEST_TYPE, requestId, option); mIntervalMillis = intervalMillis; + mSelector = null; + } + + /** @hide */ + public TimelineRequest(int requestId, @RequestOption int option, int intervalMillis, + @NonNull String selector) { + super(REQUEST_TYPE, requestId, option); + mIntervalMillis = intervalMillis; + mSelector = selector; } TimelineRequest(Parcel source) { super(REQUEST_TYPE, source); mIntervalMillis = source.readInt(); + mSelector = source.readString(); } /** @@ -64,6 +75,18 @@ public final class TimelineRequest extends BroadcastInfoRequest implements Parce return mIntervalMillis; } + /** + * Gets the timeline selector. + *

The selector describes the type and location of timeline signalling. For example + * {@code urn:dvb:css:timeline:pts} is a selector in DVB standard. + * + * @return the selector if it's set; {@code null} otherwise. + * @hide + */ + public String getSelector() { + return mSelector; + } + @Override public int describeContents() { return 0; @@ -73,5 +96,6 @@ public final class TimelineRequest extends BroadcastInfoRequest implements Parce public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(mIntervalMillis); + dest.writeString(mSelector); } } diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl index 494571fc37cf2..ad9312f6975d0 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl @@ -43,6 +43,7 @@ oneway interface ITvInteractiveAppClient { void onCommandRequest(in String cmdType, in Bundle parameters, int seq); void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters, int seq); void onSetVideoBounds(in Rect rect, int seq); + void onRequestCurrentVideoBounds(int seq); void onRequestCurrentChannelUri(int seq); void onRequestCurrentChannelLcn(int seq); void onRequestStreamVolume(int seq); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl index 599922c2bafb8..c0723f7b9d78c 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl @@ -48,6 +48,7 @@ interface ITvInteractiveAppManager { in IBinder sessionToken, in Uri biIAppUri, in Bundle params, int userId); void destroyBiInteractiveApp(in IBinder sessionToken, in String biIAppId, int userId); void setTeletextAppEnabled(in IBinder sessionToken, boolean enable, int userId); + void sendCurrentVideoBounds(in IBinder sessionToken, in Rect bounds, int userId); void sendCurrentChannelUri(in IBinder sessionToken, in Uri channelUri, int userId); void sendCurrentChannelLcn(in IBinder sessionToken, int lcn, int userId); void sendStreamVolume(in IBinder sessionToken, float volume, int userId); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl index 17a70d1d8a7d4..9ae9ca7919495 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl @@ -40,6 +40,7 @@ oneway interface ITvInteractiveAppSession { void createBiInteractiveApp(in Uri biIAppUri, in Bundle params); void destroyBiInteractiveApp(in String biIAppId); void setTeletextAppEnabled(boolean enable); + void sendCurrentVideoBounds(in Rect bounds); void sendCurrentChannelUri(in Uri channelUri); void sendCurrentChannelLcn(int lcn); void sendStreamVolume(float volume); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl index 056574295cd8a..d84affd850a9e 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl @@ -42,6 +42,7 @@ oneway interface ITvInteractiveAppSessionCallback { void onCommandRequest(in String cmdType, in Bundle parameters); void onTimeShiftCommandRequest(in String cmdType, in Bundle parameters); void onSetVideoBounds(in Rect rect); + void onRequestCurrentVideoBounds(); void onRequestCurrentChannelUri(); void onRequestCurrentChannelLcn(); void onRequestStreamVolume(); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java index b8158cd1d949b..8a23e65ec3a62 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java @@ -94,6 +94,7 @@ public class ITvInteractiveAppSessionWrapper private static final int DO_NOTIFY_TIME_SHIFT_STATUS_CHANGED = 37; private static final int DO_NOTIFY_TIME_SHIFT_START_POSITION_CHANGED = 38; private static final int DO_NOTIFY_TIME_SHIFT_CURRENT_POSITION_CHANGED = 39; + private static final int DO_SEND_CURRENT_VIDEO_BOUNDS = 40; private final HandlerCaller mCaller; private Session mSessionImpl; @@ -157,6 +158,10 @@ public class ITvInteractiveAppSessionWrapper mSessionImpl.setTeletextAppEnabled((Boolean) msg.obj); break; } + case DO_SEND_CURRENT_VIDEO_BOUNDS: { + mSessionImpl.sendCurrentVideoBounds((Rect) msg.obj); + break; + } case DO_SEND_CURRENT_CHANNEL_URI: { mSessionImpl.sendCurrentChannelUri((Uri) msg.obj); break; @@ -354,6 +359,12 @@ public class ITvInteractiveAppSessionWrapper mCaller.obtainMessageO(DO_SET_TELETEXT_APP_ENABLED, enable)); } + @Override + public void sendCurrentVideoBounds(@Nullable Rect bounds) { + mCaller.executeOrSendMessage( + mCaller.obtainMessageO(DO_SEND_CURRENT_VIDEO_BOUNDS, bounds)); + } + @Override public void sendCurrentChannelUri(@Nullable Uri channelUri) { mCaller.executeOrSendMessage( diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 7c91844519dbf..fd3c29bb24aa0 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java @@ -444,6 +444,18 @@ public final class TvInteractiveAppManager { } } + @Override + public void onRequestCurrentVideoBounds(int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postRequestCurrentVideoBounds(); + } + } + @Override public void onRequestCurrentChannelUri(int seq) { synchronized (mSessionCallbackRecordMap) { @@ -1084,6 +1096,18 @@ public final class TvInteractiveAppManager { } } + void sendCurrentVideoBounds(@NonNull Rect bounds) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.sendCurrentVideoBounds(mToken, bounds, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + void sendCurrentChannelUri(@Nullable Uri channelUri) { if (mToken == null) { Log.w(TAG, "The session has been already released"); @@ -1898,6 +1922,15 @@ public final class TvInteractiveAppManager { }); } + void postRequestCurrentVideoBounds() { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onRequestCurrentVideoBounds(mSession); + } + }); + } + void postRequestCurrentChannelUri() { mHandler.post(new Runnable() { @Override @@ -2118,6 +2151,15 @@ public final class TvInteractiveAppManager { public void onSetVideoBounds(Session session, Rect rect) { } + /** + * This is called when {@link TvInteractiveAppService.Session#RequestCurrentVideoBounds} is + * called. + * + * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. + */ + public void onRequestCurrentVideoBounds(Session session) { + } + /** * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is * called. diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java index 949e01775aeb6..be2c16c90b9e1 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java @@ -17,6 +17,7 @@ package android.media.tv.interactive; import android.annotation.CallSuper; +import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -139,6 +140,38 @@ public abstract class TvInteractiveAppService extends Service { * Playback command type: select the given track. */ public static final String PLAYBACK_COMMAND_TYPE_SELECT_TRACK = "select_track"; + + + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "PLAYBACK_COMMAND_STOP_MODE_", value = { + PLAYBACK_COMMAND_STOP_MODE_BLANK, + PLAYBACK_COMMAND_STOP_MODE_FREEZE + }) + public @interface PlaybackCommandStopMode {} + + /** + * Playback command stop mode: show a blank screen. + * @hide + */ + public static final int PLAYBACK_COMMAND_STOP_MODE_BLANK = 1; + + /** + * Playback command stop mode: freeze the video. + * @hide + */ + public static final int PLAYBACK_COMMAND_STOP_MODE_FREEZE = 2; + + /** + * Playback command parameter: stop mode. + *

Type: int + * + * @see #PLAYBACK_COMMAND_TYPE_STOP + * @hide + */ + public static final String COMMAND_PARAMETER_KEY_STOP_MODE = "command_stop_mode"; + /** * Playback command parameter: channel URI. *

Type: android.net.Uri @@ -497,6 +530,13 @@ public abstract class TvInteractiveAppService extends Service { public void onSetTeletextAppEnabled(boolean enable) { } + /** + * Receives current video bounds. + * @hide + */ + public void onCurrentVideoBounds(@NonNull Rect bounds) { + } + /** * Receives current channel URI. */ @@ -982,6 +1022,30 @@ public abstract class TvInteractiveAppService extends Service { }); } + /** + * Requests the bounds of the current video. + * @hide + */ + @CallSuper + public void requestCurrentVideoBounds() { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "requestCurrentVideoBounds"); + } + if (mSessionCallback != null) { + mSessionCallback.onRequestCurrentVideoBounds(); + } + } catch (RemoteException e) { + Log.w(TAG, "error in requestCurrentVideoBounds", e); + } + } + }); + } + /** * Requests the URI of the current channel. */ @@ -1309,6 +1373,10 @@ public abstract class TvInteractiveAppService extends Service { onSetTeletextAppEnabled(enable); } + void sendCurrentVideoBounds(@NonNull Rect bounds) { + onCurrentVideoBounds(bounds); + } + void sendCurrentChannelUri(@Nullable Uri channelUri) { onCurrentChannelUri(channelUri); } diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java index 9211a12de0d0d..af03bb86d3648 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java @@ -513,6 +513,19 @@ public class TvInteractiveAppView extends ViewGroup { } } + /** + * Sends current video bounds to related TV interactive app. + * @hide + */ + public void sendCurrentVideoBounds(@NonNull Rect bounds) { + if (DEBUG) { + Log.d(TAG, "sendCurrentVideoBounds"); + } + if (mSession != null) { + mSession.sendCurrentVideoBounds(bounds); + } + } + /** * Sends current channel URI to related TV interactive app. * @@ -943,6 +956,16 @@ public class TvInteractiveAppView extends ViewGroup { public void onSetVideoBounds(@NonNull String iAppServiceId, @NonNull Rect rect) { } + /** + * This is called when {@link TvInteractiveAppService.Session#requestCurrentVideoBounds()} + * is called. + * + * @param iAppServiceId The ID of the TV interactive app service bound to this view. + * @hide + */ + public void onRequestCurrentVideoBounds(@NonNull String iAppServiceId) { + } + /** * This is called when {@link TvInteractiveAppService.Session#requestCurrentChannelUri()} is * called. @@ -1264,6 +1287,28 @@ public class TvInteractiveAppView extends ViewGroup { } } + @Override + public void onRequestCurrentVideoBounds(Session session) { + if (DEBUG) { + Log.d(TAG, "onRequestCurrentVideoBounds"); + } + if (this != mSessionCallback) { + Log.w(TAG, "onRequestCurrentVideoBounds - session not created"); + return; + } + synchronized (mCallbackLock) { + if (mCallbackExecutor != null) { + mCallbackExecutor.execute(() -> { + synchronized (mCallbackLock) { + if (mCallback != null) { + mCallback.onRequestCurrentVideoBounds(mIAppServiceId); + } + } + }); + } + } + } + @Override public void onRequestCurrentChannelUri(Session session) { if (DEBUG) { diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java index 50b7fd21cf843..d4f2f2dcbdf81 100644 --- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java @@ -1335,6 +1335,31 @@ public class TvInteractiveAppManagerService extends SystemService { } } + @Override + public void sendCurrentVideoBounds(IBinder sessionToken, Rect bounds, int userId) { + if (DEBUG) { + Slogf.d(TAG, "sendCurrentVideoBounds(bounds=%s)", bounds.toString()); + } + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "sendCurrentVideoBounds"); + SessionState sessionState = null; + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).sendCurrentVideoBounds(bounds); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in sendCurrentVideoBounds", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void sendCurrentChannelUri(IBinder sessionToken, Uri channelUri, int userId) { if (DEBUG) { @@ -2474,6 +2499,23 @@ public class TvInteractiveAppManagerService extends SystemService { } } + @Override + public void onRequestCurrentVideoBounds() { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onRequestCurrentVideoBounds"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onRequestCurrentVideoBounds(mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onRequestCurrentVideoBounds", e); + } + } + } + @Override public void onRequestCurrentChannelUri() { synchronized (mLock) {