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