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