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