Merge "TIF: Redefine time shift status and change callback and position APIs"
diff --git a/api/current.txt b/api/current.txt
index b6380d7..b6a630b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17519,9 +17519,10 @@
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
- field public static final int TIME_SHIFT_STATUS_AVAILABLE = 0; // 0x0
- field public static final int TIME_SHIFT_STATUS_ERROR = 2; // 0x2
- field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 1; // 0x1
+ field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
+ field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
+ field public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; // 0x0
+ field public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3
field public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0
@@ -17556,7 +17557,6 @@
method public void notifyChannelRetuned(android.net.Uri);
method public void notifyContentAllowed();
method public void notifyContentBlocked(android.media.tv.TvContentRating);
- method public void notifyTimeShiftStartPositionChanged(long);
method public void notifyTimeShiftStatusChanged(int);
method public void notifyTrackSelected(int, java.lang.String);
method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
@@ -17575,6 +17575,7 @@
method public abstract boolean onSetSurface(android.view.Surface);
method public void onSurfaceChanged(int, int, int);
method public long onTimeShiftGetCurrentPosition();
+ method public long onTimeShiftGetStartPosition();
method public void onTimeShiftPause();
method public void onTimeShiftResume();
method public void onTimeShiftSeekTo(long);
@@ -17627,19 +17628,18 @@
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
method protected void onLayout(boolean, int, int, int, int);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
- method public void registerTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
method public void reset();
method public void selectTrack(int, java.lang.String);
method public void setCallback(android.media.tv.TvView.TvInputCallback);
method public void setCaptionEnabled(boolean);
method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
method public void setStreamVolume(float);
+ method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
method public void timeShiftPause();
method public void timeShiftResume();
method public void timeShiftSeekTo(long);
method public void timeShiftSetPlaybackRate(float, int);
method public void tune(java.lang.String, android.net.Uri);
- method public void unregisterTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
}
public static abstract interface TvView.OnUnhandledInputEventListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index a65c35d..318d1db 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -18882,9 +18882,10 @@
field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final java.lang.String META_DATA_CONTENT_RATING_SYSTEMS = "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
- field public static final int TIME_SHIFT_STATUS_AVAILABLE = 0; // 0x0
- field public static final int TIME_SHIFT_STATUS_ERROR = 2; // 0x2
- field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 1; // 0x1
+ field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
+ field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
+ field public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; // 0x0
+ field public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3
field public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0
@@ -18972,7 +18973,6 @@
method public void notifyContentAllowed();
method public void notifyContentBlocked(android.media.tv.TvContentRating);
method public void notifySessionEvent(java.lang.String, android.os.Bundle);
- method public void notifyTimeShiftStartPositionChanged(long);
method public void notifyTimeShiftStatusChanged(int);
method public void notifyTrackSelected(int, java.lang.String);
method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
@@ -18994,6 +18994,7 @@
method public abstract boolean onSetSurface(android.view.Surface);
method public void onSurfaceChanged(int, int, int);
method public long onTimeShiftGetCurrentPosition();
+ method public long onTimeShiftGetStartPosition();
method public void onTimeShiftPause();
method public void onTimeShiftResume();
method public void onTimeShiftSeekTo(long);
@@ -19070,7 +19071,6 @@
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
method protected void onLayout(boolean, int, int, int, int);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
- method public void registerTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
method public void requestUnblockContent(android.media.tv.TvContentRating);
method public void reset();
method public void selectTrack(int, java.lang.String);
@@ -19080,6 +19080,7 @@
method public void setMain();
method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
method public void setStreamVolume(float);
+ method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
method public void timeShiftPause();
@@ -19088,7 +19089,6 @@
method public void timeShiftSetPlaybackRate(float, int);
method public void tune(java.lang.String, android.net.Uri);
method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
- method public void unregisterTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
}
public static abstract interface TvView.OnUnhandledInputEventListener {
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index f96469e..b6491d8 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -78,7 +78,7 @@
void timeShiftResume(in IBinder sessionToken, int userId);
void timeShiftSeekTo(in IBinder sessionToken, long timeMs, int userId);
void timeShiftSetPlaybackRate(in IBinder sessionToken, float rate, int audioMode, int userId);
- void timeShiftTrackCurrentPosition(in IBinder sessionToken, boolean enabled, int userId);
+ void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 306abb8..a054200 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -51,5 +51,5 @@
void timeShiftResume();
void timeShiftSeekTo(long timeMs);
void timeShiftSetPlaybackRate(float rate, int audioMode);
- void timeShiftTrackCurrentPosition(boolean enabled);
+ void timeShiftEnablePositionTracking(boolean enable);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index f22a8fc..a3442e3 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -61,7 +61,7 @@
private static final int DO_TIME_SHIFT_RESUME = 15;
private static final int DO_TIME_SHIFT_SEEK_TO = 16;
private static final int DO_TIME_SHIFT_SET_PLAYBACK_RATE = 17;
- private static final int DO_TIME_SHIFT_TRACK_CURRENT_POSITION = 18;
+ private static final int DO_TIME_SHIFT_ENABLE_POSITION_TRACKING = 18;
private final HandlerCaller mCaller;
@@ -174,8 +174,8 @@
mTvInputSessionImpl.timeShiftSetPlaybackRate((Float) msg.obj, msg.arg1);
break;
}
- case DO_TIME_SHIFT_TRACK_CURRENT_POSITION: {
- mTvInputSessionImpl.timeShiftTrackCurrentPosition((Boolean) msg.obj);
+ case DO_TIME_SHIFT_ENABLE_POSITION_TRACKING: {
+ mTvInputSessionImpl.timeShiftEnablePositionTracking((Boolean) msg.obj);
break;
}
default: {
@@ -290,9 +290,9 @@
}
@Override
- public void timeShiftTrackCurrentPosition(boolean enabled) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TIME_SHIFT_TRACK_CURRENT_POSITION,
- Boolean.valueOf(enabled)));
+ public void timeShiftEnablePositionTracking(boolean enable) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(
+ DO_TIME_SHIFT_ENABLE_POSITION_TRACKING, Boolean.valueOf(enable)));
}
private final class TvInputEventReceiver extends InputEventReceiver {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index a4d8174..3c67ea0 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.graphics.Rect;
+import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -72,24 +73,29 @@
public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = VIDEO_UNAVAILABLE_REASON_END;
private static final int TIME_SHIFT_STATUS_START = 0;
- private static final int TIME_SHIFT_STATUS_END = 2;
+ private static final int TIME_SHIFT_STATUS_END = 3;
/**
- * Time shifting is available. In this status, the application can pause/resume the playback,
- * seek to a specific position, and change the playback rate.
+ * Status prior to calling {@link TvInputService.Session#notifyTimeShiftStatusChanged}.
*/
- public static final int TIME_SHIFT_STATUS_AVAILABLE = TIME_SHIFT_STATUS_START;
+ public static final int TIME_SHIFT_STATUS_UNKNOWN = TIME_SHIFT_STATUS_START;
/**
- * Time shifting is not available.
+ * The TV input does not support time shifting.
*/
- public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 1;
+ public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1;
/**
- * An error occurred while handling a time shift request. To recover the status, tune to a
- * new channel.
+ * Time shifting is currently not available but might work again later.
*/
- public static final int TIME_SHIFT_STATUS_ERROR = TIME_SHIFT_STATUS_END;
+ public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2;
+
+ /**
+ * Time shifting is currently available. In this status, the application assumes it can
+ * pause/resume playback, seek to a specified time position and set playback rate and audio
+ * mode.
+ */
+ public static final int TIME_SHIFT_STATUS_AVAILABLE = TIME_SHIFT_STATUS_END;
public static final long TIME_SHIFT_INVALID_TIME = Long.MIN_VALUE;
@@ -352,35 +358,41 @@
}
/**
- * This is called when the trick play status is changed.
+ * This is called when the time shift status is changed.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
- * @param status The current time shift status:
+ * @param status The current time shift status. Should be one of the followings.
* <ul>
- * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
+ * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
* <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
- * <li>{@link TvInputManager#TIME_SHIFT_STATUS_ERROR}
+ * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
* </ul>
*/
public void onTimeShiftStatusChanged(Session session, int status) {
}
/**
- * This is called when the time shift start position is changed. The application may seek to
- * a position in the range from the start position and the current time, inclusive.
+ * This is called when the start playback position is changed.
+ * <p>
+ * The start playback position of the time shifted program should be adjusted when the TV
+ * input cannot retain the whole recorded program due to some reason (e.g. limitation on
+ * storage space). This is necessary to prevent the application from allowing the user to
+ * seek to a time position that is not reachable.
+ * </p>
*
* @param session A {@link TvInputManager.Session} associated with this callback.
- * @param timeMs The start of the possible time shift range, in milliseconds since the
- * epoch.
+ * @param timeMs The start playback position of the time shifted program, in milliseconds
+ * since the epoch.
*/
public void onTimeShiftStartPositionChanged(Session session, long timeMs) {
}
/**
- * This is called when the current position is changed.
+ * This is called when the current playback position is changed.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
- * @param timeMs The current position, in milliseconds since the epoch.
+ * @param timeMs The current playback position of the time shifted program, in milliseconds
+ * since the epoch.
*/
public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) {
}
@@ -893,7 +905,7 @@
}
}
} catch (RemoteException e) {
- Log.e(TAG, "TvInputManager initialization failed: " + e);
+ Log.e(TAG, "TvInputManager initialization failed", e);
}
}
@@ -1677,12 +1689,13 @@
}
/**
- * Seeks to the specific time position. The position should be in the range from the start
- * time from the start time,
- * {@link TvInputCallback#onTimeShiftStartPositionChanged(String, long)}, to the current
- * time, inclusive.
+ * Seeks to a specified time position.
+ * <p>
+ * Normally, the position is given within range between the start and the current time,
+ * inclusively.
*
- * @param timeMs The target time, in milliseconds since the epoch.
+ * @param timeMs The time position to seek to, in milliseconds since the epoch.
+ * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged
*/
void timeShiftSeekTo(long timeMs) {
if (mToken == null) {
@@ -1697,10 +1710,10 @@
}
/**
- * Sets a playback rate and an audio mode.
+ * Sets playback rate and audio mode.
*
* @param rate The ratio between desired playback rate and normal one.
- * @param audioMode The audio playback mode. Must be one of the supported audio modes:
+ * @param audioMode Audio playback mode. Must be one of the supported audio modes:
* <ul>
* <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
* </ul>
@@ -1710,6 +1723,9 @@
Log.w(TAG, "The session has been already released");
return;
}
+ if (audioMode != MediaPlayer.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE) {
+ throw new IllegalArgumentException("Unknown audio playback mode " + audioMode);
+ }
try {
mService.timeShiftSetPlaybackRate(mToken, rate, audioMode, mUserId);
} catch (RemoteException e) {
@@ -1718,15 +1734,17 @@
}
/**
- * Returns the current playback position.
+ * Enable/disable position tracking.
+ *
+ * @param enable {@code true} to enable tracking, {@code false} otherwise.
*/
- void timeShiftTrackCurrentPosition(boolean enabled) {
+ void timeShiftEnablePositionTracking(boolean enable) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.timeShiftTrackCurrentPosition(mToken, enabled, mUserId);
+ mService.timeShiftEnablePositionTracking(mToken, enable, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 93abc2b..4e7aaa0 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -250,10 +250,10 @@
private boolean mOverlayViewEnabled;
private IBinder mWindowToken;
private Rect mOverlayFrame;
+ private long mStartPositionMs;
private long mCurrentPositionMs;
- private final TimeShiftCurrentPositionTrackingRunnable
- mTimeShiftCurrentPositionTrackingRunnable =
- new TimeShiftCurrentPositionTrackingRunnable();
+ private final TimeShiftPositionTrackingRunnable
+ mTimeShiftPositionTrackingRunnable = new TimeShiftPositionTrackingRunnable();
private final Object mLock = new Object();
// @GuardedBy("mLock")
@@ -320,7 +320,7 @@
mSessionCallback.onSessionEvent(eventType, eventArgs);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in sending event (event=" + eventType + ")");
+ Log.w(TAG, "error in sending event (event=" + eventType + ")", e);
}
}
});
@@ -341,7 +341,7 @@
mSessionCallback.onChannelRetuned(channelUri);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyChannelRetuned");
+ Log.w(TAG, "error in notifyChannelRetuned", e);
}
}
});
@@ -380,7 +380,7 @@
mSessionCallback.onTracksChanged(tracks);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyTracksChanged");
+ Log.w(TAG, "error in notifyTracksChanged", e);
}
}
});
@@ -410,7 +410,7 @@
mSessionCallback.onTrackSelected(type, trackId);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyTrackSelected");
+ Log.w(TAG, "error in notifyTrackSelected", e);
}
}
});
@@ -433,7 +433,7 @@
mSessionCallback.onVideoAvailable();
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyVideoAvailable");
+ Log.w(TAG, "error in notifyVideoAvailable", e);
}
}
});
@@ -467,7 +467,7 @@
mSessionCallback.onVideoUnavailable(reason);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyVideoUnavailable");
+ Log.w(TAG, "error in notifyVideoUnavailable", e);
}
}
});
@@ -508,7 +508,7 @@
mSessionCallback.onContentAllowed();
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyContentAllowed");
+ Log.w(TAG, "error in notifyContentAllowed", e);
}
}
});
@@ -550,30 +550,36 @@
mSessionCallback.onContentBlocked(rating.flattenToString());
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyContentBlocked");
+ Log.w(TAG, "error in notifyContentBlocked", e);
}
}
});
}
/**
- * Informs the application that the trick play status is changed.
+ * Informs the application that the time shift status is changed.
* <p>
- * The application assumes that time shift is not available by default. So, the
- * implementation should call this method with
- * {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} on tune request, if the time shift is
- * available in the given channel.
- * Note that sending {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} means the session
- * implemented {@link #onTimeShiftPause}, {@link #onTimeShiftResume},
- * {@link #onTimeShiftSeekTo}, {@link #onTimeShiftGetCurrentPosition}, and
- * {@link #onTimeShiftSetPlaybackRate}, and these are working at the moment.
+ * Prior to calling this method, the application assumes the status
+ * {@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}. Right after the session is created, it
+ * is important to invoke the method with the status
+ * {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} if the implementation does support
+ * time shifting, or {@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED} otherwise. Failure
+ * to notifying the current status change immediately might result in an undesirable
+ * behavior in the application such as hiding the play controls.
+ * </p><p>
+ * If the status {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} is reported, the
+ * application assumes it can pause/resume playback, seek to a specified time position and
+ * set playback rate and audio mode. The implementation should override
+ * {@link #onTimeShiftPause}, {@link #onTimeShiftResume}, {@link #onTimeShiftSeekTo},
+ * {@link #onTimeShiftGetStartPosition}, {@link #onTimeShiftGetCurrentPosition} and
+ * {@link #onTimeShiftSetPlaybackRate}.
* </p>
*
- * @param status The current time shift status:
+ * @param status The current time shift status. Should be one of the followings.
* <ul>
- * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
+ * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
* <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
- * <li>{@link TvInputManager#TIME_SHIFT_STATUS_ERROR}
+ * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
* </ul>
*/
public void notifyTimeShiftStatusChanged(final int status) {
@@ -586,23 +592,13 @@
mSessionCallback.onTimeShiftStatusChanged(status);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyTimeShiftStatusChanged");
+ Log.w(TAG, "error in notifyTimeShiftStatusChanged", e);
}
}
});
}
- /**
- * Informs the application that the time shift start position is changed.
- * <p>
- * The application may seek to a position in the range from the start position and the
- * current time, inclusive. So, the implementation should call this whenever the range is
- * updated.
- * </p>
- *
- * @param timeMs the start of possible time shift range, in milliseconds since the epoch.
- */
- public void notifyTimeShiftStartPositionChanged(final long timeMs) {
+ private void notifyTimeShiftStartPositionChanged(final long timeMs) {
executeOrPostRunnable(new Runnable() {
@Override
public void run() {
@@ -612,17 +608,12 @@
mSessionCallback.onTimeShiftStartPositionChanged(timeMs);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyTimeShiftStartPositionChanged");
+ Log.w(TAG, "error in notifyTimeShiftStartPositionChanged", e);
}
}
});
}
- /**
- * Informs the application that the current playback position is changed.
- *
- * @param timeMs The current position, in milliseconds since the epoch.
- */
private void notifyTimeShiftCurrentPositionChanged(final long timeMs) {
executeOrPostRunnable(new Runnable() {
@Override
@@ -633,7 +624,7 @@
mSessionCallback.onTimeShiftCurrentPositionChanged(timeMs);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in notifyTimeShiftCurrentPositionChanged");
+ Log.w(TAG, "error in notifyTimeShiftCurrentPositionChanged", e);
}
}
});
@@ -666,7 +657,7 @@
mSessionCallback.onLayoutSurface(left, top, right, bottom);
}
} catch (RemoteException e) {
- Log.w(TAG, "error in layoutSurface");
+ Log.w(TAG, "error in layoutSurface", e);
}
}
});
@@ -711,7 +702,7 @@
* When {@code setSurface(null)} is called, the implementation should stop using the Surface
* object previously given and release any references to it.
*
- * @param surface possibly {@code null} {@link Surface} an application passes to this TV
+ * @param surface possibly {@code null} {@link Surface} the application passes to this TV
* input session.
* @return {@code true} if the surface was set, {@code false} otherwise.
*/
@@ -730,10 +721,10 @@
}
/**
- * Called when a size of an overlay view is changed by an application. Even when the overlay
- * view is disabled by {@link #setOverlayViewEnabled}, this is called. The size is same as
- * the size of {@link Surface} in general. Once {@link #layoutSurface} is called, the sizes
- * of {@link Surface} and the overlay view can be different.
+ * Called when a size of an overlay view is changed by the application. Even when the
+ * overlay view is disabled by {@link #setOverlayViewEnabled}, this is called. The size is
+ * same as the size of {@link Surface} in general. Once {@link #layoutSurface} is called,
+ * the sizes of {@link Surface} and the overlay view can be different.
*
* @param width The width of the overlay view.
* @param height The height of the overlay view.
@@ -836,7 +827,7 @@
}
/**
- * Called when an application requests to create an overlay view. Each session
+ * Called when the application requests to create an overlay view. Each session
* implementation can override this method and return its own view.
*
* @return a view attached to the overlay window
@@ -846,66 +837,94 @@
}
/**
- * Called when an application requests to pause the playback.
+ * Called when the application requests to pause playback.
*
- * @see #onTimeShiftResume()
- * @see #onTimeShiftSeekTo(long)
- * @see #onTimeShiftSetPlaybackRate(float, int)
- * @see #onTimeShiftGetCurrentPosition()
+ * @see #onTimeShiftResume
+ * @see #onTimeShiftSeekTo
+ * @see #onTimeShiftSetPlaybackRate
+ * @see #onTimeShiftGetStartPosition
+ * @see #onTimeShiftGetCurrentPosition
*/
public void onTimeShiftPause() {
}
/**
- * Called when an application requests to resume the playback.
+ * Called when the application requests to resume playback.
*
- * @see #onTimeShiftPause()
- * @see #onTimeShiftSeekTo(long)
- * @see #onTimeShiftSetPlaybackRate(float, int)
- * @see #onTimeShiftGetCurrentPosition()
+ * @see #onTimeShiftPause
+ * @see #onTimeShiftSeekTo
+ * @see #onTimeShiftSetPlaybackRate
+ * @see #onTimeShiftGetStartPosition
+ * @see #onTimeShiftGetCurrentPosition
*/
public void onTimeShiftResume() {
}
/**
- * Called when an application requests to seek to a specific position. The {@code timeMs} is
- * expected to be in a range from the start time,
- * {@link #notifyTimeShiftStartPositionChanged(long)}, to the current time, inclusive. If it
- * is not, the implementation should seek to the nearest time position in the range.
+ * Called when the application requests to seek to a specified time position. Normally, the
+ * position is given within range between the start and the current time, inclusively. The
+ * implementation is expected to seek to the nearest time position if the given position is
+ * not in the range.
*
- * @param timeMs The target time, in milliseconds since the epoch
- * @see #onTimeShiftResume()
- * @see #onTimeShiftPause()
- * @see #onTimeShiftSetPlaybackRate(float, int)
- * @see #onTimeShiftGetCurrentPosition()
+ * @param timeMs The time position to seek to, in milliseconds since the epoch.
+ * @see #onTimeShiftResume
+ * @see #onTimeShiftPause
+ * @see #onTimeShiftSetPlaybackRate
+ * @see #onTimeShiftGetStartPosition
+ * @see #onTimeShiftGetCurrentPosition
*/
public void onTimeShiftSeekTo(long timeMs) {
}
/**
- * Called when an application sets a playback rate and an audio mode.
+ * Called when the application sets playback rate and audio mode.
*
* @param rate The ratio between desired playback rate and normal one.
- * @param audioMode The audio playback mode. Must be one of the supported audio modes:
+ * @param audioMode Audio playback mode. Must be one of the supported audio modes:
* <ul>
* <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
* </ul>
- * @see #onTimeShiftResume()
- * @see #onTimeShiftPause()
- * @see #onTimeShiftSeekTo(long)
- * @see #onTimeShiftGetCurrentPosition()
+ * @see #onTimeShiftResume
+ * @see #onTimeShiftPause
+ * @see #onTimeShiftSeekTo
+ * @see #onTimeShiftGetStartPosition
+ * @see #onTimeShiftGetCurrentPosition
*/
public void onTimeShiftSetPlaybackRate(float rate, int audioMode) {
}
/**
- * Returns the current playback position in milliseconds since the epoch.
- * {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if position is unknown at this moment.
+ * Returns the start playback position for time shifting, in milliseconds since the epoch.
+ * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
+ * moment.
+ * <p>
+ * The start playback position of the time shifted program should be adjusted when the
+ * implementation cannot retain the whole recorded program due to some reason (e.g.
+ * limitation on storage space). It is the earliest possible time position that the user can
+ * seek to, thus failure to notifying its change immediately might result in bad experience
+ * where the application allows the user to seek to an invalid time position.
+ * </p>
*
- * @see #onTimeShiftResume()
- * @see #onTimeShiftPause()
- * @see #onTimeShiftSeekTo(long)
- * @see #onTimeShiftSetPlaybackRate(float, int)
+ * @see #onTimeShiftResume
+ * @see #onTimeShiftPause
+ * @see #onTimeShiftSeekTo
+ * @see #onTimeShiftSetPlaybackRate
+ * @see #onTimeShiftGetCurrentPosition
+ */
+ public long onTimeShiftGetStartPosition() {
+ return TvInputManager.TIME_SHIFT_INVALID_TIME;
+ }
+
+ /**
+ * Returns the current playback position for time shifting, in milliseconds since the epoch.
+ * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the
+ * moment.
+ *
+ * @see #onTimeShiftResume
+ * @see #onTimeShiftPause
+ * @see #onTimeShiftSeekTo
+ * @see #onTimeShiftSetPlaybackRate
+ * @see #onTimeShiftGetStartPosition
*/
public long onTimeShiftGetCurrentPosition() {
return TvInputManager.TIME_SHIFT_INVALID_TIME;
@@ -1043,7 +1062,7 @@
// Removes the overlay view lastly so that any hanging on the main thread can be handled
// in {@link #scheduleOverlayViewCleanup}.
removeOverlayView(true);
- mHandler.removeCallbacks(mTimeShiftCurrentPositionTrackingRunnable);
+ mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable);
}
/**
@@ -1125,7 +1144,7 @@
* Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
* to the overlay window.
*
- * @param windowToken A window token of an application.
+ * @param windowToken A window token of the application.
* @param frame A position of the overlay view.
*/
void createOverlayView(IBinder windowToken, Rect frame) {
@@ -1245,13 +1264,16 @@
}
/**
- * Turns on/off the current position tracking.
+ * Enable/disable position tracking.
+ *
+ * @param enable {@code true} to enable tracking, {@code false} otherwise.
*/
- void timeShiftTrackCurrentPosition(boolean enabled) {
- if (enabled) {
- mHandler.post(mTimeShiftCurrentPositionTrackingRunnable);
+ void timeShiftEnablePositionTracking(boolean enable) {
+ if (enable) {
+ mHandler.post(mTimeShiftPositionTrackingRunnable);
} else {
- mHandler.removeCallbacks(mTimeShiftCurrentPositionTrackingRunnable);
+ mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable);
+ mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
}
}
@@ -1283,7 +1305,7 @@
}
isNavigationKey = isNavigationKey(keyEvent.getKeyCode());
// When media keys and KEYCODE_MEDIA_AUDIO_TRACK are dispatched to ViewRootImpl,
- // ViewRootImpl always consumes the keys. In this case, an application loses
+ // ViewRootImpl always consumes the keys. In this case, the application loses
// a chance to handle media keys. Therefore, media keys are not dispatched to
// ViewRootImpl.
skipDispatchToOverlayView = KeyEvent.isMediaKey(keyEvent.getKeyCode())
@@ -1352,16 +1374,21 @@
}
}
- private final class TimeShiftCurrentPositionTrackingRunnable implements Runnable {
+ private final class TimeShiftPositionTrackingRunnable implements Runnable {
@Override
public void run() {
- long pos = onTimeShiftGetCurrentPosition();
- if (mCurrentPositionMs != pos) {
- mCurrentPositionMs = pos;
- notifyTimeShiftCurrentPositionChanged(pos);
+ long startPositionMs = onTimeShiftGetStartPosition();
+ if (mStartPositionMs != startPositionMs) {
+ mStartPositionMs = startPositionMs;
+ notifyTimeShiftStartPositionChanged(startPositionMs);
}
- mHandler.removeCallbacks(mTimeShiftCurrentPositionTrackingRunnable);
- mHandler.postDelayed(mTimeShiftCurrentPositionTrackingRunnable,
+ long currentPositionMs = onTimeShiftGetCurrentPosition();
+ if (mCurrentPositionMs != currentPositionMs) {
+ mCurrentPositionMs = currentPositionMs;
+ notifyTimeShiftCurrentPositionChanged(currentPositionMs);
+ }
+ mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable);
+ mHandler.postDelayed(mTimeShiftPositionTrackingRunnable,
POSITION_UPDATE_INTERVAL_MS);
}
}
@@ -1538,7 +1565,7 @@
try {
mCallbacks.getBroadcastItem(i).addHardwareTvInput(deviceId, inputInfo);
} catch (RemoteException e) {
- Log.e(TAG, "Error while broadcasting.", e);
+ Log.e(TAG, "error in broadcastAddHardwareTvInput", e);
}
}
mCallbacks.finishBroadcast();
@@ -1550,7 +1577,7 @@
try {
mCallbacks.getBroadcastItem(i).addHdmiTvInput(id, inputInfo);
} catch (RemoteException e) {
- Log.e(TAG, "Error while broadcasting.", e);
+ Log.e(TAG, "error in broadcastAddHdmiTvInput", e);
}
}
mCallbacks.finishBroadcast();
@@ -1562,7 +1589,7 @@
try {
mCallbacks.getBroadcastItem(i).removeTvInput(inputId);
} catch (RemoteException e) {
- Log.e(TAG, "Error while broadcasting.", e);
+ Log.e(TAG, "error in broadcastRemoveTvInput", e);
}
}
mCallbacks.finishBroadcast();
@@ -1583,7 +1610,7 @@
// Failed to create a session.
cb.onSessionCreated(null, null);
} catch (RemoteException e) {
- Log.e(TAG, "error in onSessionCreated");
+ Log.e(TAG, "error in onSessionCreated", e);
}
return;
}
@@ -1604,7 +1631,7 @@
try {
cb.onSessionCreated(null, null);
} catch (RemoteException e) {
- Log.e(TAG, "error in onSessionCreated");
+ Log.e(TAG, "error in onSessionCreated", e);
}
return;
}
@@ -1635,7 +1662,7 @@
try {
cb.onSessionCreated(stub, hardwareSessionToken);
} catch (RemoteException e) {
- Log.e(TAG, "error in onSessionCreated");
+ Log.e(TAG, "error in onSessionCreated", e);
}
if (sessionImpl != null) {
sessionImpl.initialize(cb);
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 115d094..42c2cd7 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -42,7 +42,6 @@
import android.view.ViewRootImpl;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -104,7 +103,7 @@
private int mSurfaceViewRight;
private int mSurfaceViewTop;
private int mSurfaceViewBottom;
- private List<TimeShiftPositionCallback> mTimeShiftPositionCallbacks = new ArrayList<>();
+ private TimeShiftPositionCallback mTimeShiftPositionCallback;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
@@ -175,8 +174,8 @@
/**
* Sets the callback to be invoked when an event is dispatched to this TvView.
*
- * @param callback The callback to receive events. A value of {@code null} removes any existing
- * callbacks.
+ * @param callback The callback to receive events. A value of {@code null} removes the existing
+ * callback.
*/
public void setCallback(TvInputCallback callback) {
mCallback = callback;
@@ -422,7 +421,7 @@
}
/**
- * Pauses the playback. Call {@link #timeShiftResume()} to restart the playback.
+ * Pauses playback. No-op if it is already paused. Call {@link #timeShiftResume} to resume.
*/
public void timeShiftPause() {
if (mSession != null) {
@@ -431,7 +430,7 @@
}
/**
- * Resumes the playback. No-op if it is already playing the channel.
+ * Resumes playback. No-op if it is already resumed. Call {@link #timeShiftPause} to pause.
*/
public void timeShiftResume() {
if (mSession != null) {
@@ -440,11 +439,11 @@
}
/**
- * Seeks to the specific time position. The position should be in the range from the start time
- * from the start time, {@link TimeShiftPositionCallback#onTimeShiftStartPositionChanged},
- * to the current time, inclusive.
+ * Seeks to a specified time position. {@code timeMs} must be equal to or greater than the start
+ * position returned by {@link TimeShiftPositionCallback#onTimeShiftStartPositionChanged} and
+ * equal to or less than the current time.
*
- * @param timeMs The target time, in milliseconds since the epoch.
+ * @param timeMs The time position to seek to, in milliseconds since the epoch.
*/
public void timeShiftSeekTo(long timeMs) {
if (mSession != null) {
@@ -453,10 +452,10 @@
}
/**
- * Sets a playback rate and an audio mode.
+ * Sets playback rate and audio mode.
*
* @param rate The ratio between desired playback rate and normal one.
- * @param audioMode The audio playback mode. Must be one of the supported audio modes:
+ * @param audioMode Audio playback mode. Must be one of the supported audio modes:
* <ul>
* <li> {@link android.media.MediaPlayer#PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
* </ul>
@@ -468,36 +467,21 @@
}
/**
- * Registers a {@link TvView.TimeShiftPositionCallback}.
+ * Sets the callback to be invoked when the time shift position is changed.
*
- * @param callback A callback used to monitor the time shift range and current position.
+ * @param callback The callback to receive time shift position changes. A value of {@code null}
+ * removes the existing callback.
*/
- public void registerTimeShiftPositionCallback(TimeShiftPositionCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback can not be null.");
- }
- mTimeShiftPositionCallbacks.add(callback);
- ensureCurrentPositionTracking();
+ public void setTimeShiftPositionCallback(TimeShiftPositionCallback callback) {
+ mTimeShiftPositionCallback = callback;
+ ensurePositionTracking();
}
- /**
- * Unregisters the existing {@link TvView.TimeShiftPositionCallback}.
- *
- * @param callback The existing callback to remove.
- */
- public void unregisterTimeShiftPositionCallback(TimeShiftPositionCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback can not be null.");
- }
- mTimeShiftPositionCallbacks.remove(callback);
- ensureCurrentPositionTracking();
- }
-
- private void ensureCurrentPositionTracking() {
+ private void ensurePositionTracking() {
if (mSession == null) {
return;
}
- mSession.timeShiftTrackCurrentPosition(!mTimeShiftPositionCallbacks.isEmpty());
+ mSession.timeShiftEnablePositionTracking(mTimeShiftPositionCallback != null);
}
/**
@@ -810,17 +794,22 @@
}
/**
- * Callback used to receive the information on the possible range for time shifting and currrent
- * position.
+ * Callback used to receive time shift position changes.
*/
public abstract static class TimeShiftPositionCallback {
+
/**
- * This is called when the time shift start position is changed. The application may seek to
- * a position in the range from the start position and the current time, inclusive.
+ * This is called when the start playback position is changed.
+ * <p>
+ * The start playback position of the time shifted program can be adjusted by the TV input
+ * when it cannot retain the whole recorded program due to some reason (e.g. limitation on
+ * storage space). The application should not allow the user to seek to a position earlier
+ * than the start position.
+ * </p>
*
* @param inputId The ID of the TV input bound to this view.
- * @param timeMs the start of the possible time shift range, in milliseconds since the
- * epoch.
+ * @param timeMs The start playback position of the time shifted program, in milliseconds
+ * since the epoch.
*/
public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
}
@@ -829,7 +818,8 @@
* This is called when the current playback position is changed.
*
* @param inputId The ID of the TV input bound to this view.
- * @param timeMs The current position, in milliseconds since the epoch.
+ * @param timeMs The current playback position of the time shifted program, in milliseconds
+ * since the epoch.
*/
public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
}
@@ -958,11 +948,11 @@
* This is called when the time shift status is changed.
*
* @param inputId The ID of the TV input bound to this view.
- * @param status The current time shift status:
+ * @param status The current time shift status. Should be one of the followings.
* <ul>
- * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
+ * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
* <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
- * <li>{@link TvInputManager#TIME_SHIFT_STATUS_ERROR}
+ * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
* </ul>
*/
public void onTimeShiftStatusChanged(String inputId, int status) {
@@ -1040,7 +1030,7 @@
mAppPrivateCommandAction = null;
mAppPrivateCommandData = null;
}
- ensureCurrentPositionTracking();
+ ensurePositionTracking();
} else {
mSessionCallback = null;
if (mCallback != null) {
@@ -1234,8 +1224,8 @@
Log.w(TAG, "onTimeShiftStartPositionChanged - session not created");
return;
}
- for (TimeShiftPositionCallback callback : mTimeShiftPositionCallbacks) {
- callback.onTimeShiftStartPositionChanged(mInputId, timeMs);
+ if (mTimeShiftPositionCallback != null) {
+ mTimeShiftPositionCallback.onTimeShiftStartPositionChanged(mInputId, timeMs);
}
}
@@ -1248,8 +1238,8 @@
Log.w(TAG, "onTimeShiftCurrentPositionChanged - session not created");
return;
}
- for (TimeShiftPositionCallback callback : mTimeShiftPositionCallbacks) {
- callback.onTimeShiftCurrentPositionChanged(mInputId, timeMs);
+ if (mTimeShiftPositionCallback != null) {
+ mTimeShiftPositionCallback.onTimeShiftCurrentPositionChanged(mInputId, timeMs);
}
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 152370a..9b4b522 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1422,19 +1422,19 @@
}
@Override
- public void timeShiftTrackCurrentPosition(IBinder sessionToken, boolean enabled,
+ public void timeShiftEnablePositionTracking(IBinder sessionToken, boolean enable,
int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
- userId, "timeShiftTrackCurrentPosition");
+ userId, "timeShiftEnablePositionTracking");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId)
- .timeShiftTrackCurrentPosition(enabled);
+ .timeShiftEnablePositionTracking(enable);
} catch (RemoteException | SessionNotFoundException e) {
- Slog.e(TAG, "error in timeShiftTrackCurrentPosition", e);
+ Slog.e(TAG, "error in timeShiftEnablePositionTracking", e);
}
}
} finally {