From 938ec458a0d56608ac249e9d3a0f5e6225a6248a Mon Sep 17 00:00:00 2001 From: shubang Date: Wed, 1 Dec 2021 02:55:06 -0800 Subject: [PATCH] TIAF: Add IApp setup & start flow Bug: 208651882 Bug: 208651889 Test: mmm Change-Id: Ic8f92bd438fb9f04836dee634e51fcc0ffe22fd2 --- media/java/android/media/tv/AitInfo.aidl | 19 ++++ media/java/android/media/tv/AitInfo.java | 87 +++++++++++++++++++ .../java/android/media/tv/ITvInputClient.aidl | 6 +- .../android/media/tv/ITvInputManager.aidl | 4 +- .../android/media/tv/ITvInputSession.aidl | 4 +- .../media/tv/ITvInputSessionCallback.aidl | 4 +- .../media/tv/ITvInputSessionWrapper.java | 11 +++ .../java/android/media/tv/TvInputManager.java | 84 +++++++++++++++--- .../java/android/media/tv/TvInputService.java | 36 ++++++++ .../android/media/tv/TvRecordingClient.java | 2 +- media/java/android/media/tv/TvView.java | 60 ++++++++++++- .../media/tv/interactive/ITvIAppClient.aidl | 1 + .../media/tv/interactive/ITvIAppManager.aidl | 5 +- .../media/tv/interactive/ITvIAppService.aidl | 1 + .../media/tv/interactive/ITvIAppSession.aidl | 3 + .../interactive/ITvIAppSessionCallback.aidl | 1 + .../media/tv/interactive/TvIAppManager.java | 28 ++++++ .../media/tv/interactive/TvIAppService.java | 76 +++++++++++----- .../server/tv/TvInputManagerService.java | 41 ++++++++- .../tv/interactive/TvIAppManagerService.java | 78 ++++++++++++++--- 20 files changed, 498 insertions(+), 53 deletions(-) create mode 100644 media/java/android/media/tv/AitInfo.aidl create mode 100644 media/java/android/media/tv/AitInfo.java diff --git a/media/java/android/media/tv/AitInfo.aidl b/media/java/android/media/tv/AitInfo.aidl new file mode 100644 index 0000000000000..b7d9fe8baacfa --- /dev/null +++ b/media/java/android/media/tv/AitInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv; + +parcelable AitInfo; diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java new file mode 100644 index 0000000000000..5f8487d0dc6b1 --- /dev/null +++ b/media/java/android/media/tv/AitInfo.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * AIT info. + * @hide + */ +public final class AitInfo implements Parcelable { + static final String TAG = "AitInfo"; + + public static final Creator CREATOR = new Creator() { + @Override + public AitInfo createFromParcel(Parcel in) { + return new AitInfo(in); + } + + @Override + public AitInfo[] newArray(int size) { + return new AitInfo[size]; + } + }; + + private final int mType; + private final int mVersion; + + private AitInfo(Parcel in) { + mType = in.readInt(); + mVersion = in.readInt(); + } + + /** + * Constructs AIT info. + */ + public AitInfo(int type, int version) { + mType = type; + mVersion = version; + } + + /** + * Gets type. + */ + public int getType() { + return mType; + } + + /** + * Gets version. + */ + public int getVersion() { + return mVersion; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mVersion); + } + + @Override + public String toString() { + return "type=" + mType + ";version=" + mVersion; + } +} diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl index 5dad633a0236d..6f7db4a7b192c 100644 --- a/media/java/android/media/tv/ITvInputClient.aidl +++ b/media/java/android/media/tv/ITvInputClient.aidl @@ -17,12 +17,13 @@ package android.media.tv; import android.content.ComponentName; +import android.media.tv.AitInfo; +import android.media.tv.BroadcastInfoResponse; import android.media.tv.ITvInputSession; import android.net.Uri; import android.media.tv.TvTrackInfo; import android.os.Bundle; import android.view.InputChannel; -import android.media.tv.BroadcastInfoResponse; /** * Interface a client of the ITvInputManager implements, to identify itself and receive information @@ -44,9 +45,10 @@ oneway interface ITvInputClient { void onTimeShiftStatusChanged(int status, int seq); void onTimeShiftStartPositionChanged(long timeMs, int seq); void onTimeShiftCurrentPositionChanged(long timeMs, int seq); + void onAitInfoUpdated(in AitInfo aitInfo, int seq); + void onTuned(in Uri channelUri, int seq); // For the recording session - void onTuned(int seq, in Uri channelUri); void onRecordingStopped(in Uri recordedProgramUri, int seq); void onError(int error, int seq); diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index eaf89ba617648..0070898584813 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Intent; import android.graphics.Rect; import android.media.PlaybackParams; +import android.media.tv.BroadcastInfoRequest; import android.media.tv.DvbDeviceInfo; import android.media.tv.ITvInputClient; import android.media.tv.ITvInputHardware; @@ -35,7 +36,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.view.Surface; -import android.media.tv.BroadcastInfoRequest; /** * Interface to the TV input manager service. @@ -73,6 +73,8 @@ interface ITvInputManager { void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId); void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId); + void setIAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId); + void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data, int userId); diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl index 1eab650ba953b..984a551086ac3 100644 --- a/media/java/android/media/tv/ITvInputSession.aidl +++ b/media/java/android/media/tv/ITvInputSession.aidl @@ -18,11 +18,11 @@ package android.media.tv; import android.graphics.Rect; import android.media.PlaybackParams; +import android.media.tv.BroadcastInfoRequest; import android.media.tv.TvTrackInfo; import android.net.Uri; import android.os.Bundle; import android.view.Surface; -import android.media.tv.BroadcastInfoRequest; /** * Sub-interface of ITvInputService which is created per session and has its own context. @@ -41,6 +41,8 @@ oneway interface ITvInputSession { void setCaptionEnabled(boolean enabled); void selectTrack(int type, in String trackId); + void setIAppNotificationEnabled(boolean enable); + void appPrivateCommand(in String action, in Bundle data); void createOverlayView(in IBinder windowToken, in Rect frame); diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl index d857c00d8279b..9a0aaa38f8456 100644 --- a/media/java/android/media/tv/ITvInputSessionCallback.aidl +++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl @@ -16,11 +16,12 @@ package android.media.tv; +import android.media.tv.AitInfo; +import android.media.tv.BroadcastInfoResponse; import android.media.tv.ITvInputSession; import android.net.Uri; import android.media.tv.TvTrackInfo; import android.os.Bundle; -import android.media.tv.BroadcastInfoResponse; /** * Helper interface for ITvInputSession to allow the TV input to notify the system service when a @@ -41,6 +42,7 @@ oneway interface ITvInputSessionCallback { void onTimeShiftStatusChanged(int status); void onTimeShiftStartPositionChanged(long timeMs); void onTimeShiftCurrentPositionChanged(long timeMs); + void onAitInfoUpdated(in AitInfo aitInfo); // For the recording session void onTuned(in Uri channelUri); diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index 425714582fd58..6539472d203c0 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -71,6 +71,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand private static final int DO_PAUSE_RECORDING = 22; private static final int DO_RESUME_RECORDING = 23; private static final int DO_REQUEST_BROADCAST_INFO = 24; + private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 25; private final boolean mIsRecordingSession; private final HandlerCaller mCaller; @@ -239,6 +240,10 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mTvInputSessionImpl.requestBroadcastInfo((BroadcastInfoRequest) msg.obj); break; } + case DO_SET_IAPP_NOTIFICATION_ENABLED: { + mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj); + break; + } default: { Log.w(TAG, "Unhandled message code: " + msg.what); break; @@ -306,6 +311,12 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_SELECT_TRACK, type, trackId)); } + @Override + public void setIAppNotificationEnabled(boolean enabled) { + mCaller.executeOrSendMessage( + mCaller.obtainMessageO(DO_SET_IAPP_NOTIFICATION_ENABLED, enabled)); + } + @Override public void appPrivateCommand(String action, Bundle data) { mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index bafb03bc53ebc..b655a615794e8 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -627,14 +627,20 @@ public final class TvInputManager { public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) { } - // For the recording session only /** - * This is called when the recording session has been tuned to the given channel and is - * ready to start recording. + * This is called when AIT info is updated. + * @param session A {@link TvInputManager.Session} associated with this callback. + * @param aitInfo The current AIT info. + */ + public void onAitInfoUpdated(Session session, AitInfo aitInfo) { + } + + /** + * This is called when the session has been tuned to the given channel. * * @param channelUri The URI of a channel. */ - void onTuned(Session session, Uri channelUri) { + public void onTuned(Session session, Uri channelUri) { } // For the recording session only @@ -807,12 +813,23 @@ public final class TvInputManager { }); } - // For the recording session only + void postAitInfoUpdated(final AitInfo aitInfo) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onAitInfoUpdated(mSession, aitInfo); + } + }); + } + void postTuned(final Uri channelUri) { mHandler.post(new Runnable() { @Override public void run() { mSessionCallback.onTuned(mSession, channelUri); + if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) { + mSession.getIAppSession().notifyTuned(channelUri); + } } }); } @@ -838,12 +855,16 @@ public final class TvInputManager { } void postBroadcastInfoResponse(final BroadcastInfoResponse response) { - mHandler.post(new Runnable() { - @Override - public void run() { - mSession.getIAppSession().notifyBroadcastInfoResponse(response); - } - }); + if (mSession.mIAppNotificationEnabled) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mSession.getIAppSession() != null) { + mSession.getIAppSession().notifyBroadcastInfoResponse(response); + } + } + }); + } } } @@ -1209,7 +1230,19 @@ public final class TvInputManager { } @Override - public void onTuned(int seq, Uri channelUri) { + public void onAitInfoUpdated(AitInfo aitInfo, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postAitInfoUpdated(aitInfo); + } + } + + @Override + public void onTuned(Uri channelUri, int seq) { synchronized (mSessionCallbackRecordMap) { SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); if (record == null) { @@ -1217,6 +1250,13 @@ public final class TvInputManager { return; } record.postTuned(channelUri); + // TODO: synchronized and wrap the channelUri + if (record.mSession.mIAppNotificationEnabled) { + TvIAppManager.Session iappSession = record.mSession.mIAppSession; + if (iappSession != null) { + iappSession.notifyTuned(channelUri); + } + } } } @@ -2068,6 +2108,7 @@ public final class TvInputManager { private int mVideoHeight; private TvIAppManager.Session mIAppSession; + private boolean mIAppNotificationEnabled = false; private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId, int seq, SparseArray sessionCallbackRecordMap) { @@ -2339,6 +2380,25 @@ public final class TvInputManager { throw new IllegalArgumentException("invalid type: " + type); } + /** + * Enables interactive app notification. + * @param enabled {@code true} if you want to enable interactive app notifications. + * {@code false} otherwise. + * @hide + */ + public void setIAppNotificationEnabled(boolean enabled) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.setIAppNotificationEnabled(mToken, enabled, mUserId); + mIAppNotificationEnabled = enabled; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Responds to onTracksChanged() and updates the internal track information. Returns true if * there is an update. diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index d285b13e7762f..6743dd6acad95 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -827,6 +827,27 @@ public abstract class TvInputService extends Service { }); } + /** + * Notifies AIT info updated. + * @hide + */ + public void notifyAitInfoUpdated(@NonNull final AitInfo aitInfo) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "notifyAitInfoUpdated"); + if (mSessionCallback != null) { + mSessionCallback.onAitInfoUpdated(aitInfo); + } + } catch (RemoteException e) { + Log.w(TAG, "error in notifyAitInfoUpdated", e); + } + } + }); + } + /** * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position * is relative to the overlay view that sits on top of this surface. @@ -1020,6 +1041,14 @@ public abstract class TvInputService extends Service { return false; } + /** + * Enables or disables interactive app notification. + * @param enabled {@code true} to enable, {@code false} to disable. + * @hide + */ + public void onSetIAppNotificationEnabled(boolean enabled) { + } + /** * Processes a private command sent from the application to the TV input. This can be used * to provide domain-specific features that are only known between certain TV inputs and @@ -1368,6 +1397,13 @@ public abstract class TvInputService extends Service { // TODO: Handle failure. } + /** + * Calls {@link #onSetIAppNotificationEnabled}. + */ + void setIAppNotificationEnabled(boolean enabled) { + onSetIAppNotificationEnabled(enabled); + } + /** * Calls {@link #onAppPrivateCommand}. */ diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java index 180e2bd6845b1..87d7a0b10d10b 100644 --- a/media/java/android/media/tv/TvRecordingClient.java +++ b/media/java/android/media/tv/TvRecordingClient.java @@ -472,7 +472,7 @@ public class TvRecordingClient { } @Override - void onTuned(TvInputManager.Session session, Uri channelUri) { + public void onTuned(TvInputManager.Session session, Uri channelUri) { if (DEBUG) { Log.d(TAG, "onTuned()"); } diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 6994d284ac273..4a12cd7eb534a 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -34,8 +34,6 @@ import android.media.PlaybackParams; import android.media.tv.TvInputManager.Session; import android.media.tv.TvInputManager.Session.FinishedInputEventCallback; import android.media.tv.TvInputManager.SessionCallback; -import android.media.tv.interactive.TvIAppManager; -import android.media.tv.interactive.TvIAppView; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -482,6 +480,18 @@ public class TvView extends ViewGroup { return mSession.getSelectedTrack(type); } + /** + * Enables interactive app notification. + * @param enabled {@code true} if you want to enable interactive app notifications. + * {@code false} otherwise. + * @hide + */ + public void setIAppNotificationEnabled(boolean enabled) { + if (mSession != null) { + mSession.setIAppNotificationEnabled(enabled); + } + } + /** * Plays a given recorded TV program. * @@ -1050,6 +1060,24 @@ public class TvView extends ViewGroup { public void onTimeShiftStatusChanged( String inputId, @TvInputManager.TimeShiftStatus int status) { } + + /** + * This is called when the AIT info has been updated. + * + * @param aitInfo The current AIT info. + * @hide + */ + public void onAitInfoUpdated(String inputId, AitInfo aitInfo) { + } + + /** + * This is called when the session has been tuned to the given channel. + * + * @param channelUri The URI of a channel. + * @hide + */ + public void onTuned(String inputId, Uri channelUri) { + } } /** @@ -1346,5 +1374,33 @@ public class TvView extends ViewGroup { mTimeShiftPositionCallback.onTimeShiftCurrentPositionChanged(mInputId, timeMs); } } + + @Override + public void onAitInfoUpdated(Session session, AitInfo aitInfo) { + if (DEBUG) { + Log.d(TAG, "onAitInfoUpdated(aitInfo=" + aitInfo + ")"); + } + if (this != mSessionCallback) { + Log.w(TAG, "onAitInfoUpdated - session not created"); + return; + } + if (mCallback != null) { + mCallback.onAitInfoUpdated(mInputId, aitInfo); + } + } + + @Override + public void onTuned(Session session, Uri channelUri) { + if (DEBUG) { + Log.d(TAG, "onTuned(channelUri=" + channelUri + ")"); + } + if (this != mSessionCallback) { + Log.w(TAG, "onTuned - session not created"); + return; + } + if (mCallback != null) { + mCallback.onTuned(mInputId, channelUri); + } + } } } diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl index 39c438a093119..9fc1fe71c3183 100644 --- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl @@ -17,6 +17,7 @@ package android.media.tv.interactive; import android.media.tv.BroadcastInfoRequest; +import android.media.tv.BroadcastInfoRequest; import android.view.InputChannel; /** diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl index 2027626893c0c..cd87a09b91e07 100644 --- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl @@ -17,10 +17,11 @@ package android.media.tv.interactive; import android.graphics.Rect; +import android.media.tv.BroadcastInfoResponse; import android.media.tv.interactive.ITvIAppClient; import android.media.tv.interactive.ITvIAppManagerCallback; import android.media.tv.interactive.TvIAppInfo; -import android.media.tv.BroadcastInfoResponse; +import android.net.Uri; import android.view.Surface; /** @@ -29,10 +30,12 @@ import android.view.Surface; */ interface ITvIAppManager { List getTvIAppServiceList(int userId); + void prepare(String tiasId, int type, int userId); void startIApp(in IBinder sessionToken, int userId); void createSession( in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId); void releaseSession(in IBinder sessionToken, int userId); + void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId); void setSurface(in IBinder sessionToken, in Surface surface, int userId); void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height, int userId); diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl index 1dee9cc4ed28e..af15dd8f5a3f3 100644 --- a/media/java/android/media/tv/interactive/ITvIAppService.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl @@ -30,4 +30,5 @@ oneway interface ITvIAppService { void unregisterCallback(in ITvIAppServiceCallback callback); void createSession(in InputChannel channel, in ITvIAppSessionCallback callback, in String iAppServiceId, int type); + void prepare(int type); } \ No newline at end of file diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl index ef35c68beb6fe..0d37a2baa49d9 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl @@ -17,6 +17,8 @@ package android.media.tv.interactive; import android.graphics.Rect; +import android.net.Uri; +import android.media.tv.BroadcastInfoResponse; import android.view.Surface; import android.media.tv.BroadcastInfoResponse; @@ -27,6 +29,7 @@ import android.media.tv.BroadcastInfoResponse; oneway interface ITvIAppSession { void startIApp(); void release(); + void notifyTuned(in Uri channelUri); void setSurface(in Surface surface); void dispatchSurfaceChanged(int format, int width, int height); void notifyBroadcastInfoResponse(in BroadcastInfoResponse response); diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl index d308463cda4d9..d2b966ed939fd 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl @@ -16,6 +16,7 @@ package android.media.tv.interactive; +import android.media.tv.BroadcastInfoRequest; import android.media.tv.interactive.ITvIAppSession; import android.media.tv.BroadcastInfoRequest; diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java index 5c7f0b9fe8a23..fed976980cd76 100644 --- a/media/java/android/media/tv/interactive/TvIAppManager.java +++ b/media/java/android/media/tv/interactive/TvIAppManager.java @@ -24,6 +24,7 @@ import android.graphics.Rect; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvInputManager; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -333,6 +334,18 @@ public final class TvIAppManager { } } + /** + * Prepares TV IApp service for the given type. + * @hide + */ + public void prepare(String tvIAppServiceId, int type) { + try { + mService.prepare(tvIAppServiceId, type, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Registers a {@link TvIAppManager.TvIAppCallback}. * @@ -596,6 +609,21 @@ public final class TvIAppManager { releaseInternal(); } + /** + * Notifies IAPP session when a channels is tuned. + */ + public void notifyTuned(Uri channelUri) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.notifyTuned(mToken, channelUri, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private void flushPendingEventsLocked() { mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT); diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java index 7b30f6e0d0afd..027b8909fe63c 100644 --- a/media/java/android/media/tv/interactive/TvIAppService.java +++ b/media/java/android/media/tv/interactive/TvIAppService.java @@ -28,6 +28,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; +import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.IBinder; @@ -117,10 +118,23 @@ public abstract class TvIAppService extends Service { mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args) .sendToTarget(); } + + @Override + public void prepare(int type) { + onPrepare(type); + } }; return tvIAppServiceBinder; } + /** + * Prepares TV IApp service for the given type. + * @hide + */ + public void onPrepare(int type) { + // TODO: make it abstract when unhide + } + /** * Returns a concrete implementation of {@link Session}. @@ -236,14 +250,6 @@ public abstract class TvIAppService extends Service { public void onSurfaceChanged(int format, int width, int height) { } - /** - * Called when a broadcast info response is received from TIS. - * - * @param response response received from TIS. - */ - public void onNotifyBroadcastInfoResponse(BroadcastInfoResponse response) { - } - /** * Called when the size of the media view is changed by the application. * @@ -275,6 +281,20 @@ public abstract class TvIAppService extends Service { public void onRelease() { } + /** + * Called when the corresponding TV input tuned to a channel. + * @hide + */ + public void onTuned(Uri channelUri) { + } + + /** + * Called when a broadcast info response is received. + * @hide + */ + public void onBroadcastInfoResponse(BroadcastInfoResponse response) { + } + /** * TODO: JavaDoc of APIs related to input events. * @hide @@ -362,6 +382,10 @@ public abstract class TvIAppService extends Service { }); } + /** + * Requests broadcast related information from the related TV input. + * @param request + */ public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) { executeOrPostRunnableOnMainThread(new Runnable() { @MainThread @@ -401,6 +425,25 @@ public abstract class TvIAppService extends Service { removeMediaView(true); } + void notifyTuned(Uri channelUri) { + if (DEBUG) { + Log.d(TAG, "notifyTuned (channelUri=" + channelUri + ")"); + } + onTuned(channelUri); + } + + + /** + * Calls {@link #onBroadcastInfoResponse}. + */ + void notifyBroadcastInfoResponse(BroadcastInfoResponse response) { + if (DEBUG) { + Log.d(TAG, "notifyBroadcastInfoResponse (requestId=" + + response.getRequestId() + ")"); + } + onBroadcastInfoResponse(response); + } + /** * Takes care of dispatching incoming input events and tells whether the event was handled. */ @@ -467,18 +510,6 @@ public abstract class TvIAppService extends Service { onSurfaceChanged(format, width, height); } - /** - * - * Calls {@link #notifyBroadcastInfoResponse}. - */ - void notifyBroadcastInfoResponse(BroadcastInfoResponse response) { - if (DEBUG) { - Log.d(TAG, "notifyBroadcastInfoResponse (requestId=" - + response.getRequestId() + ")"); - } - onNotifyBroadcastInfoResponse(response); - } - private void executeOrPostRunnableOnMainThread(Runnable action) { synchronized (mLock) { if (mSessionCallback == null) { @@ -656,6 +687,11 @@ public abstract class TvIAppService extends Service { mSessionImpl.release(); } + @Override + public void notifyTuned(Uri channelUri) { + mSessionImpl.notifyTuned(channelUri); + } + @Override public void setSurface(Surface surface) { mSessionImpl.setSurface(surface); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 59b6a08ef1501..7e36f89216781 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -44,6 +44,7 @@ import android.graphics.Rect; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.media.PlaybackParams; +import android.media.tv.AitInfo; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; import android.media.tv.DvbDeviceInfo; @@ -1760,6 +1761,26 @@ public final class TvInputManagerService extends SystemService { } } + @Override + public void setIAppNotificationEnabled(IBinder sessionToken, boolean enabled, int userId) { + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "setIAppNotificationEnabled"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + getSessionLocked(sessionToken, callingUid, resolvedUserId) + .setIAppNotificationEnabled(enabled); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in setIAppNotificationEnabled", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data, int userId) { @@ -3341,7 +3362,23 @@ public final class TvInputManagerService extends SystemService { } } - // For the recording session only + @Override + public void onAitInfoUpdated(AitInfo aitInfo) { + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "onAitInfoUpdated(" + aitInfo + ")"); + } + if (mSessionState.session == null || mSessionState.client == null) { + return; + } + try { + mSessionState.client.onAitInfoUpdated(aitInfo, mSessionState.seq); + } catch (RemoteException e) { + Slog.e(TAG, "error in onAitInfoUpdated", e); + } + } + } + @Override public void onTuned(Uri channelUri) { synchronized (mLock) { @@ -3352,7 +3389,7 @@ public final class TvInputManagerService extends SystemService { return; } try { - mSessionState.client.onTuned(mSessionState.seq, channelUri); + mSessionState.client.onTuned(channelUri, mSessionState.seq); } catch (RemoteException e) { Slog.e(TAG, "error in onTuned", e); } 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 cd757d2a626ea..f12139d07f11c 100644 --- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java @@ -40,6 +40,7 @@ import android.media.tv.interactive.ITvIAppSession; import android.media.tv.interactive.ITvIAppSessionCallback; import android.media.tv.interactive.TvIAppInfo; import android.media.tv.interactive.TvIAppService; +import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.Process; @@ -523,11 +524,6 @@ public class TvIAppManagerService extends SystemService { removeSessionStateLocked(state.mSessionToken, state.mUserId); } - private SessionState getSessionState(IBinder sessionToken) { - // TODO: implement user state and get session from it. - return null; - } - private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId, String methodName) { return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false, @@ -606,6 +602,32 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void prepare(String tiasId, int type, int userId) { + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), + Binder.getCallingUid(), userId, "prepare"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + UserState userState = getOrCreateUserStateLocked(resolvedUserId); + TvIAppState iAppState = userState.mIAppMap.get(tiasId); + if (iAppState == null) { + Slogf.e(TAG, "failed to prepare TIAS - unknown TIAS id " + tiasId); + return; + } + ComponentName componentName = iAppState.mInfo.getComponent(); + ServiceState serviceState = userState.mServiceStateMap.get(componentName); + if (serviceState != null) { + serviceState.mService.prepare(type); + } + } + } catch (RemoteException e) { + Slogf.e(TAG, "error in prepare", e); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void createSession(final ITvIAppClient client, final String iAppServiceId, int type, int seq, int userId) { @@ -688,18 +710,54 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void notifyTuned(IBinder sessionToken, Uri channelUri, int userId) { + if (DEBUG) { + Slogf.d(TAG, "notifyTuned(sessionToken=" + sessionToken + + ", Uri=" + channelUri + ")"); + } + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "notifyTuned"); + SessionState sessionState = null; + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).notifyTuned(channelUri); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in notifyTuned", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void startIApp(IBinder sessionToken, int userId) { if (DEBUG) { Slogf.d(TAG, "BinderService#start(userId=%d)", userId); } + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "notifyTuned"); + SessionState sessionState = null; + final long identity = Binder.clearCallingIdentity(); try { - SessionState sessionState = getSessionState(sessionToken); - if (sessionState != null && sessionState.mSession != null) { - sessionState.mSession.startIApp(); + synchronized (mLock) { + try { + sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).startIApp(); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in start", e); + } } - } catch (RemoteException e) { - Slogf.e(TAG, "error in start", e); + } finally { + Binder.restoreCallingIdentity(identity); } }