1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.tv.dvr; 18 19import android.app.AlarmManager; 20import android.app.Service; 21import android.content.Context; 22import android.content.Intent; 23import android.os.Binder; 24import android.os.HandlerThread; 25import android.os.IBinder; 26import android.support.annotation.Nullable; 27import android.support.annotation.VisibleForTesting; 28import android.util.Log; 29 30import com.android.tv.ApplicationSingletons; 31import com.android.tv.TvApplication; 32import com.android.tv.common.feature.CommonFeatures; 33import com.android.tv.util.Clock; 34import com.android.tv.util.RecurringRunner; 35import com.android.tv.common.SoftPreconditions; 36 37/** 38 * DVR Scheduler service. 39 * 40 * <p> This service is responsible for: 41 * <ul> 42 * <li>Send record commands to TV inputs</li> 43 * <li>Wake up at proper timing for recording</li> 44 * <li>Deconflict schedule, handling overlapping times etc.</li> 45 * <li> 46 * 47 * </ul> 48 * 49 * <p>The service does not stop it self. 50 */ 51public class DvrRecordingService extends Service { 52 private static final String TAG = "DvrRecordingService"; 53 private static final boolean DEBUG = false; 54 public static final String HANDLER_THREAD_NAME = "DvrRecordingService-handler"; 55 56 public static void startService(Context context) { 57 Intent dvrSchedulerIntent = new Intent(context, DvrRecordingService.class); 58 context.startService(dvrSchedulerIntent); 59 } 60 61 private final Clock mClock = Clock.SYSTEM; 62 private RecurringRunner mReaperRunner; 63 private WritableDvrDataManager mDataManager; 64 65 /** 66 * Class for clients to access. Because we know this service always 67 * runs in the same process as its clients, we don't need to deal with 68 * IPC. 69 */ 70 public class SchedulerBinder extends Binder { 71 Scheduler getScheduler() { 72 return mScheduler; 73 } 74 } 75 76 private final IBinder mBinder = new SchedulerBinder(); 77 78 private Scheduler mScheduler; 79 private HandlerThread mHandlerThread; 80 81 @Override 82 public void onCreate() { 83 if (DEBUG) Log.d(TAG, "onCreate"); 84 super.onCreate(); 85 SoftPreconditions.checkFeatureEnabled(this, CommonFeatures.DVR, TAG); 86 ApplicationSingletons singletons = TvApplication.getSingletons(this); 87 mDataManager = (WritableDvrDataManager) singletons.getDvrDataManager(); 88 89 AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 90 // mScheduler may have been set for testing. 91 if (mScheduler == null) { 92 mHandlerThread = new HandlerThread(HANDLER_THREAD_NAME); 93 mHandlerThread.start(); 94 mScheduler = new Scheduler(mHandlerThread.getLooper(), singletons.getDvrManager(), 95 singletons.getDvrSessionManger(), mDataManager, 96 singletons.getChannelDataManager(), this, mClock, alarmManager); 97 } 98 mDataManager.addScheduledRecordingListener(mScheduler); 99 mReaperRunner = new RecurringRunner(this, java.util.concurrent.TimeUnit.DAYS.toMillis(1), 100 new ScheduledProgramReaper(mDataManager, mClock), null); 101 mReaperRunner.start(); 102 } 103 104 @Override 105 public int onStartCommand(Intent intent, int flags, int startId) { 106 if (DEBUG) Log.d(TAG, "onStartCommand (" + intent + "," + flags + "," + startId + ")"); 107 mScheduler.update(); 108 return START_STICKY; 109 } 110 111 @Override 112 public void onDestroy() { 113 if (DEBUG) Log.d(TAG, "onDestroy"); 114 mReaperRunner.stop(); 115 mDataManager.removeScheduledRecordingListener(mScheduler); 116 mScheduler = null; 117 if (mHandlerThread != null) { 118 mHandlerThread.quit(); 119 mHandlerThread = null; 120 } 121 super.onDestroy(); 122 } 123 124 @Nullable 125 @Override 126 public IBinder onBind(Intent intent) { 127 return mBinder; 128 } 129 130 @VisibleForTesting 131 void setScheduler(Scheduler scheduler) { 132 Log.i(TAG, "Setting scheduler for tests to " + scheduler); 133 mScheduler = scheduler; 134 } 135} 136