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