aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/dvr/RecordingTask.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/dvr/RecordingTask.java')
-rw-r--r--src/com/android/tv/dvr/RecordingTask.java208
1 files changed, 119 insertions, 89 deletions
diff --git a/src/com/android/tv/dvr/RecordingTask.java b/src/com/android/tv/dvr/RecordingTask.java
index 3bed5e77..804485b3 100644
--- a/src/com/android/tv/dvr/RecordingTask.java
+++ b/src/com/android/tv/dvr/RecordingTask.java
@@ -16,6 +16,8 @@
package com.android.tv.dvr;
+import android.media.tv.TvContract;
+import android.media.tv.TvRecordingClient;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -24,10 +26,9 @@ import android.support.annotation.VisibleForTesting;
import android.support.annotation.WorkerThread;
import android.util.Log;
-import com.android.tv.common.recording.TvRecording;
+import com.android.tv.common.SoftPreconditions;
import com.android.tv.data.Channel;
import com.android.tv.util.Clock;
-import com.android.tv.util.SoftPreconditions;
import com.android.tv.util.Utils;
import java.util.concurrent.TimeUnit;
@@ -39,9 +40,10 @@ import java.util.concurrent.TimeUnit;
* There is only one looper so messages must be handled quickly or start a separate thread.
*/
@WorkerThread
-class RecordingTask extends TvRecording.ClientCallback implements Handler.Callback {
+class RecordingTask extends TvRecordingClient.RecordingCallback
+ implements Handler.Callback, DvrManager.Listener {
private static final String TAG = "RecordingTask";
- private static final boolean DEBUG = true; //STOPSHIP(DVR)
+ private static final boolean DEBUG = false;
@VisibleForTesting
static final int MESSAGE_INIT = 1;
@@ -51,11 +53,10 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
static final int MESSAGE_STOP_RECORDING = 3;
@VisibleForTesting
- static long MS_BEFORE_START = TimeUnit.SECONDS.toMillis(5);
+ static final long MS_BEFORE_START = TimeUnit.SECONDS.toMillis(5);
@VisibleForTesting
- static long MS_AFTER_END = TimeUnit.SECONDS.toMillis(5);
+ static final long MS_AFTER_END = TimeUnit.SECONDS.toMillis(5);
- //STOPSHIP(DVR) don't use enums.
@VisibleForTesting
enum State {
NOT_STARTED,
@@ -64,27 +65,33 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
CONNECTED,
RECORDING_START_REQUESTED,
RECORDING_STARTED,
+ RECORDING_STOP_REQUESTED,
ERROR,
RELEASED,
}
private final DvrSessionManager mSessionManager;
+ private final DvrManager mDvrManager;
private final WritableDvrDataManager mDataManager;
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
- private TvRecording.TvRecordingClient mSession;
+ private TvRecordingClient mTvRecordingClient;
private Handler mHandler;
- private Recording mRecording;
+ private ScheduledRecording mScheduledRecording;
+ private final Channel mChannel;
private State mState = State.NOT_STARTED;
private final Clock mClock;
- RecordingTask(Recording recording, DvrSessionManager sessionManager,
+ RecordingTask(ScheduledRecording scheduledRecording, Channel channel,
+ DvrManager dvrManager, DvrSessionManager sessionManager,
WritableDvrDataManager dataManager, Clock clock) {
- mRecording = recording;
+ mScheduledRecording = scheduledRecording;
+ mChannel = channel;
mSessionManager = sessionManager;
mDataManager = dataManager;
mClock = clock;
+ mDvrManager = dvrManager;
- if (DEBUG) Log.d(TAG, "created recording task " + mRecording);
+ if (DEBUG) Log.d(TAG, "created recording task " + mScheduledRecording);
}
public void setHandler(Handler handler) {
@@ -118,60 +125,45 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
}
return true;
} catch (Exception e) {
- Log.w(TAG, "Error processing message " + msg + " for " + mRecording, e);
+ Log.w(TAG, "Error processing message " + msg + " for " + mScheduledRecording, e);
failAndQuit();
}
return false;
}
@Override
- public void onConnected() {
- if (DEBUG) Log.d(TAG, "onConnected");
- super.onConnected();
+ public void onTuned(Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "onTuned");
+ }
+ super.onTuned(channelUri);
mState = State.CONNECTED;
+ if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MESSAGE_START_RECORDING,
+ mScheduledRecording.getStartTimeMs() - MS_BEFORE_START)) {
+ mState = State.ERROR;
+ return;
+ }
}
- @Override
- public void onDisconnected() {
- if (DEBUG) Log.d(TAG, "onDisconnected");
- super.onDisconnected();
- //Do nothing
- }
-
- @Override
- public void onRecordDeleted(Uri mediaUri) {
- if (DEBUG) Log.d(TAG, "onRecordDeleted " + mediaUri);
- super.onRecordDeleted(mediaUri);
- SoftPreconditions.checkState(false, TAG, "unexpected onRecordDeleted");
-
- }
-
- @Override
- public void onRecordDeleteFailed(Uri mediaUri, int reason) {
- if (DEBUG) Log.d(TAG, "onRecordDeleteFailed " + mediaUri + ", " + reason);
- super.onRecordDeleteFailed(mediaUri, reason);
- SoftPreconditions.checkState(false, TAG, "unexpected onRecordDeleteFailed");
- }
@Override
- public void onRecordStarted(Uri mediaUri) {
- if (DEBUG) Log.d(TAG, "onRecordStarted " + mediaUri);
- super.onRecordStarted(mediaUri);
- mState = State.RECORDING_STARTED;
- updateRecording(Recording.buildFrom(mRecording)
- .setState(Recording.STATE_RECORDING_IN_PROGRESS)
- .build());
+ public void onRecordingStopped(Uri recordedProgramUri) {
+ super.onRecordingStopped(recordedProgramUri);
+ mState = State.CONNECTED;
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording)
+ .setState(ScheduledRecording.STATE_RECORDING_FINISHED).build());
+ sendRemove();
}
@Override
- public void onRecordStopped(Uri mediaUri, @TvRecording.RecordStopReason int reason) {
- if (DEBUG) Log.d(TAG, "onRecordStopped " + mediaUri + " reason " + reason);
- super.onRecordStopped(mediaUri, reason);
+ public void onError(int reason) {
+ if (DEBUG) Log.d(TAG, "onError reason " + reason);
+ super.onError(reason);
// TODO(dvr) handle success
switch (reason) {
default:
- updateRecording(Recording.buildFrom(mRecording)
- .setState(Recording.STATE_RECORDING_FAILED)
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording)
+ .setState(ScheduledRecording.STATE_RECORDING_FAILED)
.build());
}
release();
@@ -179,67 +171,78 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
}
private void handleInit() {
+ if (DEBUG) Log.d(TAG, "handleInit " + mScheduledRecording);
//TODO check recording preconditions
- Channel channel = mRecording.getChannel();
- if (channel == null) {
- Log.w(TAG, "Null channel for " + mRecording);
+
+ if (mScheduledRecording.getEndTimeMs() < mClock.currentTimeMillis()) {
+ Log.w(TAG, "End time already past, not recording " + mScheduledRecording);
failAndQuit();
return;
}
- String inputId = channel.getInputId();
- if (mSessionManager.canAcquireDvrSession(inputId, channel)) {
- mSession = mSessionManager.acquireDvrSession(inputId, channel);
- mState = State.SESSION_ACQUIRED;
- } else {
- Log.w(TAG, "Unable to acquire a session for " + mRecording);
+ if (mChannel == null) {
+ Log.w(TAG, "Null channel for " + mScheduledRecording);
+ failAndQuit();
+ return;
+ }
+ if (mChannel.getId() != mScheduledRecording.getChannelId()) {
+ Log.w(TAG, "Channel" + mChannel + " does not match scheduled recording "
+ + mScheduledRecording);
failAndQuit();
return;
}
- mSession.connect(inputId, this);
- mState = State.CONNECTION_PENDING;
-
- if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MESSAGE_START_RECORDING,
- mRecording.getStartTimeMs() - MS_BEFORE_START)) {
- mState = State.ERROR;
+ String inputId = mChannel.getInputId();
+ if (mSessionManager.canAcquireDvrSession(inputId, mChannel)) {
+ mTvRecordingClient = mSessionManager
+ .createTvRecordingClient("recordingTask-" + mScheduledRecording.getId(), this,
+ mHandler);
+ mState = State.SESSION_ACQUIRED;
+ } else {
+ Log.w(TAG, "Unable to acquire a session for " + mScheduledRecording);
+ failAndQuit();
return;
}
+ mDvrManager.addListener(this, mHandler);
+ mTvRecordingClient.tune(inputId, mChannel.getUri());
+ mState = State.CONNECTION_PENDING;
}
private void failAndQuit() {
- updateRecordingState(Recording.STATE_RECORDING_FAILED);
+ if (DEBUG) Log.d(TAG, "failAndQuit");
+ updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED);
mState = State.ERROR;
sendRemove();
}
private void sendRemove() {
+ if (DEBUG) Log.d(TAG, "sendRemove");
if (mHandler != null) {
mHandler.sendEmptyMessage(Scheduler.HandlerWrapper.MESSAGE_REMOVE);
}
}
private void handleStartRecording() {
- if (DEBUG)Log.d(TAG, "handleStartRecording " + mRecording);
+ if (DEBUG) Log.d(TAG, "handleStartRecording " + mScheduledRecording);
// TODO(DVR) handle errors
- Channel channel = mRecording.getChannel();
- mSession.startRecord(channel.getUri(), getIdAsMediaUri(mRecording));
- mState= State.RECORDING_START_REQUESTED;
+ long programId = mScheduledRecording.getProgramId();
+ mTvRecordingClient.startRecording(programId == ScheduledRecording.ID_NOT_SET ? null
+ : TvContract.buildProgramUri(programId));
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording)
+ .setState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS).build());
+ mState = State.RECORDING_STARTED;
+
if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MESSAGE_STOP_RECORDING,
- mRecording.getEndTimeMs() + MS_AFTER_END)) {
+ mScheduledRecording.getEndTimeMs() + MS_AFTER_END)) {
mState = State.ERROR;
return;
}
}
private void handleStopRecording() {
- if (DEBUG)Log.d(TAG, "handleStopRecording " + mRecording);
- mSession.stopRecord();
- // TODO: once we add an API to notify successful completion of recording,
- // the following parts need to be moved to the listener implementation.
- updateRecording(Recording.buildFrom(mRecording)
- .setState(Recording.STATE_RECORDING_FINISHED).build());
- sendRemove();
+ if (DEBUG) Log.d(TAG, "handleStopRecording " + mScheduledRecording);
+ mTvRecordingClient.stopRecording();
+ mState = State.RECORDING_STOP_REQUESTED;
}
@VisibleForTesting
@@ -248,10 +251,10 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
}
private void release() {
- if (mSession != null) {
- mSession.release();
- mSessionManager.releaseDvrSession(mSession);
+ if (mTvRecordingClient != null) {
+ mSessionManager.releaseTvRecordingClient(mTvRecordingClient);
}
+ mDvrManager.removeListener(this);
}
private boolean sendEmptyMessageAtAbsoluteTime(int what, long when) {
@@ -264,28 +267,55 @@ class RecordingTask extends TvRecording.ClientCallback implements Handler.Callba
return mHandler.sendEmptyMessageDelayed(what, delay);
}
- private void updateRecordingState(@Recording.RecordingState int state) {
- updateRecording(Recording.buildFrom(mRecording).setState(state).build());
+ private void updateRecordingState(@ScheduledRecording.RecordingState int state) {
+ updateRecording(ScheduledRecording.buildFrom(mScheduledRecording).setState(state).build());
}
- @VisibleForTesting static Uri getIdAsMediaUri(Recording recording) {
+ @VisibleForTesting
+ static Uri getIdAsMediaUri(ScheduledRecording scheduledRecording) {
// TODO define the URI format
- return new Uri.Builder().appendPath(String.valueOf(recording.getId())).build();
+ return new Uri.Builder().appendPath(String.valueOf(scheduledRecording.getId())).build();
}
- private void updateRecording(Recording updatedRecording) {
- if (DEBUG) Log.d(TAG, "updateRecording " + updatedRecording);
- mRecording = updatedRecording;
+ private void updateRecording(ScheduledRecording updatedScheduledRecording) {
+ if (DEBUG) Log.d(TAG, "updateScheduledRecording " + updatedScheduledRecording);
+ mScheduledRecording = updatedScheduledRecording;
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
- mDataManager.updateRecording(mRecording);
+ mDataManager.updateScheduledRecording(mScheduledRecording);
}
});
}
@Override
+ public void onStopRecordingRequested(ScheduledRecording recording) {
+ if (recording.getId() != mScheduledRecording.getId()) {
+ return;
+ }
+ switch (mState) {
+ case RECORDING_STARTED:
+ mHandler.removeMessages(MESSAGE_STOP_RECORDING);
+ handleStopRecording();
+ break;
+ case RECORDING_STOP_REQUESTED:
+ // Do nothing
+ break;
+ case NOT_STARTED:
+ case SESSION_ACQUIRED:
+ case CONNECTION_PENDING:
+ case CONNECTED:
+ case RECORDING_START_REQUESTED:
+ case ERROR:
+ case RELEASED:
+ default:
+ sendRemove();
+ break;
+ }
+ }
+
+ @Override
public String toString() {
- return getClass().getName() + "(" + mRecording + ")";
+ return getClass().getName() + "(" + mScheduledRecording + ")";
}
}