Merge changes Ifa5bb06f,Ie9793424
* changes:
[TIAF] Add more time shift APIs
[TIAF] Add recording callback and cueing message APIs
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index c4f60c3..c52cd59 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -52,6 +52,9 @@
void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
void onAitInfoUpdated(in AitInfo aitInfo, int seq);
void onSignalStrength(int stength, int seq);
+ void onCueingMessageAvailability(boolean available, int seq);
+ void onTimeShiftMode(int mode, int seq);
+ void onAvailableSpeeds(in float[] speeds, int seq);
void onTvMessage(in String type, in Bundle data, int seq);
void onTuned(in Uri channelUri, int seq);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 746cfa2..e9aa321 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -99,6 +99,7 @@
void timeShiftResume(in IBinder sessionToken, int userId);
void timeShiftSeekTo(in IBinder sessionToken, long timeMs, int userId);
void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
+ void timeShiftSetMode(in IBinder sessionToken, int mode, int userId);
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
List<TunedInfo> getCurrentTunedInfos(int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index e6c09a9..82875e5 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -60,6 +60,7 @@
void timeShiftResume();
void timeShiftSeekTo(long timeMs);
void timeShiftSetPlaybackParams(in PlaybackParams params);
+ void timeShiftSetMode(int mode);
void timeShiftEnablePositionTracking(boolean enable);
// For the recording session
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 0111f09..449c2d6 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -49,6 +49,9 @@
void onTimeShiftCurrentPositionChanged(long timeMs);
void onAitInfoUpdated(in AitInfo aitInfo);
void onSignalStrength(int strength);
+ void onCueingMessageAvailability(boolean available);
+ void onTimeShiftMode(int mode);
+ void onAvailableSpeeds(in float[] speeds);
// For the recording session
void onTuned(in Uri channelUri);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 847762f..46573f2 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -30,6 +30,7 @@
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.Surface;
+
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
@@ -75,6 +76,7 @@
private static final int DO_REQUEST_AD = 27;
private static final int DO_NOTIFY_AD_BUFFER = 28;
private static final int DO_SELECT_AUDIO_PRESENTATION = 29;
+ private static final int DO_TIME_SHIFT_SET_MODE = 30;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -218,6 +220,9 @@
mTvInputSessionImpl.timeShiftSetPlaybackParams((PlaybackParams) msg.obj);
break;
}
+ case DO_TIME_SHIFT_SET_MODE: {
+ mTvInputSessionImpl.timeShiftSetMode((Integer) msg.obj);
+ }
case DO_TIME_SHIFT_ENABLE_POSITION_TRACKING: {
mTvInputSessionImpl.timeShiftEnablePositionTracking((Boolean) msg.obj);
break;
@@ -401,6 +406,11 @@
}
@Override
+ public void timeShiftSetMode(int mode) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_TIME_SHIFT_SET_MODE, mode));
+ }
+
+ @Override
public void timeShiftEnablePositionTracking(boolean enable) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(
DO_TIME_SHIFT_ENABLE_POSITION_TRACKING, enable));
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index a4e2212..67d28d0 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -56,7 +56,9 @@
import android.view.KeyEvent;
import android.view.Surface;
import android.view.View;
+
import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -309,6 +311,39 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, prefix = "TIME_SHIFT_MODE_", value = {
+ TIME_SHIFT_MODE_OFF,
+ TIME_SHIFT_MODE_LOCAL,
+ TIME_SHIFT_MODE_NETWORK,
+ TIME_SHIFT_MODE_AUTO})
+ public @interface TimeShiftMode {}
+ /**
+ * Time shift mode: off.
+ * <p>Time shift is disabled.
+ * @hide
+ */
+ public static final int TIME_SHIFT_MODE_OFF = 1;
+ /**
+ * Time shift mode: local.
+ * <p>Time shift is handle locally, using on-device data. E.g. playing a local file.
+ * @hide
+ */
+ public static final int TIME_SHIFT_MODE_LOCAL = 2;
+ /**
+ * Time shift mode: network.
+ * <p>Time shift is handle remotely via network. E.g. online streaming.
+ * @hide
+ */
+ public static final int TIME_SHIFT_MODE_NETWORK = 3;
+ /**
+ * Time shift mode: auto.
+ * <p>Time shift mode is handled automatically.
+ * @hide
+ */
+ public static final int TIME_SHIFT_MODE_AUTO = 4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
RECORDING_ERROR_RESOURCE_BUSY})
public @interface RecordingError {}
@@ -719,6 +754,36 @@
}
/**
+ * This is called when cueing message becomes available or unavailable.
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param available The current availability of cueing message. {@code true} if cueing
+ * message is available; {@code false} if it becomes unavailable.
+ */
+ public void onCueingMessageAvailability(Session session, boolean available) {
+ }
+
+ /**
+ * This is called when time shift mode is set or updated.
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param mode The current time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ */
+ public void onTimeShiftMode(Session session, @TimeShiftMode int mode) {
+ }
+
+ /**
+ * Informs the app available speeds for time-shifting.
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param speeds An ordered array of playback speeds, expressed as values relative to the
+ * normal playback speed 1.0.
+ * @see PlaybackParams#getSpeed()
+ */
+ public void onAvailableSpeeds(Session session, float[] speeds) {
+ }
+
+ /**
* This is called when the session has been tuned to the given channel.
*
* @param channelUri The URI of a channel.
@@ -972,6 +1037,33 @@
});
}
+ void postCueingMessageAvailability(final boolean available) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onCueingMessageAvailability(mSession, available);
+ }
+ });
+ }
+
+ void postTimeShiftMode(final int mode) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onTimeShiftMode(mSession, mode);
+ }
+ });
+ }
+
+ void postAvailableSpeeds(float[] speeds) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onAvailableSpeeds(mSession, speeds);
+ }
+ });
+ }
+
void postTuned(final Uri channelUri) {
mHandler.post(new Runnable() {
@Override
@@ -1475,6 +1567,42 @@
}
@Override
+ public void onCueingMessageAvailability(boolean available, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postCueingMessageAvailability(available);
+ }
+ }
+
+ @Override
+ public void onTimeShiftMode(int mode, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postTimeShiftMode(mode);
+ }
+ }
+
+ @Override
+ public void onAvailableSpeeds(float[] speeds, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postAvailableSpeeds(speeds);
+ }
+ }
+
+ @Override
public void onTuned(Uri channelUri, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -3058,6 +3186,27 @@
}
/**
+ * Sets time shift mode.
+ *
+ * @param mode The time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ * @hide
+ */
+ void timeShiftSetMode(@TimeShiftMode int mode) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.timeShiftSetMode(mToken, mode, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Enable/disable position tracking.
*
* @param enable {@code true} to enable tracking, {@code false} otherwise.
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index b769757..7a4d988d 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -59,11 +59,14 @@
import android.view.WindowManager;
import android.view.accessibility.CaptioningManager;
import android.widget.FrameLayout;
+
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -1082,6 +1085,59 @@
}
/**
+ * Informs the app that the time shift mode is set or updated.
+ *
+ * @param mode The current time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ * @hide
+ */
+ public void notifyTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyTimeShiftMode");
+ if (mSessionCallback != null) {
+ mSessionCallback.onTimeShiftMode(mode);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyTimeShiftMode", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Informs the app available speeds for time-shifting.
+ * <p>This should be called when time-shifting is enabled.
+ *
+ * @param speeds An ordered array of playback speeds, expressed as values relative to the
+ * normal playback speed 1.0.
+ * @see PlaybackParams#getSpeed()
+ * @hide
+ */
+ public void notifyAvailableSpeeds(@NonNull float[] speeds) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyAvailableSpeeds");
+ if (mSessionCallback != null) {
+ Arrays.sort(speeds);
+ mSessionCallback.onAvailableSpeeds(speeds);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyAvailableSpeeds", e);
+ }
+ }
+ });
+ }
+
+ /**
* Notifies signal strength.
*/
public void notifySignalStrength(@TvInputManager.SignalStrength final int strength) {
@@ -1102,6 +1158,33 @@
}
/**
+ * Informs the application that cueing message is available or unavailable.
+ *
+ * <p>The cueing message is used for digital program insertion, based on the standard
+ * ANSI/SCTE 35 2019r1.
+ *
+ * @param available {@code true} if cueing message is available; {@code false} if it becomes
+ * unavailable.
+ * @hide
+ */
+ public void notifyCueingMessageAvailability(boolean available) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyCueingMessageAvailability");
+ if (mSessionCallback != null) {
+ mSessionCallback.onCueingMessageAvailability(available);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyCueingMessageAvailability", e);
+ }
+ }
+ });
+ }
+
+ /**
* Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
* is relative to the overlay view that sits on top of this surface.
*
@@ -1448,6 +1531,18 @@
}
/**
+ * Called when the application sets time shift mode.
+ *
+ * @param mode The time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ * @hide
+ */
+ public void onTimeShiftSetMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
+ }
+
+ /**
* Returns the start position for time shifting, in milliseconds since the epoch.
* Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
* moment.
@@ -1853,6 +1948,13 @@
}
/**
+ * Calls {@link #onTimeShiftSetMode}.
+ */
+ void timeShiftSetMode(int mode) {
+ onTimeShiftSetMode(mode);
+ }
+
+ /**
* Enable/disable position tracking.
*
* @param enable {@code true} to enable tracking, {@code false} otherwise.
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 87d7a0b..b1356f5 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -21,6 +21,7 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.media.tv.TvInputManager;
+import android.media.tv.interactive.TvInteractiveAppView;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -53,6 +54,8 @@
private boolean mIsPaused;
private boolean mIsRecordingStopping;
private final Queue<Pair<String, Bundle>> mPendingAppPrivateCommands = new ArrayDeque<>();
+ private TvInteractiveAppView mTvIAppView;
+ private String mRecordingId;
/**
* Creates a new TvRecordingClient object.
@@ -70,6 +73,26 @@
}
/**
+ * Sets the related {@link TvInteractiveAppView} instance so the interactive app service can be
+ * notified for recording events.
+ *
+ * @param view The related {@link TvInteractiveAppView} instance that is linked to this TV
+ * recording client. {@code null} to unlink the view.
+ * @param recordingId The ID of the recording which is assigned by applications. {@code null} is
+ * valid only when the TvInteractiveAppView parameter is null.
+ * @hide
+ */
+ public void setTvInteractiveAppView(
+ @Nullable TvInteractiveAppView view, @Nullable String recordingId) {
+ if (view != null && recordingId == null) {
+ throw new IllegalArgumentException(
+ "null recordingId is allowed only when the view is null");
+ }
+ mTvIAppView = view;
+ mRecordingId = view == null ? null : recordingId;
+ }
+
+ /**
* Tunes to a given channel for TV program recording. The first tune request will create a new
* recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
@@ -468,6 +491,9 @@
if (mCallback != null) {
mCallback.onConnectionFailed(mInputId);
}
+ if (mTvIAppView != null) {
+ mTvIAppView.notifyRecordingConnectionFailed(mRecordingId, mInputId);
+ }
}
}
@@ -485,7 +511,12 @@
return;
}
mIsTuned = true;
- mCallback.onTuned(channelUri);
+ if (mCallback != null) {
+ mCallback.onTuned(channelUri);
+ }
+ if (mTvIAppView != null) {
+ mTvIAppView.notifyRecordingTuned(mRecordingId, channelUri);
+ }
}
@Override
@@ -506,6 +537,9 @@
if (mCallback != null) {
mCallback.onDisconnected(mInputId);
}
+ if (mTvIAppView != null) {
+ mTvIAppView.notifyRecordingDisconnected(mRecordingId, mInputId);
+ }
}
@Override
@@ -524,7 +558,12 @@
mIsRecordingStarted = false;
mIsPaused = false;
mIsRecordingStopping = false;
- mCallback.onRecordingStopped(recordedProgramUri);
+ if (mCallback != null) {
+ mCallback.onRecordingStopped(recordedProgramUri);
+ }
+ if (mTvIAppView != null) {
+ mTvIAppView.notifyRecordingStopped(mRecordingId);
+ }
}
@Override
@@ -536,7 +575,12 @@
Log.w(TAG, "onError - session not created");
return;
}
- mCallback.onError(error);
+ if (mCallback == null) {
+ mCallback.onError(error);
+ }
+ if (mTvIAppView != null) {
+ mTvIAppView.notifyRecordingError(mRecordingId, error);
+ }
}
@Override
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index c7a63ac..3864983 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -53,9 +53,11 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
+
import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Queue;
@@ -625,6 +627,21 @@
}
/**
+ * Sets time shift mode.
+ *
+ * @param mode The time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ * @hide
+ */
+ public void timeShiftSetMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
+ if (mSession != null) {
+ mSession.timeShiftSetMode(mode);
+ }
+ }
+
+ /**
* Sets the callback to be invoked when the time shift position is changed.
*
* @param callback The callback to receive time shift position changes. A value of {@code null}
@@ -1171,6 +1188,43 @@
}
/**
+ * This is called when cueing message becomes available or unavailable.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param available The current availability of cueing message. {@code true} if cueing
+ * message is available; {@code false} if it becomes unavailable.
+ * @hide
+ */
+ public void onCueingMessageAvailability(@NonNull String inputId, boolean available) {
+ }
+
+ /**
+ * This is called when time shift mode is set or updated.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param mode The current time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ * @hide
+ */
+ public void onTimeShiftMode(
+ @NonNull String inputId, @TvInputManager.TimeShiftMode int mode) {
+ }
+
+ /**
+ * This is called when time-shifting is enabled to inform the available speeds.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param speeds An ordered array of playback speeds, expressed as values relative to the
+ * normal playback speed 1.0.
+ * @see PlaybackParams#getSpeed()
+ * @hide
+ */
+ public void onAvailableSpeeds(@NonNull String inputId, float[] speeds) {
+ }
+
+ /**
* This is called when the session has been tuned to the given channel.
*
* @param channelUri The URI of a channel.
@@ -1545,6 +1599,48 @@
}
@Override
+ public void onCueingMessageAvailability(Session session, boolean available) {
+ if (DEBUG) {
+ Log.d(TAG, "onCueingMessageAvailability(available=" + available + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onCueingMessageAvailability - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onCueingMessageAvailability(mInputId, available);
+ }
+ }
+
+ @Override
+ public void onTimeShiftMode(Session session, int mode) {
+ if (DEBUG) {
+ Log.d(TAG, "onTimeShiftMode(mode=" + mode + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onTimeShiftMode - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onTimeShiftMode(mInputId, mode);
+ }
+ }
+
+ @Override
+ public void onAvailableSpeeds(Session session, float[] speeds) {
+ if (DEBUG) {
+ Log.d(TAG, "onAvailableSpeeds(speeds=" + Arrays.toString(speeds) + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onAvailableSpeeds - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onAvailableSpeeds(mInputId, speeds);
+ }
+ }
+
+ @Override
public void onTuned(Session session, Uri channelUri) {
if (DEBUG) {
Log.d(TAG, "onTuned(channelUri=" + channelUri + ")");
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index ad9312f..aac2d61 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -49,8 +49,14 @@
void onRequestStreamVolume(int seq);
void onRequestTrackInfoList(int seq);
void onRequestCurrentTvInputId(int seq);
+ void onRequestTimeShiftMode(int seq);
+ void onRequestAvailableSpeeds(int seq);
void onRequestStartRecording(in Uri programUri, int seq);
void onRequestStopRecording(in String recordingId, int seq);
+ void onRequestScheduleRecording(in String inputId, in Uri channelUri, in Uri programUri,
+ in Bundle params, int seq);
+ void onRequestScheduleRecording2(in String inputId, in Uri channelUri, long start,
+ long duration, int repeat, in Bundle params, int seq);
void onSetTvRecordingInfo(in String recordingId, in TvRecordingInfo recordingInfo, int seq);
void onRequestTvRecordingInfo(in String recordingId, int seq);
void onRequestTvRecordingInfoList(in int type, int seq);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index c0723f7..e362af2 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -54,6 +54,8 @@
void sendStreamVolume(in IBinder sessionToken, float volume, int userId);
void sendTrackInfoList(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
void sendCurrentTvInputId(in IBinder sessionToken, in String inputId, int userId);
+ void sendTimeShiftMode(in IBinder sessionToken, int mode, int userId);
+ void sendAvailableSpeeds(in IBinder sessionToken, in float[] speeds, int userId);
void sendSigningResult(in IBinder sessionToken, in String signingId, in byte[] result,
int userId);
void sendTvRecordingInfo(in IBinder sessionToken, in TvRecordingInfo recordingInfo, int userId);
@@ -68,6 +70,16 @@
in IBinder sessionToken, in String inputId, long timeMs, int userId);
void notifyTimeShiftCurrentPositionChanged(
in IBinder sessionToken, in String inputId, long timeMs, int userId);
+ void notifyRecordingConnectionFailed(
+ in IBinder sessionToken, in String recordingId, in String inputId, int userId);
+ void notifyRecordingDisconnected(
+ in IBinder sessionToken, in String recordingId, in String inputId, int userId);
+ void notifyRecordingTuned(
+ in IBinder sessionToken, in String recordingId, in Uri channelUri, int userId);
+ void notifyRecordingError(
+ in IBinder sessionToken, in String recordingId, int err, int userId);
+ void notifyRecordingScheduled(
+ in IBinder sessionToken, in String recordingId, in String requestId, int userId);
void createSession(in ITvInteractiveAppClient client, in String iAppServiceId, int type,
int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index 9ae9ca7..8d77141 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -46,6 +46,8 @@
void sendStreamVolume(float volume);
void sendTrackInfoList(in List<TvTrackInfo> tracks);
void sendCurrentTvInputId(in String inputId);
+ void sendTimeShiftMode(int mode);
+ void sendAvailableSpeeds(in float[] speeds);
void sendSigningResult(in String signingId, in byte[] result);
void sendTvRecordingInfo(in TvRecordingInfo recordingInfo);
void sendTvRecordingInfoList(in List<TvRecordingInfo> recordingInfoList);
@@ -54,6 +56,11 @@
void notifyTimeShiftStatusChanged(in String inputId, int status);
void notifyTimeShiftStartPositionChanged(in String inputId, long timeMs);
void notifyTimeShiftCurrentPositionChanged(in String inputId, long timeMs);
+ void notifyRecordingConnectionFailed(in String recordingId, in String inputId);
+ void notifyRecordingDisconnected(in String recordingId, in String inputId);
+ void notifyRecordingTuned(in String recordingId, in Uri channelUri);
+ void notifyRecordingError(in String recordingId, int err);
+ void notifyRecordingScheduled(in String recordingId, in String requestId);
void release();
void notifyTuned(in Uri channelUri);
void notifyTrackSelected(int type, in String trackId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index d84affd..b71f23c 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -48,8 +48,14 @@
void onRequestStreamVolume();
void onRequestTrackInfoList();
void onRequestCurrentTvInputId();
+ void onRequestTimeShiftMode();
+ void onRequestAvailableSpeeds();
void onRequestStartRecording(in Uri programUri);
void onRequestStopRecording(in String recordingId);
+ void onRequestScheduleRecording(in String inputId, in Uri channelUri, in Uri programUri,
+ in Bundle params);
+ void onRequestScheduleRecording2(in String inputId, in Uri channelUri, long start,
+ long duration, int repeat, in Bundle params);
void onSetTvRecordingInfo(in String recordingId, in TvRecordingInfo recordingInfo);
void onRequestTvRecordingInfo(in String recordingId);
void onRequestTvRecordingInfoList(in int type);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index 8a23e65..fa339ce 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -95,6 +95,13 @@
private static final int DO_NOTIFY_TIME_SHIFT_START_POSITION_CHANGED = 38;
private static final int DO_NOTIFY_TIME_SHIFT_CURRENT_POSITION_CHANGED = 39;
private static final int DO_SEND_CURRENT_VIDEO_BOUNDS = 40;
+ private static final int DO_NOTIFY_RECORDING_CONNECTION_FAILED = 41;
+ private static final int DO_NOTIFY_RECORDING_DISCONNECTED = 42;
+ private static final int DO_NOTIFY_RECORDING_TUNED = 43;
+ private static final int DO_NOTIFY_RECORDING_ERROR = 44;
+ private static final int DO_NOTIFY_RECORDING_SCHEDULED = 45;
+ private static final int DO_SEND_TIME_SHIFT_MODE = 46;
+ private static final int DO_SEND_AVAILABLE_SPEEDS = 47;
private final HandlerCaller mCaller;
private Session mSessionImpl;
@@ -182,6 +189,14 @@
mSessionImpl.sendCurrentTvInputId((String) msg.obj);
break;
}
+ case DO_SEND_TIME_SHIFT_MODE: {
+ mSessionImpl.sendTimeShiftMode((Integer) msg.obj);
+ break;
+ }
+ case DO_SEND_AVAILABLE_SPEEDS: {
+ mSessionImpl.sendAvailableSpeeds((float[]) msg.obj);
+ break;
+ }
case DO_SEND_RECORDING_INFO: {
mSessionImpl.sendTvRecordingInfo((TvRecordingInfo) msg.obj);
break;
@@ -311,6 +326,37 @@
args.recycle();
break;
}
+ case DO_NOTIFY_RECORDING_CONNECTION_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyRecordingConnectionFailed(
+ (String) args.arg1, (String) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_RECORDING_DISCONNECTED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyRecordingDisconnected((String) args.arg1, (String) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_RECORDING_TUNED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyRecordingTuned((String) args.arg1, (Uri) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_RECORDING_ERROR: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyRecordingError((String) args.arg1, (Integer) args.arg2);
+ args.recycle();
+ break;
+ }
+ case DO_NOTIFY_RECORDING_SCHEDULED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mSessionImpl.notifyRecordingScheduled((String) args.arg1, (String) args.arg2);
+ args.recycle();
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -396,6 +442,18 @@
}
@Override
+ public void sendTimeShiftMode(int mode) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageI(DO_SEND_TIME_SHIFT_MODE, mode));
+ }
+
+ @Override
+ public void sendAvailableSpeeds(float[] speeds) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SEND_AVAILABLE_SPEEDS, speeds));
+ }
+
+ @Override
public void sendTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageO(DO_SEND_RECORDING_INFO, recordingInfo));
@@ -509,6 +567,36 @@
}
@Override
+ public void notifyRecordingConnectionFailed(String recordingId, String inputId) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
+ DO_NOTIFY_RECORDING_CONNECTION_FAILED, recordingId, inputId));
+ }
+
+ @Override
+ public void notifyRecordingDisconnected(String recordingId, String inputId) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
+ DO_NOTIFY_RECORDING_DISCONNECTED, recordingId, inputId));
+ }
+
+ @Override
+ public void notifyRecordingTuned(String recordingId, Uri channelUri) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
+ DO_NOTIFY_RECORDING_TUNED, recordingId, channelUri));
+ }
+
+ @Override
+ public void notifyRecordingError(String recordingId, int err) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
+ DO_NOTIFY_RECORDING_ERROR, recordingId, err));
+ }
+
+ @Override
+ public void notifyRecordingScheduled(String recordingId, String requestId) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(
+ DO_NOTIFY_RECORDING_SCHEDULED, recordingId, recordingId));
+ }
+
+ @Override
public void setSurface(Surface surface) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index fd3c29b..c88c8d4 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -33,6 +33,7 @@
import android.media.tv.TvInputManager;
import android.media.tv.TvRecordingInfo;
import android.media.tv.TvTrackInfo;
+import android.media.tv.interactive.TvInteractiveAppService.Session;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -517,6 +518,30 @@
}
@Override
+ public void onRequestTimeShiftMode(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestTimeShiftMode();
+ }
+ }
+
+ @Override
+ public void onRequestAvailableSpeeds(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestAvailableSpeeds();
+ }
+ }
+
+ @Override
public void onRequestStartRecording(Uri programUri, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -541,6 +566,33 @@
}
@Override
+ public void onRequestScheduleRecording(String inputId, Uri channelUri, Uri programUri,
+ Bundle params, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestScheduleRecording(inputId, channelUri, programUri, params);
+ }
+ }
+
+ @Override
+ public void onRequestScheduleRecording2(String inputId, Uri channelUri, long startTime,
+ long duration, int repeatDays, Bundle params, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestScheduleRecording(
+ inputId, channelUri, startTime, duration, repeatDays, params);
+ }
+ }
+
+ @Override
public void onSetTvRecordingInfo(String recordingId, TvRecordingInfo recordingInfo,
int seq) {
synchronized (mSessionCallbackRecordMap) {
@@ -1168,6 +1220,30 @@
}
}
+ void sendTimeShiftMode(int mode) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.sendTimeShiftMode(mToken, mode, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void sendAvailableSpeeds(float[] speeds) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.sendAvailableSpeeds(mToken, speeds, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
void sendTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
@@ -1289,6 +1365,66 @@
}
}
+ void notifyRecordingConnectionFailed(@NonNull String recordingId, @NonNull String inputId) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyRecordingConnectionFailed(mToken, recordingId, inputId, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void notifyRecordingDisconnected(@NonNull String recordingId, @NonNull String inputId) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyRecordingDisconnected(mToken, recordingId, inputId, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void notifyRecordingTuned(@NonNull String recordingId, @NonNull Uri channelUri) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyRecordingTuned(mToken, recordingId, channelUri, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void notifyRecordingError(@NonNull String recordingId, int err) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyRecordingError(mToken, recordingId, err, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ void notifyRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyRecordingScheduled(mToken, recordingId, recordingId, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Sets the {@link android.view.Surface} for this session.
*
@@ -1976,6 +2112,24 @@
});
}
+ void postRequestTimeShiftMode() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestTimeShiftMode(mSession);
+ }
+ });
+ }
+
+ void postRequestAvailableSpeeds() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestAvailableSpeeds(mSession);
+ }
+ });
+ }
+
void postRequestStartRecording(Uri programUri) {
mHandler.post(new Runnable() {
@Override
@@ -1994,6 +2148,28 @@
});
}
+ void postRequestScheduleRecording(String inputId, Uri channelUri, Uri programUri,
+ Bundle params) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestScheduleRecording(
+ mSession, inputId, channelUri, programUri, params);
+ }
+ });
+ }
+
+ void postRequestScheduleRecording(String inputId, Uri channelUri, long startTime,
+ long duration, int repeatDays, Bundle params) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestScheduleRecording(
+ mSession, inputId, channelUri, startTime, duration, repeatDays, params);
+ }
+ });
+ }
+
void postRequestSigning(String id, String algorithm, String alias, byte[] data) {
mHandler.post(new Runnable() {
@Override
@@ -2144,7 +2320,7 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#SetVideoBounds} is called.
+ * This is called when {@link TvInteractiveAppService.Session#setVideoBounds} is called.
*
* @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
*/
@@ -2152,7 +2328,7 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestCurrentVideoBounds} is
+ * This is called when {@link TvInteractiveAppService.Session#requestCurrentVideoBounds} is
* called.
*
* @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
@@ -2161,7 +2337,7 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is
+ * This is called when {@link TvInteractiveAppService.Session#requestCurrentChannelUri} is
* called.
*
* @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
@@ -2170,7 +2346,7 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelLcn} is
+ * This is called when {@link TvInteractiveAppService.Session#requestCurrentChannelLcn} is
* called.
*
* @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
@@ -2179,7 +2355,7 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestStreamVolume} is
+ * This is called when {@link TvInteractiveAppService.Session#requestStreamVolume} is
* called.
*
* @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
@@ -2188,7 +2364,7 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestTrackInfoList} is
+ * This is called when {@link TvInteractiveAppService.Session#requestTrackInfoList} is
* called.
*
* @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
@@ -2197,7 +2373,7 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestCurrentTvInputId} is
+ * This is called when {@link TvInteractiveAppService.Session#requestCurrentTvInputId} is
* called.
*
* @param session A {@link TvInteractiveAppService.Session} associated with this callback.
@@ -2206,7 +2382,25 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestStartRecording} is
+ * This is called when {@link TvInteractiveAppService.Session#requestTimeShiftMode()} is
+ * called.
+ *
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ */
+ public void onRequestTimeShiftMode(Session session) {
+ }
+
+ /**
+ * This is called when {@link TvInteractiveAppService.Session#requestAvailableSpeeds()} is
+ * called.
+ *
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ */
+ public void onRequestAvailableSpeeds(Session session) {
+ }
+
+ /**
+ * This is called when {@link TvInteractiveAppService.Session#requestStartRecording} is
* called.
*
* @param session A {@link TvInteractiveAppService.Session} associated with this callback.
@@ -2216,8 +2410,8 @@
}
/**
- * This is called when {@link TvInteractiveAppService.Session#RequestStopRecording} is
- * called.
+ * This is called when {@link TvInteractiveAppService.Session#requestStopRecording(String)}
+ * is called.
*
* @param session A {@link TvInteractiveAppService.Session} associated with this callback.
* @param recordingId The recordingId of the recording to be stopped.
@@ -2227,6 +2421,47 @@
/**
* This is called when
+ * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, Uri, Uri, Bundle)}
+ * is called.
+ *
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ * @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 <em>must</em> 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)
+ */
+ public void onRequestScheduleRecording(Session session, @NonNull String inputId,
+ @NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params) {
+ }
+
+ /**
+ * This is called when
+ * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, Uri, long, long, int, Bundle)}
+ * is called.
+ *
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ * @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 <em>must</em> 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)
+ */
+ public void onRequestScheduleRecording(Session session, @NonNull String inputId,
+ @NonNull Uri channelUri, long startTime, long duration, int repeatDays,
+ @NonNull Bundle params) {
+ }
+
+ /**
+ * This is called when
* {@link TvInteractiveAppService.Session#setTvRecordingInfo(String, TvRecordingInfo)} is
* called.
*
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index d1777cc..360073d 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -224,6 +224,7 @@
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 {}
@@ -262,6 +263,12 @@
* @hide
*/
public static final String TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS = "set_playback_params";
+ /**
+ * Time shift command type: set time shift mode.
+ *
+ * @hide
+ */
+ public static final String TIME_SHIFT_COMMAND_TYPE_SET_MODE = "set_mode";
/**
* Time shift command parameter: program URI.
@@ -287,6 +294,17 @@
* @hide
*/
public static final String COMMAND_PARAMETER_KEY_PLAYBACK_PARAMS = "command_playback_params";
+ /**
+ * Time shift command parameter: playback params.
+ * <p>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
+ * @hide
+ */
+ public static final String COMMAND_PARAMETER_KEY_TIME_SHIFT_MODE = "command_time_shift_mode";
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks =
@@ -570,6 +588,24 @@
}
/**
+ * Receives current time shift mode.
+ * @param mode The current time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ * @hide
+ */
+ public void onTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
+ }
+
+ /**
+ * Receives available speeds.
+ * @hide
+ */
+ public void onAvailableSpeeds(@NonNull float[] speeds) {
+ }
+
+ /**
* Receives requested recording info.
*
* @param recordingInfo The requested recording info. Null if recording not found.
@@ -608,6 +644,77 @@
}
/**
+ * 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 #notifyRecordingStarted(String)}
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onConnectionFailed(String)
+ * @hide
+ */
+ 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 #notifyRecordingStarted(String)}
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onDisconnected(String)
+ * @hide
+ */
+ 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 #notifyRecordingStarted(String)}
+ * @param channelUri The URI of the tuned channel.
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onTuned(Uri)
+ * @hide
+ */
+ 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 #notifyRecordingStarted(String)}
+ * @param err The error code. Should be one of the following.
+ * <ul>
+ * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
+ * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
+ * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
+ * </ul>
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onError(int)
+ * @hide
+ */
+ 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 requestScheduleRecording is called.
+ * {@code null} if the recording is not triggered by a request.
+ * @hide
+ */
+ 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[])}
@@ -1164,6 +1271,54 @@
}
/**
+ * Requests time shift mode.
+ * @hide
+ */
+ @CallSuper
+ public void requestTimeShiftMode() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestTimeShiftMode");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onRequestTimeShiftMode();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestTimeShiftMode", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Requests available speeds for time shift.
+ * @hide
+ */
+ @CallSuper
+ public void requestAvailableSpeeds() {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestAvailableSpeeds");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onRequestAvailableSpeeds();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestAvailableSpeeds", e);
+ }
+ }
+ });
+ }
+
+ /**
* Requests starting of recording
*
* <p> This is used to request the active {@link android.media.tv.TvRecordingClient} to
@@ -1219,7 +1374,71 @@
}
/**
- * Sets the recording info for the specified recording.
+ * Requests scheduling of a recording.
+ *
+ * @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 <em>must</em> 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)
+ * @hide
+ */
+ @CallSuper
+ public void requestScheduleRecording(@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(
+ inputId, channelUri, programUri, params);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestScheduleRecording", e);
+ }
+ });
+ }
+
+ /**
+ * Requests scheduling of a recording.
+ *
+ * @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 <em>must</em> 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)
+ * @hide
+ */
+ @CallSuper
+ public void requestScheduleRecording(@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(
+ 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)}
@@ -1399,6 +1618,14 @@
onCurrentTvInputId(inputId);
}
+ void sendTimeShiftMode(int mode) {
+ onTimeShiftMode(mode);
+ }
+
+ void sendAvailableSpeeds(@NonNull float[] speeds) {
+ onAvailableSpeeds(speeds);
+ }
+
void sendTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) {
onTvRecordingInfo(recordingInfo);
}
@@ -1540,6 +1767,41 @@
}
/**
+ * Calls {@link #onRecordingConnectionFailed(String, String)}.
+ */
+ void notifyRecordingConnectionFailed(String recordingId, String inputId) {
+ onRecordingConnectionFailed(recordingId, inputId);
+ }
+
+ /**
+ * Calls {@link #onRecordingDisconnected(String, String)}.
+ */
+ void notifyRecordingDisconnected(String recordingId, String inputId) {
+ onRecordingDisconnected(recordingId, inputId);
+ }
+
+ /**
+ * Calls {@link #onRecordingTuned(String, Uri)}.
+ */
+ void notifyRecordingTuned(String recordingId, Uri channelUri) {
+ onRecordingTuned(recordingId, channelUri);
+ }
+
+ /**
+ * Calls {@link #onRecordingError(String, int)}.
+ */
+ void notifyRecordingError(String recordingId, int err) {
+ onRecordingError(recordingId, err);
+ }
+
+ /**
+ * Calls {@link #onRecordingScheduled(String, String)}.
+ */
+ void notifyRecordingScheduled(String recordingId, String requestId) {
+ onRecordingScheduled(recordingId, requestId);
+ }
+
+ /**
* Calls {@link #onTimeShiftPlaybackParams(PlaybackParams)}.
*/
void notifyTimeShiftPlaybackParams(PlaybackParams params) {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 4a01440..78c6bcf 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -49,6 +49,7 @@
import android.view.ViewRootImpl;
import java.security.KeyStore;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -596,6 +597,42 @@
}
/**
+ * Sends current time shift mode to related TV interactive app.
+ *
+ * @param mode The current time shift mode. The value is one of the following:
+ * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
+ * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
+ * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
+ * @hide
+ */
+ public void sendTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
+ if (DEBUG) {
+ Log.d(TAG, "sendTimeShiftMode");
+ }
+ if (mSession != null) {
+ mSession.sendTimeShiftMode(mode);
+ }
+ }
+
+ /**
+ * Sends available supported speeds to related TV interactive app.
+ *
+ * @param speeds An ordered array of playback speeds, expressed as values relative to the normal
+ * playback speed 1.0.
+ * @see PlaybackParams#getSpeed()
+ * @hide
+ */
+ public void sendAvailableSpeeds(@NonNull float[] speeds) {
+ if (DEBUG) {
+ Log.d(TAG, "sendAvailableSpeeds");
+ }
+ if (mSession != null) {
+ Arrays.sort(speeds);
+ mSession.sendAvailableSpeeds(speeds);
+ }
+ }
+
+ /**
* Sends the requested {@link android.media.tv.TvRecordingInfo}.
*
* @param recordingInfo The recording info requested {@code null} if no recording found.
@@ -633,6 +670,7 @@
* @see TvInteractiveAppView#notifyRecordingStopped(String)
*/
public void notifyRecordingStarted(@NonNull String recordingId) {
+ // TODO: add request ID to identify and map the corresponding request.
if (DEBUG) {
Log.d(TAG, "notifyRecordingStarted");
}
@@ -767,6 +805,118 @@
}
}
+ /**
+ * This is called to notify the corresponding interactive app service 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 #notifyRecordingStarted(String)}
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onConnectionFailed(String)
+ * @hide
+ */
+ public void notifyRecordingConnectionFailed(
+ @NonNull String recordingId, @NonNull String inputId) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyRecordingConnectionFailed recordingId=" + recordingId
+ + "; inputId=" + inputId);
+ }
+ if (mSession != null) {
+ mSession.notifyRecordingConnectionFailed(recordingId, inputId);
+ }
+ }
+
+ /**
+ * This is called to notify the corresponding interactive app service when the connection to
+ * the current recording session is lost.
+ *
+ * @param recordingId The ID of the related recording which is sent via
+ * {@link #notifyRecordingStarted(String)}
+ * @param inputId The ID of the TV input bound to the current TvRecordingClient.
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onDisconnected(String)
+ * @hide
+ */
+ public void notifyRecordingDisconnected(
+ @NonNull String recordingId, @NonNull String inputId) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyRecordingDisconnected recordingId=" + recordingId
+ + "; inputId=" + inputId);
+ }
+ if (mSession != null) {
+ mSession.notifyRecordingDisconnected(recordingId, inputId);
+ }
+ }
+
+ /**
+ * This is called to notify the corresponding interactive app service 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 #notifyRecordingStarted(String)}
+ * @param channelUri The URI of the tuned channel.
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onTuned(Uri)
+ * @hide
+ */
+ public void notifyRecordingTuned(
+ @NonNull String recordingId, @NonNull Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyRecordingTuned recordingId=" + recordingId
+ + "; channelUri=" + channelUri);
+ }
+ if (mSession != null) {
+ mSession.notifyRecordingTuned(recordingId, channelUri);
+ }
+ }
+
+ /**
+ * This is called to notify the corresponding interactive app service 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 #notifyRecordingStarted(String)}
+ * @param err The error code. Should be one of the following.
+ * <ul>
+ * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
+ * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
+ * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
+ * </ul>
+ * @see android.media.tv.TvRecordingClient.RecordingCallback#onError(int)
+ * @hide
+ */
+ public void notifyRecordingError(
+ @NonNull String recordingId, @TvInputManager.RecordingError int err) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyRecordingError recordingId=" + recordingId
+ + "; err=" + err);
+ }
+ if (mSession != null) {
+ mSession.notifyRecordingError(recordingId, err);
+ }
+ }
+
+ /**
+ * This is called to notify the corresponding interactive app service 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 TvInteractiveAppService.Session#requestStopRecording(String)}.
+ * @param requestId The ID of the request when requestScheduleRecording is called.
+ * {@code null} if the recording is not triggered by a request.
+ * @hide
+ */
+ public void notifyRecordingScheduled(
+ @NonNull String recordingId, @Nullable String requestId) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyRecordingScheduled recordingId=" + recordingId
+ + "; requestId=" + requestId);
+ }
+ if (mSession != null) {
+ mSession.notifyRecordingScheduled(recordingId, requestId);
+ }
+ }
+
private void resetInternal() {
mSessionCallback = null;
if (mSession != null) {
@@ -1012,6 +1162,26 @@
}
/**
+ * This is called when {@link TvInteractiveAppService.Session#requestTimeShiftMode()} is
+ * called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
+ */
+ public void onRequestTimeShiftMode(@NonNull String iAppServiceId) {
+ }
+
+ /**
+ * This is called when {@link TvInteractiveAppService.Session#requestAvailableSpeeds()} is
+ * called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @hide
+ */
+ public void onRequestAvailableSpeeds(@NonNull String iAppServiceId) {
+ }
+
+ /**
* This is called when {@link TvInteractiveAppService.Session#requestStartRecording(Uri)}
* is called.
*
@@ -1041,6 +1211,50 @@
/**
* This is called when
+ * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, Uri, Uri, Bundle)}
+ * is called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @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 <em>must</em> 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)
+ * @hide
+ */
+ public void onRequestScheduleRecording(@NonNull String iAppServiceId,
+ @NonNull String inputId, @NonNull Uri channelUri, @NonNull Uri programUri,
+ @NonNull Bundle params) {
+ }
+
+ /**
+ * This is called when
+ * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, Uri, long, long, int, Bundle)}
+ * is called.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @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 <em>must</em> 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)
+ * @hide
+ */
+ public void onRequestScheduleRecording(@NonNull String iAppServiceId,
+ @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration,
+ int repeatDays, @NonNull Bundle params) {
+ }
+
+ /**
+ * This is called when
* {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
* called.
*
@@ -1453,6 +1667,34 @@
}
@Override
+ public void onRequestTimeShiftMode(Session session) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestTimeShiftMode");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestTimeShiftMode - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onRequestTimeShiftMode(mIAppServiceId);
+ }
+ }
+
+ @Override
+ public void onRequestAvailableSpeeds(Session session) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestAvailableSpeeds");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestAvailableSpeeds - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onRequestAvailableSpeeds(mIAppServiceId);
+ }
+ }
+
+ @Override
public void onRequestStartRecording(Session session, Uri programUri) {
if (DEBUG) {
Log.d(TAG, "onRequestStartRecording");
@@ -1496,6 +1738,22 @@
}
@Override
+ public void onRequestScheduleRecording(Session session, @NonNull String inputId,
+ @NonNull Uri channelUri, Uri progarmUri, @NonNull Bundle params) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestScheduleRecording");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestScheduleRecording - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onRequestScheduleRecording(mIAppServiceId, inputId, channelUri,
+ progarmUri, params);
+ }
+ }
+
+ @Override
public void onRequestTvRecordingInfo(Session session,
String recordingId) {
if (DEBUG) {
@@ -1525,6 +1783,22 @@
}
}
+ public void onRequestScheduleRecording(Session session, @NonNull String inputId,
+ @NonNull Uri channelUri, long startTime, long duration, int repeatDays,
+ @NonNull Bundle params) {
+ if (DEBUG) {
+ Log.d(TAG, "onRequestScheduleRecording");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onRequestScheduleRecording - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onRequestScheduleRecording(mIAppServiceId, inputId, channelUri, startTime,
+ duration, repeatDays, params);
+ }
+ }
+
@Override
public void onRequestSigning(
Session session, String id, String algorithm, String alias, byte[] data) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 0928bef..404ca01 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -93,6 +93,7 @@
import android.util.SparseArray;
import android.view.InputChannel;
import android.view.Surface;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -103,7 +104,9 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.android.server.SystemService;
+
import dalvik.annotation.optimization.NeverCompile;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -2081,6 +2084,26 @@
}
@Override
+ public void timeShiftSetMode(IBinder sessionToken, int mode, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "timeShiftSetMode");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .timeShiftSetMode(mode);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in timeShiftSetMode", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
int userId) {
final int callingUid = Binder.getCallingUid();
@@ -3654,6 +3677,57 @@
}
@Override
+ public void onCueingMessageAvailability(boolean available) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onCueingMessageAvailability(" + available + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onCueingMessageAvailability(available, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onCueingMessageAvailability", e);
+ }
+ }
+ }
+
+ @Override
+ public void onTimeShiftMode(int mode) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onTimeShiftMode(" + mode + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onTimeShiftMode(mode, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onTimeShiftMode", e);
+ }
+ }
+ }
+
+ @Override
+ public void onAvailableSpeeds(float[] speeds) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAvailableSpeeds(" + Arrays.toString(speeds) + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAvailableSpeeds(speeds, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAvailableSpeeds", e);
+ }
+ }
+ }
+
+ @Override
public void onTuned(Uri channelUri) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index d4f2f2d..f829449 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -1486,6 +1486,56 @@
}
@Override
+ public void sendTimeShiftMode(IBinder sessionToken, int mode, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "sendTimeShiftMode(mode=%d)", mode);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "sendTimeShiftMode");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).sendTimeShiftMode(mode);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in sendTimeShiftMode", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void sendAvailableSpeeds(IBinder sessionToken, float[] speeds, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "sendAvailableSpeeds(speeds=%s)", Arrays.toString(speeds));
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "sendAvailableSpeeds");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).sendAvailableSpeeds(speeds);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in sendAvailableSpeeds", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void sendTvRecordingInfo(IBinder sessionToken, TvRecordingInfo recordingInfo,
int userId) {
if (DEBUG) {
@@ -1701,6 +1751,151 @@
}
@Override
+ public void notifyRecordingConnectionFailed(
+ IBinder sessionToken, String recordingId, String inputId, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "notifyRecordingConnectionFailed(recordingId=%s, inputId=%s)",
+ recordingId, inputId);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(
+ Binder.getCallingPid(), callingUid, userId,
+ "notifyRecordingConnectionFailed");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState =
+ getSessionStateLocked(sessionToken, callingUid, resolvedUserId);
+ getSessionLocked(sessionState).notifyRecordingConnectionFailed(
+ recordingId, inputId);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyRecordingConnectionFailed", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void notifyRecordingDisconnected(
+ IBinder sessionToken, String recordingId, String inputId, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "notifyRecordingDisconnected(recordingId=%s, inputId=%s)",
+ recordingId, inputId);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(
+ Binder.getCallingPid(), callingUid, userId,
+ "notifyRecordingDisconnected");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState =
+ getSessionStateLocked(sessionToken, callingUid, resolvedUserId);
+ getSessionLocked(sessionState).notifyRecordingDisconnected(
+ recordingId, inputId);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyRecordingDisconnected", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void notifyRecordingTuned(
+ IBinder sessionToken, String recordingId, Uri channelUri, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "notifyRecordingTuned(recordingId=%s, channelUri=%s)",
+ recordingId, channelUri.toString());
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(
+ Binder.getCallingPid(), callingUid, userId,
+ "notifyRecordingTuned");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState =
+ getSessionStateLocked(sessionToken, callingUid, resolvedUserId);
+ getSessionLocked(sessionState).notifyRecordingTuned(
+ recordingId, channelUri);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyRecordingTuned", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void notifyRecordingError(
+ IBinder sessionToken, String recordingId, int err, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "notifyRecordingError(recordingId=%s, err=%d)",
+ recordingId, err);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(
+ Binder.getCallingPid(), callingUid, userId,
+ "notifyRecordingError");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState =
+ getSessionStateLocked(sessionToken, callingUid, resolvedUserId);
+ getSessionLocked(sessionState).notifyRecordingError(
+ recordingId, err);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyRecordingError", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void notifyRecordingScheduled(
+ IBinder sessionToken, String recordingId, String requestId, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "notifyRecordingScheduled(recordingId=%s, requestId=%s)",
+ recordingId, requestId);
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(
+ Binder.getCallingPid(), callingUid, userId,
+ "notifyRecordingScheduled");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState =
+ getSessionStateLocked(sessionToken, callingUid, resolvedUserId);
+ getSessionLocked(sessionState).notifyRecordingScheduled(
+ recordingId, requestId);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyRecordingScheduled", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void setSurface(IBinder sessionToken, Surface surface, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -2602,6 +2797,40 @@
}
@Override
+ public void onRequestTimeShiftMode() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestTimeShiftMode");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestTimeShiftMode(mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestTimeShiftMode", e);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestAvailableSpeeds() {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestAvailableSpeeds");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestAvailableSpeeds(mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestAvailableSpeeds", e);
+ }
+ }
+ }
+
+ @Override
public void onRequestStartRecording(Uri programUri) {
synchronized (mLock) {
if (DEBUG) {
@@ -2636,6 +2865,44 @@
}
@Override
+ public void onRequestScheduleRecording(
+ String inputId, Uri channelUri, Uri programUri, Bundle params) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestScheduleRecording");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestScheduleRecording(
+ inputId, channelUri, programUri, params, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestScheduleRecording", e);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestScheduleRecording2(String inputId, Uri channelUri, long start,
+ long duration, int repeat, Bundle params) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestScheduleRecording2");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestScheduleRecording2(inputId, channelUri, start,
+ duration, repeat, params, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestScheduleRecording2", e);
+ }
+ }
+ }
+
+ @Override
public void onSetTvRecordingInfo(String recordingId, TvRecordingInfo recordingInfo) {
synchronized (mLock) {
if (DEBUG) {