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 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 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 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 217 public DvrManager getDvrManager() { 218 return mDvrManager; 219 } 220 221 /** 222 * Returns the {@link DvrScheduleManager}. 223 */ 224 @Override 225 public DvrScheduleManager getDvrScheduleManager() { 226 return mDvrScheduleManager; 227 } 228 229 /** 230 * Returns the {@link DvrWatchedPositionManager}. 231 */ 232 @Override 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) 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 253 public Analytics getAnalytics() { 254 return mAnalytics; 255 } 256 257 /** 258 * Returns the default tracker. 259 */ 260 @Override 261 public Tracker getTracker() { 262 return mTracker; 263 } 264 265 /** 266 * Returns {@link ChannelDataManager}. 267 */ 268 @Override 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 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 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 308 public DvrStorageStatusManager getDvrStorageStatusManager() { 309 return mDvrStorageStatusManager; 310 } 311 312 /** 313 * Returns {@link TvInputManagerHelper}. 314 */ 315 @Override 316 public TvInputManagerHelper getTvInputManagerHelper() { 317 return mTvInputManagerHelper; 318 } 319 320 /** 321 * Returns the main activity information. 322 */ 323 @Override 324 public MainActivityWrapper getMainActivityWrapper() { 325 return mMainActivityWrapper; 326 } 327 328 /** 329 * Returns the {@link AccountHelper}. 330 */ 331 @Override 332 public AccountHelper getAccountHelper() { 333 if (mAccountHelper == null) { 334 mAccountHelper = new AccountHelper(getApplicationContext()); 335 } 336 return mAccountHelper; 337 } 338 339 @Override 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 */ 352 public void setSelectInputActivity(SelectInputActivity activity) { 353 mSelectInputActivity = activity; 354 } 355 356 /** 357 * Handles the global key KEYCODE_TV. 358 */ 359 public void handleTvKey() { 360 if (!mMainActivityWrapper.isResumed()) { 361 startMainActivity(null); 362 } 363 } 364 365 /** 366 * Handles the global key KEYCODE_TV_INPUT. 367 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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