Snap for 5316819 from c31edd529663ceeab53cbfc10a8ad7a53b3be30a to qt-release

Change-Id: Icd9d7c802479322ccc52dce08082371ef400f268
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..4268636
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,96 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+version_name = "1.20-asop"
+version_code = "417000328"
+
+android_app {
+    name: "LiveTv",
+
+    srcs: ["src/**/*.java"],
+
+    // TODO(b/122608868) turn proguard back on
+    optimize: {
+        enabled: false,
+    },
+
+    // It is required for com.android.providers.tv.permission.ALL_EPG_DATA
+    privileged: true,
+
+    sdk_version: "system_current",
+    min_sdk_version: "23", // M
+
+    resource_dirs: [
+        "res",
+        "material_res",
+
+    ],
+
+    libs: ["tv-guava-android-jar"],
+
+    static_libs: [
+        "android-support-annotations",
+        "android-support-compat",
+        "android-support-core-ui",
+        "androidx.tvprovider_tvprovider",
+        "android-support-v4",
+        "android-support-v7-appcompat",
+        "android-support-v7-palette",
+        "android-support-v7-preference",
+        "android-support-v7-recyclerview",
+        "android-support-v14-preference",
+        "android-support-v17-leanback",
+        "android-support-v17-preference-leanback",
+        "jsr330",
+        "live-channels-partner-support",
+        "live-tv-tuner-proto",
+        "live-tv-tuner",
+        "tv-auto-value-jar",
+        "tv-auto-factory-jar",
+        "tv-common",
+        "tv-error-prone-annotations-jar",
+        "tv-lib-dagger",
+        "tv-lib-exoplayer",
+        "tv-lib-exoplayer-v2-core",
+        "tv-lib-dagger-android",
+    ],
+
+    plugins: [
+        "tv-auto-value",
+        "tv-auto-factory",
+        "tv-lib-dagger-android-processor",
+        "tv-lib-dagger-compiler",
+    ],
+
+    javacflags: [
+        "-Xlint:deprecation",
+        "-Xlint:unchecked",
+    ],
+
+    aaptflags: [
+        "--version-name",
+        version_name,
+
+        "--version-code",
+        version_code,
+
+        "--extra-packages",
+        "com.android.tv.tuner",
+
+        "--extra-packages",
+        "com.android.tv.common",
+    ],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 52e9742..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,107 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-include $(LOCAL_PATH)/version.mk
-
-LOCAL_SRC_FILES += $(call all-java-files-under, src)
-
-# TODO(b/77284273): Stop compiling everything at once dagger properly supported in libraries
-LOCAL_SRC_FILES += $(call all-java-files-under, common/src)
-LOCAL_SRC_FILES += $(call all-proto-files-under, common/src)
-
-LOCAL_SRC_FILES += $(call all-java-files-under, tuner/src)
-
-
-
-LOCAL_PACKAGE_NAME := LiveTv
-
-# TODO(b/122608868) turn proguard back on
-LOCAL_PROGUARD_ENABLED := disabled
-
-# It is required for com.android.providers.tv.permission.ALL_EPG_DATA
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 23  # M
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/res \
-    $(LOCAL_PATH)/material_res \
-    $(LOCAL_PATH)/common/res \
-    $(LOCAL_PATH)/tuner/res \
-
-LOCAL_JAVA_LIBRARIES := \
-    tv-guava-android-jar \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    tv-auto-value-jar \
-    tv-auto-factory-jar \
-    android-support-annotations \
-    tv-error-prone-annotations-jar \
-    jsr330 \
-    tv-lib-dagger \
-    tv-lib-exoplayer \
-    tv-lib-exoplayer-v2-core \
-    live-tv-tuner-proto \
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    android-support-compat \
-    android-support-core-ui \
-    androidx.tvprovider_tvprovider \
-    android-support-v4 \
-    android-support-v7-appcompat \
-    android-support-v7-palette \
-    android-support-v7-preference \
-    android-support-v7-recyclerview \
-    android-support-v14-preference \
-    android-support-v17-leanback \
-    android-support-v17-preference-leanback \
-    tv-lib-dagger-android \
-    live-channels-partner-support \
-
-LOCAL_ANNOTATION_PROCESSORS := \
-    tv-auto-value-jar \
-    tv-auto-factory-jar \
-    tv-guava-jre-jar \
-    tv-lib-dagger-android-processor \
-    tv-lib-dagger-compiler \
-
-
-LOCAL_ANNOTATION_PROCESSOR_CLASSES := \
-  com.google.auto.factory.processor.AutoFactoryProcessor,com.google.auto.value.processor.AutoValueProcessor,dagger.internal.codegen.ComponentProcessor,dagger.android.processor.AndroidProcessor
-
-
-LOCAL_JAVACFLAGS := -Xlint:deprecation -Xlint:unchecked
-
-LOCAL_AAPT_FLAGS += \
-    --version-name "$(version_name_package)" \
-    --version-code $(version_code_package) \
-
-LOCAL_JNI_SHARED_LIBRARIES := libtunertvinput_jni
-LOCAL_AAPT_FLAGS += --extra-packages com.android.tv.tuner
-LOCAL_AAPT_FLAGS += --extra-packages com.android.tv.common
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 43c2a3e..a398823 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,10 +16,9 @@
 -->
 <!-- This manifest is for LiveTv -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
+  xmlns:tools="http://schemas.android.com/tools"
     package="com.android.tv" >
 
-
     <uses-sdk
         android:minSdkVersion="23"
         android:targetSdkVersion="27" />
@@ -81,17 +80,7 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.TV"
         tools:replace="android:appComponentFactory">
-        <activity
-            android:name="com.android.tv.tuner.setup.LiveTvTunerSetupActivity"
-            android:configChanges="keyboard|keyboardHidden"
-            android:label="@string/bt_app_name"
-            android:launchMode="singleInstance"
-            android:process="com.android.tv.tuner"
-            android:theme="@style/Theme.Setup.GuidedStep" >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
-        </activity>
+        >
 
         <!-- providers are listed here to keep them separate from the internal versions -->
         <provider
@@ -288,7 +277,7 @@
             android:name="com.android.tv.setup.SystemSetupActivity"
             android:configChanges="keyboard|keyboardHidden"
             android:exported="true"
-            android:label="@string/bt_app_name"
+            android:label="@string/app_name"
             android:launchMode="singleInstance"
             android:theme="@style/Theme.Setup.GuidedStep" >
             <intent-filter>
@@ -304,27 +293,8 @@
         <receiver android:name="com.android.tv.dvr.recorder.DvrStartRecordingReceiver" />
 
         <service
-            android:name="com.android.tv.tuner.tvinput.TunerStorageCleanUpService"
-            android:exported="false"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:process="com.android.tv.tuner" />
-        <service
             android:name="com.android.tv.data.epg.EpgFetchService"
             android:permission="android.permission.BIND_JOB_SERVICE" />
-        <service
-            android:name="com.android.tv.tuner.livetuner.LiveTvTunerTvInputService"
-            android:enabled="false"
-            android:label="@string/bt_app_name"
-            android:permission="android.permission.BIND_TV_INPUT"
-            android:process="com.android.tv.tuner" >
-            <intent-filter>
-                <action android:name="android.media.tv.TvInputService" />
-            </intent-filter>
-
-            <meta-data
-                android:name="android.media.tv.input"
-                android:resource="@xml/ut_tvinputservice" />
-        </service>
     </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/build.gradle b/build.gradle
index 9a29367..23e3dbd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -26,18 +26,13 @@
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:3.1.4'
-        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.5'
+        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
     }
 }
 apply plugin: 'com.android.application'
 android {
-    compileSdkVersion 26
-    buildToolsVersion '28.0.2'
-    lintOptions {
-        //  check for lint errors in release builds,
-        // but continue the build even when errors are found:
-        abortOnError false
-    }
+    compileSdkVersion 28
+    buildToolsVersion '28.0.3'
     dexOptions {
         preDexLibraries = false
         additionalParameters=['--core-library']
@@ -50,7 +45,7 @@
     }
     defaultConfig {
         minSdkVersion 23
-        targetSdkVersion 26
+        targetSdkVersion 28
         versionCode 1
         versionName "1.0"
     }
@@ -81,16 +76,24 @@
     google()
 }
 
-final String SUPPORT_LIBS_VERSION = '26.1.0'
 dependencies {
-    implementation "com.android.support:palette-v7:${SUPPORT_LIBS_VERSION}"
-    implementation "com.android.support:leanback-v17:${SUPPORT_LIBS_VERSION}"
-    implementation "com.android.support:support-tv-provider:${SUPPORT_LIBS_VERSION}"
-    /*Not building with  latest one (1.6.2)*/
+    implementation 'androidx.appcompat:appcompat:1.0.2'
+    implementation 'androidx.palette:palette:1.0.0'
+    implementation 'androidx.leanback:leanback:1.0.0'
+    implementation "androidx.tvprovider:tvprovider:1.0.0"
+    implementation "androidx.recyclerview:recyclerview:1.0.0"
+    implementation "androidx.recyclerview:recyclerview-selection:1.0.0"
+    implementation "androidx.palette:palette:1.0.0"
+
+    implementation 'com.google.dagger:dagger:2.18'
+    implementation 'com.google.dagger:dagger-android:2.18'
+    annotationProcessor 'com.google.dagger:dagger-compiler:2.18'
+    annotationProcessor 'com.google.dagger:dagger-android-processor:2.18'
+
+    /*Not building with  latest one (1.6.3)*/
     annotationProcessor 'com.google.auto.value:auto-value:1.5.4'
     implementation 'com.google.auto.value:auto-value:1.5.4'
     implementation 'javax.inject:javax.inject:1'
     implementation 'com.google.guava:guava:26.0-android'
     implementation project(':common')
-    implementation project(':tuner')
 }
\ No newline at end of file
diff --git a/common/Android.bp b/common/Android.bp
index bc40838..63759d4 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -48,6 +48,8 @@
     plugins: [
         "tv-auto-value",
         "tv-auto-factory",
+        "tv-lib-dagger-android-processor",
+        "tv-lib-dagger-compiler",
     ],
 
 
