diff --git a/media/java/android/media/tv/AdRequest.aidl b/media/java/android/media/tv/AdRequest.aidl new file mode 100644 index 0000000000000..ebfd748f0593b --- /dev/null +++ b/media/java/android/media/tv/AdRequest.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 AdRequest; diff --git a/media/java/android/media/tv/AdRequest.java b/media/java/android/media/tv/AdRequest.java new file mode 100644 index 0000000000000..536baf264dfbe --- /dev/null +++ b/media/java/android/media/tv/AdRequest.java @@ -0,0 +1,135 @@ +/* + * 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.annotation.StringDef; +import android.os.Bundle; +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** @hide */ +public final class AdRequest implements Parcelable { + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "REQUEST_TYPE_", value = { + REQUEST_TYPE_START, + REQUEST_TYPE_STOP + }) + public @interface RequestType {} + + public static final String REQUEST_TYPE_START = "START"; + public static final String REQUEST_TYPE_STOP = "STOP"; + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public AdRequest createFromParcel(Parcel source) { + return new AdRequest(source); + } + + @Override + public AdRequest[] newArray(int size) { + return new AdRequest[size]; + } + }; + + private final int mId; + private final @RequestType String mRequestType; + private final ParcelFileDescriptor mFileDescriptor; + private final long mStartTime; + private final long mStopTime; + private final long mEchoInterval; + private final String mMediaFileType; + private final Bundle mMetadata; + + public AdRequest(int id, @RequestType String requestType, ParcelFileDescriptor fileDescriptor, + long startTime, long stopTime, long echoInterval, String mediaFileType, + Bundle metadata) { + mId = id; + mRequestType = requestType; + mFileDescriptor = fileDescriptor; + mStartTime = startTime; + mStopTime = stopTime; + mEchoInterval = echoInterval; + mMediaFileType = mediaFileType; + mMetadata = metadata; + } + + private AdRequest(Parcel source) { + mId = source.readInt(); + mRequestType = source.readString(); + mFileDescriptor = source.readFileDescriptor(); + mStartTime = source.readLong(); + mStopTime = source.readLong(); + mEchoInterval = source.readLong(); + mMediaFileType = source.readString(); + mMetadata = source.readBundle(); + } + + public int getId() { + return mId; + } + + public @RequestType String getRequestType() { + return mRequestType; + } + + public ParcelFileDescriptor getFileDescriptor() { + return mFileDescriptor; + } + + public long getStartTime() { + return mStartTime; + } + + public long getStopTime() { + return mStopTime; + } + + public long getEchoInterval() { + return mEchoInterval; + } + + public String getMediaFileType() { + return mMediaFileType; + } + + public Bundle getMetadata() { + return mMetadata; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeString(mRequestType); + mFileDescriptor.writeToParcel(dest, flags); + dest.writeLong(mStartTime); + dest.writeLong(mStopTime); + dest.writeLong(mEchoInterval); + dest.writeString(mMediaFileType); + dest.writeBundle(mMetadata); + } +} diff --git a/media/java/android/media/tv/AdResponse.aidl b/media/java/android/media/tv/AdResponse.aidl new file mode 100644 index 0000000000000..9c09a0a02d05e --- /dev/null +++ b/media/java/android/media/tv/AdResponse.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 AdResponse; diff --git a/media/java/android/media/tv/AdResponse.java b/media/java/android/media/tv/AdResponse.java new file mode 100644 index 0000000000000..28cf5ac5b2b2b --- /dev/null +++ b/media/java/android/media/tv/AdResponse.java @@ -0,0 +1,96 @@ +/* + * 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.annotation.Nullable; +import android.annotation.StringDef; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** @hide */ +public final class AdResponse implements Parcelable { + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "RESPONSE_TYPE_", value = { + RESPONSE_TYPE_PLAYING, + RESPONSE_TYPE_FINISHED, + RESPONSE_TYPE_STOPPED, + RESPONSE_TYPE_ERROR + }) + public @interface ResponseType {} + + public static final String RESPONSE_TYPE_PLAYING = "PLAYING"; + public static final String RESPONSE_TYPE_FINISHED = "FINISHED"; + public static final String RESPONSE_TYPE_STOPPED = "STOPPED"; + public static final String RESPONSE_TYPE_ERROR = "ERROR"; + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public AdResponse createFromParcel(Parcel source) { + return new AdResponse(source); + } + + @Override + public AdResponse[] newArray(int size) { + return new AdResponse[size]; + } + }; + + private final int mId; + private final @ResponseType String mResponseType; + private final Long mElapsedTime; + + public AdResponse(int id, @ResponseType String responseType, @Nullable Long elapsedTime) { + mId = id; + mResponseType = responseType; + mElapsedTime = elapsedTime; + } + + private AdResponse(Parcel source) { + mId = source.readInt(); + mResponseType = source.readString(); + mElapsedTime = (Long) source.readValue(Long.class.getClassLoader()); + } + + public int getId() { + return mId; + } + + public @ResponseType String getResponseType() { + return mResponseType; + } + + public Long getElapsedTime() { + return mElapsedTime; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeString(mResponseType); + dest.writeValue(mElapsedTime); + } +} diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl index de13fc1b91490..f4f55e44255e8 100644 --- a/media/java/android/media/tv/ITvInputClient.aidl +++ b/media/java/android/media/tv/ITvInputClient.aidl @@ -17,6 +17,7 @@ package android.media.tv; import android.content.ComponentName; +import android.media.tv.AdResponse; import android.media.tv.AitInfo; import android.media.tv.BroadcastInfoResponse; import android.media.tv.ITvInputSession; @@ -54,4 +55,7 @@ oneway interface ITvInputClient { // For broadcast info void onBroadcastInfoResponse(in BroadcastInfoResponse response, int seq); + + // For ad response + void onAdResponse(in AdResponse response, int seq); } diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index a6e02174e6fd8..d34e6360d95ff 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.AdRequest; import android.media.tv.BroadcastInfoRequest; import android.media.tv.DvbDeviceInfo; import android.media.tv.ITvInputClient; @@ -106,6 +107,9 @@ interface ITvInputManager { void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId); void removeBroadcastInfo(in IBinder sessionToken, int id, int userId); + // For ad request + void requestAd(in IBinder sessionToken, in AdRequest request, int userId); + // For TV input hardware binding List getHardwareList(); ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback, diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl index 025e6f1c2df80..f427501ff9083 100644 --- a/media/java/android/media/tv/ITvInputSession.aidl +++ b/media/java/android/media/tv/ITvInputSession.aidl @@ -18,6 +18,7 @@ package android.media.tv; import android.graphics.Rect; import android.media.PlaybackParams; +import android.media.tv.AdRequest; import android.media.tv.BroadcastInfoRequest; import android.media.tv.TvTrackInfo; import android.net.Uri; @@ -67,4 +68,7 @@ oneway interface ITvInputSession { // For broadcast info void requestBroadcastInfo(in BroadcastInfoRequest request); void removeBroadcastInfo(int id); + + // For ad request + void requestAd(in AdRequest request); } diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl index 9a0aaa38f8456..9830e78a7faa8 100644 --- a/media/java/android/media/tv/ITvInputSessionCallback.aidl +++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl @@ -16,6 +16,7 @@ package android.media.tv; +import android.media.tv.AdResponse; import android.media.tv.AitInfo; import android.media.tv.BroadcastInfoResponse; import android.media.tv.ITvInputSession; @@ -51,4 +52,7 @@ oneway interface ITvInputSessionCallback { // For broadcast info void onBroadcastInfoResponse(in BroadcastInfoResponse response); + + // For ad response + void onAdResponse(in AdResponse response); } diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index ef6ef91e66101..418ab2cd59afd 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -73,6 +73,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand private static final int DO_REQUEST_BROADCAST_INFO = 24; private static final int DO_REMOVE_BROADCAST_INFO = 25; private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 26; + private static final int DO_REQUEST_AD = 27; private final boolean mIsRecordingSession; private final HandlerCaller mCaller; @@ -249,6 +250,10 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj); break; } + case DO_REQUEST_AD: { + mTvInputSessionImpl.requestAd((AdRequest) msg.obj); + break; + } default: { Log.w(TAG, "Unhandled message code: " + msg.what); break; @@ -414,6 +419,11 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_REMOVE_BROADCAST_INFO, requestId)); } + @Override + public void requestAd(AdRequest request) { + mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REQUEST_AD, request)); + } + private final class TvInputEventReceiver extends InputEventReceiver { public TvInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index cde2f5a65df39..ad86002f862d0 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -894,6 +894,19 @@ public final class TvInputManager { }); } } + + void postAdResponse(final AdResponse response) { + if (mSession.mIAppNotificationEnabled) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mSession.getIAppSession() != null) { + mSession.getIAppSession().notifyAdResponse(response); + } + } + }); + } + } } /** @@ -1317,6 +1330,18 @@ public final class TvInputManager { record.postBroadcastInfoResponse(response); } } + + @Override + public void onAdResponse(AdResponse response, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postAdResponse(response); + } + } }; ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() { @Override @@ -3033,6 +3058,18 @@ public final class TvInputManager { } } + public void requestAd(AdRequest request) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.requestAd(mToken, request, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private final class InputEventHandler extends Handler { public static final int MSG_SEND_INPUT_EVENT = 1; public static final int MSG_TIMEOUT_INPUT_EVENT = 2; diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index ee33e5c43dde3..bd5a343963df7 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -849,7 +849,7 @@ public abstract class TvInputService extends Service { /** * Notifies response for broadcast info. * - * @param response + * @param response broadcast info response. * @hide */ public void notifyBroadcastInfoResponse(@NonNull final BroadcastInfoResponse response) { @@ -869,6 +869,29 @@ public abstract class TvInputService extends Service { }); } + /** + * Notifies response for advertisement. + * + * @param response advertisement response. + * @hide + */ + public void notifyAdResponse(@NonNull final AdResponse response) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "notifyAdResponse"); + if (mSessionCallback != null) { + mSessionCallback.onAdResponse(response); + } + } catch (RemoteException e) { + Log.w(TAG, "error in notifyAdResponse", e); + } + } + }); + } + private void notifyTimeShiftStartPositionChanged(final long timeMs) { executeOrPostRunnableOnMainThread(new Runnable() { @MainThread @@ -1038,6 +1061,8 @@ public abstract class TvInputService extends Service { public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume); /** + * called when broadcast info is requested. + * * @param request broadcast info request * @hide */ @@ -1050,6 +1075,15 @@ public abstract class TvInputService extends Service { public void onRemoveBroadcastInfo(int requestId) { } + /** + * called when advertisement is requested. + * + * @param request advertisement request + * @hide + */ + public void onRequestAd(@NonNull AdRequest request) { + } + /** * Tunes to a given channel. * @@ -1663,6 +1697,10 @@ public abstract class TvInputService extends Service { onRemoveBroadcastInfo(requestId); } + void requestAd(AdRequest request) { + onRequestAd(request); + } + /** * Takes care of dispatching incoming input events and tells whether the event was handled. */ diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl index 00c464f5e0adb..892a800ca68de 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.graphics.Rect; +import android.media.tv.AdRequest; import android.media.tv.BroadcastInfoRequest; import android.net.Uri; import android.os.Bundle; @@ -41,4 +42,5 @@ oneway interface ITvIAppClient { void onRequestCurrentChannelLcn(int seq); void onRequestStreamVolume(int seq); void onRequestTrackInfoList(int seq); + void onAdRequest(in AdRequest request, int Seq); } diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl index 15a2eacee2a27..23201faa1c5a1 100644 --- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl @@ -17,6 +17,7 @@ package android.media.tv.interactive; import android.graphics.Rect; +import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvTrackInfo; import android.media.tv.interactive.ITvIAppClient; @@ -55,6 +56,7 @@ interface ITvIAppManager { int userId); void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response, int UserId); + void notifyAdResponse(in IBinder sessionToken, in AdResponse response, int UserId); void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame, int userId); @@ -63,4 +65,4 @@ interface ITvIAppManager { void registerCallback(in ITvIAppManagerCallback callback, int userId); void unregisterCallback(in ITvIAppManagerCallback callback, int userId); -} \ 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 4a85feff159a6..52f9a874aca22 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl @@ -19,6 +19,7 @@ package android.media.tv.interactive; import android.graphics.Rect; import android.media.tv.BroadcastInfoResponse; import android.net.Uri; +import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvTrackInfo; import android.os.Bundle; @@ -44,8 +45,9 @@ oneway interface ITvIAppSession { void setSurface(in Surface surface); void dispatchSurfaceChanged(int format, int width, int height); void notifyBroadcastInfoResponse(in BroadcastInfoResponse response); + void notifyAdResponse(in AdResponse response); void createMediaView(in IBinder windowToken, in Rect frame); void relayoutMediaView(in Rect frame); void removeMediaView(); -} \ No newline at end of file +} diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl index b09f787ef6808..9b9e6afb786b2 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl @@ -17,9 +17,9 @@ package android.media.tv.interactive; import android.graphics.Rect; +import android.media.tv.AdRequest; import android.media.tv.BroadcastInfoRequest; import android.media.tv.interactive.ITvIAppSession; -import android.media.tv.BroadcastInfoRequest; import android.net.Uri; import android.os.Bundle; @@ -41,4 +41,5 @@ oneway interface ITvIAppSessionCallback { void onRequestCurrentChannelLcn(); void onRequestStreamVolume(); void onRequestTrackInfoList(); + void onAdRequest(in AdRequest request); } diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java index 4c4dffb794048..d1fd1df74e1b6 100644 --- a/media/java/android/media/tv/interactive/TvIAppManager.java +++ b/media/java/android/media/tv/interactive/TvIAppManager.java @@ -22,6 +22,8 @@ import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; import android.graphics.Rect; +import android.media.tv.AdRequest; +import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvInputManager; @@ -281,6 +283,18 @@ public final class TvIAppManager { } } + @Override + public void onAdRequest(AdRequest request, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postAdRequest(request); + } + } + @Override public void onRequestCurrentChannelUri(int seq) { synchronized (mSessionCallbackRecordMap) { @@ -944,6 +958,23 @@ public final class TvIAppManager { } } + /** + * Notifies of any advertisement response passed in from TIS. + * + * @param response response passed in from TIS. + */ + public void notifyAdResponse(AdResponse response) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.notifyAdResponse(mToken, response, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Releases this session. */ @@ -1322,6 +1353,17 @@ public final class TvIAppManager { }); } + void postAdRequest(final AdRequest request) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mSession.getInputSession() != null) { + mSession.getInputSession().requestAd(request); + } + } + }); + } + void postSessionStateChanged(int state) { mHandler.post(new Runnable() { @Override diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java index a0f8413ac29d7..1480ff643d993 100644 --- a/media/java/android/media/tv/interactive/TvIAppService.java +++ b/media/java/android/media/tv/interactive/TvIAppService.java @@ -27,6 +27,8 @@ import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.media.tv.AdRequest; +import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvTrackInfo; @@ -433,6 +435,13 @@ public abstract class TvIAppService extends Service { public void onBroadcastInfoResponse(@NonNull BroadcastInfoResponse response) { } + /** + * Called when an advertisement response is received. + * @hide + */ + public void onAdResponse(AdResponse response) { + } + /** * TODO: JavaDoc of APIs related to input events. * @hide @@ -703,6 +712,29 @@ public abstract class TvIAppService extends Service { }); } + /** + * requests an advertisement request to be processed by the related TV input. + * @param request advertisement request + */ + public void requestAd(@NonNull final AdRequest request) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "requestAd (id=" + request.getId() + ")"); + } + if (mSessionCallback != null) { + mSessionCallback.onAdRequest(request); + } + } catch (RemoteException e) { + Log.w(TAG, "error in requestAd", e); + } + } + }); + } + void startIApp() { onStartIApp(); } @@ -783,6 +815,16 @@ public abstract class TvIAppService extends Service { onBroadcastInfoResponse(response); } + /** + * Calls {@link #onAdResponse}. + */ + void notifyAdResponse(AdResponse response) { + if (DEBUG) { + Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")"); + } + onAdResponse(response); + } + /** * Notifies when the session state is changed. * @param state the current state. @@ -1141,6 +1183,11 @@ public abstract class TvIAppService extends Service { mSessionImpl.notifyBroadcastInfoResponse(response); } + @Override + public void notifyAdResponse(AdResponse response) { + mSessionImpl.notifyAdResponse(response); + } + @Override public void createMediaView(IBinder windowToken, Rect frame) { mSessionImpl.createMediaView(windowToken, frame); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 332fed7c1695b..568e4b8de3bae 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -44,6 +44,8 @@ import android.graphics.Rect; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.media.PlaybackParams; +import android.media.tv.AdRequest; +import android.media.tv.AdResponse; import android.media.tv.AitInfo; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; @@ -2493,6 +2495,28 @@ public final class TvInputManagerService extends SystemService { } } + @Override + public void requestAd(IBinder sessionToken, AdRequest request, int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, + userId, "requestAd"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).requestAd(request); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in requestAd", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + }; + } + @Override public int getClientPid(String sessionId) { ensureTunerResourceAccessPermission(); @@ -3557,6 +3581,23 @@ public final class TvInputManagerService extends SystemService { } } } + + @Override + public void onAdResponse (AdResponse response) { + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "onAdResponse()"); + } + if (mSessionState.session == null || mSessionState.client == null) { + return; + } + try { + mSessionState.client.onAdResponse(response, mSessionState.seq); + } catch (RemoteException e) { + Slog.e(TAG, "error in onAdResponse", e); + } + } + } } @VisibleForTesting 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 02a56ec8d9510..3c271b2fce362 100644 --- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java @@ -29,6 +29,8 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.graphics.Rect; +import android.media.tv.AdRequest; +import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvTrackInfo; @@ -1176,6 +1178,28 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void notifyAdResponse(IBinder sessionToken, AdResponse response, int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, + "notifyAdResponse"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).notifyAdResponse(response); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in notifyAdResponse", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override public void registerCallback(final ITvIAppManagerCallback callback, int userId) { int callingPid = Binder.getCallingPid(); @@ -1906,6 +1930,23 @@ public class TvIAppManagerService extends SystemService { } } + @Override + public void onAdRequest(AdRequest request) { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onAdRequest (id=" + request.getId() + ")"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onAdRequest(request, mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onAdRequest", e); + } + } + } + @Override public void onSessionStateChanged(int state) { synchronized (mLock) {