TvApplication.java revision a1589bd48e05abbee991e0cdd27fa402a5dc5001
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;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.SharedPreferences;
25import android.content.pm.PackageInfo;
26import android.content.pm.PackageManager;
27import android.media.tv.TvContract;
28import android.media.tv.TvInputInfo;
29import android.media.tv.TvInputManager;
30import android.media.tv.TvInputManager.TvInputCallback;
31import android.os.Build;
32import android.os.Bundle;
33import android.support.annotation.Nullable;
34import android.text.TextUtils;
35import android.util.Log;
36import android.view.KeyEvent;
37import com.android.tv.common.BaseApplication;
38import com.android.tv.common.feature.CommonFeatures;
39import com.android.tv.common.recording.RecordingStorageStatusManager;
40import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
41import com.android.tv.common.util.Clock;
42import com.android.tv.common.util.Debug;
43import com.android.tv.common.util.SharedPreferencesUtils;
44import com.android.tv.data.ChannelDataManager;
45import com.android.tv.data.PreviewDataManager;
46import com.android.tv.data.ProgramDataManager;
47import com.android.tv.data.epg.EpgFetcher;
48import com.android.tv.data.epg.EpgFetcherImpl;
49import com.android.tv.dvr.DvrDataManager;
50import com.android.tv.dvr.DvrDataManagerImpl;
51import com.android.tv.dvr.DvrManager;
52import com.android.tv.dvr.DvrScheduleManager;
53import com.android.tv.dvr.DvrStorageStatusManager;
54import com.android.tv.dvr.DvrWatchedPositionManager;
55import com.android.tv.dvr.recorder.RecordingScheduler;
56import com.android.tv.perf.PerformanceMonitor;
57import com.android.tv.recommendation.ChannelPreviewUpdater;
58import com.android.tv.recommendation.RecordedProgramPreviewUpdater;
59import com.android.tv.tuner.TunerInputController;
60import com.android.tv.tuner.util.TunerInputInfoUtils;
61import com.android.tv.util.SetupUtils;
62import com.android.tv.util.TvInputManagerHelper;
63import com.android.tv.util.Utils;
64import java.util.List;
65
66/**
67 * Live TV application.
68 *
69 * <p>This includes all the Google specific hooks.
70 */
71public abstract class TvApplication extends BaseApplication implements TvSingletons, Starter {
72    private static final String TAG = "TvApplication";
73    private static final boolean DEBUG = false;
74
75    /** Namespace for LiveChannels configs. LiveChannels configs are kept in piper. */
76    public static final String CONFIGNS_P4 = "configns:p4";
77
78    /**
79     * Broadcast Action: The user has updated LC to a new version that supports tuner input. {@link
80     * TunerInputController} will receive this intent to check the existence of tuner input when the
81     * new version is first launched.
82     */
83    public static final String ACTION_APPLICATION_FIRST_LAUNCHED =
84            " com.android.tv.action.APPLICATION_FIRST_LAUNCHED";
85
86    private static final String PREFERENCE_IS_FIRST_LAUNCH = "is_first_launch";
87
88    private String mVersionName = "";
89
90    private final MainActivityWrapper mMainActivityWrapper = new MainActivityWrapper();
91
92    private SelectInputActivity mSelectInputActivity;
93    private ChannelDataManager mChannelDataManager;
94    private volatile ProgramDataManager mProgramDataManager;
95    private PreviewDataManager mPreviewDataManager;
96    private DvrManager mDvrManager;
97    private DvrScheduleManager mDvrScheduleManager;
98    private DvrDataManager mDvrDataManager;
99    private DvrWatchedPositionManager mDvrWatchedPositionManager;
100    private RecordingScheduler mRecordingScheduler;
101    private RecordingStorageStatusManager mDvrStorageStatusManager;
102    @Nullable private InputSessionManager mInputSessionManager;
103    // STOP-SHIP: Remove this variable when Tuner Process is split to another application.
104    // When this variable is null, we don't know in which process TvApplication runs.
105    private Boolean mRunningInMainProcess;
106    private PerformanceMonitor mPerformanceMonitor;
107    private TvInputManagerHelper mTvInputManagerHelper;
108    private boolean mStarted;
109    private EpgFetcher mEpgFetcher;
110    private TunerInputController mTunerInputController;
111
112    @Override
113    public void onCreate() {
114        super.onCreate();
115        SharedPreferencesUtils.initialize(
116                this,
117                new Runnable() {
118                    @Override
119                    public void run() {
120                        if (mRunningInMainProcess != null && mRunningInMainProcess) {
121                            checkTunerServiceOnFirstLaunch();
122                        }
123                    }
124                });
125        try {
126            PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
127            mVersionName = pInfo.versionName;
128        } catch (PackageManager.NameNotFoundException e) {
129            Log.w(TAG, "Unable to find package '" + getPackageName() + "'.", e);
130            mVersionName = "";
131        }
132        Log.i(TAG, "Starting Live TV " + getVersionName());
133
134        // In SetupFragment, transitions are set in the constructor. Because the fragment can be
135        // created in Activity.onCreate() by the framework, SetupAnimationHelper should be
136        // initialized here before Activity.onCreate() is called.
137        mEpgFetcher = EpgFetcherImpl.create(this);
138        SetupAnimationHelper.initialize(this);
139        getTvInputManagerHelper();
140
141        Log.i(TAG, "Started Live TV " + mVersionName);
142        Debug.getTimer(Debug.TAG_START_UP_TIMER).log("finish TvApplication.onCreate");
143    }
144
145    /** Initializes application. It is a noop if called twice. */
146    @Override
147    public void start() {
148        if (mStarted) {
149            return;
150        }
151        mStarted = true;
152        mRunningInMainProcess = true;
153        Debug.getTimer(Debug.TAG_START_UP_TIMER).log("start TvApplication.start");
154        if (mRunningInMainProcess) {
155            getTvInputManagerHelper()
156                    .addCallback(
157                            new TvInputCallback() {
158                                @Override
159                                public void onInputAdded(String inputId) {
160                                    if (TvFeatures.TUNER.isEnabled(TvApplication.this)
161                                            && TextUtils.equals(
162                                                    inputId, getEmbeddedTunerInputId())) {
163                                        TunerInputInfoUtils.updateTunerInputInfo(
164                                                TvApplication.this);
165                                    }
166                                    handleInputCountChanged();
167                                }
168
169                                @Override
170                                public void onInputRemoved(String inputId) {
171                                    handleInputCountChanged();
172                                }
173                            });
174            if (TvFeatures.TUNER.isEnabled(this)) {
175                // If the tuner input service is added before the app is started, we need to
176                // handle it here.
177                TunerInputInfoUtils.updateTunerInputInfo(TvApplication.this);
178            }
179            if (CommonFeatures.DVR.isEnabled(this)) {
180                mDvrScheduleManager = new DvrScheduleManager(this);
181                mDvrManager = new DvrManager(this);
182                mRecordingScheduler = RecordingScheduler.createScheduler(this);
183            }
184            mEpgFetcher.startRoutineService();
185            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
186                ChannelPreviewUpdater.getInstance(this).startRoutineService();
187                RecordedProgramPreviewUpdater.getInstance(this)
188                        .updatePreviewDataForRecordedPrograms();
189            }
190        }
191        Debug.getTimer(Debug.TAG_START_UP_TIMER).log("finish TvApplication.start");
192    }
193
194    private void checkTunerServiceOnFirstLaunch() {
195        SharedPreferences sharedPreferences =
196                this.getSharedPreferences(
197                        SharedPreferencesUtils.SHARED_PREF_FEATURES, Context.MODE_PRIVATE);
198        boolean isFirstLaunch = sharedPreferences.getBoolean(PREFERENCE_IS_FIRST_LAUNCH, true);
199        if (isFirstLaunch) {
200            if (DEBUG) Log.d(TAG, "Congratulations, it's the first launch!");
201            getTunerInputController()
202                    .onCheckingUsbTunerStatus(this, ACTION_APPLICATION_FIRST_LAUNCHED);
203            SharedPreferences.Editor editor = sharedPreferences.edit();
204            editor.putBoolean(PREFERENCE_IS_FIRST_LAUNCH, false);
205            editor.apply();
206        }
207    }
208
209    @Override
210    public EpgFetcher getEpgFetcher() {
211        return mEpgFetcher;
212    }
213
214    @Override
215    public synchronized SetupUtils getSetupUtils() {
216        return SetupUtils.createForTvSingletons(this);
217    }
218
219    /** Returns the {@link DvrManager}. */
220    @Override
221    public DvrManager getDvrManager() {
222        return mDvrManager;
223    }
224
225    /** Returns the {@link DvrScheduleManager}. */
226    @Override
227    public DvrScheduleManager getDvrScheduleManager() {
228        return mDvrScheduleManager;
229    }
230
231    /** Returns the {@link RecordingScheduler}. */
232    @Override
233    @Nullable
234    public RecordingScheduler getRecordingScheduler() {
235        return mRecordingScheduler;
236    }
237
238    /** Returns the {@link DvrWatchedPositionManager}. */
239    @Override
240    public DvrWatchedPositionManager getDvrWatchedPositionManager() {
241        if (mDvrWatchedPositionManager == null) {
242            mDvrWatchedPositionManager = new DvrWatchedPositionManager(this);
243        }
244        return mDvrWatchedPositionManager;
245    }
246
247    @Override
248    @TargetApi(Build.VERSION_CODES.N)
249    public InputSessionManager getInputSessionManager() {
250        if (mInputSessionManager == null) {
251            mInputSessionManager = new InputSessionManager(this);
252        }
253        return mInputSessionManager;
254    }
255
256    /** Returns {@link ChannelDataManager}. */
257    @Override
258    public ChannelDataManager getChannelDataManager() {
259        if (mChannelDataManager == null) {
260            mChannelDataManager = new ChannelDataManager(this, getTvInputManagerHelper());
261            mChannelDataManager.start();
262        }
263        return mChannelDataManager;
264    }
265
266    @Override
267    public boolean isChannelDataManagerLoadFinished() {
268        return mChannelDataManager != null && mChannelDataManager.isDbLoadFinished();
269    }
270
271    /** Returns {@link ProgramDataManager}. */
272    @Override
273    public ProgramDataManager getProgramDataManager() {
274        if (mProgramDataManager != null) {
275            return mProgramDataManager;
276        }
277        Utils.runInMainThreadAndWait(
278                new Runnable() {
279                    @Override
280                    public void run() {
281                        if (mProgramDataManager == null) {
282                            mProgramDataManager = new ProgramDataManager(TvApplication.this);
283                            mProgramDataManager.start();
284                        }
285                    }
286                });
287        return mProgramDataManager;
288    }
289
290    @Override
291    public boolean isProgramDataManagerCurrentProgramsLoadFinished() {
292        return mProgramDataManager != null && mProgramDataManager.isCurrentProgramsLoadFinished();
293    }
294
295    /** Returns {@link PreviewDataManager}. */
296    @TargetApi(Build.VERSION_CODES.O)
297    @Override
298    public PreviewDataManager getPreviewDataManager() {
299        if (mPreviewDataManager == null) {
300            mPreviewDataManager = new PreviewDataManager(this);
301            mPreviewDataManager.start();
302        }
303        return mPreviewDataManager;
304    }
305
306    /** Returns {@link DvrDataManager}. */
307    @TargetApi(Build.VERSION_CODES.N)
308    @Override
309    public DvrDataManager getDvrDataManager() {
310        if (mDvrDataManager == null) {
311            DvrDataManagerImpl dvrDataManager = new DvrDataManagerImpl(this, Clock.SYSTEM);
312            mDvrDataManager = dvrDataManager;
313            dvrDataManager.start();
314        }
315        return mDvrDataManager;
316    }
317
318    @Override
319    @TargetApi(Build.VERSION_CODES.N)
320    public RecordingStorageStatusManager getRecordingStorageStatusManager() {
321        if (mDvrStorageStatusManager == null) {
322            mDvrStorageStatusManager = new DvrStorageStatusManager(this);
323        }
324        return mDvrStorageStatusManager;
325    }
326
327    /** Returns the main activity information. */
328    @Override
329    public MainActivityWrapper getMainActivityWrapper() {
330        return mMainActivityWrapper;
331    }
332
333    /** Returns {@link TvInputManagerHelper}. */
334    @Override
335    public TvInputManagerHelper getTvInputManagerHelper() {
336        if (mTvInputManagerHelper == null) {
337            mTvInputManagerHelper = new TvInputManagerHelper(this);
338            mTvInputManagerHelper.start();
339        }
340        return mTvInputManagerHelper;
341    }
342
343    @Override
344    public synchronized TunerInputController getTunerInputController() {
345        if (mTunerInputController == null) {
346            mTunerInputController =
347                    new TunerInputController(
348                            ComponentName.unflattenFromString(getEmbeddedTunerInputId()));
349        }
350        return mTunerInputController;
351    }
352
353    @Override
354    public boolean isRunningInMainProcess() {
355        return mRunningInMainProcess != null && mRunningInMainProcess;
356    }
357
358    /**
359     * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in {@link
360     * SelectInputActivity#onDestroy}.
361     */
362    public void setSelectInputActivity(SelectInputActivity activity) {
363        mSelectInputActivity = activity;
364    }
365
366    public void handleGuideKey() {
367        if (!mMainActivityWrapper.isResumed()) {
368            startActivity(new Intent(Intent.ACTION_VIEW, TvContract.Programs.CONTENT_URI));
369        } else {
370            mMainActivityWrapper.getMainActivity().getOverlayManager().toggleProgramGuide();
371        }
372    }
373
374    /** Handles the global key KEYCODE_TV. */
375    public void handleTvKey() {
376        if (!mMainActivityWrapper.isResumed()) {
377            startMainActivity(null);
378        }
379    }
380
381    /** Handles the global key KEYCODE_TV_INPUT. */
382    public void handleTvInputKey() {
383        TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
384        List<TvInputInfo> tvInputs = tvInputManager.getTvInputList();
385        int inputCount = 0;
386        boolean hasTunerInput = false;
387        for (TvInputInfo input : tvInputs) {
388            if (input.isPassthroughInput()) {
389                if (!input.isHidden(this)) {
390                    ++inputCount;
391                }
392            } else if (!hasTunerInput) {
393                hasTunerInput = true;
394                ++inputCount;
395            }
396        }
397        if (inputCount < 2) {
398            return;
399        }
400        Activity activityToHandle =
401                mMainActivityWrapper.isResumed()
402                        ? mMainActivityWrapper.getMainActivity()
403                        : mSelectInputActivity;
404        if (activityToHandle != null) {
405            // If startActivity is called, MainActivity.onPause is unnecessarily called. To
406            // prevent it, MainActivity.dispatchKeyEvent is directly called.
407            activityToHandle.dispatchKeyEvent(
408                    new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT));
409            activityToHandle.dispatchKeyEvent(
410                    new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TV_INPUT));
411        } else if (mMainActivityWrapper.isStarted()) {
412            Bundle extras = new Bundle();
413            extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT);
414            startMainActivity(extras);
415        } else {
416            startActivity(
417                    new Intent(this, SelectInputActivity.class)
418                            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
419        }
420    }
421
422    private void startMainActivity(Bundle extras) {
423        // The use of FLAG_ACTIVITY_NEW_TASK enables arbitrary applications to access the intent
424        // sent to the root activity. Having said that, we should be fine here since such an intent
425        // does not carry any important user data.
426        Intent intent =
427                new Intent(this, MainActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
428        if (extras != null) {
429            intent.putExtras(extras);
430        }
431        startActivity(intent);
432    }
433
434    /**
435     * Returns the version name of the live channels.
436     *
437     * @see PackageInfo#versionName
438     */
439    public String getVersionName() {
440        return mVersionName;
441    }
442
443    /**
444     * Checks the input counts and enable/disable TvActivity. Also upda162 the input list in {@link
445     * SetupUtils}.
446     */
447    @Override
448    public void handleInputCountChanged() {
449        handleInputCountChanged(false, false, false);
450    }
451
452    /**
453     * Checks the input counts and enable/disable TvActivity. Also updates the input list in {@link
454     * SetupUtils}.
455     *
456     * @param calledByTunerServiceChanged true if it is called when BaseTunerTvInputService is
457     *     enabled or disabled.
458     * @param tunerServiceEnabled it's available only when calledByTunerServiceChanged is true.
459     * @param dontKillApp when TvActivity is enabled or disabled by this method, the app restarts by
460     *     default. But, if dontKillApp is true, the app won't restart.
461     */
462    public void handleInputCountChanged(
463            boolean calledByTunerServiceChanged, boolean tunerServiceEnabled, boolean dontKillApp) {
464        TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
465        boolean enable =
466                (calledByTunerServiceChanged && tunerServiceEnabled)
467                        || TvFeatures.UNHIDE.isEnabled(TvApplication.this);
468        if (!enable) {
469            List<TvInputInfo> inputs = inputManager.getTvInputList();
470            boolean skipTunerInputCheck = false;
471            // Enable the TvActivity only if there is at least one tuner type input.
472            if (!skipTunerInputCheck) {
473                for (TvInputInfo input : inputs) {
474                    if (calledByTunerServiceChanged
475                            && !tunerServiceEnabled
476                            && getEmbeddedTunerInputId().equals(input.getId())) {
477                        continue;
478                    }
479                    if (input.getType() == TvInputInfo.TYPE_TUNER) {
480                        enable = true;
481                        break;
482                    }
483                }
484            }
485            if (DEBUG) Log.d(TAG, "Enable MainActivity: " + enable);
486        }
487        PackageManager packageManager = getPackageManager();
488        ComponentName name = new ComponentName(this, TvActivity.class);
489        int newState =
490                enable
491                        ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
492                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
493        if (packageManager.getComponentEnabledSetting(name) != newState) {
494            packageManager.setComponentEnabledSetting(
495                    name, newState, dontKillApp ? PackageManager.DONT_KILL_APP : 0);
496            Log.i(TAG, (enable ? "Un-hide" : "Hide") + " Live TV.");
497        }
498        getSetupUtils().onInputListUpdated(inputManager);
499    }
500}
501