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.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