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.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.os.StrictMode;
34 import android.support.annotation.Nullable;
35 import android.support.v4.os.BuildCompat;
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.TvCommonUtils;
46 import com.android.tv.common.feature.CommonFeatures;
47 import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
48 import com.android.tv.data.ChannelDataManager;
49 import com.android.tv.data.ProgramDataManager;
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.DvrRecordingService;
54 import com.android.tv.dvr.DvrSessionManager;
55 import com.android.tv.util.Clock;
56 import com.android.tv.util.SetupUtils;
57 import com.android.tv.util.SystemProperties;
58 import com.android.tv.util.TvInputManagerHelper;
59 import com.android.tv.util.Utils;
60 import com.android.usbtuner.UsbTunerPreferences;
61 import com.android.usbtuner.setup.TunerSetupActivity;
62 import com.android.usbtuner.tvinput.UsbTunerTvInputService;
63 
64 import java.util.List;
65 
66 public class TvApplication extends Application implements ApplicationSingletons {
67     private static final String TAG = "TvApplication";
68     private static final boolean DEBUG = false;
69 
70     /**
71      * Returns the @{@link ApplicationSingletons} using the application context.
72      */
getSingletons(Context context)73     public static ApplicationSingletons getSingletons(Context context) {
74         return (ApplicationSingletons) context.getApplicationContext();
75     }
76     private String mVersionName = "";
77 
78     private final MainActivityWrapper mMainActivityWrapper = new MainActivityWrapper();
79 
80     private SelectInputActivity mSelectInputActivity;
81     private Analytics mAnalytics;
82     private Tracker mTracker;
83     private TvInputManagerHelper mTvInputManagerHelper;
84     private ChannelDataManager mChannelDataManager;
85     private ProgramDataManager mProgramDataManager;
86     private DvrManager mDvrManager;
87     private DvrDataManager mDvrDataManager;
88     @Nullable
89     private DvrSessionManager mDvrSessionManager;
90 
91     @Override
onCreate()92     public void onCreate() {
93         super.onCreate();
94         SharedPreferencesUtils.initialize(this);
95         try {
96             PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
97             mVersionName = pInfo.versionName;
98         } catch (PackageManager.NameNotFoundException e) {
99             Log.w(TAG, "Unable to find package '" + getPackageName() + "'.", e);
100             mVersionName = "";
101         }
102         Log.i(TAG, "Starting Live TV " + getVersionName());
103         // Only set StrictMode for ENG builds because the build server only produces userdebug
104         // builds.
105         if (BuildConfig.ENG && SystemProperties.ALLOW_STRICT_MODE.getValue()) {
106             StrictMode.setThreadPolicy(
107                     new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
108             StrictMode.VmPolicy.Builder vmPolicyBuilder = new StrictMode.VmPolicy.Builder()
109                     .detectAll().penaltyLog();
110             if (BuildConfig.ENG && SystemProperties.ALLOW_DEATH_PENALTY.getValue() &&
111                     !TvCommonUtils.isRunningInTest()) {
112                 // TODO turn on death penalty for tests when they stop leaking MainActivity
113             }
114             StrictMode.setVmPolicy(vmPolicyBuilder.build());
115         }
116         if (BuildConfig.ENG && !SystemProperties.ALLOW_ANALYTICS_IN_ENG.getValue()) {
117             mAnalytics = StubAnalytics.getInstance(this);
118         } else {
119             mAnalytics = StubAnalytics.getInstance(this);
120         }
121         mTracker = mAnalytics.getDefaultTracker();
122         mTvInputManagerHelper = new TvInputManagerHelper(this);
123         mTvInputManagerHelper.start();
124         mTvInputManagerHelper.addCallback(new TvInputCallback() {
125             @Override
126             public void onInputAdded(String inputId) {
127                 handleInputCountChanged();
128             }
129 
130             @Override
131             public void onInputRemoved(String inputId) {
132                 handleInputCountChanged();
133             }
134         });
135         if (CommonFeatures.DVR.isEnabled(this) && BuildCompat.isAtLeastN()) {
136             mDvrManager = new DvrManager(this);
137             //NOTE: DvrRecordingService just keeps running.
138             DvrRecordingService.startService(this);
139         }
140         // In SetupFragment, transitions are set in the constructor. Because the fragment can be
141         // created in Activity.onCreate() by the framework, SetupAnimationHelper should be
142         // initialized here before Activity.onCreate() is called.
143         SetupAnimationHelper.initialize(this);
144         if (DEBUG) Log.i(TAG, "Started Live TV " + mVersionName);
145     }
146 
147     /**
148      * Returns the {@link DvrManager}.
149      */
150     @Override
getDvrManager()151     public DvrManager getDvrManager() {
152         return mDvrManager;
153     }
154 
155     @Override
156     @TargetApi(Build.VERSION_CODES.N)
getDvrSessionManger()157     public DvrSessionManager getDvrSessionManger() {
158         if (mDvrSessionManager == null) {
159             mDvrSessionManager = new DvrSessionManager(this);
160         }
161         return mDvrSessionManager;
162     }
163 
164     /**
165      * Returns the {@link Analytics}.
166      */
167     @Override
getAnalytics()168     public Analytics getAnalytics() {
169         return mAnalytics;
170     }
171 
172     /**
173      * Returns the default tracker.
174      */
175     @Override
getTracker()176     public Tracker getTracker() {
177         return mTracker;
178     }
179 
180 
181     /**
182      * Returns {@link ChannelDataManager}.
183      */
184     @Override
getChannelDataManager()185     public ChannelDataManager getChannelDataManager() {
186         if (mChannelDataManager == null) {
187             mChannelDataManager = new ChannelDataManager(this, mTvInputManagerHelper);
188             mChannelDataManager.start();
189         }
190         return mChannelDataManager;
191     }
192 
193     /**
194      * Returns {@link ProgramDataManager}.
195      */
196     @Override
getProgramDataManager()197     public ProgramDataManager getProgramDataManager() {
198         if (mProgramDataManager == null) {
199             mProgramDataManager = new ProgramDataManager(this);
200             mProgramDataManager.start();
201         }
202         return mProgramDataManager;
203     }
204 
205     /**
206      * Returns {@link DvrDataManager}.
207      */
208     @TargetApi(Build.VERSION_CODES.N)
209     @Override
getDvrDataManager()210     public DvrDataManager getDvrDataManager() {
211         if (mDvrDataManager == null) {
212                 DvrDataManagerImpl dvrDataManager = new DvrDataManagerImpl(this, Clock.SYSTEM);
213                 mDvrDataManager = dvrDataManager;
214                 dvrDataManager.start();
215         }
216         return mDvrDataManager;
217     }
218 
219     /**
220      * Returns {@link TvInputManagerHelper}.
221      */
222     @Override
getTvInputManagerHelper()223     public TvInputManagerHelper getTvInputManagerHelper() {
224         return mTvInputManagerHelper;
225     }
226 
227     /**
228      * Returns the main activity information.
229      */
230     @Override
getMainActivityWrapper()231     public MainActivityWrapper getMainActivityWrapper() {
232         return mMainActivityWrapper;
233     }
234 
235     /**
236      * SelectInputActivity is set in {@link SelectInputActivity#onCreate} and cleared in
237      * {@link SelectInputActivity#onDestroy}.
238      */
setSelectInputActivity(SelectInputActivity activity)239     public void setSelectInputActivity(SelectInputActivity activity) {
240         mSelectInputActivity = activity;
241     }
242 
243     /**
244      * Handles the global key KEYCODE_TV.
245      */
handleTvKey()246     public void handleTvKey() {
247         if (!mMainActivityWrapper.isResumed()) {
248             startMainActivity(null);
249         }
250     }
251 
252     /**
253      * Handles the global key KEYCODE_TV_INPUT.
254      */
handleTvInputKey()255     public void handleTvInputKey() {
256         TvInputManager tvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
257         List<TvInputInfo> tvInputs = tvInputManager.getTvInputList();
258         int inputCount = 0;
259         boolean hasTunerInput = false;
260         for (TvInputInfo input : tvInputs) {
261             if (input.isPassthroughInput()) {
262                 if (!input.isHidden(this)) {
263                     ++inputCount;
264                 }
265             } else if (!hasTunerInput) {
266                 hasTunerInput = true;
267                 ++inputCount;
268             }
269         }
270         if (inputCount < 2) {
271             return;
272         }
273         Activity activityToHandle = mMainActivityWrapper.isResumed()
274                 ? mMainActivityWrapper.getMainActivity() : mSelectInputActivity;
275         if (activityToHandle != null) {
276             // If startActivity is called, MainActivity.onPause is unnecessarily called. To
277             // prevent it, MainActivity.dispatchKeyEvent is directly called.
278             activityToHandle.dispatchKeyEvent(
279                     new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TV_INPUT));
280             activityToHandle.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
281                     KeyEvent.KEYCODE_TV_INPUT));
282         } else if (mMainActivityWrapper.isStarted()) {
283             Bundle extras = new Bundle();
284             extras.putString(Utils.EXTRA_KEY_ACTION, Utils.EXTRA_ACTION_SHOW_TV_INPUT);
285             startMainActivity(extras);
286         } else {
287             startActivity(new Intent(this, SelectInputActivity.class).setFlags(
288                     Intent.FLAG_ACTIVITY_NEW_TASK));
289         }
290     }
291 
startMainActivity(Bundle extras)292     private void startMainActivity(Bundle extras) {
293         // The use of FLAG_ACTIVITY_NEW_TASK enables arbitrary applications to access the intent
294         // sent to the root activity. Having said that, we should be fine here since such an intent
295         // does not carry any important user data.
296         Intent intent = new Intent(this, MainActivity.class)
297                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
298         if (extras != null) {
299             intent.putExtras(extras);
300         }
301         startActivity(intent);
302     }
303 
304     /**
305      * Returns the version name of the live channels.
306      *
307      * @see PackageInfo#versionName
308      */
getVersionName()309     public String getVersionName() {
310         return mVersionName;
311     }
312 
313     /**
314      * Checks the input counts and enable/disable TvActivity. Also updates the input list in
315      * {@link SetupUtils}.
316      */
handleInputCountChanged()317     public void handleInputCountChanged() {
318         handleInputCountChanged(false, false, false);
319     }
320 
321     /**
322      * Checks the input counts and enable/disable TvActivity. Also updates the input list in
323      * {@link SetupUtils}.
324      *
325      * @param calledByTunerServiceChanged true if it is called when UsbTunerTvInputService
326      *        is enabled or disabled.
327      * @param tunerServiceEnabled it's available only when calledByTunerServiceChanged is true.
328      * @param dontKillApp when TvActivity is enabled or disabled by this method, the app restarts
329      *        by default. But, if dontKillApp is true, the app won't restart.
330      */
handleInputCountChanged(boolean calledByTunerServiceChanged, boolean tunerServiceEnabled, boolean dontKillApp)331     public void handleInputCountChanged(boolean calledByTunerServiceChanged,
332             boolean tunerServiceEnabled, boolean dontKillApp) {
333         TvInputManager inputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE);
334         boolean enable = (calledByTunerServiceChanged && tunerServiceEnabled)
335                 || Features.UNHIDE.isEnabled(TvApplication.this);
336         if (!enable) {
337             List<TvInputInfo> inputs = inputManager.getTvInputList();
338             boolean skipTunerInputCheck = false;
339             // Enable the TvActivity only if there is at least one tuner type input.
340             if (!skipTunerInputCheck) {
341                 for (TvInputInfo input : inputs) {
342                     if (calledByTunerServiceChanged && !tunerServiceEnabled
343                             && UsbTunerTvInputService.getInputId(this).equals(input.getId())) {
344                         continue;
345                     }
346                     if (input.getType() == TvInputInfo.TYPE_TUNER) {
347                         enable = true;
348                         break;
349                     }
350                 }
351             }
352             if (DEBUG) Log.d(TAG, "Enable MainActivity: " + enable);
353         }
354         PackageManager packageManager = getPackageManager();
355         ComponentName name = new ComponentName(this, TvActivity.class);
356         int newState = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
357                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
358         if (packageManager.getComponentEnabledSetting(name) != newState) {
359             packageManager.setComponentEnabledSetting(name, newState,
360                     dontKillApp ? PackageManager.DONT_KILL_APP : 0);
361         }
362         SetupUtils.getInstance(TvApplication.this).onInputListUpdated(inputManager);
363     }
364 }
365