diff --git a/common/build.gradle b/common/build.gradle
index 66f3b6e..f371475 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -28,11 +28,12 @@
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:3.1.4'
+        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
     }
 }
 android {
-    compileSdkVersion 26
-    buildToolsVersion '28.0.2'
+    compileSdkVersion 28
+    buildToolsVersion '28.0.3'
     dexOptions {
         preDexLibraries = false
         additionalParameters = ['--core-library']
@@ -47,18 +48,20 @@
 
     defaultConfig {
         minSdkVersion 23
-        targetSdkVersion 26
+        targetSdkVersion 28
         versionCode 1
         versionName "1.0"
     }
     buildTypes {
         debug {
             minifyEnabled false
+            buildConfigField "boolean", "AOSP", "true"
             buildConfigField "boolean", "ENG", "true"
             buildConfigField "boolean", "NO_JNI_TEST", "false"
         }
         release {
             minifyEnabled true
+            buildConfigField "boolean", "AOSP", "true"
             buildConfigField "boolean", "ENG", "false"
             buildConfigField "boolean", "NO_JNI_TEST", "false"
         }
@@ -85,17 +88,25 @@
     google()
 }
 
-final String SUPPORT_LIBS_VERSION = '26.1.0'
 dependencies {
-    implementation "com.android.support:appcompat-v7:${SUPPORT_LIBS_VERSION}"
-    implementation "com.android.support:leanback-v17:${SUPPORT_LIBS_VERSION}"
+    implementation 'androidx.appcompat:appcompat:1.0.2'
+    implementation 'androidx.palette:palette:1.0.0'
+    implementation 'androidx.leanback:leanback:1.0.0'
+    implementation "androidx.tvprovider:tvprovider:1.0.0"
+    implementation "androidx.recyclerview:recyclerview:1.0.0"
+    implementation "androidx.recyclerview:recyclerview-selection:1.0.0"
+    implementation "androidx.palette:palette:1.0.0"
     implementation 'com.google.guava:guava:26.0-android'
-    implementation 'com.google.protobuf:protobuf-java:3.6.1'
+    implementation 'com.google.protobuf:protobuf-java:3.0.0'
+    implementation 'com.google.dagger:dagger:2.18'
+    implementation 'com.google.dagger:dagger-android:2.18'
+    annotationProcessor 'com.google.dagger:dagger-compiler:2.18'
+    annotationProcessor 'com.google.dagger:dagger-android-processor:2.18'
 }
 protobuf {
     // Configure the protoc executable
     protoc {
-        artifact = 'com.google.protobuf:protoc:3.6.1'
+        artifact = 'com.google.protobuf:protoc:3.0.0'
 
         plugins {
             javalite {
diff --git a/common/src/com/android/tv/common/BaseApplication.java b/common/src/com/android/tv/common/BaseApplication.java
index 871e461..45c3256 100644
--- a/common/src/com/android/tv/common/BaseApplication.java
+++ b/common/src/com/android/tv/common/BaseApplication.java
@@ -18,7 +18,6 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.content.Intent;
 import android.os.Build;
 import android.os.StrictMode;
 import android.support.annotation.VisibleForTesting;
@@ -105,7 +104,4 @@
         }
         return mRecordingStorageStatusManager;
     }
-
-    @Override
-    public abstract Intent getTunerSetupIntent(Context context);
 }
diff --git a/common/src/com/android/tv/common/BaseSingletons.java b/common/src/com/android/tv/common/BaseSingletons.java
index f5ab636..1053061 100644
--- a/common/src/com/android/tv/common/BaseSingletons.java
+++ b/common/src/com/android/tv/common/BaseSingletons.java
@@ -16,8 +16,6 @@
 
 package com.android.tv.common;
 
-import android.content.Context;
-import android.content.Intent;
 import com.android.tv.common.buildtype.HasBuildType;
 import com.android.tv.common.flags.has.HasCloudEpgFlags;
 import com.android.tv.common.flags.has.HasConcurrentDvrPlaybackFlags;
@@ -31,6 +29,4 @@
     Clock getClock();
 
     RecordingStorageStatusManager getRecordingStorageStatusManager();
-
-    Intent getTunerSetupIntent(Context context);
 }
diff --git a/common/src/com/android/tv/common/dagger/ApplicationModule.java b/common/src/com/android/tv/common/dagger/ApplicationModule.java
index 592717b..4655f77 100644
--- a/common/src/com/android/tv/common/dagger/ApplicationModule.java
+++ b/common/src/com/android/tv/common/dagger/ApplicationModule.java
@@ -16,6 +16,7 @@
 package com.android.tv.common.dagger;
 
 import android.app.Application;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Looper;
 import com.android.tv.common.dagger.annotations.ApplicationContext;
@@ -29,21 +30,21 @@
  */
 @Module
 public final class ApplicationModule {
-    private final Application application;
+    private final Application mApplication;
 
     public ApplicationModule(Application application) {
-        this.application = application;
+        mApplication = application;
     }
 
     @Provides
     Application provideApplication() {
-        return application;
+        return mApplication;
     }
 
     @Provides
     @ApplicationContext
     Context provideContext() {
-        return application.getApplicationContext();
+        return mApplication.getApplicationContext();
     }
 
     @Provides
@@ -51,4 +52,9 @@
     static Looper provideMainLooper() {
         return Looper.getMainLooper();
     }
+
+    @Provides
+    ContentResolver provideContentResolver() {
+        return mApplication.getContentResolver();
+    }
 }
diff --git a/common/src/com/android/tv/common/experiments/Experiments.java b/common/src/com/android/tv/common/experiments/Experiments.java
index 6dfa46b..9bfdb54 100644
--- a/common/src/com/android/tv/common/experiments/Experiments.java
+++ b/common/src/com/android/tv/common/experiments/Experiments.java
@@ -27,11 +27,6 @@
  * <p>This file is maintained by hand.
  */
 public final class Experiments {
-    public static final ExperimentFlag<Boolean> CLOUD_EPG =
-            ExperimentFlag.createFlag(
-// AOSP_Comment_Out                     LiveChannels::enableCloudEpg,
-                    true);
-
     public static final ExperimentFlag<Boolean> ENABLE_UNRATED_CONTENT_SETTINGS =
             ExperimentFlag.createFlag(
 // AOSP_Comment_Out                     LiveChannels::enableUnratedContentSettings,
diff --git a/common/src/com/android/tv/common/feature/EngOnlyFeature.java b/common/src/com/android/tv/common/feature/BuildTypeFeature.java
similarity index 66%
rename from common/src/com/android/tv/common/feature/EngOnlyFeature.java
rename to common/src/com/android/tv/common/feature/BuildTypeFeature.java
index 5feb548..9e1704e 100644
--- a/common/src/com/android/tv/common/feature/EngOnlyFeature.java
+++ b/common/src/com/android/tv/common/feature/BuildTypeFeature.java
@@ -20,18 +20,23 @@
 import com.android.tv.common.BuildConfig;
 
 /** A feature that is only available on {@link BuildConfig#ENG} builds. */
-public final class EngOnlyFeature implements Feature {
-    public static final Feature ENG_ONLY_FEATURE = new EngOnlyFeature();
+public final class BuildTypeFeature implements Feature {
+    public static final Feature ENG_ONLY_FEATURE = new BuildTypeFeature(BuildConfig.ENG);
+    public static final Feature ASOP_FEATURE = new BuildTypeFeature(BuildConfig.AOSP);
 
-    private EngOnlyFeature() {}
+    private final boolean mIsBuildType;
+
+    private BuildTypeFeature(boolean isBuildType) {
+        mIsBuildType = isBuildType;
+    }
 
     @Override
     public boolean isEnabled(Context context) {
-        return BuildConfig.ENG;
+        return mIsBuildType;
     }
 
     @Override
     public String toString() {
-        return "EngOnlyFeature(" + BuildConfig.ENG + ")";
+        return getClass().getSimpleName() + "(" + mIsBuildType + ")";
     }
 }
diff --git a/common/src/com/android/tv/common/feature/CommonFeatures.java b/common/src/com/android/tv/common/feature/CommonFeatures.java
index 62986f3..04052a7 100644
--- a/common/src/com/android/tv/common/feature/CommonFeatures.java
+++ b/common/src/com/android/tv/common/feature/CommonFeatures.java
@@ -16,14 +16,13 @@
 
 package com.android.tv.common.feature;
 
-import static com.android.tv.common.feature.EngOnlyFeature.ENG_ONLY_FEATURE;
+import static com.android.tv.common.feature.BuildTypeFeature.ENG_ONLY_FEATURE;
 import static com.android.tv.common.feature.FeatureUtils.and;
 import static com.android.tv.common.feature.FeatureUtils.or;
 import static com.android.tv.common.feature.TestableFeature.createTestableFeature;
 
 import android.content.Context;
 import android.util.Log;
-import com.android.tv.common.experiments.Experiments;
 import com.android.tv.common.flags.has.HasCloudEpgFlags;
 import com.android.tv.common.util.LocationUtils;
 import com.android.tv.common.flags.CloudEpgFlags;
@@ -57,28 +56,25 @@
 
     /** Show postal code fragment before channel scan. */
     public static final Feature ENABLE_CLOUD_EPG_REGION =
-            and(
-                    ExperimentFeature.from(Experiments.CLOUD_EPG),
-                    or(
-                            FlagFeature.from(
-                                    HasCloudEpgFlags::fromContext, CloudEpgFlags::supportedRegion),
-                            new Feature() {
-                                private final String[] supportedRegions = {
-// AOSP_Comment_Out                                     "US", "GB"
-                                };
+            or(
+                    FlagFeature.from(HasCloudEpgFlags::fromContext, CloudEpgFlags::supportedRegion),
+                    new Feature() {
+                        private final String[] supportedRegions = {
+// AOSP_Comment_Out                             "US", "GB"
+                        };
 
-                                @Override
-                                public boolean isEnabled(Context context) {
-                                    String country = LocationUtils.getCurrentCountry(context);
-                                    for (int i = 0; i < supportedRegions.length; i++) {
-                                        if (supportedRegions[i].equalsIgnoreCase(country)) {
-                                            return true;
-                                        }
-                                    }
-                                    if (DEBUG) Log.d(TAG, "EPG flag false after country check");
-                                    return false;
+                        @Override
+                        public boolean isEnabled(Context context) {
+                            String country = LocationUtils.getCurrentCountry(context);
+                            for (int i = 0; i < supportedRegions.length; i++) {
+                                if (supportedRegions[i].equalsIgnoreCase(country)) {
+                                    return true;
                                 }
-                            }));
+                            }
+                            if (DEBUG) Log.d(TAG, "EPG flag false after country check");
+                            return false;
+                        }
+                    });
 
     // TODO(b/74197177): remove when UI and API finalized.
     /** Show channel signal strength. */
diff --git a/common/src/com/android/tv/common/flags/BackendKnobsFlags.java b/common/src/com/android/tv/common/flags/BackendKnobsFlags.java
index 553c227..2f9d451 100644
--- a/common/src/com/android/tv/common/flags/BackendKnobsFlags.java
+++ b/common/src/com/android/tv/common/flags/BackendKnobsFlags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,15 +21,14 @@
     /**
      * Whether or not this feature is compiled into this build.
      *
-     * <p>If the macro which generated this code does not have condtional_compilation_enabled as
-     * true, then this always returns true.
-     *
-     * <p>If the macro which generated this code does have conditional_compilation_enabled as true,
-     * this will return true or false depending on the value of the corresponding
-     * config_feature_flag controlling this feature. See go/phenotype-compile-time-features.
+     * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+     * code generation.
      */
     boolean compiled();
 
+    /** Enable fetching only part of the program data. */
+    boolean enablePartialProgramFetch();
+
     /** EPG fetcher interval in hours */
     long epgFetcherIntervalHour();
 
diff --git a/common/src/com/android/tv/common/flags/CloudEpgFlags.java b/common/src/com/android/tv/common/flags/CloudEpgFlags.java
index 824ec71..ab4c6a1 100755
--- a/common/src/com/android/tv/common/flags/CloudEpgFlags.java
+++ b/common/src/com/android/tv/common/flags/CloudEpgFlags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,12 +21,8 @@
     /**
      * Whether or not this feature is compiled into this build.
      *
-     * <p>If the macro which generated this code does not have condtional_compilation_enabled as
-     * true, then this always returns true.
-     *
-     * <p>If the macro which generated this code does have conditional_compilation_enabled as true,
-     * this will return true or false depending on the value of the corresponding
-     * config_feature_flag controlling this feature. See go/phenotype-compile-time-features.
+     * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+     * code generation.
      */
     boolean compiled();
 
diff --git a/common/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java b/common/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java
index 7ea74cc..76e1788 100755
--- a/common/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java
+++ b/common/src/com/android/tv/common/flags/ConcurrentDvrPlaybackFlags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,12 +21,8 @@
     /**
      * Whether or not this feature is compiled into this build.
      *
-     * <p>If the macro which generated this code does not have condtional_compilation_enabled as
-     * true, then this always returns true.
-     *
-     * <p>If the macro which generated this code does have conditional_compilation_enabled as true,
-     * this will return true or false depending on the value of the corresponding
-     * config_feature_flag controlling this feature. See go/phenotype-compile-time-features.
+     * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+     * code generation.
      */
     boolean compiled();
 
diff --git a/common/src/com/android/tv/common/flags/Exoplayer2Flags.java b/common/src/com/android/tv/common/flags/Exoplayer2Flags.java
index 03175cf..4a3956f 100755
--- a/common/src/com/android/tv/common/flags/Exoplayer2Flags.java
+++ b/common/src/com/android/tv/common/flags/Exoplayer2Flags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,12 +21,8 @@
     /**
      * Whether or not this feature is compiled into this build.
      *
-     * <p>If the macro which generated this code does not have condtional_compilation_enabled as
-     * true, then this always returns true.
-     *
-     * <p>If the macro which generated this code does have conditional_compilation_enabled as true,
-     * this will return true or false depending on the value of the corresponding
-     * config_feature_flag controlling this feature. See go/phenotype-compile-time-features.
+     * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+     * code generation.
      */
     boolean compiled();
 
diff --git a/common/src/com/android/tv/common/flags/TunerFlags.java b/common/src/com/android/tv/common/flags/TunerFlags.java
new file mode 100755
index 0000000..f1d9111
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/TunerFlags.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.tv.common.flags;
+
+/** Flags for tuner */
+public interface TunerFlags {
+
+    /**
+     * Whether or not this feature is compiled into this build.
+     *
+     * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+     * code generation.
+     */
+    boolean compiled();
+
+    /** Tune using current recording if available. */
+    boolean tuneUsingRecording();
+}
diff --git a/common/src/com/android/tv/common/flags/UiFlags.java b/common/src/com/android/tv/common/flags/UiFlags.java
index b69411f..55767aa 100755
--- a/common/src/com/android/tv/common/flags/UiFlags.java
+++ b/common/src/com/android/tv/common/flags/UiFlags.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,15 +21,14 @@
     /**
      * Whether or not this feature is compiled into this build.
      *
-     * <p>If the macro which generated this code does not have condtional_compilation_enabled as
-     * true, then this always returns true.
-     *
-     * <p>If the macro which generated this code does have conditional_compilation_enabled as true,
-     * this will return true or false depending on the value of the corresponding
-     * config_feature_flag controlling this feature. See go/phenotype-compile-time-features.
+     * <p>This returns true by default, unless the is_compiled_selector parameter was set during
+     * code generation.
      */
     boolean compiled();
 
     /** Unhide the launcher all the time */
     boolean uhideLauncher();
+
+    /** Use the Leanback Pin Picker */
+    boolean useLeanbackPinPicker();
 }
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java
index 2805581..53688af 100644
--- a/common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java
+++ b/common/src/com/android/tv/common/flags/impl/DefaultBackendKnobsFlags.java
@@ -25,6 +25,11 @@
     }
 
     @Override
+    public boolean enablePartialProgramFetch() {
+        return false;
+    }
+
+    @Override
     public long epgFetcherIntervalHour() {
         return 25;
     }
@@ -36,7 +41,7 @@
 
     @Override
     public long programGuideMaxHours() {
-        return 168;
+        return 336;
     }
 
     @Override
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultFlagsModule.java b/common/src/com/android/tv/common/flags/impl/DefaultFlagsModule.java
new file mode 100644
index 0000000..3ff6ddf
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultFlagsModule.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tv.common.flags.impl;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Reusable;
+import com.android.tv.common.flags.BackendKnobsFlags;
+import com.android.tv.common.flags.CloudEpgFlags;
+import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
+import com.android.tv.common.flags.Exoplayer2Flags;
+import com.android.tv.common.flags.TunerFlags;
+import com.android.tv.common.flags.UiFlags;
+
+/** Provides default flags. */
+@Module
+public class DefaultFlagsModule {
+
+    @Provides
+    @Reusable
+    BackendKnobsFlags provideBackendKnobsFlags() {
+        return new DefaultBackendKnobsFlags();
+    }
+
+    @Provides
+    @Reusable
+    CloudEpgFlags provideCloudEpgFlags() {
+        return new DefaultCloudEpgFlags();
+    }
+
+    @Provides
+    @Reusable
+    ConcurrentDvrPlaybackFlags provideConcurrentDvrPlaybackFlags() {
+        return new DefaultConcurrentDvrPlaybackFlags();
+    }
+
+    @Provides
+    @Reusable
+    Exoplayer2Flags provideExoplayer2Flags() {
+        return new DefaultExoplayer2Flags();
+    }
+
+    @Provides
+    @Reusable
+    TunerFlags provideTunerFlags() {
+        return new DefaultTunerFlags();
+    }
+
+    @Provides
+    @Reusable
+    UiFlags provideUiFlags() {
+        return new DefaultUiFlags();
+    }
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultTunerFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultTunerFlags.java
new file mode 100644
index 0000000..2494100
--- /dev/null
+++ b/common/src/com/android/tv/common/flags/impl/DefaultTunerFlags.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tv.common.flags.impl;
+
+import com.android.tv.common.flags.TunerFlags;
+
+/** Default Flags for Tuner */
+public class DefaultTunerFlags implements TunerFlags {
+
+    @Override
+    public boolean compiled() {
+        return true;
+    }
+
+    @Override
+    public boolean tuneUsingRecording() {
+        return false;
+    }
+}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java
index 2eb3d2f..bc7a418 100644
--- a/common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java
+++ b/common/src/com/android/tv/common/flags/impl/DefaultUiFlags.java
@@ -29,4 +29,9 @@
     public boolean uhideLauncher() {
         return false;
     }
+
+    @Override
+    public boolean useLeanbackPinPicker() {
+        return false;
+    }
 }
diff --git a/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java b/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java
index 53485ec..dddcd08 100644
--- a/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java
+++ b/partner_support/src/com/google/android/tv/partner/support/EpgInputs.java
@@ -59,6 +59,8 @@
                 result.add(EpgInput.createEpgChannel(contentValues));
             }
             return result;
+        } catch (Exception e) {
+            return Collections.emptySet();
         }
     }
 
diff --git a/res/values/strings-custom.xml b/res/values/strings-custom.xml
new file mode 100644
index 0000000..22f7331
--- /dev/null
+++ b/res/values/strings-custom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+
+    <!-- Name of application [CHAR LIMIT=NONE] -->
+    <string name="app_name" translatable="false">Live TV</string>
+
+</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f7d9312..3682475 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -26,9 +26,6 @@
 
     <string name="option_item_divider_font" translatable="false">@string/condensed_font</string>
 
-    <!-- Name of application [CHAR LIMIT=NONE] -->
-    <string name="app_name" translatable="false">Live TV</string>
-
     <!-- Title of an application permission, listed so the user can choose
         whether they want to allow the application to do this. -->
     <string name="permlab_receiveInputEvent" translatable="false">receive input events from <xliff:g id="app_name">Live TV</xliff:g>  app</string>
diff --git a/src/com/android/tv/MainActivity.java b/src/com/android/tv/MainActivity.java
index 12b1634..3da1523 100644
--- a/src/com/android/tv/MainActivity.java
+++ b/src/com/android/tv/MainActivity.java
@@ -136,6 +136,7 @@
 import com.android.tv.ui.sidepanel.SideFragment;
 import com.android.tv.ui.sidepanel.parentalcontrols.ParentalControlsFragment;
 import com.android.tv.util.AsyncDbTask;
+import com.android.tv.util.AsyncDbTask.DbExecutor;
 import com.android.tv.util.CaptionSettings;
 import com.android.tv.util.OnboardingUtils;
 import com.android.tv.util.RecurringRunner;
@@ -151,6 +152,7 @@
 import com.google.common.base.Optional;
 import dagger.android.AndroidInjection;
 import dagger.android.ContributesAndroidInjector;
+import com.android.tv.common.flags.BackendKnobsFlags;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayDeque;
@@ -260,6 +262,7 @@
     }
 
     private final MySingletonsImpl mMySingletons = new MySingletonsImpl();
+    @Inject @DbExecutor Executor mDbExecutor;
 
     private AccessibilityManager mAccessibilityManager;
     @Inject ChannelDataManager mChannelDataManager;
@@ -274,6 +277,7 @@
     private final DurationTimer mTuneDurationTimer = new DurationTimer();
     private DvrManager mDvrManager;
     private ConflictChecker mDvrConflictChecker;
+    private BackendKnobsFlags mBackendKnobs;
     @Inject SetupUtils mSetupUtils;
     @Inject Optional<BuiltInTunerManager> mOptionalBuiltInTunerManager;
 
