11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/* 21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project 31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License. 61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at 71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software 111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and 141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License 151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 176ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkopackage com.android.tv.dvr.recorder; 181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.annotation.TargetApi; 2065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.content.Context; 212e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport android.media.tv.TvContract; 2265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.media.tv.TvInputManager; 2365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.media.tv.TvRecordingClient.RecordingCallback; 241abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.net.Uri; 2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Build; 26ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Handler; 27ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Looper; 28ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.Message; 291abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.support.annotation.VisibleForTesting; 30ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.support.annotation.WorkerThread; 311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log; 3265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.widget.Toast; 331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.InputSessionManager; 3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.InputSessionManager.RecordingSession; 3665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.R; 3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.TvApplication; 382e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalkoimport com.android.tv.common.SoftPreconditions; 391abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.data.Channel; 406ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.DvrManager; 416ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.WritableDvrDataManager; 426ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.data.ScheduledRecording; 436ebde20b03db4c0d57f67acaac11832b610b966bNick Chalkoimport com.android.tv.dvr.recorder.InputTaskScheduler.HandlerWrapper; 441abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.util.Clock; 45ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.util.Utils; 461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 47d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.util.Comparator; 481abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.concurrent.TimeUnit; 491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/** 51ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * A Handler that actually starts and stop a recording at the right time. 52ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * 53ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * <p>This is run on the looper of thread named {@value DvrRecordingService#HANDLER_THREAD_NAME}. 54ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko * There is only one looper so messages must be handled quickly or start a separate thread. 551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 56ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko@WorkerThread 5765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko@TargetApi(Build.VERSION_CODES.N) 5865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopublic class RecordingTask extends RecordingCallback implements Handler.Callback, 5965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko DvrManager.Listener { 601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String TAG = "RecordingTask"; 612e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko private static final boolean DEBUG = false; 62ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 63d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko /** 64d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Compares the end time in ascending order. 65d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 66d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public static final Comparator<RecordingTask> END_TIME_COMPARATOR 67d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko = new Comparator<RecordingTask>() { 68d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 69d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public int compare(RecordingTask lhs, RecordingTask rhs) { 70d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return Long.compare(lhs.getEndTimeMs(), rhs.getEndTimeMs()); 71d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 72d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko }; 73d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 74d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko /** 75d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Compares ID in ascending order. 76d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 77d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public static final Comparator<RecordingTask> ID_COMPARATOR 78d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko = new Comparator<RecordingTask>() { 79d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 80d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public int compare(RecordingTask lhs, RecordingTask rhs) { 81d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return Long.compare(lhs.getScheduleId(), rhs.getScheduleId()); 82d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 83d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko }; 84d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 85d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko /** 86d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Compares the priority in ascending order. 87d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 88d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public static final Comparator<RecordingTask> PRIORITY_COMPARATOR 89d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko = new Comparator<RecordingTask>() { 90d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 91d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public int compare(RecordingTask lhs, RecordingTask rhs) { 92d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return Long.compare(lhs.getPriority(), rhs.getPriority()); 93d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 94d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko }; 95d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 96ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @VisibleForTesting 9765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko static final int MSG_INITIALIZE = 1; 98ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @VisibleForTesting 9965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko static final int MSG_START_RECORDING = 2; 100ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @VisibleForTesting 10165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko static final int MSG_STOP_RECORDING = 3; 10265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 10365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Message to update schedule. 10465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 10565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public static final int MSG_UDPATE_SCHEDULE = 4; 10665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 10765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 10865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * The time when the start command will be sent before the recording starts. 10965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 11065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public static final long RECORDING_EARLY_START_OFFSET_MS = TimeUnit.SECONDS.toMillis(3); 11165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 11265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * If the recording starts later than the scheduled start time or ends before the scheduled end 11365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * time, it's considered as clipped. 11465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 11565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final long CLIPPED_THRESHOLD_MS = TimeUnit.MINUTES.toMillis(5); 116ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 117ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @VisibleForTesting 118ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko enum State { 119ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko NOT_STARTED, 120ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko SESSION_ACQUIRED, 121ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko CONNECTION_PENDING, 122ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko CONNECTED, 123ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko RECORDING_STARTED, 1242e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko RECORDING_STOP_REQUESTED, 12565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko FINISHED, 126ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko ERROR, 127ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko RELEASED, 128ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 12965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final InputSessionManager mSessionManager; 1302e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko private final DvrManager mDvrManager; 13165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Context mContext; 132ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final WritableDvrDataManager mDataManager; 134ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); 13565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private RecordingSession mRecordingSession; 136ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private Handler mHandler; 1372e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko private ScheduledRecording mScheduledRecording; 1382e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko private final Channel mChannel; 139ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private State mState = State.NOT_STARTED; 140ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private final Clock mClock; 14165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private boolean mStartedWithClipping; 14265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private Uri mRecordedProgramUri; 14365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private boolean mCanceled; 1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 14565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko RecordingTask(Context context, ScheduledRecording scheduledRecording, Channel channel, 14665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko DvrManager dvrManager, InputSessionManager sessionManager, 1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko WritableDvrDataManager dataManager, Clock clock) { 14865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mContext = context; 1492e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mScheduledRecording = scheduledRecording; 1502e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mChannel = channel; 1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mSessionManager = sessionManager; 1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDataManager = dataManager; 1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mClock = clock; 1542e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mDvrManager = dvrManager; 155ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 1562e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (DEBUG) Log.d(TAG, "created recording task " + mScheduledRecording); 1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 159ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko public void setHandler(Handler handler) { 160ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mHandler = handler; 161ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 163ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @Override 164ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko public boolean handleMessage(Message msg) { 165ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) Log.d(TAG, "handleMessage " + msg); 16665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko SoftPreconditions.checkState(msg.what == HandlerWrapper.MESSAGE_REMOVE || mHandler != null, 16765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TAG, "Null handler trying to handle " + msg); 1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko try { 169ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko switch (msg.what) { 17065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_INITIALIZE: 171ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko handleInit(); 172ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko break; 17365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_START_RECORDING: 174ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko handleStartRecording(); 175ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko break; 17665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_STOP_RECORDING: 177ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko handleStopRecording(); 178ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko break; 17965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_UDPATE_SCHEDULE: 18065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko handleUpdateSchedule((ScheduledRecording) msg.obj); 18165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko break; 18265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case HandlerWrapper.MESSAGE_REMOVE: 18365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeCallbacksAndMessages(null); 184ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mHandler = null; 185ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko release(); 186ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko return false; 187ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko default: 188ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko SoftPreconditions.checkArgument(false, TAG, "unexpected message type " + msg); 18965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko break; 190ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 191ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko return true; 192ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } catch (Exception e) { 1932e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko Log.w(TAG, "Error processing message " + msg + " for " + mScheduledRecording, e); 194ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko failAndQuit(); 1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 196ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko return false; 197ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 198ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 199ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @Override 20065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void onDisconnected(String inputId) { 20165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "onDisconnected(" + inputId + ")"); 20265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mRecordingSession != null && mState != State.FINISHED) { 20365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko failAndQuit(); 20465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 20565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 20665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 20765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 208d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public void onConnectionFailed(String inputId) { 209d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (DEBUG) Log.d(TAG, "onConnectionFailed(" + inputId + ")"); 210d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (mRecordingSession != null) { 211d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko failAndQuit(); 212d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 213d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 214d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 215d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko @Override 2162e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko public void onTuned(Uri channelUri) { 21765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "onTuned"); 21865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mRecordingSession == null) { 21965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 2202e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 221ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mState = State.CONNECTED; 22265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mHandler == null || !sendEmptyMessageAtAbsoluteTime(MSG_START_RECORDING, 22365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduledRecording.getStartTimeMs() - RECORDING_EARLY_START_OFFSET_MS)) { 22465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko failAndQuit(); 2252e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 226ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 227ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 2292e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko public void onRecordingStopped(Uri recordedProgramUri) { 23065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "onRecordingStopped"); 23165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mRecordingSession == null) { 23265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 23365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 23465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mRecordedProgramUri = recordedProgramUri; 23565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mState = State.FINISHED; 23665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko int state = ScheduledRecording.STATE_RECORDING_FINISHED; 23765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mStartedWithClipping || mScheduledRecording.getEndTimeMs() - CLIPPED_THRESHOLD_MS 23865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko > mClock.currentTimeMillis()) { 23965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko state = ScheduledRecording.STATE_RECORDING_CLIPPED; 24065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko updateRecordingState(state); 2422e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko sendRemove(); 24365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mCanceled) { 24465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko removeRecordedProgram(); 24565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 2461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 2492e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko public void onError(int reason) { 2502e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (DEBUG) Log.d(TAG, "onError reason " + reason); 25165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mRecordingSession == null) { 25265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 25365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 254ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko switch (reason) { 25565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE: 25665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mMainThreadHandler.post(new Runnable() { 25765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 25865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void run() { 25965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (TvApplication.getSingletons(mContext).getMainActivityWrapper() 26065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko .isResumed()) { 2616ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko ScheduledRecording scheduledRecording = mDataManager 2626ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko .getScheduledRecording(mScheduledRecording.getId()); 2636ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko if (scheduledRecording != null) { 2646ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Toast.makeText(mContext.getApplicationContext(), 2656ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mContext.getString(R.string 2666ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko .dvr_error_insufficient_space_description_one_recording, 2676ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko scheduledRecording.getProgramDisplayTitle(mContext)), 2686ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Toast.LENGTH_LONG) 2696ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko .show(); 2706ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko } 27165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } else { 27265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Utils.setRecordingFailedReason(mContext.getApplicationContext(), 27365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko TvInputManager.RECORDING_ERROR_INSUFFICIENT_SPACE); 2746ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko Utils.addFailedScheduledRecordingInfo(mContext.getApplicationContext(), 2756ebde20b03db4c0d57f67acaac11832b610b966bNick Chalko mScheduledRecording.getProgramDisplayTitle(mContext)); 27665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko }); 27965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Pass through 2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko default: 28165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko failAndQuit(); 28265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko break; 283ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 2851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 286ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void handleInit() { 2872e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (DEBUG) Log.d(TAG, "handleInit " + mScheduledRecording); 2882e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (mScheduledRecording.getEndTimeMs() < mClock.currentTimeMillis()) { 2892e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko Log.w(TAG, "End time already past, not recording " + mScheduledRecording); 290ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko failAndQuit(); 291ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko return; 292ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 2932e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (mChannel == null) { 2942e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko Log.w(TAG, "Null channel for " + mScheduledRecording); 2952e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko failAndQuit(); 2962e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko return; 2972e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 2982e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (mChannel.getId() != mScheduledRecording.getChannelId()) { 2992e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko Log.w(TAG, "Channel" + mChannel + " does not match scheduled recording " 3002e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko + mScheduledRecording); 301ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko failAndQuit(); 302ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko return; 303ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 304ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 3052e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko String inputId = mChannel.getInputId(); 30665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mRecordingSession = mSessionManager.createRecordingSession(inputId, 307d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko "recordingTask-" + mScheduledRecording.getId(), this, 308d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mHandler, mScheduledRecording.getEndTimeMs()); 30965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mState = State.SESSION_ACQUIRED; 3102e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mDvrManager.addListener(this, mHandler); 31165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mRecordingSession.tune(inputId, mChannel.getUri()); 3122e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mState = State.CONNECTION_PENDING; 313ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 314ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 315ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void failAndQuit() { 3162e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (DEBUG) Log.d(TAG, "failAndQuit"); 3172e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED); 318ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mState = State.ERROR; 319ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko sendRemove(); 320ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 321ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 322ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void sendRemove() { 3232e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (DEBUG) Log.d(TAG, "sendRemove"); 324ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (mHandler != null) { 32565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage( 32665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko HandlerWrapper.MESSAGE_REMOVE)); 327ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 328ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 329ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 330ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void handleStartRecording() { 3312e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (DEBUG) Log.d(TAG, "handleStartRecording " + mScheduledRecording); 3322e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko long programId = mScheduledRecording.getProgramId(); 33365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mRecordingSession.startRecording(programId == ScheduledRecording.ID_NOT_SET ? null 3342e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko : TvContract.buildProgramUri(programId)); 33565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko updateRecordingState(ScheduledRecording.STATE_RECORDING_IN_PROGRESS); 33665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // If it starts late, it's clipped. 33765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mScheduledRecording.getStartTimeMs() + CLIPPED_THRESHOLD_MS 33865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko < mClock.currentTimeMillis()) { 33965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mStartedWithClipping = true; 34065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 3412e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mState = State.RECORDING_STARTED; 3422e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko 34365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (!sendEmptyMessageAtAbsoluteTime(MSG_STOP_RECORDING, 34465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduledRecording.getEndTimeMs())) { 34565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko failAndQuit(); 346ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 347ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 348ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 349ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void handleStopRecording() { 3502e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (DEBUG) Log.d(TAG, "handleStopRecording " + mScheduledRecording); 35165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mRecordingSession.stopRecording(); 3522e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mState = State.RECORDING_STOP_REQUESTED; 353ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 354ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 35565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void handleUpdateSchedule(ScheduledRecording schedule) { 35665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduledRecording = schedule; 35765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Check end time only. The start time is checked in InputTaskScheduler. 358d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (schedule.getEndTimeMs() != mScheduledRecording.getEndTimeMs()) { 359d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (mRecordingSession != null) { 360d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mRecordingSession.setEndTimeMs(schedule.getEndTimeMs()); 361d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 362d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (mState == State.RECORDING_STARTED) { 363d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mHandler.removeMessages(MSG_STOP_RECORDING); 364d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (!sendEmptyMessageAtAbsoluteTime(MSG_STOP_RECORDING, schedule.getEndTimeMs())) { 365d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko failAndQuit(); 366d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 36765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 36865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 36965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 37065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 371ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @VisibleForTesting 372ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko State getState() { 373ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko return mState; 374ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 375ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 376d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private long getScheduleId() { 377d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return mScheduledRecording.getId(); 378d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 379d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 38065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 38165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Returns the priority. 38265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 38365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public long getPriority() { 38465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return mScheduledRecording.getPriority(); 38565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 38665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 38765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 38865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Returns the start time of the recording. 38965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 39065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public long getStartTimeMs() { 39165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return mScheduledRecording.getStartTimeMs(); 39265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 39365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 39465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 39565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Returns the end time of the recording. 39665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 39765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public long getEndTimeMs() { 39865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return mScheduledRecording.getEndTimeMs(); 39965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 40065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 401ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private void release() { 40265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mRecordingSession != null) { 40365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessionManager.releaseRecordingSession(mRecordingSession); 40465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mRecordingSession = null; 405ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 4062e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko mDvrManager.removeListener(this); 407ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 408ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 409ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private boolean sendEmptyMessageAtAbsoluteTime(int what, long when) { 410ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko long now = mClock.currentTimeMillis(); 411ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko long delay = Math.max(0L, when - now); 412ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (DEBUG) { 413ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko Log.d(TAG, "Sending message " + what + " with a delay of " + delay / 1000 414ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko + " seconds to arrive at " + Utils.toIsoDateTimeString(when)); 415ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 416ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko return mHandler.sendEmptyMessageDelayed(what, delay); 417ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 4181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 4192e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko private void updateRecordingState(@ScheduledRecording.RecordingState int state) { 42065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "Updating the state of " + mScheduledRecording + " to " + state); 42165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduledRecording = ScheduledRecording.buildFrom(mScheduledRecording).setState(state) 42265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko .build(); 423d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko runOnMainThread(new Runnable() { 424ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko @Override 425ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko public void run() { 42665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko ScheduledRecording schedule = mDataManager.getScheduledRecording( 42765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mScheduledRecording.getId()); 42865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (schedule == null) { 42965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Schedule has been deleted. Delete the recorded program. 43065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko removeRecordedProgram(); 43165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } else { 43265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Update the state based on the object in DataManager in case when it has been 43365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // updated. mScheduledRecording will be updated from 43465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // onScheduledRecordingStateChanged. 43565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mDataManager.updateScheduledRecording(ScheduledRecording.buildFrom(schedule) 43665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko .setState(state).build()); 43765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 438ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 439ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko }); 4401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 4411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 4421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 4432e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko public void onStopRecordingRequested(ScheduledRecording recording) { 4442e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko if (recording.getId() != mScheduledRecording.getId()) { 4452e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko return; 4462e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 44765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko stop(); 44865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 44965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 45065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 45165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Starts the task. 45265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 45365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void start() { 45465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_INITIALIZE); 45565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 45665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 45765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 45865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Stops the task. 45965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 46065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void stop() { 46165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "stop"); 4622e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko switch (mState) { 4632e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case RECORDING_STARTED: 46465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeMessages(MSG_STOP_RECORDING); 4652e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko handleStopRecording(); 4662e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko break; 4672e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case RECORDING_STOP_REQUESTED: 4682e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko // Do nothing 4692e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko break; 4702e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case NOT_STARTED: 4712e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case SESSION_ACQUIRED: 4722e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case CONNECTION_PENDING: 4732e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case CONNECTED: 47465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case FINISHED: 4752e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case ERROR: 4762e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko case RELEASED: 4772e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko default: 4782e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko sendRemove(); 4792e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko break; 4802e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 4812e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko } 4822e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko 48365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 48465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Cancels the task 48565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 48665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void cancel() { 48765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "cancel"); 48865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mCanceled = true; 48965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko stop(); 49065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko removeRecordedProgram(); 49165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 49265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 493d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko /** 494d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko * Clean up the task. 495d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko */ 496d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public void cleanUp() { 497d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (mState == State.RECORDING_STARTED || mState == State.RECORDING_STOP_REQUESTED) { 498d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko updateRecordingState(ScheduledRecording.STATE_RECORDING_FAILED); 499d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 500d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko release(); 501d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (mHandler != null) { 502d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mHandler.removeCallbacksAndMessages(null); 503d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 504d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 505d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 5062e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko @Override 5071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public String toString() { 5082e1279b8bbe0603fb4399b25b73121bed5953c46Nick Chalko return getClass().getName() + "(" + mScheduledRecording + ")"; 5091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 51065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 51165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void removeRecordedProgram() { 51265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko runOnMainThread(new Runnable() { 51365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 51465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void run() { 51565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mRecordedProgramUri != null) { 51665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mDvrManager.removeRecordedProgram(mRecordedProgramUri); 51765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 51865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 51965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko }); 52065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 52165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 52265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void runOnMainThread(Runnable runnable) { 52365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (Looper.myLooper() == Looper.getMainLooper()) { 52465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko runnable.run(); 52565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } else { 52665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mMainThreadHandler.post(runnable); 52765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 52865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 5291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko} 530