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