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 171abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopackage com.android.tv.dvr; 181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.app.AlarmManager; 201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.app.Service; 211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Context; 221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.content.Intent; 231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.Binder; 24ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport android.os.HandlerThread; 251abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.os.IBinder; 261abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.support.annotation.Nullable; 271abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.support.annotation.VisibleForTesting; 281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log; 291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 301abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.ApplicationSingletons; 311abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.TvApplication; 32ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.feature.CommonFeatures; 331abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.util.Clock; 3448dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.util.RecurringRunner; 3548dadb49248271b01997862e1335912a4f2e189fYoungsang Choimport com.android.tv.common.SoftPreconditions; 361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/** 381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * DVR Scheduler service. 391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p> This service is responsible for: 411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <ul> 421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>Send record commands to TV inputs</li> 431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>Wake up at proper timing for recording</li> 441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li>Deconflict schedule, handling overlapping times etc.</li> 451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <li> 461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * </ul> 481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * 491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * <p>The service does not stop it self. 501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 511abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class DvrRecordingService extends Service { 521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final String TAG = "DvrRecordingService"; 531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private static final boolean DEBUG = false; 54ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko public static final String HANDLER_THREAD_NAME = "DvrRecordingService-handler"; 55ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko 561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public static void startService(Context context) { 571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Intent dvrSchedulerIntent = new Intent(context, DvrRecordingService.class); 581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko context.startService(dvrSchedulerIntent); 591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 6148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private final Clock mClock = Clock.SYSTEM; 6248dadb49248271b01997862e1335912a4f2e189fYoungsang Cho private RecurringRunner mReaperRunner; 631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private WritableDvrDataManager mDataManager; 641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko /** 661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Class for clients to access. Because we know this service always 671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * runs in the same process as its clients, we don't need to deal with 681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * IPC. 691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */ 701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public class SchedulerBinder extends Binder { 711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Scheduler getScheduler() { 721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return mScheduler; 731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private final IBinder mBinder = new SchedulerBinder(); 771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko private Scheduler mScheduler; 79ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko private HandlerThread mHandlerThread; 801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onCreate() { 831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) Log.d(TAG, "onCreate"); 841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko super.onCreate(); 85ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko SoftPreconditions.checkFeatureEnabled(this, CommonFeatures.DVR, TAG); 861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko ApplicationSingletons singletons = TvApplication.getSingletons(this); 871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mDataManager = (WritableDvrDataManager) singletons.getDvrDataManager(); 881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko // mScheduler may have been set for testing. 911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (mScheduler == null) { 92ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME); 93ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mHandlerThread.start(); 9448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mScheduler = new Scheduler(mHandlerThread.getLooper(), singletons.getDvrManager(), 9548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho singletons.getDvrSessionManger(), mDataManager, 9648dadb49248271b01997862e1335912a4f2e189fYoungsang Cho singletons.getChannelDataManager(), this, mClock, alarmManager); 971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 9848dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mDataManager.addScheduledRecordingListener(mScheduler); 9948dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mReaperRunner = new RecurringRunner(this, java.util.concurrent.TimeUnit.DAYS.toMillis(1), 10048dadb49248271b01997862e1335912a4f2e189fYoungsang Cho new ScheduledProgramReaper(mDataManager, mClock), null); 10148dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mReaperRunner.start(); 1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public int onStartCommand(Intent intent, int flags, int startId) { 1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) Log.d(TAG, "onStartCommand (" + intent + "," + flags + "," + startId + ")"); 1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mScheduler.update(); 1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return START_STICKY; 1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public void onDestroy() { 1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko if (DEBUG) Log.d(TAG, "onDestroy"); 11448dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mReaperRunner.stop(); 11548dadb49248271b01997862e1335912a4f2e189fYoungsang Cho mDataManager.removeScheduledRecordingListener(mScheduler); 1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mScheduler = null; 117ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko if (mHandlerThread != null) { 118ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mHandlerThread.quit(); 119ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko mHandlerThread = null; 120ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko } 1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko super.onDestroy(); 1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Nullable 1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @Override 1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko public IBinder onBind(Intent intent) { 1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko return mBinder; 1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko 1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko @VisibleForTesting 1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko void setScheduler(Scheduler scheduler) { 1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko Log.i(TAG, "Setting scheduler for tests to " + scheduler); 1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mScheduler = scheduler; 1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko } 1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko} 136