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 */ 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 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 151 public DvrManager getDvrManager() { 152 return mDvrManager; 153 } 154 155 @Override 156 @TargetApi(Build.VERSION_CODES.N) 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 168 public Analytics getAnalytics() { 169 return mAnalytics; 170 } 171 172 /** 173 * Returns the default tracker. 174 */ 175 @Override 176 public Tracker getTracker() { 177 return mTracker; 178 } 179 180 181 /** 182 * Returns {@link ChannelDataManager}. 183 */ 184 @Override 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 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 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 223 public TvInputManagerHelper getTvInputManagerHelper() { 224 return mTvInputManagerHelper; 225 } 226 227 /** 228 * Returns the main activity information. 229 */ 230 @Override 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 */ 239 public void setSelectInputActivity(SelectInputActivity activity) { 240 mSelectInputActivity = activity; 241 } 242 243 /** 244 * Handles the global key KEYCODE_TV. 245 */ 246 public void handleTvKey() { 247 if (!mMainActivityWrapper.isResumed()) { 248 startMainActivity(null); 249 } 250 } 251 252 /** 253 * Handles the global key KEYCODE_TV_INPUT. 254 */ 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 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 */ 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 */ 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 */ 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