Add APIs for streaming AD insertion
Call sequence diagram can be found in the Bug below. Bug: 262620108 Test: mmm Change-Id: Ife8e19330f905bcfd82c4b54616f7849a7889025
This commit is contained in:
19
media/java/android/media/tv/AdBuffer.aidl
Normal file
19
media/java/android/media/tv/AdBuffer.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 AdBuffer;
|
||||
163
media/java/android/media/tv/AdBuffer.java
Normal file
163
media/java/android/media/tv/AdBuffer.java
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.media.MediaCodec.BufferFlag;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SharedMemory;
|
||||
|
||||
/**
|
||||
* Buffer for advertisement data.
|
||||
* @hide
|
||||
*/
|
||||
public class AdBuffer implements Parcelable {
|
||||
private final int mId;
|
||||
@NonNull
|
||||
private final String mMimeType;
|
||||
@NonNull
|
||||
private final SharedMemory mBuffer;
|
||||
private final int mOffset;
|
||||
private final int mLength;
|
||||
private final long mPresentationTimeUs;
|
||||
private final int mFlags;
|
||||
|
||||
public AdBuffer(
|
||||
int id,
|
||||
@NonNull String mimeType,
|
||||
@NonNull SharedMemory buffer,
|
||||
int offset,
|
||||
int length,
|
||||
long presentationTimeUs,
|
||||
@BufferFlag int flags) {
|
||||
this.mId = id;
|
||||
this.mMimeType = mimeType;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mimeType);
|
||||
this.mBuffer = buffer;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, buffer);
|
||||
this.mOffset = offset;
|
||||
this.mLength = length;
|
||||
this.mPresentationTimeUs = presentationTimeUs;
|
||||
this.mFlags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets corresponding AD request ID.
|
||||
*/
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mime type of the data.
|
||||
*/
|
||||
@NonNull
|
||||
public String getMimeType() {
|
||||
return mMimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shared memory which stores the data.
|
||||
*/
|
||||
@NonNull
|
||||
public SharedMemory getSharedMemory() {
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the offset of the buffer.
|
||||
*/
|
||||
public int getOffset() {
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data length.
|
||||
*/
|
||||
public int getLength() {
|
||||
return mLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the presentation time in microseconds.
|
||||
*/
|
||||
public long getPresentationTimeUs() {
|
||||
return mPresentationTimeUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flags.
|
||||
*/
|
||||
@BufferFlag
|
||||
public int getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
|
||||
dest.writeInt(mId);
|
||||
dest.writeString(mMimeType);
|
||||
dest.writeTypedObject(mBuffer, flags);
|
||||
dest.writeInt(mOffset);
|
||||
dest.writeInt(mLength);
|
||||
dest.writeLong(mPresentationTimeUs);
|
||||
dest.writeInt(mFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private AdBuffer(@NonNull Parcel in) {
|
||||
int id = in.readInt();
|
||||
String mimeType = in.readString();
|
||||
SharedMemory buffer = (SharedMemory) in.readTypedObject(SharedMemory.CREATOR);
|
||||
int offset = in.readInt();
|
||||
int length = in.readInt();
|
||||
long presentationTimeUs = in.readLong();
|
||||
int flags = in.readInt();
|
||||
|
||||
this.mId = id;
|
||||
this.mMimeType = mimeType;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mMimeType);
|
||||
this.mBuffer = buffer;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mBuffer);
|
||||
this.mOffset = offset;
|
||||
this.mLength = length;
|
||||
this.mPresentationTimeUs = presentationTimeUs;
|
||||
this.mFlags = flags;
|
||||
}
|
||||
|
||||
public static final @NonNull Parcelable.Creator<AdBuffer> CREATOR =
|
||||
new Parcelable.Creator<AdBuffer>() {
|
||||
@Override
|
||||
public AdBuffer[] newArray(int size) {
|
||||
return new AdBuffer[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdBuffer createFromParcel(Parcel in) {
|
||||
return new AdBuffer(in);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package android.media.tv;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -69,10 +70,25 @@ public final class AdRequest implements Parcelable {
|
||||
private final long mEchoInterval;
|
||||
private final String mMediaFileType;
|
||||
private final Bundle mMetadata;
|
||||
private final Uri mUri;
|
||||
|
||||
public AdRequest(int id, @RequestType int requestType,
|
||||
@Nullable ParcelFileDescriptor fileDescriptor, long startTime, long stopTime,
|
||||
long echoInterval, @Nullable String mediaFileType, @NonNull Bundle metadata) {
|
||||
this(id, requestType, fileDescriptor, null, startTime, stopTime, echoInterval,
|
||||
mediaFileType, metadata);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public AdRequest(int id, @RequestType int requestType, @Nullable Uri uri, long startTime,
|
||||
long stopTime, long echoInterval, @NonNull Bundle metadata) {
|
||||
this(id, requestType, null, uri, startTime, stopTime, echoInterval, null, metadata);
|
||||
}
|
||||
|
||||
private AdRequest(int id, @RequestType int requestType,
|
||||
@Nullable ParcelFileDescriptor fileDescriptor, @Nullable Uri uri, long startTime,
|
||||
long stopTime, long echoInterval, @Nullable String mediaFileType,
|
||||
@NonNull Bundle metadata) {
|
||||
mId = id;
|
||||
mRequestType = requestType;
|
||||
mFileDescriptor = fileDescriptor;
|
||||
@@ -81,15 +97,23 @@ public final class AdRequest implements Parcelable {
|
||||
mEchoInterval = echoInterval;
|
||||
mMediaFileType = mediaFileType;
|
||||
mMetadata = metadata;
|
||||
mUri = uri;
|
||||
}
|
||||
|
||||
private AdRequest(Parcel source) {
|
||||
mId = source.readInt();
|
||||
mRequestType = source.readInt();
|
||||
if (source.readInt() != 0) {
|
||||
int readInt = source.readInt();
|
||||
if (readInt == 1) {
|
||||
mFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(source);
|
||||
mUri = null;
|
||||
} else if (readInt == 2) {
|
||||
String stringUri = source.readString();
|
||||
mUri = stringUri == null ? null : Uri.parse(stringUri);
|
||||
mFileDescriptor = null;
|
||||
} else {
|
||||
mFileDescriptor = null;
|
||||
mUri = null;
|
||||
}
|
||||
mStartTime = source.readLong();
|
||||
mStopTime = source.readLong();
|
||||
@@ -117,13 +141,25 @@ public final class AdRequest implements Parcelable {
|
||||
* Gets the file descriptor of the AD media.
|
||||
*
|
||||
* @return The file descriptor of the AD media. Can be {@code null} for
|
||||
* {@link #REQUEST_TYPE_STOP}
|
||||
* {@link #REQUEST_TYPE_STOP} or a URI is used.
|
||||
*/
|
||||
@Nullable
|
||||
public ParcelFileDescriptor getFileDescriptor() {
|
||||
return mFileDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URI of the AD media.
|
||||
*
|
||||
* @return The URI of the AD media. Can be {@code null} for {@link #REQUEST_TYPE_STOP} or a file
|
||||
* descriptor is used.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public Uri getUri() {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start time of the AD media in milliseconds.
|
||||
* <p>0 means start immediately
|
||||
@@ -189,6 +225,10 @@ public final class AdRequest implements Parcelable {
|
||||
if (mFileDescriptor != null) {
|
||||
dest.writeInt(1);
|
||||
mFileDescriptor.writeToParcel(dest, flags);
|
||||
} else if (mUri != null) {
|
||||
dest.writeInt(2);
|
||||
String stringUri = mUri.toString();
|
||||
dest.writeString(stringUri);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public final class AdResponse implements Parcelable {
|
||||
RESPONSE_TYPE_PLAYING,
|
||||
RESPONSE_TYPE_FINISHED,
|
||||
RESPONSE_TYPE_STOPPED,
|
||||
RESPONSE_TYPE_ERROR
|
||||
RESPONSE_TYPE_ERROR,
|
||||
RESPONSE_TYPE_BUFFERING
|
||||
})
|
||||
public @interface ResponseType {}
|
||||
|
||||
@@ -42,6 +43,8 @@ public final class AdResponse implements Parcelable {
|
||||
public static final int RESPONSE_TYPE_FINISHED = 2;
|
||||
public static final int RESPONSE_TYPE_STOPPED = 3;
|
||||
public static final int RESPONSE_TYPE_ERROR = 4;
|
||||
/** @hide */
|
||||
public static final int RESPONSE_TYPE_BUFFERING = 5;
|
||||
|
||||
public static final @NonNull Parcelable.Creator<AdResponse> CREATOR =
|
||||
new Parcelable.Creator<AdResponse>() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media.tv;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.AitInfo;
|
||||
import android.media.tv.BroadcastInfoResponse;
|
||||
@@ -59,4 +60,5 @@ oneway interface ITvInputClient {
|
||||
|
||||
// For ad response
|
||||
void onAdResponse(in AdResponse response, int seq);
|
||||
void onAdBufferConsumed(in AdBuffer buffer, int seq);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.BroadcastInfoRequest;
|
||||
import android.media.tv.DvbDeviceInfo;
|
||||
@@ -110,6 +111,7 @@ interface ITvInputManager {
|
||||
|
||||
// For ad request
|
||||
void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
|
||||
void notifyAdBuffer(in IBinder sessionToken, in AdBuffer buffer, int userId);
|
||||
|
||||
// For TV input hardware binding
|
||||
List<TvInputHardwareInfo> getHardwareList();
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.media.tv;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.BroadcastInfoRequest;
|
||||
import android.media.tv.TvTrackInfo;
|
||||
@@ -71,4 +72,5 @@ oneway interface ITvInputSession {
|
||||
|
||||
// For ad request
|
||||
void requestAd(in AdRequest request);
|
||||
void notifyAdBuffer(in AdBuffer buffer);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.media.tv;
|
||||
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.AitInfo;
|
||||
import android.media.tv.BroadcastInfoResponse;
|
||||
@@ -56,4 +57,5 @@ oneway interface ITvInputSessionCallback {
|
||||
|
||||
// For ad response
|
||||
void onAdResponse(in AdResponse response);
|
||||
void onAdBufferConsumed(in AdBuffer buffer);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
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 static final int DO_NOTIFY_AD_BUFFER = 28;
|
||||
|
||||
private final boolean mIsRecordingSession;
|
||||
private final HandlerCaller mCaller;
|
||||
@@ -224,6 +225,7 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
case DO_START_RECORDING: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
mTvInputRecordingSessionImpl.startRecording((Uri) args.arg1, (Bundle) args.arg2);
|
||||
args.recycle();
|
||||
break;
|
||||
}
|
||||
case DO_STOP_RECORDING: {
|
||||
@@ -254,6 +256,10 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
mTvInputSessionImpl.requestAd((AdRequest) msg.obj);
|
||||
break;
|
||||
}
|
||||
case DO_NOTIFY_AD_BUFFER: {
|
||||
mTvInputSessionImpl.notifyAdBuffer((AdBuffer) msg.obj);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Log.w(TAG, "Unhandled message code: " + msg.what);
|
||||
break;
|
||||
@@ -424,6 +430,11 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REQUEST_AD, request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAdBuffer(AdBuffer buffer) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER, buffer));
|
||||
}
|
||||
|
||||
private final class TvInputEventReceiver extends InputEventReceiver {
|
||||
public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
|
||||
super(inputChannel, looper);
|
||||
|
||||
@@ -965,6 +965,19 @@ public final class TvInputManager {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void postAdBufferConsumed(AdBuffer buffer) {
|
||||
if (mSession.mIAppNotificationEnabled) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mSession.getInteractiveAppSession() != null) {
|
||||
mSession.getInteractiveAppSession().notifyAdBufferConsumed(buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1412,6 +1425,18 @@ public final class TvInputManager {
|
||||
record.postAdResponse(response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdBufferConsumed(AdBuffer buffer, int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for seq " + seq);
|
||||
return;
|
||||
}
|
||||
record.postAdBufferConsumed(buffer);
|
||||
}
|
||||
}
|
||||
};
|
||||
ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
|
||||
@Override
|
||||
@@ -3204,6 +3229,21 @@ public final class TvInputManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies when the advertisement buffer is filled and ready to be read.
|
||||
*/
|
||||
public void notifyAdBuffer(AdBuffer buffer) {
|
||||
if (mToken == null) {
|
||||
Log.w(TAG, "The session has been already released");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mService.notifyAdBuffer(mToken, buffer, 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;
|
||||
|
||||
@@ -913,6 +913,27 @@ public abstract class TvInputService extends Service {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the advertisement buffer is consumed.
|
||||
* @hide
|
||||
*/
|
||||
public void notifyAdBufferConsumed(AdBuffer buffer) {
|
||||
executeOrPostRunnableOnMainThread(new Runnable() {
|
||||
@MainThread
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (DEBUG) Log.d(TAG, "notifyAdBufferConsumed");
|
||||
if (mSessionCallback != null) {
|
||||
mSessionCallback.onAdBufferConsumed(buffer);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "error in notifyAdBufferConsumed", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void notifyTimeShiftStartPositionChanged(final long timeMs) {
|
||||
executeOrPostRunnableOnMainThread(new Runnable() {
|
||||
@MainThread
|
||||
@@ -1128,6 +1149,13 @@ public abstract class TvInputService extends Service {
|
||||
public void onRequestAd(@NonNull AdRequest request) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when advertisement buffer is ready.
|
||||
* @hide
|
||||
*/
|
||||
public void onAdBuffer(AdBuffer buffer) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tunes to a given channel.
|
||||
*
|
||||
@@ -1753,6 +1781,10 @@ public abstract class TvInputService extends Service {
|
||||
onRequestAd(request);
|
||||
}
|
||||
|
||||
void notifyAdBuffer(AdBuffer buffer) {
|
||||
onAdBuffer(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes care of dispatching incoming input events and tells whether the event was handled.
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media.tv.interactive;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.BroadcastInfoRequest;
|
||||
import android.net.Uri;
|
||||
@@ -37,6 +38,7 @@ oneway interface ITvInteractiveAppClient {
|
||||
void onSessionStateChanged(int state, int err, int seq);
|
||||
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
|
||||
void onTeletextAppStateChanged(int state, int seq);
|
||||
void onAdBuffer(in AdBuffer buffer, int seq);
|
||||
void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
|
||||
void onSetVideoBounds(in Rect rect, int seq);
|
||||
void onRequestCurrentChannelUri(int seq);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media.tv.interactive;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.BroadcastInfoResponse;
|
||||
import android.media.tv.TvTrackInfo;
|
||||
@@ -71,6 +72,7 @@ interface ITvInteractiveAppManager {
|
||||
void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
|
||||
int UserId);
|
||||
void notifyAdResponse(in IBinder sessionToken, in AdResponse response, int UserId);
|
||||
void notifyAdBufferConsumed(in IBinder sessionToken, in AdBuffer buffer, int userId);
|
||||
|
||||
void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
|
||||
int userId);
|
||||
|
||||
@@ -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.AdBuffer;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.BroadcastInfoResponse;
|
||||
import android.media.tv.TvTrackInfo;
|
||||
@@ -59,6 +60,7 @@ oneway interface ITvInteractiveAppSession {
|
||||
void dispatchSurfaceChanged(int format, int width, int height);
|
||||
void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
|
||||
void notifyAdResponse(in AdResponse response);
|
||||
void notifyAdBufferConsumed(in AdBuffer buffer);
|
||||
|
||||
void createMediaView(in IBinder windowToken, in Rect frame);
|
||||
void relayoutMediaView(in Rect frame);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.media.tv.interactive;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.BroadcastInfoRequest;
|
||||
import android.media.tv.interactive.ITvInteractiveAppSession;
|
||||
@@ -36,6 +37,7 @@ oneway interface ITvInteractiveAppSessionCallback {
|
||||
void onSessionStateChanged(int state, int err);
|
||||
void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
|
||||
void onTeletextAppStateChanged(int state);
|
||||
void onAdBuffer(in AdBuffer buffer);
|
||||
void onCommandRequest(in String cmdType, in Bundle parameters);
|
||||
void onSetVideoBounds(in Rect rect);
|
||||
void onRequestCurrentChannelUri();
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.BroadcastInfoResponse;
|
||||
import android.media.tv.TvContentRating;
|
||||
@@ -83,6 +84,7 @@ public class ITvInteractiveAppSessionWrapper
|
||||
private static final int DO_REMOVE_MEDIA_VIEW = 29;
|
||||
private static final int DO_NOTIFY_RECORDING_STARTED = 30;
|
||||
private static final int DO_NOTIFY_RECORDING_STOPPED = 31;
|
||||
private static final int DO_NOTIFY_AD_BUFFER_CONSUMED = 32;
|
||||
|
||||
private final HandlerCaller mCaller;
|
||||
private Session mSessionImpl;
|
||||
@@ -253,6 +255,10 @@ public class ITvInteractiveAppSessionWrapper
|
||||
mSessionImpl.removeMediaView(true);
|
||||
break;
|
||||
}
|
||||
case DO_NOTIFY_AD_BUFFER_CONSUMED: {
|
||||
mSessionImpl.notifyAdBufferConsumed((AdBuffer) msg.obj);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Log.w(TAG, "Unhandled message code: " + msg.what);
|
||||
break;
|
||||
@@ -424,6 +430,11 @@ public class ITvInteractiveAppSessionWrapper
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_RESPONSE, response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAdBufferConsumed(AdBuffer buffer) {
|
||||
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER_CONSUMED, buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createMediaView(IBinder windowToken, Rect frame) {
|
||||
mCaller.executeOrSendMessage(
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.BroadcastInfoRequest;
|
||||
@@ -558,6 +559,18 @@ public final class TvInteractiveAppManager {
|
||||
record.postTeletextAppStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdBuffer(AdBuffer buffer, int seq) {
|
||||
synchronized (mSessionCallbackRecordMap) {
|
||||
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
|
||||
if (record == null) {
|
||||
Log.e(TAG, "Callback not found for seq " + seq);
|
||||
return;
|
||||
}
|
||||
record.postAdBuffer(buffer);
|
||||
}
|
||||
}
|
||||
};
|
||||
ITvInteractiveAppManagerCallback managerCallback =
|
||||
new ITvInteractiveAppManagerCallback.Stub() {
|
||||
@@ -1277,6 +1290,21 @@ public final class TvInteractiveAppManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the advertisement buffer is consumed.
|
||||
*/
|
||||
public void notifyAdBufferConsumed(AdBuffer buffer) {
|
||||
if (mToken == null) {
|
||||
Log.w(TAG, "The session has been already released");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mService.notifyAdBufferConsumed(mToken, buffer, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases this session.
|
||||
*/
|
||||
@@ -1808,6 +1836,17 @@ public final class TvInteractiveAppManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void postAdBuffer(AdBuffer buffer) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mSession.getInputSession() != null) {
|
||||
mSession.getInputSession().notifyAdBuffer(buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.BroadcastInfoRequest;
|
||||
@@ -614,6 +615,13 @@ public abstract class TvInteractiveAppService extends Service {
|
||||
public void onAdResponse(@NonNull AdResponse response) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an advertisement buffer is consumed.
|
||||
* @hide
|
||||
*/
|
||||
public void onAdBufferConsumed(AdBuffer buffer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
|
||||
return false;
|
||||
@@ -1188,6 +1196,17 @@ public abstract class TvInteractiveAppService extends Service {
|
||||
onAdResponse(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #onAdBufferConsumed}.
|
||||
*/
|
||||
void notifyAdBufferConsumed(AdBuffer buffer) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
"notifyAdBufferConsumed (buffer=" + buffer + ")");
|
||||
}
|
||||
onAdBufferConsumed(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #onRecordingStarted(String)}.
|
||||
*/
|
||||
@@ -1289,6 +1308,33 @@ public abstract class TvInteractiveAppService extends Service {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifies when the advertisement buffer is filled and ready to be read.
|
||||
* @hide
|
||||
*/
|
||||
@CallSuper
|
||||
public void notifyAdBuffer(AdBuffer buffer) {
|
||||
executeOrPostRunnableOnMainThread(new Runnable() {
|
||||
@MainThread
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
"notifyAdBuffer(buffer=" + buffer + ")");
|
||||
}
|
||||
if (mSessionCallback != null) {
|
||||
mSessionCallback.onAdBuffer(buffer);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "error in notifyAdBuffer", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Takes care of dispatching incoming input events and tells whether the event was handled.
|
||||
*/
|
||||
|
||||
@@ -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.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.AitInfo;
|
||||
@@ -2520,7 +2521,30 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAdBuffer(
|
||||
IBinder sessionToken, AdBuffer buffer, int userId) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
|
||||
userId, "notifyAdBuffer");
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
|
||||
resolvedUserId);
|
||||
getSessionLocked(sessionState).notifyAdBuffer(buffer);
|
||||
} catch (RemoteException | SessionNotFoundException e) {
|
||||
Slog.e(TAG, "error in notifyAdBuffer", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3624,7 +3648,7 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBroadcastInfoResponse (BroadcastInfoResponse response) {
|
||||
public void onBroadcastInfoResponse(BroadcastInfoResponse response) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onBroadcastInfoResponse()");
|
||||
@@ -3641,7 +3665,7 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdResponse (AdResponse response) {
|
||||
public void onAdResponse(AdResponse response) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onAdResponse()");
|
||||
@@ -3656,6 +3680,24 @@ public final class TvInputManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdBufferConsumed(AdBuffer buffer) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "onAdBufferConsumed()");
|
||||
}
|
||||
if (mSessionState.session == null || mSessionState.client == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSessionState.client.onAdBufferConsumed(
|
||||
buffer, mSessionState.seq);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "error in onAdBufferConsumed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.media.tv.AdBuffer;
|
||||
import android.media.tv.AdRequest;
|
||||
import android.media.tv.AdResponse;
|
||||
import android.media.tv.BroadcastInfoRequest;
|
||||
@@ -1512,6 +1513,29 @@ public class TvInteractiveAppManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAdBufferConsumed(
|
||||
IBinder sessionToken, AdBuffer buffer, int userId) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
|
||||
"notifyAdBufferConsumed");
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
|
||||
resolvedUserId);
|
||||
getSessionLocked(sessionState).notifyAdBufferConsumed(buffer);
|
||||
} catch (RemoteException | SessionNotFoundException e) {
|
||||
Slogf.e(TAG, "error in notifyAdBufferConsumed", e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCallback(final ITvInteractiveAppManagerCallback callback, int userId) {
|
||||
int callingPid = Binder.getCallingPid();
|
||||
@@ -2378,6 +2402,23 @@ public class TvInteractiveAppManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAdBuffer(AdBuffer buffer) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) {
|
||||
Slogf.d(TAG, "onAdBuffer(buffer=" + buffer + ")");
|
||||
}
|
||||
if (mSessionState.mSession == null || mSessionState.mClient == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSessionState.mClient.onAdBuffer(buffer, mSessionState.mSeq);
|
||||
} catch (RemoteException e) {
|
||||
Slogf.e(TAG, "error in onAdBuffer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean addSessionTokenToClientStateLocked(ITvInteractiveAppSession session) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user