165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko/* 265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Copyright (C) 2016 The Android Open Source Project 365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * you may not use this file except in compliance with the License. 665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * You may obtain a copy of the License at 765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * 1065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Unless required by applicable law or agreed to in writing, software 1165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 1265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * See the License for the specific language governing permissions and 1465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * limitations under the License. 1565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 1665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 17633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkopackage com.android.tv.dvr.recorder; 1865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 1965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.content.Context; 2065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.media.tv.TvInputInfo; 2165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Handler; 2265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Looper; 2365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.os.Message; 2465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.support.annotation.VisibleForTesting; 2565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.util.ArrayMap; 2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.util.Log; 2765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport android.util.LongSparseArray; 2865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.InputSessionManager; 29944779887775bd950cf1abf348d2df461593f6abLive Channels Teamimport com.android.tv.common.util.Clock; 3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.data.ChannelDataManager; 310cc0713c1bf8027642987b750b80217569d2932aLive Channels Teamimport com.android.tv.data.api.Channel; 32633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkoimport com.android.tv.dvr.DvrDataManager; 33633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkoimport com.android.tv.dvr.DvrManager; 34633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkoimport com.android.tv.dvr.WritableDvrDataManager; 35633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalkoimport com.android.tv.dvr.data.ScheduledRecording; 36d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport com.android.tv.util.CompositeComparator; 3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.ArrayList; 3865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Collections; 39d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalkoimport java.util.Comparator; 4065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Iterator; 4165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.List; 4265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport java.util.Map; 4365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 4495961816a768da387f0b5523cf4363ace2044089Nick Chalko/** The scheduler for a TV input. */ 4565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopublic class InputTaskScheduler { 4665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final String TAG = "InputTaskScheduler"; 4765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final boolean DEBUG = false; 4865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 4965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final int MSG_ADD_SCHEDULED_RECORDING = 1; 5065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final int MSG_REMOVE_SCHEDULED_RECORDING = 2; 5165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final int MSG_UPDATE_SCHEDULED_RECORDING = 3; 5265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private static final int MSG_BUILD_SCHEDULE = 4; 53d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private static final int MSG_STOP_SCHEDULE = 5; 54d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 55d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private static final float MIN_REMAIN_DURATION_PERCENT = 0.05f; 56d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 57d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // The candidate comparator should be the consistent with 58d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // DvrScheduleManager#CANDIDATE_COMPARATOR. 59d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private static final Comparator<RecordingTask> CANDIDATE_COMPARATOR = 60d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko new CompositeComparator<>( 61d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko RecordingTask.PRIORITY_COMPARATOR, 62d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko RecordingTask.END_TIME_COMPARATOR, 63d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko RecordingTask.ID_COMPARATOR); 64d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 6595961816a768da387f0b5523cf4363ace2044089Nick Chalko /** Returns the comparator which the schedules are sorted with when executed. */ 66d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public static Comparator<ScheduledRecording> getRecordingOrderComparator() { 67d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko return ScheduledRecording.START_TIME_THEN_PRIORITY_THEN_ID_COMPARATOR; 68d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 6965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 7065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko /** 7165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko * Wraps a {@link RecordingTask} removing it from {@link #mPendingRecordings} when it is done. 7265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko */ 7365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public final class HandlerWrapper extends Handler { 7465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public static final int MESSAGE_REMOVE = 999; 7565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final long mId; 7665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final RecordingTask mTask; 7765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 7895961816a768da387f0b5523cf4363ace2044089Nick Chalko HandlerWrapper( 7995961816a768da387f0b5523cf4363ace2044089Nick Chalko Looper looper, ScheduledRecording scheduledRecording, RecordingTask recordingTask) { 8065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko super(looper, recordingTask); 8165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mId = scheduledRecording.getId(); 8265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTask = recordingTask; 8365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mTask.setHandler(this); 8465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 8565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 8665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 8765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void handleMessage(Message msg) { 8865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // The RecordingTask gets a chance first. 8965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // It must return false to pass this message to here. 9065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (msg.what == MESSAGE_REMOVE) { 9195961816a768da387f0b5523cf4363ace2044089Nick Chalko if (DEBUG) Log.d(TAG, "done " + mId); 9265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mPendingRecordings.remove(mId); 9365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 9465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko removeCallbacksAndMessages(null); 9565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeMessages(MSG_BUILD_SCHEDULE); 9665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_BUILD_SCHEDULE); 9765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko super.handleMessage(msg); 9865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 9965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 10065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 10165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private TvInputInfo mInput; 10265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Looper mLooper; 10365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final ChannelDataManager mChannelDataManager; 10465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final DvrManager mDvrManager; 10565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final WritableDvrDataManager mDataManager; 10665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final InputSessionManager mSessionManager; 10765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Clock mClock; 10865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Context mContext; 10965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 11065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final LongSparseArray<HandlerWrapper> mPendingRecordings = new LongSparseArray<>(); 11165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Map<Long, ScheduledRecording> mWaitingSchedules = new ArrayMap<>(); 11265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Handler mMainThreadHandler; 11365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Handler mHandler; 11465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final Object mInputLock = new Object(); 11565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private final RecordingTaskFactory mRecordingTaskFactory; 11665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 11795961816a768da387f0b5523cf4363ace2044089Nick Chalko public InputTaskScheduler( 11895961816a768da387f0b5523cf4363ace2044089Nick Chalko Context context, 11995961816a768da387f0b5523cf4363ace2044089Nick Chalko TvInputInfo input, 12095961816a768da387f0b5523cf4363ace2044089Nick Chalko Looper looper, 12195961816a768da387f0b5523cf4363ace2044089Nick Chalko ChannelDataManager channelDataManager, 12295961816a768da387f0b5523cf4363ace2044089Nick Chalko DvrManager dvrManager, 12395961816a768da387f0b5523cf4363ace2044089Nick Chalko DvrDataManager dataManager, 12495961816a768da387f0b5523cf4363ace2044089Nick Chalko InputSessionManager sessionManager, 12595961816a768da387f0b5523cf4363ace2044089Nick Chalko Clock clock) { 12695961816a768da387f0b5523cf4363ace2044089Nick Chalko this( 12795961816a768da387f0b5523cf4363ace2044089Nick Chalko context, 12895961816a768da387f0b5523cf4363ace2044089Nick Chalko input, 12995961816a768da387f0b5523cf4363ace2044089Nick Chalko looper, 13095961816a768da387f0b5523cf4363ace2044089Nick Chalko channelDataManager, 13195961816a768da387f0b5523cf4363ace2044089Nick Chalko dvrManager, 13295961816a768da387f0b5523cf4363ace2044089Nick Chalko dataManager, 13395961816a768da387f0b5523cf4363ace2044089Nick Chalko sessionManager, 13495961816a768da387f0b5523cf4363ace2044089Nick Chalko clock, 13595961816a768da387f0b5523cf4363ace2044089Nick Chalko null); 13665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 13765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 13865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @VisibleForTesting 13995961816a768da387f0b5523cf4363ace2044089Nick Chalko InputTaskScheduler( 14095961816a768da387f0b5523cf4363ace2044089Nick Chalko Context context, 14195961816a768da387f0b5523cf4363ace2044089Nick Chalko TvInputInfo input, 14295961816a768da387f0b5523cf4363ace2044089Nick Chalko Looper looper, 14395961816a768da387f0b5523cf4363ace2044089Nick Chalko ChannelDataManager channelDataManager, 14495961816a768da387f0b5523cf4363ace2044089Nick Chalko DvrManager dvrManager, 14595961816a768da387f0b5523cf4363ace2044089Nick Chalko DvrDataManager dataManager, 14695961816a768da387f0b5523cf4363ace2044089Nick Chalko InputSessionManager sessionManager, 14795961816a768da387f0b5523cf4363ace2044089Nick Chalko Clock clock, 14865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko RecordingTaskFactory recordingTaskFactory) { 14965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (DEBUG) Log.d(TAG, "Creating scheduler for " + input); 15065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mContext = context; 15165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mInput = input; 15265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mLooper = looper; 15365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mChannelDataManager = channelDataManager; 15465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mDvrManager = dvrManager; 15565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mDataManager = (WritableDvrDataManager) dataManager; 15665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mSessionManager = sessionManager; 15765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mClock = clock; 158633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko mMainThreadHandler = new Handler(Looper.getMainLooper()); 15995961816a768da387f0b5523cf4363ace2044089Nick Chalko mRecordingTaskFactory = 16095961816a768da387f0b5523cf4363ace2044089Nick Chalko recordingTaskFactory != null 16195961816a768da387f0b5523cf4363ace2044089Nick Chalko ? recordingTaskFactory 16295961816a768da387f0b5523cf4363ace2044089Nick Chalko : new RecordingTaskFactory() { 16395961816a768da387f0b5523cf4363ace2044089Nick Chalko @Override 16495961816a768da387f0b5523cf4363ace2044089Nick Chalko public RecordingTask createRecordingTask( 16595961816a768da387f0b5523cf4363ace2044089Nick Chalko ScheduledRecording schedule, 16695961816a768da387f0b5523cf4363ace2044089Nick Chalko Channel channel, 16795961816a768da387f0b5523cf4363ace2044089Nick Chalko DvrManager dvrManager, 16895961816a768da387f0b5523cf4363ace2044089Nick Chalko InputSessionManager sessionManager, 16995961816a768da387f0b5523cf4363ace2044089Nick Chalko WritableDvrDataManager dataManager, 17095961816a768da387f0b5523cf4363ace2044089Nick Chalko Clock clock) { 17195961816a768da387f0b5523cf4363ace2044089Nick Chalko return new RecordingTask( 17295961816a768da387f0b5523cf4363ace2044089Nick Chalko mContext, 17395961816a768da387f0b5523cf4363ace2044089Nick Chalko schedule, 17495961816a768da387f0b5523cf4363ace2044089Nick Chalko channel, 17595961816a768da387f0b5523cf4363ace2044089Nick Chalko mDvrManager, 17695961816a768da387f0b5523cf4363ace2044089Nick Chalko mSessionManager, 17795961816a768da387f0b5523cf4363ace2044089Nick Chalko mDataManager, 17895961816a768da387f0b5523cf4363ace2044089Nick Chalko mClock); 17995961816a768da387f0b5523cf4363ace2044089Nick Chalko } 18095961816a768da387f0b5523cf4363ace2044089Nick Chalko }; 181633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko mHandler = new WorkerThreadHandler(looper); 18265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 18365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 18495961816a768da387f0b5523cf4363ace2044089Nick Chalko /** Adds a {@link ScheduledRecording}. */ 18565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void addSchedule(ScheduledRecording schedule) { 18665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendMessage(mHandler.obtainMessage(MSG_ADD_SCHEDULED_RECORDING, schedule)); 18765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 18865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 18965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @VisibleForTesting 19065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko void handleAddSchedule(ScheduledRecording schedule) { 19165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mPendingRecordings.get(schedule.getId()) != null 19265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko || mWaitingSchedules.containsKey(schedule.getId())) { 19365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 19465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 19565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mWaitingSchedules.put(schedule.getId(), schedule); 19665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeMessages(MSG_BUILD_SCHEDULE); 19765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_BUILD_SCHEDULE); 19865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 19965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 20095961816a768da387f0b5523cf4363ace2044089Nick Chalko /** Removes the {@link ScheduledRecording}. */ 20165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void removeSchedule(ScheduledRecording schedule) { 20265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_SCHEDULED_RECORDING, schedule)); 20365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 20465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 20565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @VisibleForTesting 20665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko void handleRemoveSchedule(ScheduledRecording schedule) { 20765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko HandlerWrapper wrapper = mPendingRecordings.get(schedule.getId()); 20865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (wrapper != null) { 20965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko wrapper.mTask.cancel(); 21065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 21165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 21265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mWaitingSchedules.containsKey(schedule.getId())) { 21365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mWaitingSchedules.remove(schedule.getId()); 21465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeMessages(MSG_BUILD_SCHEDULE); 21565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_BUILD_SCHEDULE); 21665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 21765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 21865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 21995961816a768da387f0b5523cf4363ace2044089Nick Chalko /** Updates the {@link ScheduledRecording}. */ 22065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void updateSchedule(ScheduledRecording schedule) { 22165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_SCHEDULED_RECORDING, schedule)); 22265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 22365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 22465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @VisibleForTesting 22565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko void handleUpdateSchedule(ScheduledRecording schedule) { 22665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko HandlerWrapper wrapper = mPendingRecordings.get(schedule.getId()); 22765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (wrapper != null) { 22865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (schedule.getStartTimeMs() > mClock.currentTimeMillis() 22965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko && schedule.getStartTimeMs() > wrapper.mTask.getStartTimeMs()) { 23065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // It shouldn't have started. Cancel and put to the waiting list. 23165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // The schedules will be rebuilt when the task is removed. 232633eb826b8c97731dfc5ef12c7bf78a63734275dNick Chalko // The reschedule is called in RecordingScheduler. 23365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko wrapper.mTask.cancel(); 23465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mWaitingSchedules.put(schedule.getId(), schedule); 23565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 23665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 23765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko wrapper.sendMessage(wrapper.obtainMessage(RecordingTask.MSG_UDPATE_SCHEDULE, schedule)); 23865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 23965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mWaitingSchedules.containsKey(schedule.getId())) { 24165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mWaitingSchedules.put(schedule.getId(), schedule); 24265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.removeMessages(MSG_BUILD_SCHEDULE); 24365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mHandler.sendEmptyMessage(MSG_BUILD_SCHEDULE); 24465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 24665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 24795961816a768da387f0b5523cf4363ace2044089Nick Chalko /** Updates the TV input. */ 24865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void updateTvInputInfo(TvInputInfo input) { 24965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mInputLock) { 25065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mInput = input; 25165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 25265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 25365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 25495961816a768da387f0b5523cf4363ace2044089Nick Chalko /** Stops the input task scheduler. */ 255d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko public void stop() { 256d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mHandler.removeCallbacksAndMessages(null); 257d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mHandler.sendEmptyMessage(MSG_STOP_SCHEDULE); 258d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 259d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 260d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko private void handleStopSchedule() { 261d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mWaitingSchedules.clear(); 262d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko int size = mPendingRecordings.size(); 263d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko for (int i = 0; i < size; ++i) { 264d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko RecordingTask task = mPendingRecordings.get(mPendingRecordings.keyAt(i)).mTask; 265d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko task.cleanUp(); 266d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 267d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 268d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko 26965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @VisibleForTesting 27065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko void handleBuildSchedule() { 27165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mWaitingSchedules.isEmpty()) { 27265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 27365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 27465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long currentTimeMs = mClock.currentTimeMillis(); 27565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Remove past schedules. 27665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (Iterator<ScheduledRecording> iter = mWaitingSchedules.values().iterator(); 27765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko iter.hasNext(); ) { 27865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko ScheduledRecording schedule = iter.next(); 279d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (schedule.getEndTimeMs() - currentTimeMs 280d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko <= MIN_REMAIN_DURATION_PERCENT * schedule.getDuration()) { 2819850ee71f931f597658b39fba8fd18bead506955shubang Log.e(TAG, "Error! Program ended before recording started:" + schedule); 2829850ee71f931f597658b39fba8fd18bead506955shubang fail(schedule, 2839850ee71f931f597658b39fba8fd18bead506955shubang ScheduledRecording.FAILED_REASON_PROGRAM_ENDED_BEFORE_RECORDING_STARTED); 28465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko iter.remove(); 28565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 28665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 28765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mWaitingSchedules.isEmpty()) { 28865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 28965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 29065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Record the schedules which should start now. 29165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko List<ScheduledRecording> schedulesToStart = new ArrayList<>(); 29265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (ScheduledRecording schedule : mWaitingSchedules.values()) { 29365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (schedule.getState() != ScheduledRecording.STATE_RECORDING_CANCELED 29465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko && schedule.getStartTimeMs() - RecordingTask.RECORDING_EARLY_START_OFFSET_MS 29595961816a768da387f0b5523cf4363ace2044089Nick Chalko <= currentTimeMs 29695961816a768da387f0b5523cf4363ace2044089Nick Chalko && schedule.getEndTimeMs() > currentTimeMs) { 29765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko schedulesToStart.add(schedule); 29865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 29965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 300d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // The schedules will be executed with the following order. 301d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // 1. The schedule which starts early. It can be replaced later when the schedule with the 302d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // higher priority needs to start. 303d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // 2. The schedule with the higher priority. It can be replaced later when the schedule with 304d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // the higher priority needs to start. 305d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // 3. The schedule which was created recently. 306d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko Collections.sort(schedulesToStart, getRecordingOrderComparator()); 30765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko int tunerCount; 30865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko synchronized (mInputLock) { 30965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko tunerCount = mInput.canRecord() ? mInput.getTunerCount() : 0; 31065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 31165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (ScheduledRecording schedule : schedulesToStart) { 31265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (hasTaskWhichFinishEarlier(schedule)) { 31365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // If there is a schedule which finishes earlier than the new schedule, rebuild the 31465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // schedules after it finishes. 31565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 31665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 31765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mPendingRecordings.size() < tunerCount) { 31865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Tuners available. 31965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko createRecordingTask(schedule).start(); 32065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mWaitingSchedules.remove(schedule.getId()); 32165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } else { 32265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // No available tuners. 32365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko RecordingTask task = getReplacableTask(schedule); 32465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (task != null) { 32565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko task.stop(); 32665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Just return. The schedules will be rebuilt after the task is stopped. 32765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 32865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 32965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 33065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 33165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (mWaitingSchedules.isEmpty()) { 33265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return; 33365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 33465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // Set next scheduling. 33565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko long earliest = Long.MAX_VALUE; 33665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (ScheduledRecording schedule : mWaitingSchedules.values()) { 337d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // The conflicting schedules will be removed if they end before conflicting resolved. 338d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (schedulesToStart.contains(schedule)) { 339d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (earliest > schedule.getEndTimeMs()) { 340d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko earliest = schedule.getEndTimeMs(); 341d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 342d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } else { 34395961816a768da387f0b5523cf4363ace2044089Nick Chalko if (earliest 34495961816a768da387f0b5523cf4363ace2044089Nick Chalko > schedule.getStartTimeMs() 34595961816a768da387f0b5523cf4363ace2044089Nick Chalko - RecordingTask.RECORDING_EARLY_START_OFFSET_MS) { 34695961816a768da387f0b5523cf4363ace2044089Nick Chalko earliest = 34795961816a768da387f0b5523cf4363ace2044089Nick Chalko schedule.getStartTimeMs() 34895961816a768da387f0b5523cf4363ace2044089Nick Chalko - RecordingTask.RECORDING_EARLY_START_OFFSET_MS; 349d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 35065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 35165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 352d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko mHandler.sendEmptyMessageDelayed(MSG_BUILD_SCHEDULE, earliest - currentTimeMs); 35365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 35465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 35565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private RecordingTask createRecordingTask(ScheduledRecording schedule) { 35665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko Channel channel = mChannelDataManager.getChannel(schedule.getChannelId()); 35795961816a768da387f0b5523cf4363ace2044089Nick Chalko RecordingTask recordingTask = 35895961816a768da387f0b5523cf4363ace2044089Nick Chalko mRecordingTaskFactory.createRecordingTask( 35995961816a768da387f0b5523cf4363ace2044089Nick Chalko schedule, channel, mDvrManager, mSessionManager, mDataManager, mClock); 36065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko HandlerWrapper handlerWrapper = new HandlerWrapper(mLooper, schedule, recordingTask); 36165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mPendingRecordings.put(schedule.getId(), handlerWrapper); 36265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return recordingTask; 36365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 36465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 36565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private boolean hasTaskWhichFinishEarlier(ScheduledRecording schedule) { 36665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko int size = mPendingRecordings.size(); 36765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (int i = 0; i < size; ++i) { 36865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko RecordingTask task = mPendingRecordings.get(mPendingRecordings.keyAt(i)).mTask; 36965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (task.getEndTimeMs() <= schedule.getStartTimeMs()) { 37065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return true; 37165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 37265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 37365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return false; 37465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 37565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 37665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private RecordingTask getReplacableTask(ScheduledRecording schedule) { 377d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // Returns the recording with the following priority. 378d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // 1. The recording with the lowest priority is returned. 379d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // 2. If the priorities are the same, the recording which finishes early is returned. 380d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko // 3. If 1) and 2) are the same, the early created schedule is returned. 38165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko int size = mPendingRecordings.size(); 38265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko RecordingTask candidate = null; 38365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko for (int i = 0; i < size; ++i) { 38465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko RecordingTask task = mPendingRecordings.get(mPendingRecordings.keyAt(i)).mTask; 385d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (schedule.getPriority() > task.getPriority()) { 386d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko if (candidate == null || CANDIDATE_COMPARATOR.compare(candidate, task) > 0) { 387d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko candidate = task; 388d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko } 38965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 39065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 39165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko return candidate; 39265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 39365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 3949850ee71f931f597658b39fba8fd18bead506955shubang private void fail(ScheduledRecording schedule, int reason) { 39565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko // It's called when the scheduling has been failed without creating RecordingTask. 39695961816a768da387f0b5523cf4363ace2044089Nick Chalko runOnMainHandler( 39795961816a768da387f0b5523cf4363ace2044089Nick Chalko new Runnable() { 39895961816a768da387f0b5523cf4363ace2044089Nick Chalko @Override 39995961816a768da387f0b5523cf4363ace2044089Nick Chalko public void run() { 40095961816a768da387f0b5523cf4363ace2044089Nick Chalko ScheduledRecording scheduleInManager = 40195961816a768da387f0b5523cf4363ace2044089Nick Chalko mDataManager.getScheduledRecording(schedule.getId()); 40295961816a768da387f0b5523cf4363ace2044089Nick Chalko if (scheduleInManager != null) { 40395961816a768da387f0b5523cf4363ace2044089Nick Chalko // The schedule should be updated based on the object from DataManager 4049850ee71f931f597658b39fba8fd18bead506955shubang // in case when it has been updated. 40595961816a768da387f0b5523cf4363ace2044089Nick Chalko mDataManager.changeState( 4069850ee71f931f597658b39fba8fd18bead506955shubang scheduleInManager, 4079850ee71f931f597658b39fba8fd18bead506955shubang ScheduledRecording.STATE_RECORDING_FAILED, 4089850ee71f931f597658b39fba8fd18bead506955shubang reason); 40995961816a768da387f0b5523cf4363ace2044089Nick Chalko } 41095961816a768da387f0b5523cf4363ace2044089Nick Chalko } 41195961816a768da387f0b5523cf4363ace2044089Nick Chalko }); 41265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 41365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 41465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private void runOnMainHandler(Runnable runnable) { 41565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko if (Looper.myLooper() == mMainThreadHandler.getLooper()) { 41665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko runnable.run(); 41765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } else { 41865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko mMainThreadHandler.post(runnable); 41965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 42065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 42165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 42265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @VisibleForTesting 42365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko interface RecordingTaskFactory { 42495961816a768da387f0b5523cf4363ace2044089Nick Chalko RecordingTask createRecordingTask( 42595961816a768da387f0b5523cf4363ace2044089Nick Chalko ScheduledRecording scheduledRecording, 42695961816a768da387f0b5523cf4363ace2044089Nick Chalko Channel channel, 42795961816a768da387f0b5523cf4363ace2044089Nick Chalko DvrManager dvrManager, 42895961816a768da387f0b5523cf4363ace2044089Nick Chalko InputSessionManager sessionManager, 42995961816a768da387f0b5523cf4363ace2044089Nick Chalko WritableDvrDataManager dataManager, 43095961816a768da387f0b5523cf4363ace2044089Nick Chalko Clock clock); 43165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 43265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 43365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko private class WorkerThreadHandler extends Handler { 43465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public WorkerThreadHandler(Looper looper) { 43565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko super(looper); 43665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 43765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko 43865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko @Override 43965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko public void handleMessage(Message msg) { 44065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko switch (msg.what) { 44165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_ADD_SCHEDULED_RECORDING: 44265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko handleAddSchedule((ScheduledRecording) msg.obj); 44365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko break; 44465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_REMOVE_SCHEDULED_RECORDING: 44565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko handleRemoveSchedule((ScheduledRecording) msg.obj); 44665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko break; 44765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_UPDATE_SCHEDULED_RECORDING: 44865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko handleUpdateSchedule((ScheduledRecording) msg.obj); 449944779887775bd950cf1abf348d2df461593f6abLive Channels Team break; 45065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko case MSG_BUILD_SCHEDULE: 45165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko handleBuildSchedule(); 45265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko break; 453d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko case MSG_STOP_SCHEDULE: 454d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko handleStopSchedule(); 455d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko break; 45665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 45765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 45865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko } 45965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko} 460