/*
* Copyright 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.interactive;
import android.annotation.CallSuper;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Px;
import android.annotation.SdkConstant;
import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.media.PlaybackParams;
import android.media.tv.AdBuffer;
import android.media.tv.AdRequest;
import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvRecordingInfo;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
import android.media.tv.flags.Flags;
import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback;
import android.net.Uri;
import android.net.http.SslCertificate;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import android.view.Gravity;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import com.android.internal.os.SomeArgs;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* A TV interactive application service is a service that provides runtime environment and runs TV
* interactive applications.
*/
public abstract class TvInteractiveAppService extends Service {
private static final boolean DEBUG = false;
private static final String TAG = "TvInteractiveAppService";
private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000;
/**
* This is the interface name that a service implementing a TV Interactive App service should
* say that it supports -- that is, this is the action it uses for its intent filter. To be
* supported, the service must also require the
* {@link android.Manifest.permission#BIND_TV_INTERACTIVE_APP} permission so that other
* applications cannot abuse it.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
"android.media.tv.interactive.TvInteractiveAppService";
/**
* Name under which a TvInteractiveAppService component publishes information about itself. This
* meta-data must reference an XML resource containing an
* <{@link android.R.styleable#TvInteractiveAppService tv-interactive-app}>
* tag.
*/
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "PLAYBACK_COMMAND_TYPE_", value = {
PLAYBACK_COMMAND_TYPE_TUNE,
PLAYBACK_COMMAND_TYPE_TUNE_NEXT,
PLAYBACK_COMMAND_TYPE_TUNE_PREV,
PLAYBACK_COMMAND_TYPE_STOP,
PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME,
PLAYBACK_COMMAND_TYPE_SELECT_TRACK,
PLAYBACK_COMMAND_TYPE_FREEZE
})
public @interface PlaybackCommandType {}
/**
* Playback command type: tune to the given channel.
* @see #COMMAND_PARAMETER_KEY_CHANNEL_URI
*/
public static final String PLAYBACK_COMMAND_TYPE_TUNE = "tune";
/**
* Playback command type: tune to the next channel.
*/
public static final String PLAYBACK_COMMAND_TYPE_TUNE_NEXT = "tune_next";
/**
* Playback command type: tune to the previous channel.
*/
public static final String PLAYBACK_COMMAND_TYPE_TUNE_PREV = "tune_previous";
/**
* Playback command type: stop the playback.
*/
public static final String PLAYBACK_COMMAND_TYPE_STOP = "stop";
/**
* Playback command type: set the volume.
*/
public static final String PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME =
"set_stream_volume";
/**
* Playback command type: select the given track.
*/
public static final String PLAYBACK_COMMAND_TYPE_SELECT_TRACK = "select_track";
/**
* Playback command type: freeze the video playback on the current frame.
* @hide
*/
public static final String PLAYBACK_COMMAND_TYPE_FREEZE = "freeze";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "COMMAND_PARAMETER_VALUE_STOP_MODE_", value = {
COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK,
COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE
})
public @interface PlaybackCommandStopMode {}
/**
* Playback command stop mode: show a blank screen.
* @see #PLAYBACK_COMMAND_TYPE_STOP
*/
public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK = 1;
/**
* Playback command stop mode: freeze the video.
* @see #PLAYBACK_COMMAND_TYPE_STOP
*/
public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE = 2;
/**
* Playback command parameter: stop mode.
*
Type: int * * @see #PLAYBACK_COMMAND_TYPE_STOP */ public static final String COMMAND_PARAMETER_KEY_STOP_MODE = "command_stop_mode"; /** * Playback command parameter: channel URI. *
Type: android.net.Uri * * @see #PLAYBACK_COMMAND_TYPE_TUNE */ public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri"; /** * Playback command parameter: TV input ID. *
Type: String * * @see TvInputInfo#getId() */ public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id"; /** * Playback command parameter: stream volume. *
Type: float * * @see #PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME */ public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume"; /** * Playback command parameter: track type. *
Type: int * * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK * @see TvTrackInfo#getType() */ public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type"; /** * Playback command parameter: track ID. *
Type: String * * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK * @see TvTrackInfo#getId() */ public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id"; /** * Command to quiet channel change. No channel banner or channel info is shown. *
Refer to HbbTV Spec 2.0.4 chapter A.2.4.3. */ public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY = "command_change_channel_quietly"; /** @hide */ @Retention(RetentionPolicy.SOURCE) @StringDef(prefix = "TIME_SHIFT_COMMAND_TYPE_", value = { TIME_SHIFT_COMMAND_TYPE_PLAY, TIME_SHIFT_COMMAND_TYPE_PAUSE, TIME_SHIFT_COMMAND_TYPE_RESUME, TIME_SHIFT_COMMAND_TYPE_SEEK_TO, TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS, TIME_SHIFT_COMMAND_TYPE_SET_MODE, }) public @interface TimeShiftCommandType {} /** * Time shift command type: play. * * @see TvView#timeShiftPlay(String, Uri) */ public static final String TIME_SHIFT_COMMAND_TYPE_PLAY = "play"; /** * Time shift command type: pause. * * @see TvView#timeShiftPause() */ public static final String TIME_SHIFT_COMMAND_TYPE_PAUSE = "pause"; /** * Time shift command type: resume. * * @see TvView#timeShiftResume() */ public static final String TIME_SHIFT_COMMAND_TYPE_RESUME = "resume"; /** * Time shift command type: seek to. * * @see TvView#timeShiftSeekTo(long) */ public static final String TIME_SHIFT_COMMAND_TYPE_SEEK_TO = "seek_to"; /** * Time shift command type: set playback params. * * @see TvView#timeShiftSetPlaybackParams(PlaybackParams) */ public static final String TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS = "set_playback_params"; /** * Time shift command type: set time shift mode. */ public static final String TIME_SHIFT_COMMAND_TYPE_SET_MODE = "set_mode"; /** * Time shift command parameter: program URI. *
Type: android.net.Uri * * @see #TIME_SHIFT_COMMAND_TYPE_PLAY */ public static final String COMMAND_PARAMETER_KEY_PROGRAM_URI = "command_program_uri"; /** * Time shift command parameter: time position for time shifting, in milliseconds. *
Type: long * * @see #TIME_SHIFT_COMMAND_TYPE_SEEK_TO */ public static final String COMMAND_PARAMETER_KEY_TIME_POSITION = "command_time_position"; /** * Time shift command parameter: playback params. *
Type: android.media.PlaybackParams * * @see #TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS */ public static final String COMMAND_PARAMETER_KEY_PLAYBACK_PARAMS = "command_playback_params"; /** * Time shift command parameter: playback params. *
Type: Integer. One of {@link TvInputManager#TIME_SHIFT_MODE_OFF},
* {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
* {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
* {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
*
* @see #TIME_SHIFT_COMMAND_TYPE_SET_MODE
*/
public static final String COMMAND_PARAMETER_KEY_TIME_SHIFT_MODE = "command_time_shift_mode";
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList May return {@code null} if this TV Interactive App service fails to create a session for
* some reason.
*
* @param iAppServiceId The ID of the TV Interactive App associated with the session.
* @param type The type of the TV Interactive App associated with the session.
*/
@Nullable
public abstract Session onCreateSession(
@NonNull String iAppServiceId,
@TvInteractiveAppServiceInfo.InteractiveAppType int type);
/**
* Notifies the system when the state of the interactive app RTE has been changed.
*
* @param type the interactive app type
* @param state the current state of the service of the given type
* @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
* used when the state is not
* {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}.
*/
public final void notifyStateChanged(
@TvInteractiveAppServiceInfo.InteractiveAppType int type,
@TvInteractiveAppManager.ServiceState int state,
@TvInteractiveAppManager.ErrorCode int error) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = type;
args.arg2 = state;
args.arg3 = error;
mServiceHandler
.obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, args).sendToTarget();
}
/**
* Base class for derived classes to implement to provide a TV interactive app session.
*
* A session is associated with a {@link TvInteractiveAppView} instance and handles
* corresponding communications. It also handles the communications with
* {@link android.media.tv.TvInputService.Session} if connected.
*
* @see TvInteractiveAppView#setTvView(TvView)
*/
public abstract static class Session implements KeyEvent.Callback {
private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
private final Object mLock = new Object();
// @GuardedBy("mLock")
private ITvInteractiveAppSessionCallback mSessionCallback;
// @GuardedBy("mLock")
private final List By default, the media view is disabled. Must be called explicitly after the
* session is created to enable the media view.
*
* The TV Interactive App service can disable its media view when needed.
*
* @param enable {@code true} if you want to enable the media view. {@code false}
* otherwise.
*/
@CallSuper
public void setMediaViewEnabled(final boolean enable) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (enable == mMediaViewEnabled) {
return;
}
mMediaViewEnabled = enable;
if (enable) {
if (mWindowToken != null) {
createMediaView(mWindowToken, mMediaFrame);
}
} else {
removeMediaView(false);
}
}
});
}
/**
* Returns {@code true} if media view is enabled, {@code false} otherwise.
*
* @see #setMediaViewEnabled(boolean)
*/
public boolean isMediaViewEnabled() {
return mMediaViewEnabled;
}
/**
* Starts TvInteractiveAppService session.
*/
public void onStartInteractiveApp() {
}
/**
* Stops TvInteractiveAppService session.
*/
public void onStopInteractiveApp() {
}
/**
* Resets TvInteractiveAppService session.
*/
public void onResetInteractiveApp() {
}
/**
* Creates broadcast-independent(BI) interactive application.
*
* The implementation should call {@link #notifyBiInteractiveAppCreated(Uri, String)},
* no matter if it's created successfully or not.
*
* @see #notifyBiInteractiveAppCreated(Uri, String)
* @see #onDestroyBiInteractiveAppRequest(String)
*/
public void onCreateBiInteractiveAppRequest(
@NonNull Uri biIAppUri, @Nullable Bundle params) {
}
/**
* Destroys broadcast-independent(BI) interactive application.
*
* @param biIAppId the BI interactive app ID from
* {@link #onCreateBiInteractiveAppRequest(Uri, Bundle)}
*
* @see #onCreateBiInteractiveAppRequest(Uri, Bundle)
*/
public void onDestroyBiInteractiveAppRequest(@NonNull String biIAppId) {
}
/**
* To toggle Digital Teletext Application if there is one in AIT app list.
* @param enable {@code true} to enable teletext app; {@code false} otherwise.
*/
public void onSetTeletextAppEnabled(boolean enable) {
}
/**
* Receives current video bounds.
*
* @param bounds the rectangle area for rendering the current video.
*/
public void onCurrentVideoBounds(@NonNull Rect bounds) {
}
/**
* Receives current channel URI.
*/
public void onCurrentChannelUri(@Nullable Uri channelUri) {
}
/**
* Receives logical channel number (LCN) of current channel.
*/
public void onCurrentChannelLcn(int lcn) {
}
/**
* Receives current stream volume.
*
* @param volume a volume value between {@code 0.0f} and {@code 1.0f}, inclusive.
*/
public void onStreamVolume(float volume) {
}
/**
* Receives track list.
*/
public void onTrackInfoList(@NonNull List When a scheduled recording is started, this is also called, and the request ID in this
* case is {@code null}.
*
* @param recordingId The ID of the recording started. The TV app should provide and
* maintain this ID to identify the recording in the future.
* @param requestId The ID of the request when
* {@link #requestStartRecording(String, Uri)} is called.
* {@code null} if the recording is not triggered by a
* {@link #requestStartRecording(String, Uri)} request.
* This ID should be created by the {@link TvInteractiveAppService} and
* can be any string.
* @see #onRecordingStopped(String)
*/
public void onRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
}
/**
* This is called when the recording has been stopped.
*
* @param recordingId The ID of the recording stopped. This ID is created and maintained by
* the TV app when the recording was started.
* @see #onRecordingStarted(String, String)
*/
public void onRecordingStopped(@NonNull String recordingId) {
}
/**
* This is called when an error occurred while establishing a connection to the recording
* session for the corresponding TV input.
*
* @param recordingId The ID of the related recording which is sent via
* {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
* @param inputId The ID of the TV input bound to the current TvRecordingClient.
* @see android.media.tv.TvRecordingClient.RecordingCallback#onConnectionFailed(String)
*/
public void onRecordingConnectionFailed(
@NonNull String recordingId, @NonNull String inputId) {
}
/**
* This is called when the connection to the current recording session is lost.
*
* @param recordingId The ID of the related recording which is sent via
* {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
* @param inputId The ID of the TV input bound to the current TvRecordingClient.
* @see android.media.tv.TvRecordingClient.RecordingCallback#onDisconnected(String)
*/
public void onRecordingDisconnected(@NonNull String recordingId, @NonNull String inputId) {
}
/**
* This is called when the recording session has been tuned to the given channel and is
* ready to start recording.
*
* @param recordingId The ID of the related recording which is sent via
* {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
* @param channelUri The URI of the tuned channel.
* @see android.media.tv.TvRecordingClient.RecordingCallback#onTuned(Uri)
*/
public void onRecordingTuned(@NonNull String recordingId, @NonNull Uri channelUri) {
}
/**
* This is called when an issue has occurred. It may be called at any time after the current
* recording session is created until it is released.
*
* @param recordingId The ID of the related recording which is sent via
* {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
* @param err The error code. Should be one of the following.
* The TV Interactive App service should render interactive app UI onto the given
* surface. When called with {@code null}, the Interactive App service should immediately
* free any references to the currently set surface and stop using it.
*
* @param surface The surface to be used for interactive app UI rendering. Can be
* {@code null}.
* @return {@code true} if the surface was set successfully, {@code false} otherwise.
*/
public abstract boolean onSetSurface(@Nullable Surface surface);
/**
* Called after any structural changes (format or size) have been made to the surface passed
* in {@link #onSetSurface}. This method is always called at least once, after
* {@link #onSetSurface} is called with non-null surface.
*
* @param format The new {@link PixelFormat} of the surface.
* @param width The new width of the surface.
* @param height The new height of the surface.
*/
public void onSurfaceChanged(@PixelFormat.Format int format, int width, int height) {
}
/**
* Called when the size of the media view is changed by the application.
*
* This is always called at least once when the session is created regardless of whether
* the media view is enabled or not. The media view container size is the same as the
* containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can
* be different if the surface was changed by calling {@link #layoutSurface}.
*
* @param width The width of the media view, in pixels.
* @param height The height of the media view, in pixels.
*/
public void onMediaViewSizeChanged(@Px int width, @Px int height) {
}
/**
* Called when the application requests to create an media view. Each session
* implementation can override this method and return its own view.
*
* @return a view attached to the media window
*/
@Nullable
public View onCreateMediaView() {
return null;
}
/**
* Releases TvInteractiveAppService session.
*/
public abstract void onRelease();
/**
* Called when the corresponding TV input tuned to a channel.
*
* @param channelUri The tuned channel URI.
*/
public void onTuned(@NonNull Uri channelUri) {
}
/**
* Called when the corresponding TV input selected to a track.
*
* If the track is deselected and no track is currently selected,
* trackId is an empty string.
*/
public void onTrackSelected(@TvTrackInfo.Type int type, @NonNull String trackId) {
}
/**
* Called when the tracks are changed.
*/
public void onTracksChanged(@NonNull List When a selected track changes as a result of a new selection,
* {@link #onTrackSelected(int, String)} should be used instead to communicate the specific
* track selection.
*
* @param tracks A list of {@link TvTrackInfo} that are currently selected
*/
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void onSelectedTrackInfo(@NonNull List Normally, track info cannot be synchronized until the channel has
* been changed. This is used when the session of the {@link TvInteractiveAppService}
* is newly created and the normal synchronization has not happened yet.
*
* The track info will be returned in {@link #onSelectedTrackInfo(List)}
*/
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
@CallSuper
public void requestSelectedTrackInfo() {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "requestSelectedTrackInfo");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestSelectedTrackInfo();
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestSelectedTrackInfo", e);
}
});
}
/**
* Requests starting of recording
*
* This is used to request the active {@link android.media.tv.TvRecordingClient} to
* call {@link android.media.tv.TvRecordingClient#startRecording(Uri)} with the provided
* {@code programUri}.
* A non-null {@code programUri} implies the started recording should be of that specific
* program, whereas null {@code programUri} does not impose such a requirement and the
* recording can span across multiple TV programs.
*
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingStarted(String, String)} for this request is the
* same as the ID sent here. This should be defined by the
* {@link TvInteractiveAppService} and can be any string.
* Should this API be called with the same requestId twice, both
* requests should be handled regardless by the TV application.
* @param programUri The URI for the TV program to record, built by
* {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
* @see android.media.tv.TvRecordingClient#startRecording(Uri)
*/
@CallSuper
public void requestStartRecording(@NonNull String requestId, @Nullable Uri programUri) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "requestStartRecording");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestStartRecording(requestId, programUri);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestStartRecording", e);
}
});
}
/**
* Requests the recording associated with the recordingId to stop.
*
* This is used to request the associated {@link android.media.tv.TvRecordingClient} to
* call {@link android.media.tv.TvRecordingClient#stopRecording()}.
*
* @param recordingId The ID of the recording to stop. This is provided by the TV app in
* {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
* @see android.media.tv.TvRecordingClient#stopRecording()
*/
@CallSuper
public void requestStopRecording(@NonNull String recordingId) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "requestStopRecording");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestStopRecording(recordingId);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestStopRecording", e);
}
});
}
/**
* Requests scheduling of a recording.
*
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingScheduled(String, String)} for this request is the
* same as the ID sent here. This should be defined by the
* {@link TvInteractiveAppService} and can be any string.
* Should this API be called with the same requestId twice, both requests
* should be handled regardless by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param programUri The URI of the TV program to be recorded.
* @param params Domain-specific data for this tune request. Keys must be a scoped
* name, i.e. prefixed with a package name you own, so that different developers
* will not create conflicting keys.
* @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle)
* @see android.media.tv.TvRecordingClient#startRecording(Uri)
*/
@CallSuper
public void requestScheduleRecording(@NonNull String requestId, @NonNull String inputId,
@NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "requestScheduleRecording");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestScheduleRecording(
requestId, inputId, channelUri, programUri, params);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestScheduleRecording", e);
}
});
}
/**
* Requests scheduling of a recording.
*
* @param requestId The ID of this request which is used to match the corresponding
* response. The request ID in
* {@link #onRecordingScheduled(String, String)} for this request is the
* same as the ID sent here. This should be defined by the
* {@link TvInteractiveAppService} and can be any string. Should this API
* be called with the same requestId twice, both requests should be handled
* regardless by the TV application.
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel to be recorded.
* @param startTime The start time of the recording in milliseconds since epoch.
* @param duration The duration of the recording in milliseconds.
* @param repeatDays The repeated days. 0 if not repeated.
* @param params Domain-specific data for this tune request. Keys must be a scoped
* name, i.e. prefixed with a package name you own, so that different developers
* will not create conflicting keys.
* @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle)
* @see android.media.tv.TvRecordingClient#startRecording(Uri)
*/
@CallSuper
public void requestScheduleRecording(@NonNull String requestId, @NonNull String inputId,
@NonNull Uri channelUri, long startTime, long duration, int repeatDays,
@NonNull Bundle params) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "requestScheduleRecording");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestScheduleRecording2(requestId, inputId, channelUri,
startTime, duration, repeatDays, params);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestScheduleRecording", e);
}
});
}
/**
* Sets the recording info for the specified recording
*
* @param recordingId The ID of the recording to set the info for. This is provided by the
* TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
* @param recordingInfo The {@link TvRecordingInfo} to set to the recording.
*/
@CallSuper
public void setTvRecordingInfo(
@NonNull String recordingId, @NonNull TvRecordingInfo recordingInfo) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "setTvRecordingInfo");
}
if (mSessionCallback != null) {
mSessionCallback.onSetTvRecordingInfo(recordingId, recordingInfo);
}
} catch (RemoteException e) {
Log.w(TAG, "error in setTvRecordingInfo", e);
}
});
}
/**
* Gets the recording info for the specified recording
* @param recordingId The ID of the recording to set the info for. This is provided by the
* TV app in
* {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
*/
@CallSuper
public void requestTvRecordingInfo(@NonNull String recordingId) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "requestTvRecordingInfo");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestTvRecordingInfo(recordingId);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestTvRecordingInfo", e);
}
});
}
/**
* Gets a list of {@link TvRecordingInfo} for the specified recording type.
*
* @param type The type of recording to retrieve.
*/
@CallSuper
public void requestTvRecordingInfoList(@TvRecordingInfo.TvRecordingListType int type) {
executeOrPostRunnableOnMainThread(() -> {
try {
if (DEBUG) {
Log.d(TAG, "requestTvRecordingInfoList");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestTvRecordingInfoList(type);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestTvRecordingInfoList", e);
}
});
}
/**
* Requests signing of the given data.
*
* This is used when the corresponding server of the broadcast-independent interactive
* app requires signing during handshaking, and the interactive app service doesn't have
* the built-in private key. The private key is provided by the content providers and
* pre-built in the related app, such as TV app.
*
* @param signingId the ID to identify the request. When a result is received, this ID can
* be used to correlate the result with the request.
* @param algorithm the standard name of the signature algorithm requested, such as
* MD5withRSA, SHA256withDSA, etc. The name is from standards like
* FIPS PUB 186-4 and PKCS #1.
* @param alias the alias of the corresponding {@link java.security.KeyStore}.
* @param data the original bytes to be signed.
*
* @see #onSigningResult(String, byte[])
* @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle)
* @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS
*/
@CallSuper
public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
@NonNull String alias, @NonNull byte[] data) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
public void run() {
try {
if (DEBUG) {
Log.d(TAG, "requestSigning");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestSigning(signingId, algorithm, alias, data);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestSigning", e);
}
}
});
}
/**
* Requests signing of the given data.
*
* This is used when the corresponding server of the broadcast-independent interactive
* app requires signing during handshaking, and the interactive app service doesn't have
* the built-in private key. The private key is provided by the content providers and
* pre-built in the related app, such as TV app.
*
* @param signingId the ID to identify the request. When a result is received, this ID can
* be used to correlate the result with the request.
* @param algorithm the standard name of the signature algorithm requested, such as
* MD5withRSA, SHA256withDSA, etc. The name is from standards like
* FIPS PUB 186-4 and PKCS #1.
* @param host the host of the SSL client authentication server.
* @param port the port of the SSL client authentication server.
* @param data the original bytes to be signed.
*
* @see #onSigningResult(String, byte[])
* @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle)
* @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS
*/
@CallSuper
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
@NonNull String host, int port, @NonNull byte[] data) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
public void run() {
try {
if (DEBUG) {
Log.d(TAG, "requestSigning");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestSigning2(signingId, algorithm,
host, port, data);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestSigning", e);
}
}
});
}
/**
* Requests a SSL certificate for client validation.
*
* @param host the host name of the SSL authentication server.
* @param port the port of the SSL authentication server. E.g., 443
*/
@CallSuper
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void requestCertificate(@NonNull String host, int port) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@Override
public void run() {
try {
if (DEBUG) {
Log.d(TAG, "requestCertificate");
}
if (mSessionCallback != null) {
mSessionCallback.onRequestCertificate(host, port);
}
} catch (RemoteException e) {
Log.w(TAG, "error in requestCertificate", e);
}
}
});
}
/**
* Sends an advertisement request to be processed by the related TV input.
*
* @param request The advertisement request
*/
@CallSuper
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 startInteractiveApp() {
onStartInteractiveApp();
}
void stopInteractiveApp() {
onStopInteractiveApp();
}
void resetInteractiveApp() {
onResetInteractiveApp();
}
void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
onCreateBiInteractiveAppRequest(biIAppUri, params);
}
void destroyBiInteractiveApp(@NonNull String biIAppId) {
onDestroyBiInteractiveAppRequest(biIAppId);
}
void setTeletextAppEnabled(boolean enable) {
onSetTeletextAppEnabled(enable);
}
void sendCurrentVideoBounds(@NonNull Rect bounds) {
onCurrentVideoBounds(bounds);
}
void sendCurrentChannelUri(@Nullable Uri channelUri) {
onCurrentChannelUri(channelUri);
}
void sendCurrentChannelLcn(int lcn) {
onCurrentChannelLcn(lcn);
}
void sendStreamVolume(float volume) {
onStreamVolume(volume);
}
void sendTrackInfoList(@NonNull List
*
* @see android.media.tv.TvRecordingClient.RecordingCallback#onError(int)
*/
public void onRecordingError(
@NonNull String recordingId, @TvInputManager.RecordingError int err) {
}
/**
* This is called when the recording has been scheduled.
*
* @param recordingId The ID assigned to this recording by the app. It can be used to send
* recording related requests such as
* {@link #requestStopRecording(String)}.
* @param requestId The ID of the request when
* {@link #requestScheduleRecording} is called.
* {@code null} if the recording is not triggered by a request.
* This ID should be created by the {@link TvInteractiveAppService} and
* can be any string.
*/
public void onRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) {
}
/**
* Receives signing result.
* @param signingId the ID to identify the request. It's the same as the corresponding ID in
* {@link Session#requestSigning(String, String, String, byte[])}
* @param result the signed result.
*
* @see #requestSigning(String, String, String, byte[])
*/
public void onSigningResult(@NonNull String signingId, @NonNull byte[] result) {
}
/**
* Receives the requested Certificate
*
* @param host the host name of the SSL authentication server.
* @param port the port of the SSL authentication server. E.g., 443
* @param cert the SSL certificate received.
*/
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
public void onCertificate(@NonNull String host, int port, @NonNull SslCertificate cert) {
}
/**
* Called when the application sends information of an error.
*
* @param errMsg the message of the error.
* @param params additional parameters of the error. For example, the signingId of {@link
* TvInteractiveAppCallback#onRequestSigning(String, String, String, String, byte[])}
* can be included to identify the related signing request, and the method name
* "onRequestSigning" can also be added to the params.
*
* @see TvInteractiveAppView#ERROR_KEY_METHOD_NAME
*/
public void onError(@NonNull String errMsg, @NonNull Bundle params) {
}
/**
* Called when the time shift {@link android.media.PlaybackParams} is set or changed.
*
* @param params The new {@link PlaybackParams} that was set or changed.
* @see TvView#timeShiftSetPlaybackParams(PlaybackParams)
*/
public void onTimeShiftPlaybackParams(@NonNull PlaybackParams params) {
}
/**
* Called when time shift status is changed.
*
* @see TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)
* @see android.media.tv.TvInputService.Session#notifyTimeShiftStatusChanged(int)
* @param inputId The ID of the input for which the time shift status has changed.
* @param status The status of which the input has changed to. Should be one of the
* following.
*
*
*/
public void onTimeShiftStatusChanged(
@NonNull String inputId, @TvInputManager.TimeShiftStatus int status) {}
/**
* Called when time shift start position is changed.
*
* @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged(String, long)
* @param inputId The ID of the input for which the time shift start position has changed.
* @param timeMs The start position for time shifting, in milliseconds since the epoch.
*/
public void onTimeShiftStartPositionChanged(@NonNull String inputId, long timeMs) {
}
/**
* Called when time shift current position is changed.
*
* @see TvView.TimeShiftPositionCallback#onTimeShiftCurrentPositionChanged(String, long)
* @param inputId The ID of the input for which the time shift current position has changed.
* @param timeMs The current position for time shifting, in milliseconds since the epoch.
*/
public void onTimeShiftCurrentPositionChanged(@NonNull String inputId, long timeMs) {
}
/**
* Called when the application sets the surface.
*
*