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