diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl index 30ef50342c11c..a5f23315956dc 100644 --- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl @@ -15,9 +15,9 @@ */ package android.media.tv.interactive; -import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoRequest; +import android.os.Bundle; import android.view.InputChannel; /** @@ -31,4 +31,5 @@ oneway interface ITvIAppClient { void onLayoutSurface(int left, int top, int right, int bottom, int seq); void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq); void onSessionStateChanged(int state, int seq); -} \ No newline at end of file + void onCommandRequest(in String cmdType, in Bundle parameters, int seq); +} diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl index ff8af88fa5fa4..66f5fc1d54aa2 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl @@ -19,6 +19,7 @@ package android.media.tv.interactive; import android.media.tv.BroadcastInfoRequest; import android.media.tv.interactive.ITvIAppSession; import android.media.tv.BroadcastInfoRequest; +import android.os.Bundle; /** * Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when @@ -30,4 +31,5 @@ oneway interface ITvIAppSessionCallback { void onLayoutSurface(int left, int top, int right, int bottom); void onBroadcastInfoRequest(in BroadcastInfoRequest request); void onSessionStateChanged(int state); -} \ No newline at end of file + void onCommandRequest(in String cmdType, in Bundle parameters); +} diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java index a232e3181e813..9390d8db08fb4 100644 --- a/media/java/android/media/tv/interactive/TvIAppManager.java +++ b/media/java/android/media/tv/interactive/TvIAppManager.java @@ -243,6 +243,19 @@ public final class TvIAppManager { } } + @Override + public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType, + Bundle parameters, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postCommandRequest(cmdType, parameters); + } + } + @Override public void onSessionStateChanged(int state, int seq) { synchronized (mSessionCallbackRecordMap) { @@ -1046,6 +1059,16 @@ public final class TvIAppManager { }); } + void postCommandRequest(final @TvIAppService.IAppServiceCommandType String cmdType, + final Bundle parameters) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onCommandRequest(mSession, cmdType, parameters); + } + }); + } + void postSessionStateChanged(int state) { mHandler.post(new Runnable() { @Override @@ -1092,6 +1115,17 @@ public final class TvIAppManager { public void onLayoutSurface(Session session, int left, int top, int right, int bottom) { } + /** + * This is called when {@link TvIAppService.Session#requestCommand} is called. + * + * @param session A {@link TvIAppManager.Session} associated with this callback. + * @param cmdType type of the command. + * @param parameters parameters of the command. + */ + public void onCommandRequest(Session session, + @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) { + } + /** * This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called. * diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java index 1e11bafa6c3bc..6475f903f257b 100644 --- a/media/java/android/media/tv/interactive/TvIAppService.java +++ b/media/java/android/media/tv/interactive/TvIAppService.java @@ -19,6 +19,7 @@ package android.media.tv.interactive; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringDef; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.Service; @@ -53,6 +54,8 @@ import android.widget.FrameLayout; import com.android.internal.os.SomeArgs; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -83,6 +86,28 @@ public abstract class TvIAppService extends Service { */ public static final String SERVICE_META_DATA = "android.media.tv.interactive.app"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "IAPP_SERVICE_COMMAND_TYPE_", value = { + IAPP_SERVICE_COMMAND_TYPE_TUNE, + IAPP_SERVICE_COMMAND_TYPE_TUNENEXT, + IAPP_SERVICE_COMMAND_TYPE_TUNEPREV, + IAPP_SERVICE_COMMAND_TYPE_STOP, + IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME + }) + public @interface IAppServiceCommandType {} + + /** @hide */ + public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE = "Tune"; + /** @hide */ + public static final String IAPP_SERVICE_COMMAND_TYPE_TUNENEXT = "TuneNext"; + /** @hide */ + public static final String IAPP_SERVICE_COMMAND_TYPE_TUNEPREV = "TunePrevious"; + /** @hide */ + public static final String IAPP_SERVICE_COMMAND_TYPE_STOP = "Stop"; + /** @hide */ + public static final String IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME = "setStreamVolume"; + private final Handler mServiceHandler = new ServiceHandler(); private final RemoteCallbackList mCallbacks = new RemoteCallbackList<>(); @@ -443,6 +468,31 @@ public abstract class TvIAppService extends Service { }); } + /** + * requests a specific command to be processed by the related TV input. + * @param cmdType type of the specific command + * @param parameters parameters of the specific command + */ + public void requestCommand(@IAppServiceCommandType String cmdType, Bundle parameters) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters=" + + parameters.toString() + ")"); + } + if (mSessionCallback != null) { + mSessionCallback.onCommandRequest(cmdType, parameters); + } + } catch (RemoteException e) { + Log.w(TAG, "error in requestCommand", e); + } + } + }); + } + void startIApp() { onStartIApp(); } diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java index 803198162d303..efbe9e3bcf4bc 100644 --- a/media/java/android/media/tv/interactive/TvIAppView.java +++ b/media/java/android/media/tv/interactive/TvIAppView.java @@ -26,6 +26,7 @@ import android.media.tv.TvInputManager; import android.media.tv.TvView; import android.media.tv.interactive.TvIAppManager.Session; import android.media.tv.interactive.TvIAppManager.SessionCallback; +import android.os.Bundle; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; @@ -53,6 +54,7 @@ public class TvIAppView extends ViewGroup { private final Handler mHandler = new Handler(); private Session mSession; private MySessionCallback mSessionCallback; + private TvIAppCallback mCallback; private SurfaceView mSurfaceView; private Surface mSurface; @@ -123,6 +125,16 @@ public class TvIAppView extends ViewGroup { mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app"); } + /** + * Sets the callback to be invoked when an event is dispatched to this TvIAppView. + * + * @param callback The callback to receive events. A value of {@code null} removes the existing + * callback. + */ + public void setCallback(@Nullable TvIAppCallback callback) { + mCallback = callback; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -322,6 +334,23 @@ public class TvIAppView extends ViewGroup { return UNSET_TVVIEW_SUCCESS; } + /** + * Callback used to receive various status updates on the {@link TvIAppView}. + */ + public abstract static class TvIAppCallback { + + /** + * This is called when a command is requested to be processed by the related TV input. + * + * @param iAppServiceId The ID of the TV interactive app service bound to this view. + * @param cmdType type of the command + * @param parameters parameters of the command + */ + public void onCommandRequest(String iAppServiceId, + @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) { + } + } + private class MySessionCallback extends SessionCallback { final String mIAppServiceId; int mType; @@ -395,5 +424,21 @@ public class TvIAppView extends ViewGroup { mUseRequestedSurfaceLayout = true; requestLayout(); } + + @Override + public void onCommandRequest(Session session, + @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) { + if (DEBUG) { + Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters=" + + parameters.toString() + ")"); + } + if (this != mSessionCallback) { + Log.w(TAG, "onCommandRequest - session not created"); + return; + } + if (mCallback != null) { + mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters); + } + } } } diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java index 873d6885f9e1c..d70f970958164 100644 --- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java @@ -1548,6 +1548,25 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType, + Bundle parameters) { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters=" + + parameters.toString() + ")"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onCommandRequest(cmdType, parameters, mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onCommandRequest", e); + } + } + } + @Override public void onSessionStateChanged(int state) { synchronized (mLock) {