diff options
Diffstat (limited to 'src/com/android/tv/dvr/RecordingTask.java')
-rw-r--r-- | src/com/android/tv/dvr/RecordingTask.java | 208 |
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 + ")"; } } |