@@ -438,12 +442,14 @@
                 public void onInputAdded(String inputId) {
                     if (mOptionalBuiltInTunerManager.isPresent()
                             && CommonPreferences.shouldShowSetupActivity(MainActivity.this)) {
-                        String tunerInputId =
-                                mOptionalBuiltInTunerManager.get().getEmbeddedTunerInputId();
+                        BuiltInTunerManager builtInTunerManager =
+                                mOptionalBuiltInTunerManager.get();
+                        String tunerInputId = builtInTunerManager.getEmbeddedTunerInputId();
                         if (tunerInputId.equals(inputId)) {
                             Intent intent =
-                                    TvSingletons.getSingletons(MainActivity.this)
-                                            .getTunerSetupIntent(MainActivity.this);
+                                    builtInTunerManager
+                                            .getTunerInputController()
+                                            .createSetupIntent(MainActivity.this);
                             startActivity(intent);
                             CommonPreferences.setShouldShowSetupActivity(MainActivity.this, false);
                             mSetupUtils.markAsKnownInput(tunerInputId);
@@ -490,6 +496,7 @@
             finishAndRemoveTask();
             return;
         }
+        mBackendKnobs = tvSingletons.getBackendKnobs();
 
         TvSingletons tvApplication = (TvSingletons) getApplication();
         // In API 23, TvContract.isChannelUriForPassthroughInput is hidden.
@@ -1495,7 +1502,7 @@
             long channelIdFromIntent = ContentUriUtils.safeParseId(mInitChannelUri);
             if (programUriFromIntent != null && channelIdFromIntent != Channel.INVALID_ID) {
                 new AsyncQueryProgramTask(
-                                TvSingletons.getSingletons(this).getDbExecutor(),
+                                mDbExecutor,
                                 programUriFromIntent,
                                 Program.PROJECTION,
                                 null,
@@ -2817,6 +2824,11 @@
             Debug.getTimer(Debug.TAG_START_UP_TIMER).log("MainActivity.MyOnTuneListener.onTune");
             mChannel = channel;
             mWasUnderShrunkenTvView = wasUnderShrunkenTvView;
+
+            if (mBackendKnobs.enablePartialProgramFetch()) {
+                // Fetch complete projection of tuned channel.
+                mProgramDataManager.prefetchChannel(channel.getId());
+            }
         }
 
         @Override
diff --git a/src/com/android/tv/SetupPassthroughActivity.java b/src/com/android/tv/SetupPassthroughActivity.java
index 9ff6471..5185b12 100644
--- a/src/com/android/tv/SetupPassthroughActivity.java
+++ b/src/com/android/tv/SetupPassthroughActivity.java
@@ -28,7 +28,6 @@
 import android.util.Log;
 import com.android.tv.common.SoftPreconditions;
 import com.android.tv.common.actions.InputSetupActionUtils;
-import com.android.tv.common.experiments.Experiments;
 import com.android.tv.data.ChannelDataManager;
 import com.android.tv.data.ChannelDataManager.Listener;
 import com.android.tv.data.epg.EpgFetcher;
@@ -70,9 +69,7 @@
         mEpgInputWhiteList = new EpgInputWhiteList(tvSingletons.getCloudEpgFlags());
         mActivityAfterCompletion = InputSetupActionUtils.getExtraActivityAfter(intent);
         boolean needToFetchEpg =
-                mTvInputInfo != null
-                        && Utils.isInternalTvInput(this, mTvInputInfo.getId())
-                        && Experiments.CLOUD_EPG.get();
+                mTvInputInfo != null && Utils.isInternalTvInput(this, mTvInputInfo.getId());
         if (needToFetchEpg) {
             // In case when the activity is restored, this flag should be restored as well.
             mEpgFetcherDuringScan = true;
diff --git a/src/com/android/tv/TvApplication.java b/src/com/android/tv/TvApplication.java
index 2eb94a8..5f25a24 100644
--- a/src/com/android/tv/TvApplication.java
+++ b/src/com/android/tv/TvApplication.java
@@ -36,10 +36,8 @@
 import android.view.KeyEvent;
 import android.widget.Toast;
 import com.android.tv.common.BaseApplication;
-import com.android.tv.common.concurrent.NamedThreadFactory;
 import com.android.tv.common.feature.CommonFeatures;
 import com.android.tv.common.recording.RecordingStorageStatusManager;
-import com.android.tv.common.singletons.HasTvInputId;
 import com.android.tv.common.ui.setup.animation.SetupAnimationHelper;
 import com.android.tv.common.util.Clock;
 import com.android.tv.common.util.Debug;
@@ -64,14 +62,14 @@
 import com.android.tv.recommendation.RecordedProgramPreviewUpdater;
 import com.android.tv.tunerinputcontroller.BuiltInTunerManager;
 import com.android.tv.tunerinputcontroller.TunerInputController;
+import com.android.tv.util.AsyncDbTask.DbExecutor;
 import com.android.tv.util.SetupUtils;
 import com.android.tv.util.TvInputManagerHelper;
 import com.android.tv.util.Utils;
 import com.google.common.base.Optional;
+import dagger.Lazy;
 import java.util.List;
 import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 import javax.inject.Inject;
 
 /**
@@ -99,10 +97,6 @@
 
     private static final String PREFERENCE_IS_FIRST_LAUNCH = "is_first_launch";
 
-    private static final NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory("tv-app-db");
-    private static final ExecutorService DB_EXECUTOR =
-            Executors.newSingleThreadExecutor(THREAD_FACTORY);
-
     private String mVersionName = "";
 
     private final MainActivityWrapper mMainActivityWrapper = new MainActivityWrapper();
@@ -121,12 +115,13 @@
     // STOP-SHIP: Remove this variable when Tuner Process is split to another application.
     // When this variable is null, we don't know in which process TvApplication runs.
     private Boolean mRunningInMainProcess;
-    private TvInputManagerHelper mTvInputManagerHelper;
+    @Inject Lazy<TvInputManagerHelper> mLazyTvInputManagerHelper;
     private boolean mStarted;
     private EpgFetcher mEpgFetcher;
-    @Inject Optional<BuiltInTunerManager> mOptionalBuiltInTunerManager;
 
+    @Inject Optional<BuiltInTunerManager> mOptionalBuiltInTunerManager;
     @Inject SetupUtils mSetupUtils;
+    @Inject @DbExecutor Executor mDbExecutor;
 
     @Override
     public void onCreate() {
@@ -365,11 +360,7 @@
     /** Returns {@link TvInputManagerHelper}. */
     @Override
     public TvInputManagerHelper getTvInputManagerHelper() {
-        if (mTvInputManagerHelper == null) {
-            mTvInputManagerHelper = new TvInputManagerHelper(this);
-            mTvInputManagerHelper.start();
-        }
-        return mTvInputManagerHelper;
+        return mLazyTvInputManagerHelper.get();
     }
 
     @Override
@@ -495,8 +486,9 @@
         if (!enable) {
             List<TvInputInfo> inputs = inputManager.getTvInputList();
             boolean skipTunerInputCheck = false;
-            Optional<String> optionalEmbeddedTunerInputId = getBuiltInTunerManager()
-                    .transform(HasTvInputId::getEmbeddedTunerInputId);
+            Optional<String> optionalEmbeddedTunerInputId =
+                    mOptionalBuiltInTunerManager.transform(
+                            BuiltInTunerManager::getEmbeddedTunerInputId);
             // Enable the TvActivity only if there is at least one tuner type input.
             if (!skipTunerInputCheck) {
                 for (TvInputInfo input : inputs) {
@@ -530,6 +522,6 @@
 
     @Override
     public Executor getDbExecutor() {
-        return DB_EXECUTOR;
+        return mDbExecutor;
     }
 }
diff --git a/src/com/android/tv/TvSingletons.java b/src/com/android/tv/TvSingletons.java
index ae2ff0d..20edf3d 100644
--- a/src/com/android/tv/TvSingletons.java
+++ b/src/com/android/tv/TvSingletons.java
@@ -116,6 +116,8 @@
 
     ExperimentLoader getExperimentLoader();
 
+    /** @deprecated use injection instead. */
+    @Deprecated
     Executor getDbExecutor();
 
     BackendKnobsFlags getBackendKnobs();
diff --git a/src/com/android/tv/app/LiveTvApplication.java b/src/com/android/tv/app/LiveTvApplication.java
index c65d45e..38e85e4 100644
--- a/src/com/android/tv/app/LiveTvApplication.java
+++ b/src/com/android/tv/app/LiveTvApplication.java
@@ -16,15 +16,11 @@
 
 package com.android.tv.app;
 
-import android.content.Context;
-import android.content.Intent;
-import com.android.tv.TvActivity;
 import com.android.tv.TvApplication;
 import com.android.tv.TvSingletons;
 import com.android.tv.analytics.Analytics;
 import com.android.tv.analytics.StubAnalytics;
 import com.android.tv.analytics.Tracker;
-import com.android.tv.common.actions.InputSetupActionUtils;
 import com.android.tv.common.dagger.ApplicationModule;
 import com.android.tv.common.experiments.ExperimentLoader;
 import com.android.tv.common.flags.impl.DefaultBackendKnobsFlags;
@@ -32,13 +28,11 @@
 import com.android.tv.common.flags.impl.DefaultConcurrentDvrPlaybackFlags;
 import com.android.tv.common.flags.impl.DefaultUiFlags;
 import com.android.tv.common.singletons.HasSingletons;
-import com.android.tv.common.util.CommonUtils;
 import com.android.tv.data.epg.EpgReader;
 import com.android.tv.data.epg.StubEpgReader;
 import com.android.tv.modules.TvSingletonsModule;
 import com.android.tv.perf.PerformanceMonitor;
 import com.android.tv.perf.PerformanceMonitorManagerFactory;
-import com.android.tv.tuner.setup.LiveTvTunerSetupActivity;
 import com.android.tv.tunerinputcontroller.BuiltInTunerManager;
 import com.android.tv.util.account.AccountHelper;
 import com.android.tv.util.account.AccountHelperImpl;
@@ -70,15 +64,14 @@
     private AccountHelper mAccountHelper;
     private Analytics mAnalytics;
     private Tracker mTracker;
-    private String mEmbeddedInputId;
     private ExperimentLoader mExperimentLoader;
     private PerformanceMonitor mPerformanceMonitor;
 
     @Override
     protected AndroidInjector<LiveTvApplication> applicationInjector() {
         return DaggerLiveTvApplicationComponent.builder()
-                .tvSingletonsModule(new TvSingletonsModule(this))
                 .applicationModule(new ApplicationModule(this))
+                .tvSingletonsModule(new TvSingletonsModule(this))
                 .build();
     }
 
@@ -140,19 +133,6 @@
     }
 
     @Override
-    public Intent getTunerSetupIntent(Context context) {
-        // Make an intent to launch the setup activity of TV tuner input.
-        Intent intent =
-                CommonUtils.createSetupIntent(
-                        new Intent(context, LiveTvTunerSetupActivity.class), mEmbeddedInputId);
-        intent.putExtra(InputSetupActionUtils.EXTRA_INPUT_ID, mEmbeddedInputId);
-        Intent tvActivityIntent = new Intent(context, TvActivity.class);
-
-        intent.putExtra(InputSetupActionUtils.EXTRA_ACTIVITY_AFTER_COMPLETION, tvActivityIntent);
-        return intent;
-    }
-
-    @Override
     public DefaultCloudEpgFlags getCloudEpgFlags() {
         return mCloudEpgFlags;
     }
diff --git a/src/com/android/tv/app/LiveTvModule.java b/src/com/android/tv/app/LiveTvModule.java
index b821379..a28749b 100644
--- a/src/com/android/tv/app/LiveTvModule.java
+++ b/src/com/android/tv/app/LiveTvModule.java
@@ -15,15 +15,15 @@
  */
 package com.android.tv.app;
 
+import com.android.tv.common.flags.impl.DefaultFlagsModule;
 import com.android.tv.modules.TvApplicationModule;
-import com.android.tv.tuner.setup.LiveTvTunerSetupActivity;
 import com.android.tv.tunerinputcontroller.BuiltInTunerManager;
 import com.google.common.base.Optional;
 import dagger.Module;
 import dagger.Provides;
 
 /** Dagger module for {@link LiveTvApplication}. */
-@Module(includes = {TvApplicationModule.class, LiveTvTunerSetupActivity.Module.class})
+@Module(includes = {DefaultFlagsModule.class, TvApplicationModule.class})
 class LiveTvModule {
 
     @Provides
diff --git a/src/com/android/tv/data/Program.java b/src/com/android/tv/data/Program.java
index 0119db2..b688927 100644
--- a/src/com/android/tv/data/Program.java
+++ b/src/com/android/tv/data/Program.java
@@ -89,6 +89,16 @@
 
     public static final String[] PROJECTION = createProjection();
 
+    public static final String[] PARTIAL_PROJECTION = {
+        TvContract.Programs._ID,
+        TvContract.Programs.COLUMN_CHANNEL_ID,
+        TvContract.Programs.COLUMN_TITLE,
+        TvContract.Programs.COLUMN_EPISODE_TITLE,
+        TvContract.Programs.COLUMN_CANONICAL_GENRE,
+        TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
+        TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
+    };
+
     private static String[] createProjection() {
         return CollectionUtils.concatAll(
                 PROJECTION_BASE,
@@ -154,6 +164,21 @@
         return builder.build();
     }
 
+    /** Creates {@code Program} object from cursor. */
+    public static Program fromCursorPartialProjection(Cursor cursor) {
+        // Columns read must match the order of match {@link #PARTIAL_PROJECTION}
+        Builder builder = new Builder();
+        int index = 0;
+        builder.setId(cursor.getLong(index++));
+        builder.setChannelId(cursor.getLong(index++));
+        builder.setTitle(cursor.getString(index++));
+        builder.setEpisodeTitle(cursor.getString(index++));
+        builder.setCanonicalGenres(cursor.getString(index++));
+        builder.setStartTimeUtcMillis(cursor.getLong(index++));
+        builder.setEndTimeUtcMillis(cursor.getLong(index++));
+        return builder.build();
+    }
+
     public static Program fromParcel(Parcel in) {
         Program program = new Program();
         program.mId = in.readLong();
diff --git a/src/com/android/tv/data/ProgramDataManager.java b/src/com/android/tv/data/ProgramDataManager.java
index aa4948e..2f20c89 100644
--- a/src/com/android/tv/data/ProgramDataManager.java
+++ b/src/com/android/tv/data/ProgramDataManager.java
@@ -108,14 +108,15 @@
     private final MultiLongSparseArray<OnCurrentProgramUpdatedListener>
             mChannelId2ProgramUpdatedListeners = new MultiLongSparseArray<>();
     private final Handler mHandler;
-    private final Set<Listener> mListeners = new ArraySet<>();
+    private final Set<Callback> mCallbacks = new ArraySet<>();
+    private Map<Long, ArrayList<Program>> mChannelIdProgramCache = new ConcurrentHashMap<>();
+    private final Set<Long> mCompleteInfoChannelIds = new HashSet<>();
     private final ContentObserver mProgramObserver;
 
     private boolean mPrefetchEnabled;
     private long mProgramPrefetchUpdateWaitMs;
     private long mLastPrefetchTaskRunMs;
     private ProgramsPrefetchTask mProgramsPrefetchTask;
-    private Map<Long, ArrayList<Program>> mChannelIdProgramCache = new HashMap<>();
 
     // Any program that ends prior to this time will be removed from the cache
     // when a channel's current program is updated.
@@ -258,24 +259,43 @@
         }
     }
 
-    /** A listener interface to receive notification on program data retrieval from DB. */
-    public interface Listener {
+    public void prefetchChannel(long channelId) {
+        if (mCompleteInfoChannelIds.add(channelId)) {
+            long startTimeMs =
+                    Utils.floorTime(
+                            mClock.currentTimeMillis() - PROGRAM_GUIDE_SNAP_TIME_MS,
+                            PROGRAM_GUIDE_SNAP_TIME_MS);
+            long endTimeMs = startTimeMs + TimeUnit.HOURS.toMillis(getFetchDuration());
+            new SingleChannelPrefetchTask(channelId, startTimeMs, endTimeMs).executeOnDbThread();
+        }
+    }
+
+    /** A Callback interface to receive notification on program data retrieval from DB. */
+    public interface Callback {
         /**
          * Called when a Program data is now available through getProgram() after the DB operation
          * is done which wasn't before. This would be called only if fetched data is around the
          * selected program.
          */
         void onProgramUpdated();
+
+        /**
+         * Called when we update complete program data of specific channel during scrolling. Data is
+         * loaded from DB on request basis.
+         *
+         * @param channelId
+         */
+        void onSingleChannelUpdated(long channelId);
     }
 
-    /** Adds the {@link Listener}. */
-    public void addListener(Listener listener) {
-        mListeners.add(listener);
+    /** Adds the {@link Callback}. */
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
     }
 
-    /** Removes the {@link Listener}. */
-    public void removeListener(Listener listener) {
-        mListeners.remove(listener);
+    /** Removes the {@link Callback}. */
+    public void removeCallback(Callback callback) {
+        mCallbacks.remove(callback);
     }
 
     /** Enables or Disables program prefetch. */
@@ -480,26 +500,7 @@
             long time = mClock.currentTimeMillis();
             mStartTimeMs =
                     Utils.floorTime(time - PROGRAM_GUIDE_SNAP_TIME_MS, PROGRAM_GUIDE_SNAP_TIME_MS);
-            long durationHours;
-            if (mChannelIdProgramCache.isEmpty()) {
-                durationHours = Math.max(1L, mBackendKnobsFlags.programGuideInitialFetchHours());
-            } else {
-                int channelCount = mChannelDataManager.getChannelCount();
-                long knobsMaxHours = mBackendKnobsFlags.programGuideMaxHours();
-                long targetChannelCount = mBackendKnobsFlags.epgTargetChannelCount();
-                if (channelCount <= targetChannelCount) {
-                    durationHours = Math.max(48L, knobsMaxHours);
-                } else {
-                    // 2 days <= duration <= 14 days (336 hours)
-                    durationHours = knobsMaxHours * targetChannelCount / channelCount;
-                    if (durationHours < 48L) {
-                        durationHours = 48L;
-                    } else if (durationHours > 336L) {
-                        durationHours = 336L;
-                    }
-                }
-            }
-            mEndTimeMs = mStartTimeMs + TimeUnit.HOURS.toMillis(durationHours);
+            mEndTimeMs = mStartTimeMs + TimeUnit.HOURS.toMillis(getFetchDuration());
             mSuccess = false;
         }
 
@@ -538,10 +539,15 @@
                 }
                 programMap.clear();
 
-                String[] projection = Program.PROJECTION;
+                String[] projection =
+                        mBackendKnobsFlags.enablePartialProgramFetch()
+                                ? Program.PARTIAL_PROJECTION
+                                : Program.PROJECTION;
                 if (TvProviderUtils.checkSeriesIdColumn(mContext, Programs.CONTENT_URI)) {
                     if (Utils.isProgramsUri(uri)) {
-                        projection = TvProviderUtils.addExtraColumnsToProjection(projection);
+                        projection =
+                                TvProviderUtils.addExtraColumnsToProjection(
+                                        projection, TvProviderUtils.EXTRA_PROGRAM_COLUMN_SERIES_ID);
                     }
                 }
                 try (Cursor c = mContentResolver.query(uri, projection, null, null, SORT_BY_TIME)) {
@@ -556,7 +562,10 @@
                             }
                             return null;
                         }
-                        Program program = Program.fromCursor(c);
+                        Program program =
+                                mBackendKnobsFlags.enablePartialProgramFetch()
+                                        ? Program.fromCursorPartialProjection(c)
+                                        : Program.fromCursor(c);
                         if (Program.isDuplicate(program, lastReadProgram)) {
                             duplicateCount++;
                             continue;
@@ -566,6 +575,15 @@
                         ArrayList<Program> programs = programMap.get(program.getChannelId());
                         if (programs == null) {
                             programs = new ArrayList<>();
+                            if (mBackendKnobsFlags.enablePartialProgramFetch()) {
+                                // To skip already loaded complete data.
+                                Program currentProgramInfo =
+                                        mChannelIdCurrentProgramMap.get(program.getChannelId());
+                                if (currentProgramInfo != null
+                                        && Program.isDuplicate(program, currentProgramInfo)) {
+                                    program = currentProgramInfo;
+                                }
+                            }
                             programMap.put(program.getChannelId(), programs);
                         }
                         programs.add(program);
@@ -615,6 +633,10 @@
                     nextMessageDelayedTime = 0;
                 }
                 mChannelIdProgramCache = programs;
+                if (mBackendKnobsFlags.enablePartialProgramFetch()) {
+                    // Since cache has partial data we need to reset the map of complete data.
+                    mCompleteInfoChannelIds.clear();
+                }
                 notifyProgramUpdated();
                 if (mFromEmptyCacheTimeEvent != null) {
                     mPerformanceMonitor.stopTimer(
@@ -632,9 +654,70 @@
         }
     }
 
+    private long getFetchDuration() {
+        if (mChannelIdProgramCache.isEmpty()) {
+            return Math.max(1L, mBackendKnobsFlags.programGuideInitialFetchHours());
+        } else {
+            long durationHours;
+            int channelCount = mChannelDataManager.getChannelCount();
+            long knobsMaxHours = mBackendKnobsFlags.programGuideMaxHours();
+            long targetChannelCount = mBackendKnobsFlags.epgTargetChannelCount();
+            if (channelCount <= targetChannelCount) {
+                durationHours = Math.max(48L, knobsMaxHours);
+            } else {
+                // 2 days <= duration <= 14 days (336 hours)
+                durationHours = knobsMaxHours * targetChannelCount / channelCount;
+                if (durationHours < 48L) {
+                    durationHours = 48L;
+                } else if (durationHours > 336L) {
+                    durationHours = 336L;
+                }
+            }
+            return durationHours;
+        }
+    }
+
+    private class SingleChannelPrefetchTask extends AsyncDbTask.AsyncQueryTask<ArrayList<Program>> {
+        long mChannelId;
+
+        public SingleChannelPrefetchTask(long channelId, long startTimeMs, long endTimeMs) {
+            super(
+                    mDbExecutor,
+                    mContext,
+                    TvContract.buildProgramsUriForChannel(channelId, startTimeMs, endTimeMs),
+                    Program.PROJECTION,
+                    null,
+                    null,
+                    SORT_BY_TIME);
+            mChannelId = channelId;
+        }
+
+        @Override
+        protected ArrayList<Program> onQuery(Cursor c) {
+            ArrayList<Program> programMap = new ArrayList<>();
+            while (c.moveToNext()) {
+                Program program = Program.fromCursor(c);
+                programMap.add(program);
+            }
+            return programMap;
+        }
+
+        @Override
+        protected void onPostExecute(ArrayList<Program> programs) {
+            mChannelIdProgramCache.put(mChannelId, programs);
+            notifySingleChannelUpdated(mChannelId);
+        }
+    }
+
     private void notifyProgramUpdated() {
-        for (Listener listener : mListeners) {
-            listener.onProgramUpdated();
+        for (Callback callback : mCallbacks) {
+            callback.onProgramUpdated();
+        }
+    }
+
+    private void notifySingleChannelUpdated(long channelId) {
+        for (Callback callback : mCallbacks) {
+            callback.onSingleChannelUpdated(channelId);
         }
     }
 
@@ -694,6 +777,9 @@
                 for (Long channelId : removedChannelIds) {
                     if (mPrefetchEnabled) {
                         mChannelIdProgramCache.remove(channelId);
+                        if (mBackendKnobsFlags.enablePartialProgramFetch()) {
+                            mCompleteInfoChannelIds.remove(channelId);
+                        }
                     }
                     mChannelIdCurrentProgramMap.remove(channelId);
                     notifyCurrentProgramUpdate(channelId, null);
diff --git a/src/com/android/tv/data/epg/EpgFetchHelper.java b/src/com/android/tv/data/epg/EpgFetchHelper.java
index e56185d..3843ca9 100644
--- a/src/com/android/tv/data/epg/EpgFetchHelper.java
+++ b/src/com/android/tv/data/epg/EpgFetchHelper.java
@@ -201,7 +201,9 @@
             Context context, long channelId, long startTimeMs, long endTimeMs) {
         String[] projection = Program.PROJECTION;
         if (TvProviderUtils.checkSeriesIdColumn(context, Programs.CONTENT_URI)) {
-            projection = TvProviderUtils.addExtraColumnsToProjection(projection);
+            projection =
+                    TvProviderUtils.addExtraColumnsToProjection(
+                            projection, TvProviderUtils.EXTRA_PROGRAM_COLUMN_SERIES_ID);
         }
         try (Cursor c =
                 context.getContentResolver()
diff --git a/src/com/android/tv/data/epg/EpgFetcherImpl.java b/src/com/android/tv/data/epg/EpgFetcherImpl.java
index 0251e66..b191421 100644
--- a/src/com/android/tv/data/epg/EpgFetcherImpl.java
+++ b/src/com/android/tv/data/epg/EpgFetcherImpl.java
@@ -41,6 +41,7 @@
 import com.android.tv.TvSingletons;
 import com.android.tv.common.BuildConfig;
 import com.android.tv.common.SoftPreconditions;
+import com.android.tv.common.buildtype.HasBuildType;
 import com.android.tv.common.util.Clock;
 import com.android.tv.common.util.CommonUtils;
 import com.android.tv.common.util.LocationUtils;
@@ -60,6 +61,7 @@
 import com.android.tv.util.Utils;
 import com.google.android.tv.partner.support.EpgInput;
 import com.google.android.tv.partner.support.EpgInputs;
+import com.google.common.collect.ImmutableSet;
 import com.android.tv.common.flags.BackendKnobsFlags;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -114,7 +116,9 @@
     private final ChannelDataManager mChannelDataManager;
     private final EpgReader mEpgReader;
     private final PerformanceMonitor mPerformanceMonitor;
+    private final EpgInputWhiteList mEpgInputWhiteList;
     private final BackendKnobsFlags mBackendKnobsFlags;
+    private final HasBuildType.BuildType mBuildType;
     private FetchAsyncTask mFetchTask;
     private FetchDuringScanHandler mFetchDuringScanHandler;
     private long mEpgTimeStamp;
@@ -133,30 +137,39 @@
         PerformanceMonitor performanceMonitor = tvSingletons.getPerformanceMonitor();
         EpgReader epgReader = tvSingletons.providesEpgReader().get();
         Clock clock = tvSingletons.getClock();
+        EpgInputWhiteList epgInputWhiteList =
+                new EpgInputWhiteList(tvSingletons.getCloudEpgFlags());
         BackendKnobsFlags backendKnobsFlags = tvSingletons.getBackendKnobs();
+        HasBuildType.BuildType buildType = tvSingletons.getBuildType();
         return new EpgFetcherImpl(
                 context,
+                epgInputWhiteList,
                 channelDataManager,
                 epgReader,
                 performanceMonitor,
                 clock,
-                backendKnobsFlags);
+                backendKnobsFlags,
+                buildType);
     }
 
     @VisibleForTesting
     EpgFetcherImpl(
             Context context,
+            EpgInputWhiteList epgInputWhiteList,
             ChannelDataManager channelDataManager,
             EpgReader epgReader,
             PerformanceMonitor performanceMonitor,
             Clock clock,
-            BackendKnobsFlags backendKnobsFlags) {
+            BackendKnobsFlags backendKnobsFlags,
+            HasBuildType.BuildType buildType) {
         mContext = context;
         mChannelDataManager = channelDataManager;
         mEpgReader = epgReader;
         mPerformanceMonitor = performanceMonitor;
         mClock = clock;
+        mEpgInputWhiteList = epgInputWhiteList;
         mBackendKnobsFlags = backendKnobsFlags;
+        mBuildType = buildType;
     }
 
     private long getFastFetchDurationSec() {
@@ -352,6 +365,19 @@
             if (DEBUG) Log.d(TAG, "Cannot start routine service: scanning channels.");
             return false;
         }
+        if (!TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.isEnabled(mContext)
+                && mBuildType != HasBuildType.BuildType.AOSP) {
+            if (getTunerChannelCount() == 0) {
+                if (DEBUG) Log.d(TAG, "Cannot start routine service: no internal tuner channels.");
+                return false;
+            }
+            if (!TextUtils.isEmpty(EpgFetchHelper.getLastLineupId(mContext))) {
+                return true;
+            }
+            if (!TextUtils.isEmpty(PostalCodeUtils.getLastPostalCode(mContext))) {
+                return true;
+            }
+        }
         return true;
     }
 
@@ -511,6 +537,17 @@
         return numbers.size();
     }
 
+    private boolean isInputInWhiteList(EpgInput epgInput) {
+        if (mBuildType == HasBuildType.BuildType.AOSP) {
+            return false;
+        }
+        return (BuildConfig.ENG
+                        && epgInput.getInputId()
+                                .equals(
+                                        "com.example.partnersupportsampletvinput/.SampleTvInputService"))
+                || mEpgInputWhiteList.isInputWhiteListed(epgInput.getInputId());
+    }
+
     @VisibleForTesting
     class FetchAsyncTask extends AsyncTask<Void, Void, Integer> {
         private final JobService mService;
@@ -538,12 +575,45 @@
                 Integer builtInResult = fetchEpgForBuiltInTuner();
                 boolean anyCloudEpgFailure = false;
                 boolean anyCloudEpgSuccess = false;
+                if (TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.isEnabled(mContext)
+                        && mBuildType != HasBuildType.BuildType.AOSP) {
+                    for (EpgInput epgInput : getEpgInputs()) {
+                        if (DEBUG) Log.d(TAG, "Start EPG fetch for " + epgInput);
+                        if (isCancelled()) {
+                            break;
+                        }
+                        if (isInputInWhiteList(epgInput)) {
+                            // TODO(b/66191312) check timestamp and result code and decide if update
+                            // is needed.
+                            Set<Channel> channels = getExistingChannelsFor(epgInput.getInputId());
+                            Integer result = fetchEpgFor(epgInput.getLineupId(), channels);
+                            anyCloudEpgFailure = anyCloudEpgFailure || result != null;
+                            anyCloudEpgSuccess = anyCloudEpgSuccess || result == null;
+                            updateCloudEpgInput(epgInput, result);
+                        } else {
+                            Log.w(
+                                    TAG,
+                                    "Fetching the EPG for "
+                                            + epgInput.getInputId()
+                                            + " is not supported.");
+                        }
+                    }
+                }
+                if (builtInResult == null || builtInResult == REASON_NO_BUILT_IN_CHANNELS) {
+                    return anyCloudEpgFailure
+                            ? ((Integer) REASON_CLOUD_EPG_FAILURE)
+                            : anyCloudEpgSuccess ? null : builtInResult;
+                }
                 return builtInResult;
             } finally {
                 TrafficStats.setThreadStatsTag(oldTag);
             }
         }
 
+        private void updateCloudEpgInput(EpgInput unusedEpgInput, Integer unusedResult) {
+            // TODO(b/66191312) write the result and timestamp to the input table
+        }
+
         private Set<Channel> getExistingChannelsFor(String inputId) {
             Set<Channel> result = new HashSet<>();
             try (Cursor cursor =
@@ -563,6 +633,15 @@
             }
         }
 
+        private Set<EpgInput> getEpgInputs() {
+            if (mBuildType == HasBuildType.BuildType.AOSP) {
+                return ImmutableSet.of();
+            }
+            Set<EpgInput> epgInputs = EpgInputs.queryEpgInputs(mContext.getContentResolver());
+            if (DEBUG) Log.d(TAG, "getEpgInputs " + epgInputs);
+            return epgInputs;
+        }
+
         private Integer fetchEpgForBuiltInTuner() {
             try {
                 Integer failureReason = prepareFetchEpg(false);
diff --git a/src/com/android/tv/dvr/data/RecordedProgram.java b/src/com/android/tv/dvr/data/RecordedProgram.java
index d40904e..899e65a 100644
--- a/src/com/android/tv/dvr/data/RecordedProgram.java
+++ b/src/com/android/tv/dvr/data/RecordedProgram.java
@@ -122,11 +122,10 @@
         }
         index++;
         if (TvProviderUtils.getRecordedProgramHasSeriesIdColumn()) {
-            builder.setSeriesId(StringUtils.nullToEmpty(cursor.getString(index)));
+            builder.setSeriesId(StringUtils.nullToEmpty(cursor.getString(index++)));
         }
-        index++;
         if (TvProviderUtils.getRecordedProgramHasStateColumn()) {
-            builder.setState(cursor.getString(index));
+            builder.setState(cursor.getString(index++));
         }
         return builder.build();
     }
diff --git a/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java b/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java
index 8654e6e..696038c 100644
--- a/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java
+++ b/src/com/android/tv/dvr/recorder/SeriesRecordingScheduler.java
@@ -29,7 +29,6 @@
 import android.util.LongSparseArray;
 import com.android.tv.TvSingletons;
 import com.android.tv.common.SoftPreconditions;
-import com.android.tv.common.experiments.Experiments;
 import com.android.tv.common.util.CollectionUtils;
 import com.android.tv.common.util.SharedPreferencesUtils;
 import com.android.tv.data.Program;
@@ -260,14 +259,11 @@
     }
 
     private void executeFetchSeriesInfoTask(SeriesRecording seriesRecording) {
-        if (Experiments.CLOUD_EPG.get()) {
-            FetchSeriesInfoTask task =
-                    new FetchSeriesInfoTask(
-                            seriesRecording,
-                            TvSingletons.getSingletons(mContext).providesEpgReader());
-            task.execute();
-            mFetchSeriesInfoTasks.put(seriesRecording.getId(), task);
-        }
+        FetchSeriesInfoTask task =
+                new FetchSeriesInfoTask(
+                        seriesRecording, TvSingletons.getSingletons(mContext).providesEpgReader());
+        task.execute();
+        mFetchSeriesInfoTasks.put(seriesRecording.getId(), task);
     }
 
     /** Pauses the updates of the series recordings. */
diff --git a/src/com/android/tv/features/TvFeatures.java b/src/com/android/tv/features/TvFeatures.java
index 4f6e8d9..208d53f 100644
--- a/src/com/android/tv/features/TvFeatures.java
+++ b/src/com/android/tv/features/TvFeatures.java
@@ -16,10 +16,12 @@
 
 package com.android.tv.features;
 
-import static com.android.tv.common.feature.EngOnlyFeature.ENG_ONLY_FEATURE;
+import static com.android.tv.common.feature.BuildTypeFeature.ASOP_FEATURE;
+import static com.android.tv.common.feature.BuildTypeFeature.ENG_ONLY_FEATURE;
 import static com.android.tv.common.feature.FeatureUtils.OFF;
 import static com.android.tv.common.feature.FeatureUtils.ON;
 import static com.android.tv.common.feature.FeatureUtils.and;
+import static com.android.tv.common.feature.FeatureUtils.not;
 import static com.android.tv.common.feature.FeatureUtils.or;
 
 import android.content.Context;
@@ -39,7 +41,6 @@
 import com.android.tv.common.singletons.HasSingletons;
 import com.android.tv.common.util.PermissionUtils;
 
-
 /**
  * List of {@link Feature} for the Live TV App.
  *
@@ -60,6 +61,24 @@
      */
     public static final Feature ANALYTICS_V2 = and(ON, ANALYTICS_OPT_IN);
 
+    private static final Feature TV_PROVIDER_ALLOWS_INSERT_TO_PROGRAM_TABLE =
+            or(Sdk.AT_LEAST_O, PartnerFeatures.TVPROVIDER_ALLOWS_SYSTEM_INSERTS_TO_PROGRAM_TABLE);
+
+    /**
+     * Enable cloud EPG for third parties.
+     *
+     * @see <a href="http://go/cloud-epg-3p-proposal">go/cloud-epg-3p-proposal</a>
+     */
+    // TODO verify customization for N
+    public static final TestableFeature CLOUD_EPG_FOR_3RD_PARTY =
+            TestableFeature.createTestableFeature(
+                    and(
+                            not(ASOP_FEATURE),
+                            // TODO(b/66696290): use newer version of robolectric.
+                            or(
+                                    TV_PROVIDER_ALLOWS_INSERT_TO_PROGRAM_TABLE,
+                                    FeatureUtils.ROBOLECTRIC)));
+
     // TODO(b/76149661): Fix EPG search or remove it
     public static final Feature EPG_SEARCH = OFF;
 
@@ -70,7 +89,7 @@
                             context -> HasSingletons.get(HasUiFlags.class, context),
                             input -> input.getUiFlags().uhideLauncher()),
                     // If LC app runs as non-system app, we unhide the app.
-                    FeatureUtils.not(PermissionUtils::hasAccessAllEpg));
+                    not(PermissionUtils::hasAccessAllEpg));
 
     public static final Feature PICTURE_IN_PICTURE =
             new Feature() {
diff --git a/src/com/android/tv/guide/ProgramGuide.java b/src/com/android/tv/guide/ProgramGuide.java
index 69e2d68..bc1b11b 100644
--- a/src/com/android/tv/guide/ProgramGuide.java
+++ b/src/com/android/tv/guide/ProgramGuide.java
@@ -65,6 +65,7 @@
 import com.android.tv.ui.hideable.AutoHideScheduler;
 import com.android.tv.util.TvInputManagerHelper;
 import com.android.tv.util.Utils;
+import com.android.tv.common.flags.BackendKnobsFlags;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -182,14 +183,17 @@
             Runnable preShowRunnable,
             Runnable postHideRunnable) {
         mActivity = activity;
-        mPerformanceMonitor = TvSingletons.getSingletons(mActivity).getPerformanceMonitor();
+        TvSingletons singletons = TvSingletons.getSingletons(mActivity);
+        mPerformanceMonitor = singletons.getPerformanceMonitor();
+        BackendKnobsFlags backendKnobsFlags = singletons.getBackendKnobs();
         mProgramManager =
                 new ProgramManager(
                         tvInputManagerHelper,
                         channelDataManager,
                         programDataManager,
                         dvrDataManager,
-                        dvrScheduleManager);
+                        dvrScheduleManager,
+                        backendKnobsFlags);
         mChannelTuner = channelTuner;
         mTracker = tracker;
         mPreShowRunnable = preShowRunnable;
diff --git a/src/com/android/tv/guide/ProgramManager.java b/src/com/android/tv/guide/ProgramManager.java
index 9a70ddd..3a5a4a0 100644
--- a/src/com/android/tv/guide/ProgramManager.java
+++ b/src/com/android/tv/guide/ProgramManager.java
@@ -32,6 +32,7 @@
 import com.android.tv.dvr.data.ScheduledRecording;
 import com.android.tv.util.TvInputManagerHelper;
 import com.android.tv.util.Utils;
+import com.android.tv.common.flags.BackendKnobsFlags;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -59,6 +60,7 @@
     private final ProgramDataManager mProgramDataManager;
     private final DvrDataManager mDvrDataManager; // Only set if DVR is enabled
     private final DvrScheduleManager mDvrScheduleManager;
+    private final BackendKnobsFlags mBackendKnobsFlags;
 
     private long mStartUtcMillis;
     private long mEndUtcMillis;
@@ -114,12 +116,26 @@
                 }
             };
 
-    private final ProgramDataManager.Listener mProgramDataManagerListener =
-            new ProgramDataManager.Listener() {
+    private final ProgramDataManager.Callback mProgramDataManagerCallback =
+            new ProgramDataManager.Callback() {
                 @Override
                 public void onProgramUpdated() {
                     updateTableEntries(true);
                 }
+
+                @Override
+                public void onSingleChannelUpdated(long channelId) {
+                    boolean parentalControlsEnabled =
+                            mTvInputManagerHelper
+                                    .getParentalControlSettings()
+                                    .isParentalControlsEnabled();
+                    // Inline the updating of the mChannelIdEntriesMap here so we can only call
+                    // getParentalControlSettings once.
+                    List<TableEntry> entries =
+                            createProgramEntries(channelId, parentalControlsEnabled);
+                    mChannelIdEntriesMap.put(channelId, entries);
+                    notifyTableEntriesUpdated();
+                }
             };
 
     private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener =
@@ -199,19 +215,21 @@
             ChannelDataManager channelDataManager,
             ProgramDataManager programDataManager,
             @Nullable DvrDataManager dvrDataManager,
-            @Nullable DvrScheduleManager dvrScheduleManager) {
+            @Nullable DvrScheduleManager dvrScheduleManager,
+            BackendKnobsFlags backendKnobsFlags) {
         mTvInputManagerHelper = tvInputManagerHelper;
         mChannelDataManager = channelDataManager;
         mProgramDataManager = programDataManager;
         mDvrDataManager = dvrDataManager;
         mDvrScheduleManager = dvrScheduleManager;
+        mBackendKnobsFlags = backendKnobsFlags;
     }
 
     void programGuideVisibilityChanged(boolean visible) {
         mProgramDataManager.setPauseProgramUpdate(visible);
         if (visible) {
             mChannelDataManager.addListener(mChannelDataManagerListener);
-            mProgramDataManager.addListener(mProgramDataManagerListener);
+            mProgramDataManager.addCallback(mProgramDataManagerCallback);
             if (mDvrDataManager != null) {
                 if (!mDvrDataManager.isDvrScheduleLoadFinished()) {
                     mDvrDataManager.addDvrScheduleLoadFinishedListener(mDvrLoadedListener);
@@ -224,7 +242,7 @@
             }
         } else {
             mChannelDataManager.removeListener(mChannelDataManagerListener);
-            mProgramDataManager.removeListener(mProgramDataManagerListener);
+            mProgramDataManager.removeCallback(mProgramDataManagerCallback);
             if (mDvrDataManager != null) {
                 mDvrDataManager.removeDvrScheduleLoadFinishedListener(mDvrLoadedListener);
                 mDvrDataManager.removeScheduledRecordingListener(mScheduledRecordingListener);
@@ -413,6 +431,9 @@
      * (e.g., whose channelId is INVALID_ID), when it corresponds to a gap between programs.
      */
     TableEntry getTableEntry(long channelId, int index) {
+        if (mBackendKnobsFlags.enablePartialProgramFetch()) {
+            mProgramDataManager.prefetchChannel(channelId);
+        }
         return mChannelIdEntriesMap.get(channelId).get(index);
     }
 
diff --git a/src/com/android/tv/modules/TvApplicationModule.java b/src/com/android/tv/modules/TvApplicationModule.java
index a252723..45383ae 100644
--- a/src/com/android/tv/modules/TvApplicationModule.java
+++ b/src/com/android/tv/modules/TvApplicationModule.java
@@ -15,11 +15,20 @@
  */
 package com.android.tv.modules;
 
+import android.content.Context;
 import com.android.tv.MainActivity;
 import com.android.tv.TvApplication;
+import com.android.tv.common.concurrent.NamedThreadFactory;
 import com.android.tv.common.dagger.ApplicationModule;
+import com.android.tv.common.dagger.annotations.ApplicationContext;
 import com.android.tv.onboarding.OnboardingActivity;
+import com.android.tv.util.AsyncDbTask;
+import com.android.tv.util.TvInputManagerHelper;
 import dagger.Module;
+import dagger.Provides;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import javax.inject.Singleton;
 
 /** Dagger module for {@link TvApplication}. */
 @Module(
@@ -29,4 +38,21 @@
             MainActivity.Module.class,
             OnboardingActivity.Module.class
         })
-public class TvApplicationModule {}
+public class TvApplicationModule {
+    private static final NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory("tv-app-db");
+
+    @Provides
+    @AsyncDbTask.DbExecutor
+    @Singleton
+    Executor providesDbExecutor() {
+        return Executors.newSingleThreadExecutor(THREAD_FACTORY);
+    }
+
+    @Provides
+    @Singleton
+    TvInputManagerHelper providesTvInputManagerHelper(@ApplicationContext Context context) {
+        TvInputManagerHelper tvInputManagerHelper = new TvInputManagerHelper(context);
+        tvInputManagerHelper.start();
+        return tvInputManagerHelper;
+    }
+}
diff --git a/src/com/android/tv/modules/TvSingletonsModule.java b/src/com/android/tv/modules/TvSingletonsModule.java
index 3959347..f998c08 100644
--- a/src/com/android/tv/modules/TvSingletonsModule.java
+++ b/src/com/android/tv/modules/TvSingletonsModule.java
@@ -18,7 +18,6 @@
 import com.android.tv.TvSingletons;
 import com.android.tv.data.ChannelDataManager;
 import com.android.tv.data.ProgramDataManager;
-import com.android.tv.util.TvInputManagerHelper;
 import dagger.Module;
 import dagger.Provides;
 
@@ -28,6 +27,7 @@
  * <p>Use this module to inject items directly instead of using {@code TvSingletons}.
  */
 @Module
+@SuppressWarnings("deprecation")
 public class TvSingletonsModule {
     private final TvSingletons mTvSingletons;
 
@@ -44,9 +44,4 @@
     ProgramDataManager providesProgramDataManager() {
         return mTvSingletons.getProgramDataManager();
     }
-
-    @Provides
-    TvInputManagerHelper providesTvInputManagerHelper() {
-        return mTvSingletons.getTvInputManagerHelper();
-    }
 }
diff --git a/src/com/android/tv/receiver/BootCompletedReceiver.java b/src/com/android/tv/receiver/BootCompletedReceiver.java
index 8ed926e..0eb03be 100644
--- a/src/com/android/tv/receiver/BootCompletedReceiver.java
+++ b/src/com/android/tv/receiver/BootCompletedReceiver.java
@@ -70,7 +70,7 @@
         // Grant permission to already set up packages after the system has finished booting.
         SetupUtils.grantEpgPermissionToSetUpPackages(context);
 
-        if (TvFeatures.UNHIDE.isEnabled(context)) {
+        if (TvFeatures.UNHIDE.isEnabled(context.getApplicationContext())) {
             if (OnboardingUtils.isFirstBoot(context)) {
                 // Enable the application if this is the first "unhide" feature is enabled just in
                 // case when the app has been disabled before.
diff --git a/src/com/android/tv/recommendation/ChannelPreviewUpdater.java b/src/com/android/tv/recommendation/ChannelPreviewUpdater.java
index 7dea255..2590a33 100644
--- a/src/com/android/tv/recommendation/ChannelPreviewUpdater.java
+++ b/src/com/android/tv/recommendation/ChannelPreviewUpdater.java
@@ -174,7 +174,7 @@
                     for (Channel channel : channels) {
                         if (channel.isPhysicalTunerChannel()) {
                             final Program program =
-                                Utils.getCurrentProgram(mContext, channel.getId());
+                                    Utils.getCurrentProgram(mContext, channel.getId());
                             if (program != null
                                     && isChannelRecommendationApplicable(channel, program)) {
                                 programs.add(program);
@@ -246,6 +246,17 @@
                                 }
                             }
                         });
+            } else if (mJobService != null && mJobParams != null) {
+                if (DEBUG) {
+                    Log.d(
+                            TAG,
+                            "Preview channel not created because there is only "
+                                    + programs.size()
+                                    + " programs");
+                }
+                mJobService.jobFinished(mJobParams, false);
+                mJobService = null;
+                mJobParams = null;
             }
         } else {
             updatePreviewProgramsForPreviewChannel(
diff --git a/src/com/android/tv/search/DataManagerSearch.java b/src/com/android/tv/search/DataManagerSearch.java
index 304ef98..a649c0a 100644
--- a/src/com/android/tv/search/DataManagerSearch.java
+++ b/src/com/android/tv/search/DataManagerSearch.java
@@ -249,7 +249,7 @@
             result.setIntentData(buildIntentData(channelId));
             result.setContentType(Programs.CONTENT_ITEM_TYPE);
             result.setIsLive(true);
-            result.setProgressPercentage(LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE);
+            result.setProgressPercentage(SearchInterface.PROGRESS_PERCENTAGE_HIDE);
         } else {
             result.setTitle(program.getTitle());
             result.setDescription(
@@ -293,7 +293,7 @@
     private int getProgressPercentage(long startUtcMillis, long endUtcMillis) {
         long current = System.currentTimeMillis();
         if (startUtcMillis > current || endUtcMillis <= current) {
-            return LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
+            return SearchInterface.PROGRESS_PERCENTAGE_HIDE;
         }
         return (int) (100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis));
     }
diff --git a/src/com/android/tv/search/LocalSearchProvider.java b/src/com/android/tv/search/LocalSearchProvider.java
index 7f15f66..5652c98 100644
--- a/src/com/android/tv/search/LocalSearchProvider.java
+++ b/src/com/android/tv/search/LocalSearchProvider.java
@@ -49,8 +49,6 @@
     /** The authority for LocalSearchProvider. */
     public static final String AUTHORITY = CommonConstants.BASE_PACKAGE + ".search";
 
-    public static final int PROGRESS_PERCENTAGE_HIDE = -1;
-
     // TODO: Remove this once added to the SearchManager.
     private static final String SUGGEST_COLUMN_PROGRESS_BAR_PERCENTAGE = "progress_bar_percentage";
 
diff --git a/src/com/android/tv/search/SearchInterface.java b/src/com/android/tv/search/SearchInterface.java
index 4866ee8..d16270e 100644
--- a/src/com/android/tv/search/SearchInterface.java
+++ b/src/com/android/tv/search/SearchInterface.java
@@ -26,6 +26,7 @@
     int ACTION_TYPE_SWITCH_CHANNEL = 2;
     int ACTION_TYPE_SWITCH_INPUT = 3;
     int ACTION_TYPE_END = 3;
+    int PROGRESS_PERCENTAGE_HIDE = -1;
 
     /**
      * Search channels, inputs, or programs. This assumes that parental control settings will not be
diff --git a/src/com/android/tv/search/TvProviderSearch.java b/src/com/android/tv/search/TvProviderSearch.java
index e267fad..8a1f51f 100644
--- a/src/com/android/tv/search/TvProviderSearch.java
+++ b/src/com/android/tv/search/TvProviderSearch.java
@@ -271,7 +271,7 @@
                     result.setIntentData(buildIntentData(id));
                     result.setContentType(Programs.CONTENT_ITEM_TYPE);
                     result.setIsLive(true);
-                    result.setProgressPercentage(LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE);
+                    result.setProgressPercentage(SearchInterface.PROGRESS_PERCENTAGE_HIDE);
 
                     searchResults.add(result.build());
 
@@ -344,7 +344,7 @@
     private int getProgressPercentage(long startUtcMillis, long endUtcMillis) {
         long current = System.currentTimeMillis();
         if (startUtcMillis > current || endUtcMillis <= current) {
-            return LocalSearchProvider.PROGRESS_PERCENTAGE_HIDE;
+            return SearchInterface.PROGRESS_PERCENTAGE_HIDE;
         }
         return (int) (100 * (current - startUtcMillis) / (endUtcMillis - startUtcMillis));
     }
diff --git a/src/com/android/tv/tunerinputcontroller/TunerInputController.java b/src/com/android/tv/tunerinputcontroller/TunerInputController.java
index 6c3df1c..f822dbe 100644
--- a/src/com/android/tv/tunerinputcontroller/TunerInputController.java
+++ b/src/com/android/tv/tunerinputcontroller/TunerInputController.java
@@ -17,10 +17,13 @@
 package com.android.tv.tunerinputcontroller;
 
 import android.content.Context;
+import android.content.Intent;
 
 /** Controls the package visibility of built in tuner services. */
 public interface TunerInputController {
 
+    Intent createSetupIntent(Context context);
+
     void onCheckingUsbTunerStatus(Context context, String action);
 
     void executeNetworkTunerDiscoveryAsyncTask(Context context);
diff --git a/src/com/android/tv/util/AsyncDbTask.java b/src/com/android/tv/util/AsyncDbTask.java
index 261a1ef..b352395 100644
--- a/src/com/android/tv/util/AsyncDbTask.java
+++ b/src/com/android/tv/util/AsyncDbTask.java
@@ -40,6 +40,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
+import javax.inject.Qualifier;
 
 /**
  * {@link AsyncTask} that defaults to executing on its own single threaded Executor Service.
@@ -53,6 +54,10 @@
     private static final String TAG = "AsyncDbTask";
     private static final boolean DEBUG = false;
 
+    /** Annotation for requesting the {@link Executor} for data base access. */
+    @Qualifier
+    public @interface DbExecutor {}
+
     private final Executor mExecutor;
     boolean mCalledExecuteOnDbThread;
 
@@ -78,7 +83,7 @@
         private String[] mProjection;
 
         public AsyncQueryTask(
-                Executor executor,
+                @DbExecutor Executor executor,
                 Context context,
                 Uri uri,
                 String[] projection,
@@ -117,14 +122,24 @@
             if (context == null) {
                 return null;
             }
-            if ((Utils.isProgramsUri(mUri)
-                            && TvProviderUtils.checkSeriesIdColumn(context, Programs.CONTENT_URI))
-                    || (Utils.isRecordedProgramsUri(mUri)
-                            && TvProviderUtils.checkSeriesIdColumn(
-                                    context, TvContract.RecordedPrograms.CONTENT_URI)
-                            && TvProviderUtils.checkStateColumn(
-                                    context, TvContract.RecordedPrograms.CONTENT_URI))) {
-                mProjection = TvProviderUtils.addExtraColumnsToProjection(mProjection);
+            if (Utils.isProgramsUri(mUri)
+                            && TvProviderUtils.checkSeriesIdColumn(context, Programs.CONTENT_URI)) {
+                mProjection =
+                        TvProviderUtils.addExtraColumnsToProjection(
+                                mProjection, TvProviderUtils.EXTRA_PROGRAM_COLUMN_SERIES_ID);
+            } else if (Utils.isRecordedProgramsUri(mUri)) {
+                if (TvProviderUtils.checkSeriesIdColumn(
+                        context, TvContract.RecordedPrograms.CONTENT_URI)) {
+                    mProjection =
+                            TvProviderUtils.addExtraColumnsToProjection(
+                                    mProjection, TvProviderUtils.EXTRA_PROGRAM_COLUMN_SERIES_ID);
+                }
+                if (TvProviderUtils.checkStateColumn(
+                        context, TvContract.RecordedPrograms.CONTENT_URI)) {
+                    mProjection =
+                            TvProviderUtils.addExtraColumnsToProjection(
+                                    mProjection, TvProviderUtils.EXTRA_PROGRAM_COLUMN_STATE);
+                }
             }
             if (DEBUG) {
                 Log.v(TAG, "Starting query for " + this);
diff --git a/src/com/android/tv/util/TvInputManagerHelper.java b/src/com/android/tv/util/TvInputManagerHelper.java
index 6d2095c..cb7d985 100644
--- a/src/com/android/tv/util/TvInputManagerHelper.java
+++ b/src/com/android/tv/util/TvInputManagerHelper.java
@@ -38,6 +38,7 @@
 import android.util.Log;
 import com.android.tv.common.SoftPreconditions;
 import com.android.tv.common.compat.TvInputInfoCompat;
+import com.android.tv.common.dagger.annotations.ApplicationContext;
 import com.android.tv.common.util.CommonUtils;
 import com.android.tv.common.util.SystemProperties;
 import com.android.tv.features.TvFeatures;
@@ -53,9 +54,12 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import javax.inject.Inject;
+import javax.inject.Singleton;
 
 /** Helper class for {@link TvInputManager}. */
 @UiThread
+@Singleton
 public class TvInputManagerHelper {
     private static final String TAG = "TvInputManagerHelper";
     private static final boolean DEBUG = false;
@@ -287,7 +291,8 @@
     private final Comparator<TvInputInfo> mTvInputInfoComparator;
     private boolean mAllow3rdPartyInputs;
 
-    public TvInputManagerHelper(Context context) {
+    @Inject
+    public TvInputManagerHelper(@ApplicationContext Context context) {
         this(context, createTvInputManagerWrapper(context));
     }
 
diff --git a/src/com/android/tv/util/TvProviderUtils.java b/src/com/android/tv/util/TvProviderUtils.java
index 67673f3..6b5aaec 100644
--- a/src/com/android/tv/util/TvProviderUtils.java
+++ b/src/com/android/tv/util/TvProviderUtils.java
@@ -23,11 +23,14 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.StringDef;
 import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
 import android.util.Log;
 import com.android.tv.data.BaseProgram;
 import com.android.tv.features.PartnerFeatures;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -42,6 +45,11 @@
     public static final String EXTRA_PROGRAM_COLUMN_SERIES_ID = BaseProgram.COLUMN_SERIES_ID;
     public static final String EXTRA_PROGRAM_COLUMN_STATE = BaseProgram.COLUMN_STATE;
 
+    /** Possible extra columns in TV provider. */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({EXTRA_PROGRAM_COLUMN_SERIES_ID, EXTRA_PROGRAM_COLUMN_STATE})
+    public @interface TvProviderExtraColumn {}
+
     private static boolean sProgramHasSeriesIdColumn;
     private static boolean sRecordedProgramHasSeriesIdColumn;
     private static boolean sRecordedProgramHasStateColumn;
@@ -136,13 +144,11 @@
         return TRUE.equals(sRecordedProgramHasStateColumn);
     }
 
-    public static String[] addExtraColumnsToProjection(String[] projection) {
+    public static String[] addExtraColumnsToProjection(String[] projection,
+            @TvProviderExtraColumn String column) {
         List<String> projectionList = new ArrayList<>(Arrays.asList(projection));
-        if (!projectionList.contains(EXTRA_PROGRAM_COLUMN_SERIES_ID)) {
-            projectionList.add(EXTRA_PROGRAM_COLUMN_SERIES_ID);
-        }
-        if (!projectionList.contains(EXTRA_PROGRAM_COLUMN_STATE)) {
-            projectionList.add(EXTRA_PROGRAM_COLUMN_STATE);
+        if (!projectionList.contains(column)) {
+            projectionList.add(column);
         }
         projection = projectionList.toArray(projection);
         return projection;
diff --git a/src/com/android/tv/util/Utils.java b/src/com/android/tv/util/Utils.java
index 53ed82f..5117373 100644
--- a/src/com/android/tv/util/Utils.java
+++ b/src/com/android/tv/util/Utils.java
@@ -327,7 +327,9 @@
         String[] projection = Program.PROJECTION;
         if (TvProviderUtils.checkSeriesIdColumn(context, TvContract.Programs.CONTENT_URI)) {
             if (Utils.isProgramsUri(uri)) {
-                projection = TvProviderUtils.addExtraColumnsToProjection(projection);
+                projection =
+                        TvProviderUtils.addExtraColumnsToProjection(
+                                projection, TvProviderUtils.EXTRA_PROGRAM_COLUMN_SERIES_ID);
             }
         }
         try (Cursor cursor = resolver.query(uri, projection, null, null, null)) {
diff --git a/tests/common/src/com/android/tv/testing/TestSingletonApp.java b/tests/common/src/com/android/tv/testing/TestSingletonApp.java
index c918ba8..74fc192 100644
--- a/tests/common/src/com/android/tv/testing/TestSingletonApp.java
+++ b/tests/common/src/com/android/tv/testing/TestSingletonApp.java
@@ -17,8 +17,6 @@
 package com.android.tv.testing;
 
 import android.app.Application;
-import android.content.Context;
-import android.content.Intent;
 import android.media.tv.TvInputManager;
 import android.os.AsyncTask;
 import com.android.tv.InputSessionManager;
@@ -51,8 +49,9 @@
 import com.android.tv.testing.dvr.DvrDataManagerInMemoryImpl;
 import com.android.tv.testing.testdata.TestData;
 import com.android.tv.tuner.singletons.TunerSingletons;
+import com.android.tv.tuner.source.TsDataSourceManager;
+import com.android.tv.tuner.source.TunerTsStreamerManager;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
-import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.HasTunerSessionFactory;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactoryImpl;
 import com.android.tv.tunerinputcontroller.BuiltInTunerManager;
 import com.android.tv.util.SetupUtils;
@@ -64,10 +63,7 @@
 
 /** Test application for Live TV. */
 public class TestSingletonApp extends Application
-        implements TvSingletons,
-                TunerSingletons,
-                HasTunerSessionFactory,
-                HasSingletons<TvSingletons> {
+        implements TvSingletons, TunerSingletons, HasSingletons<TvSingletons> {
     public final FakeClock fakeClock = FakeClock.createWithCurrentTime();
     public final FakeEpgReader epgReader = new FakeEpgReader(fakeClock);
     public final FakeEpgFetcher epgFetcher = new FakeEpgFetcher();
@@ -84,8 +80,13 @@
     private final DefaultUiFlags mUiFlags = new DefaultUiFlags();
     private final DefaultConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags =
             new DefaultConcurrentDvrPlaybackFlags();
+    private final TsDataSourceManager.Factory mTsDataSourceManagerFactory =
+            new TsDataSourceManager.Factory(() -> new TunerTsStreamerManager(null));
     private final TunerSessionFactoryImpl mTunerSessionFactory =
-            new TunerSessionFactoryImpl(new DefaultExoplayer2Flags(), mConcurrentDvrPlaybackFlags);
+            new TunerSessionFactoryImpl(
+                    new DefaultExoplayer2Flags(),
+                    mConcurrentDvrPlaybackFlags,
+                    mTsDataSourceManagerFactory);
     private PerformanceMonitor mPerformanceMonitor;
     private ChannelDataManager mChannelDataManager;
 
@@ -228,11 +229,6 @@
     }
 
     @Override
-    public Intent getTunerSetupIntent(Context context) {
-        return null;
-    }
-
-    @Override
     public boolean isRunningInMainProcess() {
         return false;
     }
@@ -280,7 +276,6 @@
         return mConcurrentDvrPlaybackFlags;
     }
 
-    @Override
     public TunerSessionFactory getTunerSessionFactory() {
         return mTunerSessionFactory;
     }
diff --git a/tests/unit/src/com/android/tv/util/MockTvSingletons.java b/tests/unit/src/com/android/tv/util/MockTvSingletons.java
index 534be83..fd4b43c 100644
--- a/tests/unit/src/com/android/tv/util/MockTvSingletons.java
+++ b/tests/unit/src/com/android/tv/util/MockTvSingletons.java
@@ -17,7 +17,6 @@
 package com.android.tv.util;
 
 import android.content.Context;
-import android.content.Intent;
 import com.android.tv.InputSessionManager;
 import com.android.tv.MainActivityWrapper;
 import com.android.tv.TvApplication;
@@ -184,11 +183,6 @@
     }
 
     @Override
-    public Intent getTunerSetupIntent(Context context) {
-        return mApp.getTunerSetupIntent(context);
-    }
-
-    @Override
     public boolean isRunningInMainProcess() {
         return mApp.isRunningInMainProcess();
     }
diff --git a/tuner/SampleDvbTuner/Android.mk b/tuner/SampleDvbTuner/Android.mk
deleted file mode 100644
index 759c910..0000000
--- a/tuner/SampleDvbTuner/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# Include all java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SampleDvbTuner
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_JAVA_LIBRARIES := \
-    tv-guava-android-jar \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    tv-auto-value-jar \
-    android-support-annotations \
-    tv-error-prone-annotations-jar \
-    jsr330 \
-    tv-lib-dagger \
-    tv-lib-exoplayer \
-    tv-lib-exoplayer-v2-core \
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    tv-lib-dagger-android \
-    live-channels-partner-support \
-    live-tv-tuner \
-    tv-common \
-
-LOCAL_ANNOTATION_PROCESSORS := \
-    tv-guava-jre-jar \
-    jsr330 \
-    tv-lib-dagger-android-processor \
-    tv-lib-dagger-compiler \
-
-
-LOCAL_ANNOTATION_PROCESSOR_CLASSES := \
-   dagger.internal.codegen.ComponentProcessor,dagger.android.processor.AndroidProcessor
-
-
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 23  # M
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-include $(BUILD_PACKAGE)
diff --git a/tuner/SampleDvbTuner/AndroidManifest.xml b/tuner/SampleDvbTuner/AndroidManifest.xml
index 5a73d4c..5ad927e 100755
--- a/tuner/SampleDvbTuner/AndroidManifest.xml
+++ b/tuner/SampleDvbTuner/AndroidManifest.xml
@@ -23,15 +23,12 @@
 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_TV_LISTINGS" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
     <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
-    <!--Permission to modify Recorded Program-->
-    <uses-permission android:name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA" />
 
     <!-- Permissions/feature for USB tuner -->
     <uses-permission android:name="android.permission.DVB_DEVICE" />
@@ -53,12 +50,10 @@
 
     <application
         android:name="com.android.tv.tuner.sample.dvb.app.SampleDvbTuner"
+        android:appComponentFactory="android.support.v4.app.CoreComponentFactory"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/sample_dvb_tuner_app_name" >
-        <activity
-            android:name="com.google.android.gms.common.api.GoogleApiActivity"
-            android:exported="false"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+
 
         <activity
             android:name="com.android.tv.tuner.sample.dvb.setup.SampleDvbTunerSetupActivity"
@@ -76,7 +71,7 @@
             android:name="com.android.tv.tuner.sample.dvb.tvinput.SampleDvbTunerTvInputService"
             android:label="@string/sample_dvb_tuner_app_name"
             android:permission="android.permission.BIND_TV_INPUT"
-            android:process="com.google.android.tv.tuner.sample.dvb.tvinput" >
+            android:process="com.android.tv.tuner.sample.dvb.tvinput" >
             <intent-filter>
                 <action android:name="android.media.tv.TvInputService" />
             </intent-filter>
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml
index 47c2dc0..dc04228 100644
--- a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/AndroidManifest.xml
@@ -37,5 +37,9 @@
     <uses-feature android:name="android.software.live_tv" android:required="true" />
     <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
     <uses-sdk android:targetSdkVersion="27" android:minSdkVersion="23"/>
-    <application />
+    <application
+        android:name=".app.SampleDvbTuner"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/sample_dvb_tuner_app_name"
+        />
 </manifest>
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java
index bbb6779..568e3c9 100644
--- a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTuner.java
@@ -17,54 +17,42 @@
 package com.android.tv.tuner.sample.dvb.app;
 
 import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
 import android.media.tv.TvContract;
 import com.android.tv.common.BaseApplication;
-import com.android.tv.common.actions.InputSetupActionUtils;
-import com.android.tv.common.flags.impl.DefaultCloudEpgFlags;
-import com.android.tv.common.flags.impl.DefaultConcurrentDvrPlaybackFlags;
-import com.android.tv.common.flags.impl.DefaultExoplayer2Flags;
 import com.android.tv.common.singletons.HasSingletons;
-import com.android.tv.common.util.CommonUtils;
 import com.android.tv.tuner.modules.TunerSingletonsModule;
 import com.android.tv.tuner.sample.dvb.singletons.SampleDvbSingletons;
 import com.android.tv.tuner.sample.dvb.tvinput.SampleDvbTunerTvInputService;
-import com.android.tv.tuner.setup.LiveTvTunerSetupActivity;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactoryImpl;
 import dagger.android.AndroidInjector;
+import com.android.tv.common.flags.CloudEpgFlags;
+import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
+import javax.inject.Inject;
 
 /** The top level application for Sample DVB Tuner. */
 public class SampleDvbTuner extends BaseApplication
         implements SampleDvbSingletons, HasSingletons<SampleDvbSingletons> {
 
     private String mEmbeddedInputId;
-    private final DefaultCloudEpgFlags mCloudEpgFlags = new DefaultCloudEpgFlags();
-    private final DefaultConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags =
-            new DefaultConcurrentDvrPlaybackFlags();
-    private final DefaultExoplayer2Flags mExoplayer2Flags = new DefaultExoplayer2Flags();
-    private final TunerSessionFactoryImpl mTunerSessionFactory =
-            new TunerSessionFactoryImpl(mExoplayer2Flags, mConcurrentDvrPlaybackFlags);
+    @Inject CloudEpgFlags mCloudEpgFlags;
+    @Inject ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags;
+    @Inject TunerSessionFactoryImpl mTunerSessionFactory;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
 
     @Override
     protected AndroidInjector<SampleDvbTuner> applicationInjector() {
         return DaggerSampleDvbTunerComponent.builder()
+                .sampleDvbTunerModule(new SampleDvbTunerModule(this))
                 .tunerSingletonsModule(new TunerSingletonsModule(this))
                 .build();
     }
 
     @Override
-    public Intent getTunerSetupIntent(Context context) {
-        // Make an intent to launch the setup activity of TV tuner input.
-        Intent intent =
-                CommonUtils.createSetupIntent(
-                        new Intent(context, LiveTvTunerSetupActivity.class), mEmbeddedInputId);
-        intent.putExtra(InputSetupActionUtils.EXTRA_INPUT_ID, mEmbeddedInputId);
-        return intent;
-    }
-
-    @Override
     public synchronized String getEmbeddedTunerInputId() {
         if (mEmbeddedInputId == null) {
             mEmbeddedInputId =
@@ -75,7 +63,7 @@
     }
 
     @Override
-    public DefaultCloudEpgFlags getCloudEpgFlags() {
+    public CloudEpgFlags getCloudEpgFlags() {
         return mCloudEpgFlags;
     }
 
@@ -85,7 +73,7 @@
     }
 
     @Override
-    public DefaultConcurrentDvrPlaybackFlags getConcurrentDvrPlaybackFlags() {
+    public ConcurrentDvrPlaybackFlags getConcurrentDvrPlaybackFlags() {
         return mConcurrentDvrPlaybackFlags;
     }
 
@@ -94,7 +82,6 @@
         return this;
     }
 
-    @Override
     public TunerSessionFactory getTunerSessionFactory() {
         return mTunerSessionFactory;
     }
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerComponent.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerComponent.java
index cb529ce..e6c80ea 100644
--- a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerComponent.java
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerComponent.java
@@ -18,7 +18,9 @@
 import dagger.Component;
 import dagger.android.AndroidInjectionModule;
 import dagger.android.AndroidInjector;
+import javax.inject.Singleton;
 
 /** Dagger component for {@link SampleDvbTuner}. */
+@Singleton
 @Component(modules = {AndroidInjectionModule.class, SampleDvbTunerModule.class})
 public interface SampleDvbTunerComponent extends AndroidInjector<SampleDvbTuner> {}
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerModule.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerModule.java
index 7297ff2..4da3ca9 100644
--- a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerModule.java
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/app/SampleDvbTunerModule.java
@@ -15,9 +15,38 @@
  */
 package com.android.tv.tuner.sample.dvb.app;
 
+import com.android.tv.common.flags.impl.DefaultFlagsModule;
+import com.android.tv.tuner.api.TunerFactory;
+import com.android.tv.tuner.builtin.BuiltInTunerHalFactory;
 import com.android.tv.tuner.modules.TunerModule;
+import com.android.tv.tuner.sample.dvb.setup.SampleDvbTunerSetupActivity;
+import com.android.tv.tuner.sample.dvb.tvinput.SampleDvbTunerTvInputService;
+import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
 import dagger.Module;
+import dagger.Provides;
 
 /** Dagger module for {@link SampleDvbTuner}. */
-@Module(includes = {TunerModule.class})
-class SampleDvbTunerModule {}
+@Module(
+        includes = {
+            DefaultFlagsModule.class,
+            SampleDvbTunerTvInputService.Module.class,
+            SampleDvbTunerSetupActivity.Module.class,
+            TunerModule.class,
+        })
+class SampleDvbTunerModule {
+    private final SampleDvbTuner mSampleDvbTuner;
+
+    SampleDvbTunerModule(SampleDvbTuner sampleDvbTuner) {
+        mSampleDvbTuner = sampleDvbTuner;
+    }
+
+    @Provides
+    public TunerSessionFactory providesTunerSessionFactory() {
+        return mSampleDvbTuner.getTunerSessionFactory();
+    }
+
+    @Provides
+    TunerFactory providesTunerFactory() {
+        return BuiltInTunerHalFactory.INSTANCE;
+    }
+}
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java
index c85562a..f9ef29c 100644
--- a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/setup/SampleDvbTunerSetupActivity.java
@@ -35,7 +35,6 @@
 import com.android.tv.common.ui.setup.SetupFragment;
 import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
 import com.android.tv.common.util.PostalCodeUtils;
-import com.android.tv.tuner.BuiltInTunerHalFactory;
 import com.android.tv.tuner.sample.dvb.R;
 import com.android.tv.tuner.setup.BaseTunerSetupActivity;
 import com.android.tv.tuner.setup.ConnectionTypeFragment;
@@ -96,9 +95,7 @@
         new AsyncTask<Void, Void, Integer>() {
             @Override
             protected Integer doInBackground(Void... arg0) {
-                return BuiltInTunerHalFactory.INSTANCE.getTunerTypeAndCount(
-                                SampleDvbTunerSetupActivity.this)
-                        .first;
+                return mTunerFactory.getTunerTypeAndCount(SampleDvbTunerSetupActivity.this).first;
             }
 
             @Override
diff --git a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java
index ae15aff..a31faa8 100644
--- a/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java
+++ b/tuner/SampleDvbTuner/src/com/android/tv/tuner/sample/dvb/tvinput/SampleDvbTunerTvInputService.java
@@ -16,6 +16,18 @@
 package com.android.tv.tuner.sample.dvb.tvinput;
 
 import com.android.tv.tuner.tvinput.BaseTunerTvInputService;
+import dagger.android.ContributesAndroidInjector;
 
 /** Sample DVB Tuner {@link android.media.tv.TvInputService}. */
-public class SampleDvbTunerTvInputService extends BaseTunerTvInputService {}
+public class SampleDvbTunerTvInputService extends BaseTunerTvInputService {
+
+    /**
+     * Exports {@link SampleDvbTunerTvInputService} for Dagger codegen to create the appropriate
+     * injector.
+     */
+    @dagger.Module
+    public abstract static class Module {
+        @ContributesAndroidInjector
+        abstract SampleDvbTunerTvInputService contributesSampleDvbTunerTvInputServiceInjector();
+    }
+}
diff --git a/tuner/SampleNetworkTuner/Android.mk b/tuner/SampleNetworkTuner/Android.mk
deleted file mode 100644
index 3fca65e..0000000
--- a/tuner/SampleNetworkTuner/Android.mk
+++ /dev/null
@@ -1,46 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# Include all java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SampleNetworkTuner
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_JAVA_LIBRARIES := \
-    tv-guava-android-jar \
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    tv-auto-value-jar \
-    android-support-annotations \
-    tv-error-prone-annotations-jar \
-    jsr330 \
-    tv-lib-dagger \
-    tv-lib-exoplayer \
-    tv-lib-exoplayer-v2-core \
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    tv-lib-dagger-android \
-    live-channels-partner-support \
-    live-tv-tuner \
-    tv-common \
-
-LOCAL_ANNOTATION_PROCESSORS := \
-    tv-guava-jre-jar \
-    jsr330 \
-    tv-lib-dagger-android-processor\
-    tv-lib-dagger-compiler \
-
-
-LOCAL_ANNOTATION_PROCESSOR_CLASSES := \
-   dagger.internal.codegen.ComponentProcessor,dagger.android.processor.AndroidProcessor
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 23  # M
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-include $(BUILD_PACKAGE)
diff --git a/tuner/SampleNetworkTuner/AndroidManifest.xml b/tuner/SampleNetworkTuner/AndroidManifest.xml
index 5e099d2..0ec9afc 100755
--- a/tuner/SampleNetworkTuner/AndroidManifest.xml
+++ b/tuner/SampleNetworkTuner/AndroidManifest.xml
@@ -23,7 +23,6 @@
 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_TV_LISTINGS" />
@@ -51,12 +50,10 @@
 
     <application
         android:name="com.android.tv.tuner.sample.network.app.SampleNetworkTuner"
+        android:appComponentFactory="android.support.v4.app.CoreComponentFactory"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/sample_network_tuner_app_name" >
-        <activity
-            android:name="com.google.android.gms.common.api.GoogleApiActivity"
-            android:exported="false"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+
 
         <activity
             android:name="com.android.tv.tuner.sample.network.setup.SampleNetworkTunerSetupActivity"
@@ -74,7 +71,7 @@
             android:name="com.android.tv.tuner.sample.network.tvinput.SampleNetworkTunerTvInputService"
             android:label="@string/sample_network_tuner_app_name"
             android:permission="android.permission.BIND_TV_INPUT"
-            android:process="com.google.android.tv.tuner.sample.network.tvinput" >
+            android:process="com.android.tv.tuner.sample.network.tvinput" >
             <intent-filter>
                 <action android:name="android.media.tv.TvInputService" />
             </intent-filter>
diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/AndroidManifest.xml b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/AndroidManifest.xml
index a678097..dddd8a4 100644
--- a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/AndroidManifest.xml
+++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/AndroidManifest.xml
@@ -37,5 +37,9 @@
     <uses-feature android:name="android.software.live_tv" android:required="true" />
     <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
     <uses-sdk android:targetSdkVersion="27" android:minSdkVersion="23"/>
-    <application />
+    <application
+        android:name=".app.SampleNetworkTuner"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/sample_network_tuner_app_name"
+        />
 </manifest>
diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTuner.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTuner.java
index 5eb44e9..eb5b2ad 100644
--- a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTuner.java
+++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTuner.java
@@ -17,54 +17,42 @@
 package com.android.tv.tuner.sample.network.app;
 
 import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
 import android.media.tv.TvContract;
 import com.android.tv.common.BaseApplication;
-import com.android.tv.common.actions.InputSetupActionUtils;
-import com.android.tv.common.flags.impl.DefaultCloudEpgFlags;
-import com.android.tv.common.flags.impl.DefaultConcurrentDvrPlaybackFlags;
-import com.android.tv.common.flags.impl.DefaultExoplayer2Flags;
 import com.android.tv.common.singletons.HasSingletons;
-import com.android.tv.common.util.CommonUtils;
 import com.android.tv.tuner.modules.TunerSingletonsModule;
 import com.android.tv.tuner.sample.network.singletons.SampleNetworkSingletons;
 import com.android.tv.tuner.sample.network.tvinput.SampleNetworkTunerTvInputService;
-import com.android.tv.tuner.setup.LiveTvTunerSetupActivity;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactoryImpl;
 import dagger.android.AndroidInjector;
+import com.android.tv.common.flags.CloudEpgFlags;
+import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
+import javax.inject.Inject;
 
 /** The top level application for Sample DVB Tuner. */
 public class SampleNetworkTuner extends BaseApplication
         implements SampleNetworkSingletons, HasSingletons<SampleNetworkSingletons> {
 
     private String mEmbeddedInputId;
-    private final DefaultCloudEpgFlags mCloudEpgFlags = new DefaultCloudEpgFlags();
-    private final DefaultConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags =
-            new DefaultConcurrentDvrPlaybackFlags();
-    private final DefaultExoplayer2Flags mExoplayer2Flags = new DefaultExoplayer2Flags();
-    private final TunerSessionFactoryImpl mTunerSessionFactory =
-            new TunerSessionFactoryImpl(mExoplayer2Flags, mConcurrentDvrPlaybackFlags);
+    @Inject CloudEpgFlags mCloudEpgFlags;
+    @Inject ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags;
+    @Inject TunerSessionFactoryImpl mTunerSessionFactory;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
 
     @Override
     protected AndroidInjector<SampleNetworkTuner> applicationInjector() {
         return DaggerSampleNetworkTunerComponent.builder()
+                .sampleNetworkTunerModule(new SampleNetworkTunerModule(this))
                 .tunerSingletonsModule(new TunerSingletonsModule(this))
                 .build();
     }
 
     @Override
-    public Intent getTunerSetupIntent(Context context) {
-        // Make an intent to launch the setup activity of TV tuner input.
-        Intent intent =
-                CommonUtils.createSetupIntent(
-                        new Intent(context, LiveTvTunerSetupActivity.class), mEmbeddedInputId);
-        intent.putExtra(InputSetupActionUtils.EXTRA_INPUT_ID, mEmbeddedInputId);
-        return intent;
-    }
-
-    @Override
     public synchronized String getEmbeddedTunerInputId() {
         if (mEmbeddedInputId == null) {
             mEmbeddedInputId =
@@ -75,7 +63,7 @@
     }
 
     @Override
-    public DefaultCloudEpgFlags getCloudEpgFlags() {
+    public CloudEpgFlags getCloudEpgFlags() {
         return mCloudEpgFlags;
     }
 
@@ -85,7 +73,7 @@
     }
 
     @Override
-    public DefaultConcurrentDvrPlaybackFlags getConcurrentDvrPlaybackFlags() {
+    public ConcurrentDvrPlaybackFlags getConcurrentDvrPlaybackFlags() {
         return mConcurrentDvrPlaybackFlags;
     }
 
@@ -94,7 +82,6 @@
         return this;
     }
 
-    @Override
     public TunerSessionFactory getTunerSessionFactory() {
         return mTunerSessionFactory;
     }
diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerComponent.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerComponent.java
index 71a20be..b10105b 100644
--- a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerComponent.java
+++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerComponent.java
@@ -18,7 +18,9 @@
 import dagger.Component;
 import dagger.android.AndroidInjectionModule;
 import dagger.android.AndroidInjector;
+import javax.inject.Singleton;
 
 /** Dagger component for {@link SampleNetworkTuner}. */
+@Singleton
 @Component(modules = {AndroidInjectionModule.class, SampleNetworkTunerModule.class})
 public interface SampleNetworkTunerComponent extends AndroidInjector<SampleNetworkTuner> {}
diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerModule.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerModule.java
index 57e4ee6..d974e20 100644
--- a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerModule.java
+++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/app/SampleNetworkTunerModule.java
@@ -15,9 +15,38 @@
  */
 package com.android.tv.tuner.sample.network.app;
 
+import com.android.tv.common.flags.impl.DefaultFlagsModule;
+import com.android.tv.tuner.api.TunerFactory;
+import com.android.tv.tuner.builtin.BuiltInTunerHalFactory;
 import com.android.tv.tuner.modules.TunerModule;
+import com.android.tv.tuner.sample.network.setup.SampleNetworkTunerSetupActivity;
+import com.android.tv.tuner.sample.network.tvinput.SampleNetworkTunerTvInputService;
+import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
 import dagger.Module;
+import dagger.Provides;
 
 /** Dagger module for {@link SampleNetworkTuner}. */
-@Module(includes = {TunerModule.class})
-class SampleNetworkTunerModule {}
+@Module(
+        includes = {
+            DefaultFlagsModule.class,
+            SampleNetworkTunerTvInputService.Module.class,
+            SampleNetworkTunerSetupActivity.Module.class,
+            TunerModule.class,
+        })
+class SampleNetworkTunerModule {
+    private final SampleNetworkTuner mSampleNetworkTuner;
+
+    SampleNetworkTunerModule(SampleNetworkTuner sampleNetworkTuner) {
+        mSampleNetworkTuner = sampleNetworkTuner;
+    }
+
+    @Provides
+    public TunerSessionFactory providesTunerSessionFactory() {
+        return mSampleNetworkTuner.getTunerSessionFactory();
+    }
+
+    @Provides
+    TunerFactory providesTunerFactory() {
+        return BuiltInTunerHalFactory.INSTANCE;
+    }
+}
diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/setup/SampleNetworkTunerSetupActivity.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/setup/SampleNetworkTunerSetupActivity.java
index d11e62a..fd783c4 100644
--- a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/setup/SampleNetworkTunerSetupActivity.java
+++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/setup/SampleNetworkTunerSetupActivity.java
@@ -35,7 +35,6 @@
 import com.android.tv.common.ui.setup.SetupFragment;
 import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
 import com.android.tv.common.util.PostalCodeUtils;
-import com.android.tv.tuner.BuiltInTunerHalFactory;
 import com.android.tv.tuner.sample.network.R;
 import com.android.tv.tuner.setup.BaseTunerSetupActivity;
 import com.android.tv.tuner.setup.ConnectionTypeFragment;
@@ -96,8 +95,7 @@
         new AsyncTask<Void, Void, Integer>() {
             @Override
             protected Integer doInBackground(Void... arg0) {
-                return BuiltInTunerHalFactory.INSTANCE.getTunerTypeAndCount(
-                                SampleNetworkTunerSetupActivity.this)
+                return mTunerFactory.getTunerTypeAndCount(SampleNetworkTunerSetupActivity.this)
                         .first;
             }
 
diff --git a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/tvinput/SampleNetworkTunerTvInputService.java b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/tvinput/SampleNetworkTunerTvInputService.java
index e3abde5..de5ff22 100644
--- a/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/tvinput/SampleNetworkTunerTvInputService.java
+++ b/tuner/SampleNetworkTuner/src/com/android/tv/tuner/sample/network/tvinput/SampleNetworkTunerTvInputService.java
@@ -16,6 +16,19 @@
 package com.android.tv.tuner.sample.network.tvinput;
 
 import com.android.tv.tuner.tvinput.BaseTunerTvInputService;
+import dagger.android.ContributesAndroidInjector;
 
 /** Sample DVB Tuner {@link android.media.tv.TvInputService}. */
-public class SampleNetworkTunerTvInputService extends BaseTunerTvInputService {}
+public class SampleNetworkTunerTvInputService extends BaseTunerTvInputService {
+
+    /**
+     * Exports {@link SampleNetworkTunerTvInputService} for Dagger codegen to create the appropriate
+     * injector.
+     */
+    @dagger.Module
+    public abstract static class Module {
+        @ContributesAndroidInjector
+        abstract SampleNetworkTunerTvInputService
+                contributesSampleNetworkTunerTvInputServiceInjector();
+    }
+}
diff --git a/tuner/src/com/android/tv/tuner/api/TunerFactory.java b/tuner/src/com/android/tv/tuner/api/TunerFactory.java
new file mode 100644
index 0000000..bc29c7c
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/api/TunerFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tv.tuner.api;
+
+import android.content.Context;
+import android.support.annotation.WorkerThread;
+import android.util.Pair;
+
+/** Factory for {@link Tuner}. */
+public interface TunerFactory {
+    @WorkerThread
+    Tuner createInstance(Context context);
+
+    boolean useBuiltInTuner(Context context);
+
+    @WorkerThread
+    Pair<Integer, Integer> getTunerTypeAndCount(Context context);
+}
diff --git a/tuner/src/com/android/tv/tuner/BuiltInTunerHalFactory.java b/tuner/src/com/android/tv/tuner/builtin/BuiltInTunerHalFactory.java
similarity index 90%
rename from tuner/src/com/android/tv/tuner/BuiltInTunerHalFactory.java
rename to tuner/src/com/android/tv/tuner/builtin/BuiltInTunerHalFactory.java
index 92e9c38..9a0be74 100644
--- a/tuner/src/com/android/tv/tuner/BuiltInTunerHalFactory.java
+++ b/tuner/src/com/android/tv/tuner/builtin/BuiltInTunerHalFactory.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.tv.tuner;
+package com.android.tv.tuner.builtin;
 
 import android.content.Context;
 import android.support.annotation.WorkerThread;
@@ -22,17 +22,19 @@
 import android.util.Pair;
 import com.android.tv.common.customization.CustomizationManager;
 import com.android.tv.common.feature.Model;
+import com.android.tv.tuner.DvbTunerHal;
 import com.android.tv.tuner.api.Tuner;
+import com.android.tv.tuner.api.TunerFactory;
 
 
 /** TunerHal factory that creates all built in tuner types. */
-public final class BuiltInTunerHalFactory {
+public final class BuiltInTunerHalFactory implements TunerFactory {
     private static final String TAG = "BuiltInTunerHalFactory";
     private static final boolean DEBUG = false;
 
     private Integer mBuiltInTunerType;
 
-    public static final BuiltInTunerHalFactory INSTANCE = new BuiltInTunerHalFactory();
+    public static final TunerFactory INSTANCE = new BuiltInTunerHalFactory();
 
     private BuiltInTunerHalFactory() {}
 
@@ -54,6 +56,7 @@
      * @param context context for creating the TunerHal instance
      * @return the TunerHal instance
      */
+    @Override
     @WorkerThread
     public synchronized Tuner createInstance(Context context) {
         Tuner tunerHal = null;
@@ -68,11 +71,13 @@
      * Returns if tuner input service would use built-in tuners instead of USB tuners or network
      * tuners.
      */
+    @Override
     public boolean useBuiltInTuner(Context context) {
         return getBuiltInTunerType(context) != 0;
     }
 
     /** Gets the number of tuner devices currently present. */
+    @Override
     @WorkerThread
     public Pair<Integer, Integer> getTunerTypeAndCount(Context context) {
         if (useBuiltInTuner(context)) {
diff --git a/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java
index b5411d9..e48cb03 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java
@@ -365,7 +365,9 @@
                         mMediaPeriod =
                                 mSampleSource.createPeriod(
                                         new MediaSource.MediaPeriodId(0),
-                                        new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE));
+                                        new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE)
+// AOSP_Comment_Out                                         , 0
+                                );
                         mMediaPeriod.prepare(this, 0);
                         try {
                             mMediaPeriod.maybeThrowPrepareError();
diff --git a/tuner/src/com/android/tv/tuner/livetuner/LiveTvTunerTvInputService.java b/tuner/src/com/android/tv/tuner/livetuner/LiveTvTunerTvInputService.java
index f741fdb..92701db 100644
--- a/tuner/src/com/android/tv/tuner/livetuner/LiveTvTunerTvInputService.java
+++ b/tuner/src/com/android/tv/tuner/livetuner/LiveTvTunerTvInputService.java
@@ -17,6 +17,18 @@
 package com.android.tv.tuner.livetuner;
 
 import com.android.tv.tuner.tvinput.BaseTunerTvInputService;
+import dagger.android.ContributesAndroidInjector;
 
 /** Live TV embedded tuner. */
-public class LiveTvTunerTvInputService extends BaseTunerTvInputService {}
+public class LiveTvTunerTvInputService extends BaseTunerTvInputService {
+
+    /**
+     * Exports {@link LiveTvTunerTvInputService} for Dagger codegen to create the appropriate
+     * injector.
+     */
+    @dagger.Module
+    public abstract static class Module {
+        @ContributesAndroidInjector
+        abstract LiveTvTunerTvInputService contributesLiveTvTunerTvInputServiceInjector();
+    }
+}
diff --git a/tuner/src/com/android/tv/tuner/modules/TunerModule.java b/tuner/src/com/android/tv/tuner/modules/TunerModule.java
index b0b58e8..4843f38 100644
--- a/tuner/src/com/android/tv/tuner/modules/TunerModule.java
+++ b/tuner/src/com/android/tv/tuner/modules/TunerModule.java
@@ -15,8 +15,9 @@
  */
 package com.android.tv.tuner.modules;
 
+import com.android.tv.tuner.source.TunerSourceModule;
 import dagger.Module;
 
 /** Dagger module for TV Tuners. */
-@Module(includes = {TunerSingletonsModule.class})
+@Module(includes = {TunerSingletonsModule.class, TunerSourceModule.class})
 public class TunerModule {}
diff --git a/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java b/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java
index 00d7ff2..44f689b 100644
--- a/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java
+++ b/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java
@@ -22,6 +22,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -36,7 +37,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.widget.Toast;
-import com.android.tv.common.BaseApplication;
 import com.android.tv.common.SoftPreconditions;
 import com.android.tv.common.feature.CommonFeatures;
 import com.android.tv.common.ui.setup.SetupActivity;
@@ -44,11 +44,12 @@
 import com.android.tv.common.ui.setup.SetupMultiPaneFragment;
 import com.android.tv.common.util.AutoCloseableUtils;
 import com.android.tv.common.util.PostalCodeUtils;
-import com.android.tv.tuner.BuiltInTunerHalFactory;
 import com.android.tv.tuner.R;
 import com.android.tv.tuner.api.Tuner;
+import com.android.tv.tuner.api.TunerFactory;
 import com.android.tv.tuner.prefs.TunerPreferences;
 import java.util.concurrent.Executor;
+import javax.inject.Inject;
 
 /** The base setup activity class for tuner. */
 public abstract class BaseTunerSetupActivity extends SetupActivity {
@@ -85,6 +86,7 @@
     protected String mPreviousPostalCode;
     protected boolean mActivityStopped;
     protected boolean mPendingShowInitialFragment;
+    @Inject protected TunerFactory mTunerFactory;
 
     private TunerHalCreator mTunerHalCreator;
 
@@ -93,11 +95,12 @@
         if (DEBUG) {
             Log.d(TAG, "onCreate");
         }
+        super.onCreate(savedInstanceState);
         mActivityStopped = false;
         executeGetTunerTypeAndCountAsyncTask();
         mTunerHalCreator =
-                new TunerHalCreator(getApplicationContext(), AsyncTask.THREAD_POOL_EXECUTOR);
-        super.onCreate(savedInstanceState);
+                new TunerHalCreator(
+                        getApplicationContext(), AsyncTask.THREAD_POOL_EXECUTOR, mTunerFactory);
         try {
             // Updating postal code takes time, therefore we called it here for "warm-up".
             mPreviousPostalCode = PostalCodeUtils.getLastPostalCode(this);
@@ -329,25 +332,28 @@
     /**
      * A callback to be invoked when the TvInputService is enabled or disabled.
      *
+     * @param tunerSetupIntent
      * @param context a {@link Context} instance
      * @param enabled {@code true} for the {@link TunerTvInputService} to be enabled; otherwise
      *     {@code false}
      */
-    public static void onTvInputEnabled(Context context, boolean enabled, Integer tunerType) {
+    public static void onTvInputEnabled(
+            Context context, boolean enabled, Integer tunerType, Intent tunerSetupIntent) {
         // Send a notification for tuner setup if there's no channels and the tuner TV input
         // setup has been not done.
         boolean channelScanDoneOnPreference = TunerPreferences.isScanDone(context);
         int channelCountOnPreference = TunerPreferences.getScannedChannelCount(context);
         if (enabled && !channelScanDoneOnPreference && channelCountOnPreference == 0) {
             TunerPreferences.setShouldShowSetupActivity(context, true);
-            sendNotification(context, tunerType);
+            sendNotification(context, tunerType, tunerSetupIntent);
         } else {
             TunerPreferences.setShouldShowSetupActivity(context, false);
             cancelNotification(context);
         }
     }
 
-    private static void sendNotification(Context context, Integer tunerType) {
+    private static void sendNotification(
+            Context context, Integer tunerType, Intent tunerSetupIntent) {
         SoftPreconditions.checkState(
                 tunerType != null, TAG, "tunerType is null when send notification");
         if (tunerType == null) {
@@ -370,16 +376,16 @@
         }
         String contentText = resources.getString(contentTextId);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            sendNotificationInternal(context, contentTitle, contentText);
+            sendNotificationInternal(context, contentTitle, contentText, tunerSetupIntent);
         } else {
             Bitmap largeIcon =
                     BitmapFactory.decodeResource(resources, R.drawable.recommendation_antenna);
-            sendRecommendationCard(context, contentTitle, contentText, largeIcon);
+            sendRecommendationCard(context, contentTitle, contentText, largeIcon, tunerSetupIntent);
         }
     }
 
     private static void sendNotificationInternal(
-            Context context, String contentTitle, String contentText) {
+            Context context, String contentTitle, String contentText, Intent tunerSetupIntent) {
         NotificationManager notificationManager =
                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.createNotificationChannel(
@@ -396,7 +402,8 @@
                                 context.getResources()
                                         .getIdentifier(
                                                 TAG_ICON, TAG_DRAWABLE, context.getPackageName()))
-                        .setContentIntent(createPendingIntentForSetupActivity(context))
+                        .setContentIntent(
+                                createPendingIntentForSetupActivity(context, tunerSetupIntent))
                         .setVisibility(Notification.VISIBILITY_PUBLIC)
                         .extend(new Notification.TvExtender())
                         .build();
@@ -406,10 +413,15 @@
     /**
      * Sends the recommendation card to start the tuner TV input setup activity.
      *
+     * @param tunerSetupIntent
      * @param context a {@link Context} instance
      */
     private static void sendRecommendationCard(
-            Context context, String contentTitle, String contentText, Bitmap largeIcon) {
+            Context context,
+            String contentTitle,
+            String contentText,
+            Bitmap largeIcon,
+            Intent tunerSetupIntent) {
         // Build and send the notification.
         Notification notification =
                 new NotificationCompat.BigPictureStyle(
@@ -427,7 +439,8 @@
                                                                 TAG_DRAWABLE,
                                                                 context.getPackageName()))
                                         .setContentIntent(
-                                                createPendingIntentForSetupActivity(context)))
+                                                createPendingIntentForSetupActivity(
+                                                        context, tunerSetupIntent)))
                         .build();
         NotificationManager notificationManager =
                 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -438,13 +451,12 @@
      * Returns a {@link PendingIntent} to launch the tuner TV input service.
      *
      * @param context a {@link Context} instance
+     * @param tunerSetupIntent
      */
-    private static PendingIntent createPendingIntentForSetupActivity(Context context) {
+    private static PendingIntent createPendingIntentForSetupActivity(
+            Context context, Intent tunerSetupIntent) {
         return PendingIntent.getActivity(
-                context,
-                0,
-                BaseApplication.getSingletons(context).getTunerSetupIntent(context),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                context, 0, tunerSetupIntent, PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     /** Creates {@link Tuner} instances in a worker thread * */
@@ -454,14 +466,12 @@
         @VisibleForTesting Tuner mTunerHal;
         private TunerHalCreator.GenerateTunerHalTask mGenerateTunerHalTask;
         private final Executor mExecutor;
+        private final TunerFactory mTunerFactory;
 
-        TunerHalCreator(Context context) {
-            this(context, AsyncTask.SERIAL_EXECUTOR);
-        }
-
-        TunerHalCreator(Context context, Executor executor) {
+        TunerHalCreator(Context context, Executor executor, TunerFactory tunerFactory) {
             mContext = context;
             mExecutor = executor;
+            mTunerFactory = tunerFactory;
         }
 
         /**
@@ -507,7 +517,7 @@
 
         @WorkerThread
         protected Tuner createInstance() {
-            return BuiltInTunerHalFactory.INSTANCE.createInstance(mContext);
+            return mTunerFactory.createInstance(mContext);
         }
 
         class GenerateTunerHalTask extends AsyncTask<Void, Void, Tuner> {
diff --git a/tuner/src/com/android/tv/tuner/ChannelScanFileParser.java b/tuner/src/com/android/tv/tuner/setup/ChannelScanFileParser.java
similarity index 95%
rename from tuner/src/com/android/tv/tuner/ChannelScanFileParser.java
rename to tuner/src/com/android/tv/tuner/setup/ChannelScanFileParser.java
index e27b084..43c584e 100644
--- a/tuner/src/com/android/tv/tuner/ChannelScanFileParser.java
+++ b/tuner/src/com/android/tv/tuner/setup/ChannelScanFileParser.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.tv.tuner;
+package com.android.tv.tuner.setup;
 
 import android.util.Log;
 import com.android.tv.tuner.api.ScanChannel;
@@ -26,7 +26,7 @@
 import java.util.List;
 
 /** Parses plain text formatted scan files, which contain the list of channels. */
-public class ChannelScanFileParser {
+public final class ChannelScanFileParser {
     private static final String TAG = "ChannelScanFileParser";
 
     /**
@@ -65,4 +65,6 @@
         }
         return scanChannelList;
     }
+
+   private ChannelScanFileParser(){}
 }
diff --git a/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java b/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java
index ec965f3..741edc7 100644
--- a/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java
+++ b/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java
@@ -22,9 +22,7 @@
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.view.KeyEvent;
-import com.android.tv.common.experiments.Experiments;
 import com.android.tv.common.util.PostalCodeUtils;
-import com.android.tv.tuner.BuiltInTunerHalFactory;
 import dagger.android.ContributesAndroidInjector;
 
 /** An activity that serves tuner setup process. */
@@ -49,9 +47,7 @@
         new AsyncTask<Void, Void, Integer>() {
             @Override
             protected Integer doInBackground(Void... arg0) {
-                return BuiltInTunerHalFactory.INSTANCE.getTunerTypeAndCount(
-                                LiveTvTunerSetupActivity.this)
-                        .first;
+                return mTunerFactory.getTunerTypeAndCount(LiveTvTunerSetupActivity.this).first;
             }
 
             @Override
@@ -98,9 +94,7 @@
     public void onRequestPermissionsResult(
             int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
         if (requestCode == PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION) {
-            if (grantResults.length > 0
-                    && grantResults[0] == PackageManager.PERMISSION_GRANTED
-                    && Experiments.CLOUD_EPG.get()) {
+            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                 try {
                     // Updating postal code takes time, therefore we should update postal code
                     // right after the permission is granted, so that the subsequent operations,
diff --git a/tuner/src/com/android/tv/tuner/setup/ScanFragment.java b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
index 6213da8..7d59284 100644
--- a/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
+++ b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
@@ -37,7 +37,6 @@
 import android.widget.TextView;
 import com.android.tv.common.SoftPreconditions;
 import com.android.tv.common.ui.setup.SetupFragment;
-import com.android.tv.tuner.ChannelScanFileParser;
 import com.android.tv.tuner.R;
 import com.android.tv.tuner.api.ScanChannel;
 import com.android.tv.tuner.api.Tuner;
@@ -100,7 +99,7 @@
         if (DEBUG) Log.d(TAG, "onCreateView");
         View view = super.onCreateView(inflater, container, savedInstanceState);
         mChannelNumbers = new ArrayList<>();
-        mChannelDataManager = new ChannelDataManager(getActivity());
+        mChannelDataManager = new ChannelDataManager(getActivity().getApplicationContext());
         mChannelDataManager.checkDataVersion(getActivity());
         mAdapter = new ChannelAdapter();
         mProgressBar = (ProgressBar) view.findViewById(R.id.tune_progress);
diff --git a/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java b/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java
index abaebf3..48b17dc 100644
--- a/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java
+++ b/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java
@@ -16,7 +16,6 @@
 package com.android.tv.tuner.singletons;
 
 import com.android.tv.common.singletons.HasTvInputId;
-import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
 
 /** Singletons used in tuner applications */
-public interface TunerSingletons extends HasTvInputId, TunerSessionFactory.HasTunerSessionFactory {}
+public interface TunerSingletons extends HasTvInputId {}
diff --git a/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java b/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java
index 507ff03..28756a9 100644
--- a/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java
+++ b/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java
@@ -22,45 +22,54 @@
 import com.android.tv.tuner.data.TunerChannel;
 import com.android.tv.tuner.data.nano.Channel;
 import com.android.tv.tuner.ts.EventDetector.EventListener;
+import com.google.auto.factory.AutoFactory;
+import com.google.auto.factory.Provided;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.inject.Inject;
+import javax.inject.Provider;
 
 /**
- * Manages {@link DataSource} for playback and recording. The class hides handling of {@link Tuner}
- * and {@link TsStreamer} from other classes. One TsDataSourceManager should be created for per
- * session.
+ * Manages {@link TsDataSource} for playback and recording. The class hides handling of {@link
+ * Tuner} and {@link TsStreamer} from other classes. One TsDataSourceManager should be created for
+ * per session.
  */
+@AutoFactory
 public class TsDataSourceManager {
-    private static final Object sLock = new Object();
     private static final Map<TsDataSource, TsStreamer> sTsStreamers = new ConcurrentHashMap<>();
 
-    private static int sSequenceId;
+    private static final AtomicInteger sSequenceId = new AtomicInteger();
 
-    private final int mId;
+    private final int mId = sSequenceId.incrementAndGet();
     private final boolean mIsRecording;
-    private final TunerTsStreamerManager mTunerStreamerManager =
-            TunerTsStreamerManager.getInstance();
+    private final TunerTsStreamerManager mTunerStreamerManager;
 
     private boolean mKeepTuneStatus;
 
     /**
-     * Creates TsDataSourceManager to create and release {@link DataSource} which will be used for
-     * playing and recording.
+     * Factory for {@link }TsDataSourceManager}.
      *
-     * @param isRecording {@code true} when for recording, {@code false} otherwise
-     * @return {@link TsDataSourceManager}
+     * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory}
+     * generated class.
      */
-    public static TsDataSourceManager createSourceManager(boolean isRecording) {
-        int id;
-        synchronized (sLock) {
-            id = ++sSequenceId;
+    public static final class Factory {
+        private final TsDataSourceManagerFactory mDelegate;
+
+        @Inject
+        public Factory(Provider<TunerTsStreamerManager> tunerStreamerManagerProvider) {
+            mDelegate = new TsDataSourceManagerFactory(tunerStreamerManagerProvider);
         }
-        return new TsDataSourceManager(id, isRecording);
+
+        public TsDataSourceManager create(boolean isRecording) {
+            return mDelegate.create(isRecording);
+        }
     }
 
-    private TsDataSourceManager(int id, boolean isRecording) {
-        mId = id;
+    TsDataSourceManager(
+            boolean isRecording, @Provided TunerTsStreamerManager tunerStreamerManager) {
         mIsRecording = isRecording;
+        this.mTunerStreamerManager = tunerStreamerManager;
         mKeepTuneStatus = true;
     }
 
@@ -114,10 +123,10 @@
     }
 
     /**
-     * Indicates whether the underlying {@link TunerHal} should be kept or not when data source is
+     * Indicates whether the underlying {@link Tuner} should be kept or not when data source is
      * being released. TODO: If b/30750953 is fixed, we can remove this function.
      *
-     * @param keepTuneStatus underlying {@link TunerHal} will be reused when data source releasing.
+     * @param keepTuneStatus underlying {@link Tuner} will be reused when data source releasing.
      */
     public void setKeepTuneStatus(boolean keepTuneStatus) {
         mKeepTuneStatus = keepTuneStatus;
diff --git a/tuner/src/com/android/tv/tuner/source/TunerSourceModule.java b/tuner/src/com/android/tv/tuner/source/TunerSourceModule.java
new file mode 100644
index 0000000..12d2de1
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/source/TunerSourceModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tv.tuner.source;
+
+import com.android.tv.tuner.api.TunerFactory;
+import dagger.Module;
+import dagger.Provides;
+import javax.inject.Singleton;
+
+/** Dagger module for TV Tuners Sources. */
+@Module()
+public class TunerSourceModule {
+    @Provides
+    @Singleton
+    TunerTsStreamerManager providesTunerTsStreamerManager(TunerFactory tunerFactory) {
+        return new TunerTsStreamerManager(tunerFactory);
+    }
+}
diff --git a/tuner/src/com/android/tv/tuner/source/TunerTsStreamerManager.java b/tuner/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
index 15e8d51..076206c 100644
--- a/tuner/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
+++ b/tuner/src/com/android/tv/tuner/source/TunerTsStreamerManager.java
@@ -17,10 +17,11 @@
 package com.android.tv.tuner.source;
 
 import android.content.Context;
+import android.support.annotation.VisibleForTesting;
 import com.android.tv.common.SoftPreconditions;
 import com.android.tv.common.util.AutoCloseableUtils;
-import com.android.tv.tuner.BuiltInTunerHalFactory;
 import com.android.tv.tuner.api.Tuner;
+import com.android.tv.tuner.api.TunerFactory;
 import com.android.tv.tuner.data.TunerChannel;
 import com.android.tv.tuner.ts.EventDetector.EventListener;
 import java.util.HashMap;
@@ -28,13 +29,17 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
 
 /**
  * Manages {@link TunerTsStreamer} for playback and recording. The class hides handling of {@link
  * Tuner} from other classes. This class is used by {@link TsDataSourceManager}. Don't use this
  * class directly.
  */
-class TunerTsStreamerManager {
+@Singleton
+@VisibleForTesting
+public class TunerTsStreamerManager {
     // The lock will protect mStreamerFinder, mSourceToStreamerMap and some part of TsStreamCreator
     // to support timely {@link TunerTsStreamer} cancellation due to a new tune request from
     // the same session.
@@ -43,23 +48,14 @@
     private final Map<Integer, TsStreamerCreator> mCreators = new HashMap<>();
     private final Map<Integer, EventListener> mListeners = new HashMap<>();
     private final Map<TsDataSource, TunerTsStreamer> mSourceToStreamerMap = new HashMap<>();
-    private final TunerHalManager mTunerHalManager = new TunerHalManager();
-    private static TunerTsStreamerManager sInstance;
+    private final TunerHalManager mTunerHalManager;
 
-    /**
-     * Returns the singleton instance for the class
-     *
-     * @return TunerTsStreamerManager
-     */
-    static synchronized TunerTsStreamerManager getInstance() {
-        if (sInstance == null) {
-            sInstance = new TunerTsStreamerManager();
-        }
-        return sInstance;
+    @Inject
+    @VisibleForTesting
+    public TunerTsStreamerManager(TunerFactory tunerFactory) {
+        mTunerHalManager = new TunerHalManager(tunerFactory);
     }
 
-    private TunerTsStreamerManager() {}
-
     synchronized TsDataSource createDataSource(
             Context context,
             TunerChannel channel,
@@ -253,6 +249,11 @@
      */
     private static class TunerHalManager {
         private final Map<Integer, Tuner> mTunerHals = new HashMap<>();
+        private final TunerFactory mTunerFactory;
+
+        private TunerHalManager(TunerFactory mTunerFactory) {
+            this.mTunerFactory = mTunerFactory;
+        }
 
         private Tuner getOrCreateTunerHal(Context context, int sessionId) {
             // Handles session affinity.
@@ -269,7 +270,7 @@
                 mTunerHals.remove(key);
                 return hal;
             }
-            return BuiltInTunerHalFactory.INSTANCE.createInstance(context);
+            return mTunerFactory.createInstance(context);
         }
 
         private void releaseTunerHal(Tuner hal, int sessionId, boolean reuse) {
diff --git a/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
index 7967657..d22b639 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
@@ -23,14 +23,16 @@
 import android.media.tv.TvInputService;
 import android.util.Log;
 import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.common.flags.has.HasConcurrentDvrPlaybackFlags;
+import com.android.tv.tuner.source.TsDataSourceManager;
 import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
-import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.HasTunerSessionFactory;
+import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
+import dagger.android.AndroidInjection;
 import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
 import java.util.Collections;
 import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.concurrent.TimeUnit;
+import javax.inject.Inject;
 
 /** {@link BaseTunerTvInputService} serves TV channels coming from a tuner device. */
 public class BaseTunerTvInputService extends TvInputService {
@@ -41,7 +43,9 @@
 
     private final Set<Session> mTunerSessions = Collections.newSetFromMap(new WeakHashMap<>());
     private ChannelDataManager mChannelDataManager;
-    private ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags;
+    @Inject ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags;
+    @Inject TsDataSourceManager.Factory mTsDataSourceManagerFactory;
+    @Inject TunerSessionFactory mTunerSessionFactory;
 
     @Override
     public void onCreate() {
@@ -50,11 +54,10 @@
             this.stopSelf();
             return;
         }
+        AndroidInjection.inject(this);
         super.onCreate();
         if (DEBUG) Log.d(TAG, "onCreate");
         mChannelDataManager = new ChannelDataManager(getApplicationContext());
-        mConcurrentDvrPlaybackFlags =
-                HasConcurrentDvrPlaybackFlags.fromContext(getApplicationContext());
         if (CommonFeatures.DVR.isEnabled(this)) {
             JobScheduler jobScheduler =
                     (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
@@ -84,7 +87,11 @@
     @Override
     public RecordingSession onCreateRecordingSession(String inputId) {
         return new TunerRecordingSession(
-                this, inputId, mChannelDataManager, mConcurrentDvrPlaybackFlags);
+                this,
+                inputId,
+                mChannelDataManager,
+                mConcurrentDvrPlaybackFlags,
+                mTsDataSourceManagerFactory);
     }
 
     @Override
@@ -97,9 +104,7 @@
                 return null;
             }
             final Session session =
-                    HasTunerSessionFactory.cast(this)
-                            .getTunerSessionFactory()
-                            .create(this, mChannelDataManager, this::onReleased);
+                    mTunerSessionFactory.create(this, mChannelDataManager, this::onReleased);
             mTunerSessions.add(session);
             session.setOverlayViewEnabled(true);
             return session;
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java
index 4bb705a..5561693 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java
@@ -23,6 +23,7 @@
 import android.support.annotation.WorkerThread;
 import android.util.Log;
 import com.android.tv.common.compat.RecordingSessionCompat;
+import com.android.tv.tuner.source.TsDataSourceManager;
 import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
 import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
 
@@ -37,11 +38,17 @@
             Context context,
             String inputId,
             ChannelDataManager channelDataManager,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         super(context);
         mSessionWorker =
                 new TunerRecordingSessionWorker(
-                        context, inputId, channelDataManager, this, concurrentDvrPlaybackFlags);
+                        context,
+                        inputId,
+                        channelDataManager,
+                        this,
+                        concurrentDvrPlaybackFlags,
+                        tsDataSourceManagerFactory);
     }
 
     // RecordingSession
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java
index 0ef9250..2c0c09a 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java
@@ -174,7 +174,8 @@
             String inputId,
             ChannelDataManager dataManager,
             TunerRecordingSession session,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags;
         mRandom.setSeed(System.nanoTime());
         mContext = context;
@@ -185,7 +186,7 @@
                 BaseApplication.getSingletons(context).getRecordingStorageStatusManager();
         mChannelDataManager = dataManager;
         mChannelDataManager.checkDataVersion(context);
-        mSourceManager = TsDataSourceManager.createSourceManager(true);
+        mSourceManager = tsDataSourceManagerFactory.create(true);
         mCapabilities = new DvbDeviceAccessor(context).getRecordingCapability(inputId);
         mInputId = inputId;
         if (DEBUG) Log.d(TAG, mCapabilities.toString());
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java
index 8292341..fedb5f6 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java
@@ -30,6 +30,7 @@
 import com.android.tv.common.CommonPreferences.CommonPreferencesChangedListener;
 import com.android.tv.common.compat.TisSessionCompat;
 import com.android.tv.tuner.prefs.TunerPreferences;
+import com.android.tv.tuner.source.TsDataSourceManager;
 import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.SessionReleasedCallback;
 import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
@@ -53,7 +54,8 @@
             Context context,
             ChannelDataManager channelDataManager,
             SessionReleasedCallback releasedCallback,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         super(context);
         mReleasedCallback = releasedCallback;
         mTunerSessionOverlay = new TunerSessionOverlay(context);
@@ -63,7 +65,8 @@
                         channelDataManager,
                         this,
                         mTunerSessionOverlay,
-                        concurrentDvrPlaybackFlags);
+                        concurrentDvrPlaybackFlags,
+                        tsDataSourceManagerFactory);
         TunerPreferences.setCommonPreferencesChangedListener(this);
     }
 
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java
index 2c5995b..4eca44d 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java
@@ -30,6 +30,7 @@
 import com.android.tv.common.CommonPreferences.CommonPreferencesChangedListener;
 import com.android.tv.common.compat.TisSessionCompat;
 import com.android.tv.tuner.prefs.TunerPreferences;
+import com.android.tv.tuner.source.TsDataSourceManager;
 import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
 import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.SessionReleasedCallback;
 import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
@@ -51,7 +52,8 @@
             Context context,
             ChannelDataManager channelDataManager,
             SessionReleasedCallback releasedCallback,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         super(context);
         mReleasedCallback = releasedCallback;
         mTunerSessionOverlay = new TunerSessionOverlay(context);
@@ -61,7 +63,8 @@
                         channelDataManager,
                         this,
                         mTunerSessionOverlay,
-                        concurrentDvrPlaybackFlags);
+                        concurrentDvrPlaybackFlags,
+                        tsDataSourceManagerFactory);
         TunerPreferences.setCommonPreferencesChangedListener(this);
     }
 
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java
index 770e243..d3f9409 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java
@@ -241,14 +241,16 @@
             ChannelDataManager channelDataManager,
             TunerSession tunerSession,
             TunerSessionOverlay tunerSessionOverlay,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         this(
                 context,
                 channelDataManager,
                 tunerSession,
                 tunerSessionOverlay,
                 null,
-                concurrentDvrPlaybackFlags);
+                concurrentDvrPlaybackFlags,
+                tsDataSourceManagerFactory);
     }
 
     @VisibleForTesting
@@ -258,7 +260,8 @@
             TunerSession tunerSession,
             TunerSessionOverlay tunerSessionOverlay,
             @Nullable Handler handler,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         this.mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags;
         if (DEBUG) Log.d(TAG, "TunerSessionWorker created");
         mContext = context;
@@ -276,7 +279,7 @@
         mChannelDataManager = channelDataManager;
         mChannelDataManager.setListener(this);
         mChannelDataManager.checkDataVersion(mContext);
-        mSourceManager = TsDataSourceManager.createSourceManager(false);
+        mSourceManager = tsDataSourceManagerFactory.create(false);
         mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
         mTvTracks = new ArrayList<>();
         mAudioCapabilitiesReceiver =
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java
index 2f07952..82afff1 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java
@@ -240,14 +240,16 @@
             ChannelDataManager channelDataManager,
             TunerSessionExoV2 tunerSession,
             TunerSessionOverlay tunerSessionOverlay,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         this(
                 context,
                 channelDataManager,
                 tunerSession,
                 tunerSessionOverlay,
                 null,
-                concurrentDvrPlaybackFlags);
+                concurrentDvrPlaybackFlags,
+                tsDataSourceManagerFactory);
     }
 
     @VisibleForTesting
@@ -257,7 +259,8 @@
             TunerSessionExoV2 tunerSession,
             TunerSessionOverlay tunerSessionOverlay,
             @Nullable Handler handler,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags;
         if (DEBUG) {
             Log.d(TAG, "TunerSessionWorkerExoV2 created");
@@ -277,7 +280,7 @@
         mChannelDataManager = channelDataManager;
         mChannelDataManager.setListener(this);
         mChannelDataManager.checkDataVersion(mContext);
-        mSourceManager = TsDataSourceManager.createSourceManager(false);
+        mSourceManager = tsDataSourceManagerFactory.create(false);
         mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
         mTvTracks = new ArrayList<>();
         mAudioCapabilitiesReceiver =
diff --git a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java
index 9ab6872..a27cb22 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java
@@ -7,17 +7,6 @@
 /** {@link android.media.tv.TvInputService.Session} factory */
 public interface TunerSessionFactory {
 
-    /** Has a {@link TunerSessionFactory}. */
-    interface HasTunerSessionFactory {
-        static HasTunerSessionFactory cast(Context context) {
-            Context appContext = context.getApplicationContext();
-            context = appContext != null ? appContext : context;
-            return (HasTunerSessionFactory) context;
-        }
-
-        TunerSessionFactory getTunerSessionFactory();
-    }
-
     /** Called when a session is released */
     interface SessionReleasedCallback {
 
diff --git a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactoryImpl.java b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactoryImpl.java
index 1864ad6..6617995 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactoryImpl.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactoryImpl.java
@@ -2,22 +2,29 @@
 
 import android.content.Context;
 import android.media.tv.TvInputService.Session;
+import com.android.tv.tuner.source.TsDataSourceManager;
 import com.android.tv.tuner.tvinput.TunerSession;
 import com.android.tv.tuner.tvinput.TunerSessionExoV2;
 import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
 import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags;
 import com.android.tv.common.flags.Exoplayer2Flags;
+import javax.inject.Inject;
 
 /** Creates a {@link TunerSessionFactory}. */
 public class TunerSessionFactoryImpl implements TunerSessionFactory {
+
     private final Exoplayer2Flags mExoplayer2Flags;
     private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags;
+    private final TsDataSourceManager.Factory mTsDataSourceManagerFactory;
 
+    @Inject
     public TunerSessionFactoryImpl(
             Exoplayer2Flags exoplayer2Flags,
-            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) {
+            ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags,
+            TsDataSourceManager.Factory tsDataSourceManagerFactory) {
         mExoplayer2Flags = exoplayer2Flags;
         mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags;
+        mTsDataSourceManagerFactory = tsDataSourceManagerFactory;
     }
 
     @Override
@@ -27,8 +34,16 @@
             SessionReleasedCallback releasedCallback) {
         return mExoplayer2Flags.enabled()
                 ? new TunerSessionExoV2(
-                        context, channelDataManager, releasedCallback, mConcurrentDvrPlaybackFlags)
+                        context,
+                        channelDataManager,
+                        releasedCallback,
+                        mConcurrentDvrPlaybackFlags,
+                        mTsDataSourceManagerFactory)
                 : new TunerSession(
-                        context, channelDataManager, releasedCallback, mConcurrentDvrPlaybackFlags);
+                        context,
+                        channelDataManager,
+                        releasedCallback,
+                        mConcurrentDvrPlaybackFlags,
+                        mTsDataSourceManagerFactory);
     }
 }