aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:18:56 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:18:56 +0000
commitcbe9756b0ba9ede9474dcf18c82b588af280db8a (patch)
tree5e6e393b2123114d919c401c5df2e78adb9ab92f
parent613abdf0b82bf86ea2a05f2ace1cc342a0334030 (diff)
parent92f369e880de8a5c063869e790547911a767a84b (diff)
downloadTV-android13-mainline-conscrypt-release.tar.gz
Snap for 8564071 from 92f369e880de8a5c063869e790547911a767a84b to mainline-conscrypt-releaseaml_con_331413000aml_con_331411000aml_con_331312000aml_con_331115000aml_con_331011010android13-mainline-conscrypt-release
Change-Id: I36eade25efd3bd046053a9095e515096556a507a
-rw-r--r--Android.bp17
-rw-r--r--AndroidManifest.xml412
-rw-r--r--OWNERS1
-rw-r--r--com.android.tv.xml3
-rw-r--r--common/Android.bp4
-rw-r--r--common/lint-baseline.xml15
-rw-r--r--common/src/com/android/tv/common/CommonPreferences.java3
-rwxr-xr-xcommon/src/com/android/tv/common/flags/SetupFlags.java6
-rw-r--r--common/src/com/android/tv/common/flags/impl/DefaultSetupFlags.java4
-rw-r--r--common/tests/robotests/Android.mk4
-rw-r--r--jni/Android.bp5
-rw-r--r--jni/minijail/Android.bp32
-rw-r--r--jni/minijail/Android.mk29
-rw-r--r--libs/Android.bp4
-rw-r--r--lint-baseline.xml422
-rw-r--r--partner_support/Android.bp4
-rw-r--r--partner_support/sample_customization/Android.bp16
-rw-r--r--partner_support/sample_customization/Android.mk18
-rw-r--r--partner_support/samples/Android.bp4
-rw-r--r--partner_support/samples/AndroidManifest.xml45
-rw-r--r--ratings/Android.bp4
-rw-r--r--res/layout/menu_card_channel.xml6
-rw-r--r--src/com/android/tv/MainActivity.java45
-rw-r--r--src/com/android/tv/SetupPassthroughActivity.java13
-rw-r--r--src/com/android/tv/TimeShiftManager.java86
-rw-r--r--src/com/android/tv/data/ProgramDataManager.java18
-rw-r--r--src/com/android/tv/data/ProgramImpl.java4
-rw-r--r--src/com/android/tv/dvr/data/RecordedProgram.java2
-rw-r--r--src/com/android/tv/features/TvFeatures.java4
-rw-r--r--src/com/android/tv/guide/ProgramManager.java7
-rw-r--r--src/com/android/tv/menu/MenuRowView.java3
-rw-r--r--src/com/android/tv/menu/MenuView.java7
-rw-r--r--src/com/android/tv/parental/ContentRatingSystem.java2
-rw-r--r--src/com/android/tv/recommendation/NotificationService.java3
-rw-r--r--src/com/android/tv/util/GtvUtils.java56
-rw-r--r--src/com/android/tv/util/TvInputManagerHelper.java18
-rw-r--r--tests/common/Android.bp4
-rw-r--r--tests/common/Android.mk6
-rw-r--r--tests/func/Android.bp21
-rw-r--r--tests/func/Android.mk24
-rw-r--r--tests/input/Android.bp46
-rw-r--r--tests/input/Android.mk26
-rw-r--r--tests/input/AndroidManifest.xml58
-rw-r--r--tests/jank/Android.bp22
-rw-r--r--tests/jank/Android.mk25
-rw-r--r--tests/robotests/Android.mk4
-rw-r--r--tests/robotests/src/com/android/tv/SetupPassthroughActivityTest.java57
-rw-r--r--tests/unit/Android.mk3
-rw-r--r--tuner/Android.bp4
-rw-r--r--tuner/SampleDvbTuner/Android.bp4
-rwxr-xr-xtuner/SampleDvbTuner/AndroidManifest.xml99
-rw-r--r--tuner/SampleNetworkTuner/Android.bp4
-rwxr-xr-xtuner/SampleNetworkTuner/AndroidManifest.xml101
-rw-r--r--tuner/lint-baseline.xml172
-rw-r--r--tuner/proto/Android.bp4
-rw-r--r--tuner/sampletunertvinput/Android.bp14
-rw-r--r--tuner/sampletunertvinput/AndroidManifest.xml6
-rw-r--r--tuner/sampletunertvinput/com.android.tv.samples.sampletunertvinput.xml8
-rw-r--r--tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml6
-rw-r--r--tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java412
-rw-r--r--tuner/src/com/android/tv/tuner/data/Cea708Parser.java2
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java2
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerExtractorsFactory.java41
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerSampleExtractor.java225
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/FileSampleExtractor.java189
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaPeriod.java260
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaSource.java72
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/MpegTsPlayerV2.java101
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/MpegTsSampleExtractor.java156
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/SampleExtractor.java62
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer2/buffer/SampleChunkIoHelper.java5
-rw-r--r--tuner/src/com/android/tv/tuner/modules/TunerModule.java49
-rw-r--r--tuner/src/com/android/tv/tuner/setup/ScanFragment.java2
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java15
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java2
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorkerExoV2.java29
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java222
-rw-r--r--tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java4
-rw-r--r--tuner/tests/robotests/Android.mk4
-rw-r--r--tuner/tests/robotests/javatests/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2Test.java50
-rw-r--r--tuner/tests/testing/Android.mk2
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTestExoV2.java181
82 files changed, 2876 insertions, 1255 deletions
diff --git a/Android.bp b/Android.bp
index 79ae05c2..951d6b0e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,23 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["packages_apps_TV_license"],
+}
+
+// See: http://go/android-license-faq
+license {
+ name: "packages_apps_TV_license",
+ package_name: "Android Live TV App",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ "SPDX-license-identifier-MIT",
+ ],
+ license_text: ["res/raw/third_party_licenses"],
+}
+
version_name = "1.24-asop"
version_code = "417000452"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0cbc55da..75e2c4d5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,296 +15,270 @@
~ limitations under the License.
-->
<!-- This manifest is for LiveTv -->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.tv" >
-
- <uses-sdk
- android:minSdkVersion="23"
- android:targetSdkVersion="29" />
-
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE" />
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
- <uses-permission android:name="android.permission.HDMI_CEC" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS" />
- <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="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
- <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
- <uses-permission android:name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA" />
- <uses-permission android:name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS" />
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.tv">
+
+ <uses-sdk android:minSdkVersion="23"
+ android:targetSdkVersion="29"/>
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.HDMI_CEC"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS"/>
+ <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="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA"/>
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/>
+ <uses-permission android:name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"/>
+ <uses-permission android:name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"/>
<!-- Permissions/feature for USB tuner -->
- <uses-permission android:name="android.permission.DVB_DEVICE" />
+ <uses-permission android:name="android.permission.DVB_DEVICE"/>
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
- <uses-feature
- android:name="android.hardware.usb.host"
- android:required="false" />
+ <uses-feature android:name="android.hardware.usb.host"
+ android:required="false"/>
<!-- Limit only for Android TV -->
- <uses-feature
- android:name="android.software.leanback"
- android:required="true" />
- <uses-feature
- android:name="android.software.live_tv"
- android:required="true" />
- <uses-feature
- android:name="android.hardware.touchscreen"
- android:required="false" />
+ <uses-feature android:name="android.software.leanback"
+ android:required="true"/>
+ <uses-feature android:name="android.software.live_tv"
+ android:required="true"/>
+ <uses-feature android:name="android.hardware.touchscreen"
+ android:required="false"/>
<!-- Receives input events from the TV app. -->
- <permission
- android:name="com.android.tv.permission.RECEIVE_INPUT_EVENT"
- android:description="@string/permdesc_receiveInputEvent"
- android:label="@string/permlab_receiveInputEvent"
- android:protectionLevel="signatureOrSystem" />
+ <permission android:name="com.android.tv.permission.RECEIVE_INPUT_EVENT"
+ android:description="@string/permdesc_receiveInputEvent"
+ android:label="@string/permlab_receiveInputEvent"
+ android:protectionLevel="signatureOrSystem"/>
<!-- Customizes Live TV with customization packages. -->
- <permission
- android:name="com.android.tv.permission.CUSTOMIZE_TV_APP"
- android:description="@string/permdesc_customizeTvApp"
- android:label="@string/permlab_customizeTvApp"
- android:protectionLevel="signatureOrSystem" />
-
- <application
- android:name="com.android.tv.app.LiveTvApplication"
- android:allowBackup="true"
- android:appComponentFactory="android.support.v4.app.CoreComponentFactory"
- android:banner="@drawable/live_tv_banner"
- android:icon="@drawable/ic_tv_app"
- android:label="@string/app_name"
- android:supportsRtl="true"
- android:theme="@style/Theme.TV"
- tools:replace="android:appComponentFactory" >
+ <permission android:name="com.android.tv.permission.CUSTOMIZE_TV_APP"
+ android:description="@string/permdesc_customizeTvApp"
+ android:label="@string/permlab_customizeTvApp"
+ android:protectionLevel="signatureOrSystem"/>
+
+ <application android:name="com.android.tv.app.LiveTvApplication"
+ android:allowBackup="true"
+ android:appComponentFactory="android.support.v4.app.CoreComponentFactory"
+ android:banner="@drawable/live_tv_banner"
+ android:icon="@drawable/ic_tv_app"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.TV"
+ tools:replace="android:appComponentFactory">
<!-- providers are listed here to keep them separate from the internal versions -->
- <provider
- android:name="com.android.tv.search.LocalSearchProvider"
- android:authorities="com.android.tv.search"
- android:enabled="true"
- android:exported="true" >
- <meta-data
- android:name="SupportedSwitchActionType"
- android:value="CHANNEL|TVINPUT" />
+ <provider android:name="com.android.tv.search.LocalSearchProvider"
+ android:authorities="com.android.tv.search"
+ android:enabled="true"
+ android:exported="true">
+ <meta-data android:name="SupportedSwitchActionType"
+ android:value="CHANNEL|TVINPUT"/>
</provider>
- <provider
- android:name="com.android.tv.common.CommonPreferenceProvider"
- android:authorities="com.android.tv.common.preferences"
- android:exported="false"
- android:process="com.android.tv.common" />
+ <provider android:name="com.android.tv.common.CommonPreferenceProvider"
+ android:authorities="com.android.tv.common.preferences"
+ android:exported="false"
+ android:process="com.android.tv.common"/>
- <receiver
- android:name="com.android.tv.livetv.receiver.GlobalKeyReceiver"
- android:exported="true" >
+ <receiver android:name="com.android.tv.livetv.receiver.GlobalKeyReceiver"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.GLOBAL_BUTTON" />
+ <action android:name="android.intent.action.GLOBAL_BUTTON"/>
</intent-filter>
<!--
- Not directly related to GlobalKeyReceiver but needed to be able to provide our
- content rating definitions to the system service.
- -->
+ Not directly related to GlobalKeyReceiver but needed to be able to provide our
+ content rating definitions to the system service.
+ -->
<intent-filter>
- <action android:name="android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS" />
+ <action android:name="android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS"/>
</intent-filter>
- <meta-data
- android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
- android:resource="@xml/tv_content_rating_systems" />
+ <meta-data android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
+ android:resource="@xml/tv_content_rating_systems"/>
</receiver>
- <activity
- android:name="com.android.tv.TvActivity"
- android:exported="true"
- android:launchMode="singleTask" >
+ <activity android:name="com.android.tv.TvActivity"
+ android:exported="true"
+ android:launchMode="singleTask">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity
- android:name="com.android.tv.MainActivity"
- android:configChanges="keyboard|keyboardHidden|screenSize|smallestScreenSize|screenLayout|orientation"
- android:launchMode="singleTask"
- android:resizeableActivity="true"
- android:screenOrientation="landscape"
- android:supportsPictureInPicture="true"
- android:theme="@style/Theme.TV.MainActivity" >
+ <activity android:name="com.android.tv.MainActivity"
+ android:configChanges="keyboard|keyboardHidden|screenSize|smallestScreenSize|screenLayout|orientation"
+ android:launchMode="singleTask"
+ android:resizeableActivity="true"
+ android:screenOrientation="landscape"
+ android:supportsPictureInPicture="true"
+ android:theme="@style/Theme.TV.MainActivity"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.VIEW"/>
- <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="vnd.android.cursor.item/channel" />
- <data android:mimeType="vnd.android.cursor.dir/channel" />
- <data android:mimeType="vnd.android.cursor.item/program" />
- <data android:mimeType="vnd.android.cursor.dir/program" />
+ <data android:mimeType="vnd.android.cursor.item/channel"/>
+ <data android:mimeType="vnd.android.cursor.dir/channel"/>
+ <data android:mimeType="vnd.android.cursor.item/program"/>
+ <data android:mimeType="vnd.android.cursor.dir/program"/>
</intent-filter>
<intent-filter>
- <action android:name="android.media.tv.action.SETUP_INPUTS" />
+ <action android:name="android.media.tv.action.SETUP_INPUTS"/>
- <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.SEARCH" />
+ <action android:name="android.intent.action.SEARCH"/>
</intent-filter>
- <meta-data
- android:name="supports_leanback"
- android:value="true" />
- <meta-data
- android:name="android.app.searchable"
- android:resource="@xml/searchable" />
+ <meta-data android:name="supports_leanback"
+ android:value="true"/>
+ <meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable"/>
</activity>
- <activity
- android:name="com.android.tv.LauncherActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:theme="@android:style/Theme.Translucent.NoTitleBar" />
- <activity
- android:name="com.android.tv.SetupPassthroughActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:exported="true"
- android:theme="@android:style/Theme.Translucent.NoTitleBar" >
+ <activity android:name="com.android.tv.LauncherActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
+ <activity android:name="com.android.tv.SetupPassthroughActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="true"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
- <action android:name="com.android.tv.action.LAUNCH_INPUT_SETUP" />
+ <action android:name="com.android.tv.action.LAUNCH_INPUT_SETUP"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.tv.SelectInputActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:launchMode="singleTask"
+ android:theme="@style/Theme.SelectInputActivity">
+ <intent-filter>
+ <action android:name="com.android.tv.action.VIEW_INPUTS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity
- android:name="com.android.tv.SelectInputActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:launchMode="singleTask"
- android:theme="@style/Theme.SelectInputActivity" />
- <activity
- android:name="com.android.tv.onboarding.OnboardingActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:launchMode="singleTop"
- android:theme="@style/Theme.Setup.GuidedStep" />
- <activity
- android:name="com.android.tv.dvr.ui.browse.DvrBrowseActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:launchMode="singleTask"
- android:theme="@style/Theme.Leanback.Browse" >
+ <activity android:name="com.android.tv.onboarding.OnboardingActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:launchMode="singleTop"
+ android:theme="@style/Theme.Setup.GuidedStep"/>
+ <activity android:name="com.android.tv.dvr.ui.browse.DvrBrowseActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:launchMode="singleTask"
+ android:theme="@style/Theme.Leanback.Browse"
+ android:exported="true">
<intent-filter>
- <action android:name="android.media.tv.action.VIEW_RECORDING_SCHEDULES" />
+ <action android:name="android.media.tv.action.VIEW_RECORDING_SCHEDULES"/>
- <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.VIEW"/>
- <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="vnd.android.cursor.dir/recorded_program" />
+ <data android:mimeType="vnd.android.cursor.dir/recorded_program"/>
</intent-filter>
</activity>
- <activity
- android:name="com.android.tv.dvr.ui.playback.DvrPlaybackActivity"
- android:configChanges="keyboard|keyboardHidden|screenSize|smallestScreenSize|screenLayout|orientation"
- android:launchMode="singleTask"
- android:theme="@style/Theme.Leanback" >
+ <activity android:name="com.android.tv.dvr.ui.playback.DvrPlaybackActivity"
+ android:configChanges="keyboard|keyboardHidden|screenSize|smallestScreenSize|screenLayout|orientation"
+ android:launchMode="singleTask"
+ android:theme="@style/Theme.Leanback"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.VIEW"/>
- <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT"/>
- <data android:mimeType="vnd.android.cursor.item/recorded_program" />
+ <data android:mimeType="vnd.android.cursor.item/recorded_program"/>
</intent-filter>
</activity>
- <activity
- android:name="com.android.tv.ui.DetailsActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:exported="true"
- android:theme="@style/Theme.TV.Dvr.Browse.Details" />
- <activity
- android:name="com.android.tv.dvr.ui.DvrRecordingSettingsActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:exported="false"
- android:theme="@style/Theme.TV.Dvr.Series.Settings.GuidedStep" />
- <activity
- android:name="com.android.tv.dvr.ui.DvrSeriesSettingsActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:theme="@style/Theme.TV.Dvr.Series.Settings.GuidedStep" />
- <activity
- android:name="com.android.tv.dvr.ui.DvrSeriesDeletionActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:theme="@style/Theme.TV.Dvr.Series.Deletion.GuidedStep" />
- <activity
- android:name="com.android.tv.dvr.ui.DvrSeriesScheduledDialogActivity"
- android:theme="@style/Theme.TV.dialog.HalfSizedDialog" />
- <activity
- android:name="com.android.tv.dvr.ui.list.DvrSchedulesActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:theme="@style/Theme.Leanback.Details" />
- <activity
- android:name="com.android.tv.dvr.ui.list.DvrHistoryActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:exported="false"
- android:theme="@style/Theme.Leanback.Details" />
-
- <service
- android:name="com.android.tv.recommendation.NotificationService"
- android:exported="false" />
- <service
- android:name="com.android.tv.recommendation.ChannelPreviewUpdater$ChannelPreviewUpdateService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
-
- <receiver
- android:name="com.android.tv.receiver.BootCompletedReceiver"
- android:exported="true" >
+ <activity android:name="com.android.tv.ui.DetailsActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="true"
+ android:theme="@style/Theme.TV.Dvr.Browse.Details"/>
+ <activity android:name="com.android.tv.dvr.ui.DvrRecordingSettingsActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="false"
+ android:theme="@style/Theme.TV.Dvr.Series.Settings.GuidedStep"/>
+ <activity android:name="com.android.tv.dvr.ui.DvrSeriesSettingsActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:theme="@style/Theme.TV.Dvr.Series.Settings.GuidedStep"/>
+ <activity android:name="com.android.tv.dvr.ui.DvrSeriesDeletionActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:theme="@style/Theme.TV.Dvr.Series.Deletion.GuidedStep"/>
+ <activity android:name="com.android.tv.dvr.ui.DvrSeriesScheduledDialogActivity"
+ android:theme="@style/Theme.TV.dialog.HalfSizedDialog"/>
+ <activity android:name="com.android.tv.dvr.ui.list.DvrSchedulesActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:theme="@style/Theme.Leanback.Details"/>
+ <activity android:name="com.android.tv.dvr.ui.list.DvrHistoryActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="false"
+ android:theme="@style/Theme.Leanback.Details"/>
+
+ <service android:name="com.android.tv.recommendation.NotificationService"
+ android:exported="false"/>
+ <service android:name="com.android.tv.recommendation.ChannelPreviewUpdater$ChannelPreviewUpdateService"
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
+
+ <receiver android:name="com.android.tv.receiver.BootCompletedReceiver"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
- <receiver
- android:name="com.android.tv.receiver.PackageIntentsReceiver"
- android:exported="true" >
+ <receiver android:name="com.android.tv.receiver.PackageIntentsReceiver"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.PACKAGE_ADDED" />
+ <action android:name="android.intent.action.PACKAGE_ADDED"/>
<!-- PACKAGE_CHANGED for package enabled/disabled notification -->
- <action android:name="android.intent.action.PACKAGE_CHANGED" />
- <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
- <action android:name="android.intent.action.PACKAGE_REMOVED" />
+ <action android:name="android.intent.action.PACKAGE_CHANGED"/>
+ <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
+ <action android:name="android.intent.action.PACKAGE_REMOVED"/>
- <data android:scheme="package" />
+ <data android:scheme="package"/>
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED" />
+ <action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver> <!-- System initial setup component definition -->
- <activity
- android:name="com.android.tv.setup.SystemSetupActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:exported="true"
- android:label="@string/app_name"
- android:launchMode="singleInstance"
- android:theme="@style/Theme.Setup.GuidedStep" >
+ <activity android:name="com.android.tv.setup.SystemSetupActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="true"
+ android:label="@string/app_name"
+ android:launchMode="singleInstance"
+ android:theme="@style/Theme.Setup.GuidedStep">
<intent-filter>
- <action android:name="com.android.tv.action.LAUNCH_SYSTEM_SETUP" />
+ <action android:name="com.android.tv.action.LAUNCH_SYSTEM_SETUP"/>
- <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity> <!-- DVR -->
- <service
- android:name="com.android.tv.dvr.recorder.DvrRecordingService"
- android:label="@string/dvr_service_name" />
+ <service android:name="com.android.tv.dvr.recorder.DvrRecordingService"
+ android:label="@string/dvr_service_name"/>
- <receiver
- android:name="com.android.tv.dvr.recorder.DvrStartRecordingReceiver"
- android:exported="false" />
+ <receiver android:name="com.android.tv.dvr.recorder.DvrStartRecordingReceiver"
+ android:exported="false"/>
- <service
- android:name="com.android.tv.data.epg.EpgFetchService"
- android:permission="android.permission.BIND_JOB_SERVICE" />
+ <service android:name="com.android.tv.data.epg.EpgFetchService"
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
</application>
-</manifest> \ No newline at end of file
+</manifest>
diff --git a/OWNERS b/OWNERS
index 4aa5fe52..e904f5c6 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,2 +1,3 @@
nchalko@google.com
shubang@google.com
+quxiangfang@google.com
diff --git a/com.android.tv.xml b/com.android.tv.xml
index 245d275d..5ac6c0c3 100644
--- a/com.android.tv.xml
+++ b/com.android.tv.xml
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<permissions>
+ <feature name="com.google.android.tv.installed" />
+
<privapp-permissions package="com.android.tv">
<permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
<permission name="android.permission.DVB_DEVICE"/>
@@ -7,6 +9,7 @@
<permission name="android.permission.HDMI_CEC"/>
<permission name="android.permission.MODIFY_PARENTAL_CONTROLS"/>
<permission name="android.permission.READ_CONTENT_RATING_SYSTEMS"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"/>
<permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"/>
</privapp-permissions>
diff --git a/common/Android.bp b/common/Android.bp
index 728587f6..f6ba940c 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_library {
name: "tv-common",
srcs: [
diff --git a/common/lint-baseline.xml b/common/lint-baseline.xml
new file mode 100644
index 00000000..b6f9dfcb
--- /dev/null
+++ b/common/lint-baseline.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 26 (current min is 23): `android.os.StrictMode.VmPolicy.Builder#detectContentUriWithoutPermission`"
+ errorLine1=" .detectContentUriWithoutPermission()"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/common/src/com/android/tv/common/BaseApplication.java"
+ line="84"
+ column="30"/>
+ </issue>
+
+</issues>
diff --git a/common/src/com/android/tv/common/CommonPreferences.java b/common/src/com/android/tv/common/CommonPreferences.java
index 5a94eecb..72acfd1d 100644
--- a/common/src/com/android/tv/common/CommonPreferences.java
+++ b/common/src/com/android/tv/common/CommonPreferences.java
@@ -164,7 +164,8 @@ public class CommonPreferences {
}
}
- public static synchronized @TrickplaySetting int getTrickplaySetting(Context context) {
+ @TrickplaySetting
+ public static synchronized int getTrickplaySetting(Context context) {
SoftPreconditions.checkState(sInitialized);
if (useContentProvider(context)) {
return sPreferenceValues.getInt(PREFS_KEY_TRICKPLAY_SETTING, TRICKPLAY_SETTING_NOT_SET);
diff --git a/common/src/com/android/tv/common/flags/SetupFlags.java b/common/src/com/android/tv/common/flags/SetupFlags.java
index 0a7f2002..e5901e49 100755
--- a/common/src/com/android/tv/common/flags/SetupFlags.java
+++ b/common/src/com/android/tv/common/flags/SetupFlags.java
@@ -29,8 +29,8 @@ public interface SetupFlags {
/** Packages allowed to send intents to SetupPassthroughActivity. */
com.android.tv.common.flags.proto.TypedFeatures.StringListParam
- setupPassThroughPackageWhitelist();
+ setupPassThroughPackageAllowlist();
- /** Use a whitelist for packages allowed to start SetupPassthroughActivity */
- boolean useWhitelistForSetupPassThrough();
+ /** Use a allowlist for packages allowed to start SetupPassthroughActivity */
+ boolean useAllowlistForSetupPassThrough();
}
diff --git a/common/src/com/android/tv/common/flags/impl/DefaultSetupFlags.java b/common/src/com/android/tv/common/flags/impl/DefaultSetupFlags.java
index 3abe6627..a778068f 100644
--- a/common/src/com/android/tv/common/flags/impl/DefaultSetupFlags.java
+++ b/common/src/com/android/tv/common/flags/impl/DefaultSetupFlags.java
@@ -27,12 +27,12 @@ public class DefaultSetupFlags implements SetupFlags {
}
@Override
- public StringListParam setupPassThroughPackageWhitelist() {
+ public StringListParam setupPassThroughPackageAllowlist() {
return StringListParam.getDefaultInstance();
}
@Override
- public boolean useWhitelistForSetupPassThrough() {
+ public boolean useAllowlistForSetupPassThrough() {
return false;
}
}
diff --git a/common/tests/robotests/Android.mk b/common/tests/robotests/Android.mk
index 04528784..85512fb6 100644
--- a/common/tests/robotests/Android.mk
+++ b/common/tests/robotests/Android.mk
@@ -5,6 +5,8 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := TvCommonRoboTests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -34,6 +36,8 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
#############################################################
include $(CLEAR_VARS)
LOCAL_MODULE := RunTvCommonRoboTests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.)
diff --git a/jni/Android.bp b/jni/Android.bp
index bbf27787..596cb5d9 100644
--- a/jni/Android.bp
+++ b/jni/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_library_shared {
name: "libtunertvinput_jni",
srcs: [
@@ -26,5 +30,6 @@ cc_library_shared {
],
sdk_version: "23",
stl: "c++_static",
+ header_libs: ["jni_headers"],
shared_libs: ["liblog"],
}
diff --git a/jni/minijail/Android.bp b/jni/minijail/Android.bp
new file mode 100644
index 00000000..cb921159
--- /dev/null
+++ b/jni/minijail/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2017 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_shared {
+ name: "libminijail_jni",
+ srcs: ["minijail.cpp"],
+ stl: "none",
+ header_libs: ["jni_headers"],
+ static_libs: [
+ "libc++_static",
+ "libminijail",
+ ],
+ shared_libs: ["liblog"],
+}
diff --git a/jni/minijail/Android.mk b/jni/minijail/Android.mk
deleted file mode 100644
index 7a3ad584..00000000
--- a/jni/minijail/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2017 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 := libminijail_jni
-LOCAL_SRC_FILES := minijail.cpp
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CXX_STL := none
-LOCAL_STATIC_LIBRARIES := libc++_static libminijail
-LOCAL_LDLIBS := -llog
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/Android.bp b/libs/Android.bp
index 6c768fcb..ae5e1a9f 100644
--- a/libs/Android.bp
+++ b/libs/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
java_import {
name: "tv-auto-common-jar",
jars: ["m2/auto-common-0.10.jar"],
diff --git a/lint-baseline.xml b/lint-baseline.xml
new file mode 100644
index 00000000..d91a1894
--- /dev/null
+++ b/lint-baseline.xml
@@ -0,0 +1,422 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+ <issue
+ id="NewApi"
+ message="Field requires API level 24 (current min is 23): `android.media.tv.TvContract.RecordedPrograms#CONTENT_URI`"
+ errorLine1=" context, TvContract.RecordedPrograms.CONTENT_URI)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/AsyncDbTask.java"
+ line="137"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Field requires API level 24 (current min is 23): `android.media.tv.TvContract.RecordedPrograms#CONTENT_URI`"
+ errorLine1=" context, TvContract.RecordedPrograms.CONTENT_URI)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/AsyncDbTask.java"
+ line="143"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `updateAndStartServiceIfNeeded`"
+ errorLine1=" scheduler.updateAndStartServiceIfNeeded();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/receiver/BootCompletedReceiver.java"
+ line="90"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" if (!TvContract.isChannelUriForPassthroughInput(uri)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/data/ChannelImpl.java"
+ line="444"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#canRecord`"
+ errorLine1=" if (info.canRecord()) {"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/menu/ChannelsRowAdapter.java"
+ line="255"
+ column="26"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.session.MediaController.TransportControls#prepare`"
+ errorLine1=" getActivity().getMediaController().getTransportControls().prepare();"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/dvr/ui/playback/DvrPlaybackOverlayFragment.java"
+ line="448"
+ column="67"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvView#timeShiftPlay`"
+ errorLine1=" mTvView.timeShiftPlay(mInputId, mRecordedProgramUri);"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/dvr/DvrTvView.java"
+ line="77"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" CharSequence customLabel = input.loadCustomLabel(getContext());"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/ui/InputBannerView.java"
+ line="75"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#canRecord`"
+ errorLine1=" tunerCount = mInput.canRecord() ? mInput.getTunerCount() : 0;"
+ errorLine2=" ~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/dvr/recorder/InputTaskScheduler.java"
+ line="310"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#getTunerCount`"
+ errorLine1=" tunerCount = mInput.canRecord() ? mInput.getTunerCount() : 0;"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/dvr/recorder/InputTaskScheduler.java"
+ line="310"
+ column="54"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" TvContract.isChannelUriForPassthroughInput(getIntent().getData());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="534"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="1002"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" if ((channelUri == null || !TvContract.isChannelUriForPassthroughInput(channelUri))"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="1029"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" TvContract.isChannelUriForPassthroughInput(channelUri)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="1037"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" if (TvContract.isChannelUriForPassthroughInput(channelUri)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="1065"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" } else if (TvContract.isChannelUriForPassthroughInput(mInitChannelUri)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="1544"
+ column="35"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Method reference requires API level 24 (current min is 23): `MainActivity.super::enterPictureInPictureMode`"
+ errorLine1=" mHandler.post(MainActivity.super::enterPictureInPictureMode);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="2402"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" return TvContract.isChannelUriForPassthroughInput(uri)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/MainActivity.java"
+ line="2813"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 28 (current min is 23): `android.media.tv.TvInputManager#getBlockedRatings`"
+ errorLine1=" for (TvContentRating tvContentRating : mTvInputManager.getBlockedRatings()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/parental/ParentalControlSettings.java"
+ line="74"
+ column="68"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 28 (current min is 23): `android.media.tv.TvInputManager#getBlockedRatings`"
+ errorLine1=" mRatings = new HashSet&lt;>(mTvInputManager.getBlockedRatings());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/parental/ParentalControlSettings.java"
+ line="89"
+ column="50"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 28 (current min is 23): `android.media.tv.TvInputManager#getBlockedRatings`"
+ errorLine1=" Set&lt;TvContentRating> removed = new HashSet&lt;>(mTvInputManager.getBlockedRatings());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/parental/ParentalControlSettings.java"
+ line="93"
+ column="70"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 28 (current min is 23): `android.media.tv.TvInputManager#getBlockedRatings`"
+ errorLine1=" added.removeAll(mTvInputManager.getBlockedRatings());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/parental/ParentalControlSettings.java"
+ line="100"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" if (TvContract.isChannelUriForPassthroughInput(channelUri)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/SelectInputActivity.java"
+ line="69"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#isHidden`"
+ errorLine1=" if (!input.isHidden(getContext())) {"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/ui/SelectInputView.java"
+ line="253"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" CharSequence customLabel = input.loadCustomLabel(getContext());"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/ui/SelectInputView.java"
+ line="287"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvView#tune`"
+ errorLine1=" mTvView.tune(mInputInfo.getId(), mCurrentChannel.getUri(), params);"
+ errorLine2=" ~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/ui/TunableTvView.java"
+ line="671"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#getTunerCount`"
+ errorLine1=" input.getTunerCount(),"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/ui/TunableTvView.java"
+ line="1174"
+ column="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `createScheduler`"
+ errorLine1=" mRecordingScheduler = RecordingScheduler.createScheduler(this);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/TvApplication.java"
+ line="216"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#isHidden`"
+ errorLine1=" if (!input.isHidden(this)) {"
+ errorLine2=" ~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/TvApplication.java"
+ line="402"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" CharSequence inputCustomLabel = info.loadCustomLabel(mContext);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/TvInputManagerHelper.java"
+ line="216"
+ column="62"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" CharSequence inputCustomLabel = info.loadCustomLabel(mContext);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/TvInputManagerHelper.java"
+ line="257"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputManager.TvInputCallback#onInputUpdated`"
+ errorLine1=" callback.onInputUpdated(inputId);"
+ errorLine2=" ~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/TvInputManagerHelper.java"
+ line="265"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" CharSequence inputCustomLabel = inputInfo.loadCustomLabel(mContext);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/TvInputManagerHelper.java"
+ line="279"
+ column="63"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputManager.TvInputCallback#onTvInputInfoUpdated`"
+ errorLine1=" callback.onTvInputInfoUpdated(inputInfo);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/TvInputManagerHelper.java"
+ line="284"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" CharSequence customLabelCharSequence = info.loadCustomLabel(mContext);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/TvInputManagerHelper.java"
+ line="472"
+ column="57"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" String customLabel = canonicalizeLabel(input.loadCustomLabel(mContext));"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/search/TvProviderSearch.java"
+ line="510"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputInfo#loadCustomLabel`"
+ errorLine1=" String customLabel = canonicalizeLabel(input.loadCustomLabel(mContext));"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/search/TvProviderSearch.java"
+ line="535"
+ column="58"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvContract#isChannelUriForPassthroughInput`"
+ errorLine1=" return isChannelUriForTunerInput(uri) || TvContract.isChannelUriForPassthroughInput(uri);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/apps/TV/src/com/android/tv/util/Utils.java"
+ line="276"
+ column="61"/>
+ </issue>
+
+</issues>
diff --git a/partner_support/Android.bp b/partner_support/Android.bp
index 4775fc11..36364f72 100644
--- a/partner_support/Android.bp
+++ b/partner_support/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_library {
name: "live-channels-partner-support",
srcs: ["src/**/*.java"],
diff --git a/partner_support/sample_customization/Android.bp b/partner_support/sample_customization/Android.bp
new file mode 100644
index 00000000..f428f402
--- /dev/null
+++ b/partner_support/sample_customization/Android.bp
@@ -0,0 +1,16 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+ name: "PartnerSupportSampleCustomization",
+ optimize: {
+ enabled: false,
+ },
+ // Overlay view related functionality requires system APIs.
+ sdk_version: "system_current",
+ min_sdk_version: "23",
+ // Required for customization
+ privileged: true,
+}
diff --git a/partner_support/sample_customization/Android.mk b/partner_support/sample_customization/Android.mk
deleted file mode 100644
index 11ed13b0..00000000
--- a/partner_support/sample_customization/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := PartnerSupportSampleCustomization
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_PROGUARD_ENABLED := disabled
-# Overlay view related functionality requires system APIs.
-LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 23 # M
-
-# Required for customization
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_RESOURCE_DIR := \
- $(LOCAL_PATH)/res
-
-include $(BUILD_PACKAGE)
diff --git a/partner_support/samples/Android.bp b/partner_support/samples/Android.bp
index 9c1d2db8..4e71e061 100644
--- a/partner_support/samples/Android.bp
+++ b/partner_support/samples/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_app {
name: "PartnerSupportSampleTvInput",
diff --git a/partner_support/samples/AndroidManifest.xml b/partner_support/samples/AndroidManifest.xml
index e90e489b..08aa2317 100644
--- a/partner_support/samples/AndroidManifest.xml
+++ b/partner_support/samples/AndroidManifest.xml
@@ -16,41 +16,44 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.example.partnersupportsampletvinput">
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.example.partnersupportsampletvinput">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- TODO: READ_EPG_DATA and WRITE_EPG_DATA need to be removed, once we fully
- migrate all test environment from LMP to MNC, because the permissions
- are not required from MNC. -->
- <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
- <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
- <uses-permission android:name="com.android.tv.permission.RECEIVE_INPUT_EVENT" />
- <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="23"/>
+ migrate all test environment from LMP to MNC, because the permissions
+ are not required from MNC. -->
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA"/>
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/>
+ <uses-permission android:name="com.android.tv.permission.RECEIVE_INPUT_EVENT"/>
+ <uses-sdk android:targetSdkVersion="29"
+ android:minSdkVersion="23"/>
<!--TODO(b/68949299): remove tool hint when we have smaller dependency targets-->
<application android:label="@string/partner_support_sample_tv_input"
- tools:replace="android:label,icon,theme,appComponentFactory"
- android:icon="@mipmap/ic_launcher"
- android:theme="@android:style/Theme.Holo.Light.NoActionBar"
- android:appComponentFactory="android.support.v4.app.CoreComponentFactory" >
+ tools:replace="android:label,icon,theme,appComponentFactory"
+ android:icon="@mipmap/ic_launcher"
+ android:theme="@android:style/Theme.Holo.Light.NoActionBar"
+ android:appComponentFactory="android.support.v4.app.CoreComponentFactory">
<activity android:name=".SampleTvInputSetupActivity"
- android:theme="@style/Theme.Leanback.GuidedStep">
+ android:theme="@style/Theme.Leanback.GuidedStep"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<service android:name=".SampleTvInputService"
- android:permission="android.permission.BIND_TV_INPUT"
- android:label="@string/partner_support_sample_tv_input"
- android:process="com.example.partnersupportsampletvinput">
+ android:permission="android.permission.BIND_TV_INPUT"
+ android:label="@string/partner_support_sample_tv_input"
+ android:process="com.example.partnersupportsampletvinput"
+ android:exported="true">
<intent-filter>
- <action android:name="android.media.tv.TvInputService" />
+ <action android:name="android.media.tv.TvInputService"/>
</intent-filter>
<meta-data android:name="android.media.tv.input"
- android:resource="@xml/sampletvinputservice" />
+ android:resource="@xml/sampletvinputservice"/>
</service>
</application>
</manifest>
diff --git a/ratings/Android.bp b/ratings/Android.bp
index ced758f0..fca2aee7 100644
--- a/ratings/Android.bp
+++ b/ratings/Android.bp
@@ -12,6 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_library {
name: "tv-ratings-resources",
sdk_version: "system_current",
diff --git a/res/layout/menu_card_channel.xml b/res/layout/menu_card_channel.xml
index ed7b8273..40e73845 100644
--- a/res/layout/menu_card_channel.xml
+++ b/res/layout/menu_card_channel.xml
@@ -32,9 +32,9 @@
android:layout_gravity="top"
android:scaleType="centerCrop"/>
- <!-- The bottom margin specified in the redline is 8dp, but the redline doesn't consider
- the descenders. So actually, if the bottom margin is set to 8dp, the bottom line of
- the text lies 11dp above the bottom of the poster art due to the descenders.
+ <!-- The bottom margin specified in the UI annotation is 8dp, but the annotation doesn't
+ consider the descenders. So actually, if the bottom margin is set to 8dp, the bottom
+ line of the text lies 11dp above the bottom of the poster art due to the descenders.
Therefore the bottom margin needs to be set to 5dp(=8dp-3dp) here.
If the text size or font is changed, the bottom margin needs to be changed. -->
<TextView android:id="@+id/channel_number_and_name"
diff --git a/src/com/android/tv/MainActivity.java b/src/com/android/tv/MainActivity.java
index 5cfdd194..8dbafe47 100644
--- a/src/com/android/tv/MainActivity.java
+++ b/src/com/android/tv/MainActivity.java
@@ -149,6 +149,7 @@ import com.android.tv.ui.sidepanel.parentalcontrols.RatingsFragment;
import com.android.tv.util.AsyncDbTask;
import com.android.tv.util.AsyncDbTask.DbExecutor;
import com.android.tv.util.CaptionSettings;
+import com.android.tv.util.GtvUtils;
import com.android.tv.util.OnboardingUtils;
import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
@@ -230,20 +231,20 @@ public class MainActivity extends Activity
private static final String SCREEN_BEHIND_NAME = "Behind";
private static final float REFRESH_RATE_EPSILON = 0.01f;
- private static final HashSet<Integer> BLACKLIST_KEYCODE_TO_TIS;
+ private static final HashSet<Integer> BLOCKLIST_KEYCODE_TO_TIS;
// These keys won't be passed to TIS in addition to gamepad buttons.
static {
- BLACKLIST_KEYCODE_TO_TIS = new HashSet<>();
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_TV_INPUT);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_MENU);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_CHANNEL_UP);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_CHANNEL_DOWN);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_UP);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_DOWN);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_MUTE);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_MUTE);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_SEARCH);
- BLACKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_WINDOW);
+ BLOCKLIST_KEYCODE_TO_TIS = new HashSet<>();
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_TV_INPUT);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_MENU);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_CHANNEL_UP);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_CHANNEL_DOWN);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_UP);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_DOWN);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_VOLUME_MUTE);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_MUTE);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_SEARCH);
+ BLOCKLIST_KEYCODE_TO_TIS.add(KeyEvent.KEYCODE_WINDOW);
}
private static final IntentFilter SYSTEM_INTENT_FILTER = new IntentFilter();
@@ -330,7 +331,7 @@ public class MainActivity extends Activity
private boolean mActivityResumed;
private boolean mActivityStarted;
private boolean mShouldTuneToTunerChannel;
- private boolean mUseKeycodeBlacklist;
+ private boolean mUseKeycodeBlocklist;
private boolean mShowLockedChannelsTemporarily;
private boolean mBackKeyPressed;
private boolean mNeedShowBackKeyGuide;
@@ -456,7 +457,11 @@ public class MainActivity extends Activity
}
@Override
- public void onChannelChanged(Channel previousChannel, Channel currentChannel) {}
+ public void onChannelChanged(Channel previousChannel, Channel currentChannel) {
+ if (currentChannel != null) {
+ GtvUtils.broadcastInputId(MainActivity.this, currentChannel.getInputId());
+ }
+ }
};
private final Runnable mRestoreMainViewRunnable = this::restoreMainTvView;
@@ -1441,16 +1446,16 @@ public class MainActivity extends Activity
|| mOverlayManager.getSideFragmentManager().isActive()) {
return super.dispatchKeyEvent(event);
}
- if (BLACKLIST_KEYCODE_TO_TIS.contains(event.getKeyCode())
+ if (BLOCKLIST_KEYCODE_TO_TIS.contains(event.getKeyCode())
|| KeyEvent.isGamepadButton(event.getKeyCode())) {
- // If the event is in blacklisted or gamepad key, do not pass it to session.
- // Gamepad keys are blacklisted to support TV UIs and here's the detail.
+ // If the event is in blocklisted or gamepad key, do not pass it to session.
+ // Gamepad keys are blocklisted to support TV UIs and here's the detail.
// If there's a TIS granted RECEIVE_INPUT_EVENT, TIF sends key events to TIS
// and return immediately saying that the event is handled.
// In this case, fallback key will be injected but with FLAG_CANCELED
// while gamepads support DPAD_CENTER and BACK by fallback.
// Since we don't expect that TIS want to handle gamepad buttons now,
- // blacklist gamepad buttons and wait for next fallback keys.
+ // blocklist gamepad buttons and wait for next fallback keys.
// TODO: Need to consider other fallback keys (e.g. ESCAPE)
return super.dispatchKeyEvent(event);
}
@@ -2215,7 +2220,7 @@ public class MainActivity extends Activity
if (event.isCanceled()) {
// Ignore canceled key.
// Note that if there's a TIS granted RECEIVE_INPUT_EVENT,
- // fallback keys not blacklisted will have FLAG_CANCELED.
+ // fallback keys not blocklisted will have FLAG_CANCELED.
// See dispatchKeyEvent() for detail.
return true;
}
@@ -2330,7 +2335,7 @@ public class MainActivity extends Activity
return true;
case KeyEvent.KEYCODE_CTRL_LEFT:
case KeyEvent.KEYCODE_CTRL_RIGHT:
- mUseKeycodeBlacklist = !mUseKeycodeBlacklist;
+ mUseKeycodeBlocklist = !mUseKeycodeBlocklist;
return true;
case KeyEvent.KEYCODE_O:
mOverlayManager.getSideFragmentManager().show(new DisplayModeFragment());
diff --git a/src/com/android/tv/SetupPassthroughActivity.java b/src/com/android/tv/SetupPassthroughActivity.java
index 25049f1d..e7f89108 100644
--- a/src/com/android/tv/SetupPassthroughActivity.java
+++ b/src/com/android/tv/SetupPassthroughActivity.java
@@ -118,13 +118,12 @@ public class SetupPassthroughActivity extends Activity {
setupIntent.putExtras(extras);
try {
ComponentName callingActivity = getCallingActivity();
- if (callingActivity != null
- && !callingActivity.getPackageName().equals(CommonConstants.BASE_PACKAGE)) {
- Log.w(
- TAG,
- "Calling activity "
- + callingActivity.getPackageName()
- + " is not trusted. Not forwarding intent.");
+ if (callingActivity == null
+ || !callingActivity.getPackageName().equals(CommonConstants.BASE_PACKAGE)) {
+ String name =
+ callingActivity == null ? "null" : callingActivity.getPackageName();
+ Log.w(TAG,
+ "Calling activity " + name + " is not trusted. Not forwarding intent.");
finish();
return;
}
diff --git a/src/com/android/tv/TimeShiftManager.java b/src/com/android/tv/TimeShiftManager.java
index f08b5e85..3167a631 100644
--- a/src/com/android/tv/TimeShiftManager.java
+++ b/src/com/android/tv/TimeShiftManager.java
@@ -110,7 +110,7 @@ public class TimeShiftManager {
private static final int MSG_GET_CURRENT_POSITION = 1000;
private static final int MSG_PREFETCH_PROGRAM = 1001;
private static final long REQUEST_CURRENT_POSITION_INTERVAL = TimeUnit.SECONDS.toMillis(1);
- private static final long MAX_DUMMY_PROGRAM_DURATION = TimeUnit.MINUTES.toMillis(30);
+ private static final long MAX_PLACEHOLDER_PROGRAM_DURATION = TimeUnit.MINUTES.toMillis(30);
@VisibleForTesting static final long INVALID_TIME = -1;
static final long CURRENT_TIME = -2;
private static final long PREFETCH_TIME_OFFSET_FROM_PROGRAM_END = TimeUnit.MINUTES.toMillis(1);
@@ -489,7 +489,7 @@ public class TimeShiftManager {
Program program = mProgramManager.getProgramAt(timeMs);
if (program == null) {
// Guard just in case when the program prefetch handler doesn't work on time.
- mProgramManager.addDummyProgramsAt(timeMs);
+ mProgramManager.addPlaceholderProgramsAt(timeMs);
program = mProgramManager.getProgramAt(timeMs);
}
return program;
@@ -544,8 +544,8 @@ public class TimeShiftManager {
/**
* Returns the current program which airs right now.
*
- * <p>If the program is a dummy program, which means there's no program information, returns
- * {@code null}.
+ * <p>If the program is a placeholder program, which means there's no program information,
+ * returns {@code null}.
*/
@Nullable
public Program getCurrentProgram() {
@@ -909,11 +909,11 @@ public class TimeShiftManager {
prefetchStartTimeMs = program.getEndTimeUtcMillis();
} else {
prefetchStartTimeMs =
- Utils.floorTime(currentPositionMs, MAX_DUMMY_PROGRAM_DURATION);
+ Utils.floorTime(currentPositionMs, MAX_PLACEHOLDER_PROGRAM_DURATION);
}
- // Create dummy program
+ // Create placeholder program
mPrograms.addAll(
- createDummyPrograms(
+ createPlaceholderPrograms(
prefetchStartTimeMs,
currentPositionMs + PREFETCH_DURATION_FOR_NEXT));
schedulePrefetchPrograms();
@@ -929,12 +929,12 @@ public class TimeShiftManager {
endTimeMs = System.currentTimeMillis();
}
- long fetchStartTimeMs = Utils.floorTime(startTimeMs, MAX_DUMMY_PROGRAM_DURATION);
+ long fetchStartTimeMs = Utils.floorTime(startTimeMs, MAX_PLACEHOLDER_PROGRAM_DURATION);
long fetchEndTimeMs =
Utils.ceilTime(
- endTimeMs + PREFETCH_DURATION_FOR_NEXT, MAX_DUMMY_PROGRAM_DURATION);
+ endTimeMs + PREFETCH_DURATION_FOR_NEXT, MAX_PLACEHOLDER_PROGRAM_DURATION);
removeOutdatedPrograms(fetchStartTimeMs);
- boolean needToLoad = addDummyPrograms(fetchStartTimeMs, fetchEndTimeMs);
+ boolean needToLoad = addPlaceholderPrograms(fetchStartTimeMs, fetchEndTimeMs);
if (needToLoad) {
Range<Long> period = Range.create(fetchStartTimeMs, fetchEndTimeMs);
mProgramLoadQueue.add(period);
@@ -983,60 +983,60 @@ public class TimeShiftManager {
}
}
- void addDummyProgramsAt(long timeMs) {
- addDummyPrograms(timeMs, timeMs + PREFETCH_DURATION_FOR_NEXT);
+ void addPlaceholderProgramsAt(long timeMs) {
+ addPlaceholderPrograms(timeMs, timeMs + PREFETCH_DURATION_FOR_NEXT);
}
- private boolean addDummyPrograms(Range<Long> period) {
- return addDummyPrograms(period.getLower(), period.getUpper());
+ private boolean addPlaceholderPrograms(Range<Long> period) {
+ return addPlaceholderPrograms(period.getLower(), period.getUpper());
}
- private boolean addDummyPrograms(long startTimeMs, long endTimeMs) {
+ private boolean addPlaceholderPrograms(long startTimeMs, long endTimeMs) {
boolean added = false;
if (mPrograms.isEmpty()) {
- // Insert dummy program.
- mPrograms.addAll(createDummyPrograms(startTimeMs, endTimeMs));
+ // Insert placeholder program.
+ mPrograms.addAll(createPlaceholderPrograms(startTimeMs, endTimeMs));
return true;
}
- // Insert dummy program to the head of the list if needed.
+ // Insert placeholder program to the head of the list if needed.
Program firstProgram = mPrograms.get(0);
if (startTimeMs < firstProgram.getStartTimeUtcMillis()) {
if (!firstProgram.isValid()) {
- // Already the firstProgram is dummy.
+ // Already the firstProgram is a placeholder.
mPrograms.remove(0);
mPrograms.addAll(
0,
- createDummyPrograms(startTimeMs, firstProgram.getEndTimeUtcMillis()));
+ createPlaceholderPrograms(startTimeMs, firstProgram.getEndTimeUtcMillis()));
} else {
mPrograms.addAll(
0,
- createDummyPrograms(startTimeMs, firstProgram.getStartTimeUtcMillis()));
+ createPlaceholderPrograms(startTimeMs, firstProgram.getStartTimeUtcMillis()));
}
added = true;
}
- // Insert dummy program to the tail of the list if needed.
+ // Insert placeholder program to the tail of the list if needed.
Program lastProgram = mPrograms.get(mPrograms.size() - 1);
if (endTimeMs > lastProgram.getEndTimeUtcMillis()) {
if (!lastProgram.isValid()) {
- // Already the lastProgram is dummy.
+ // Already the lastProgram is a placeholder.
mPrograms.remove(mPrograms.size() - 1);
mPrograms.addAll(
- createDummyPrograms(lastProgram.getStartTimeUtcMillis(), endTimeMs));
+ createPlaceholderPrograms(lastProgram.getStartTimeUtcMillis(), endTimeMs));
} else {
mPrograms.addAll(
- createDummyPrograms(lastProgram.getEndTimeUtcMillis(), endTimeMs));
+ createPlaceholderPrograms(lastProgram.getEndTimeUtcMillis(), endTimeMs));
}
added = true;
}
- // Insert dummy programs if the holes exist in the list.
+ // Insert placeholder programs if the holes exist in the list.
for (int i = 1; i < mPrograms.size(); ++i) {
long endOfPrevious = mPrograms.get(i - 1).getEndTimeUtcMillis();
long startOfCurrent = mPrograms.get(i).getStartTimeUtcMillis();
if (startOfCurrent > endOfPrevious) {
- List<Program> dummyPrograms =
- createDummyPrograms(endOfPrevious, startOfCurrent);
- mPrograms.addAll(i, dummyPrograms);
- i += dummyPrograms.size();
+ List<Program> placeholderPrograms =
+ createPlaceholderPrograms(endOfPrevious, startOfCurrent);
+ mPrograms.addAll(i, placeholderPrograms);
+ i += placeholderPrograms.size();
added = true;
}
}
@@ -1049,7 +1049,7 @@ public class TimeShiftManager {
}
}
- private void removeDummyPrograms() {
+ private void removePlaceholderPrograms() {
for (Iterator<Program> it = mPrograms.listIterator(); it.hasNext(); ) {
if (!it.next().isValid()) {
it.remove();
@@ -1084,18 +1084,18 @@ public class TimeShiftManager {
}
}
- // Returns a list of dummy programs.
- // The maximum duration of a dummy program is {@link MAX_DUMMY_PROGRAM_DURATION}.
+ // Returns a list of placeholder programs.
+ // The maximum duration of a placeholder program is {@link MAX_PLACEHOLDER_PROGRAM_DURATION}.
// So if the duration ({@code endTimeMs}-{@code startTimeMs}) is greater than the duration,
- // we need to create multiple dummy programs.
+ // we need to create multiple placeholder programs.
// The reason of the limitation of the duration is because we want the trick play viewer
- // to show the time-line duration of {@link MAX_DUMMY_PROGRAM_DURATION} at most
- // for a dummy program.
- private List<Program> createDummyPrograms(long startTimeMs, long endTimeMs) {
+ // to show the time-line duration of {@link MAX_PLACEHOLDER_PROGRAM_DURATION} at most
+ // for a placeholder program.
+ private List<Program> createPlaceholderPrograms(long startTimeMs, long endTimeMs) {
SoftPreconditions.checkArgument(
endTimeMs - startTimeMs <= TWO_WEEKS_MS,
TAG,
- "createDummyProgram: long duration of dummy programs are requested ( %s , %s)",
+ "createPlaceholderProgram: long duration of placeholder programs are requested ( %s , %s)",
Utils.toTimeString(startTimeMs),
Utils.toTimeString(endTimeMs));
if (startTimeMs >= endTimeMs) {
@@ -1103,7 +1103,7 @@ public class TimeShiftManager {
}
List<Program> programs = new ArrayList<>();
long start = startTimeMs;
- long end = Utils.ceilTime(startTimeMs, MAX_DUMMY_PROGRAM_DURATION);
+ long end = Utils.ceilTime(startTimeMs, MAX_PLACEHOLDER_PROGRAM_DURATION);
while (end < endTimeMs) {
programs.add(
new ProgramImpl.Builder()
@@ -1111,7 +1111,7 @@ public class TimeShiftManager {
.setEndTimeUtcMillis(end)
.build());
start = end;
- end += MAX_DUMMY_PROGRAM_DURATION;
+ end += MAX_PLACEHOLDER_PROGRAM_DURATION;
}
programs.add(
new ProgramImpl.Builder()
@@ -1256,7 +1256,7 @@ public class TimeShiftManager {
}
if (programs == null || programs.isEmpty()) {
mEmptyFetchCount++;
- if (addDummyPrograms(mPeriod)) {
+ if (addPlaceholderPrograms(mPeriod)) {
TimeShiftManager.this.onProgramInfoChanged();
}
schedulePrefetchPrograms();
@@ -1265,7 +1265,7 @@ public class TimeShiftManager {
}
mEmptyFetchCount = 0;
if (!mPrograms.isEmpty()) {
- removeDummyPrograms();
+ removePlaceholderPrograms();
removeOverlappedPrograms(programs);
Program loadedProgram = programs.get(0);
for (int i = 0; i < mPrograms.size() && !programs.isEmpty(); ++i) {
@@ -1282,7 +1282,7 @@ public class TimeShiftManager {
}
}
mPrograms.addAll(programs);
- addDummyPrograms(mPeriod);
+ addPlaceholderPrograms(mPeriod);
TimeShiftManager.this.onProgramInfoChanged();
schedulePrefetchPrograms();
startNextLoadingIfNeeded();
diff --git a/src/com/android/tv/data/ProgramDataManager.java b/src/com/android/tv/data/ProgramDataManager.java
index a866c78e..dcfa69e2 100644
--- a/src/com/android/tv/data/ProgramDataManager.java
+++ b/src/com/android/tv/data/ProgramDataManager.java
@@ -402,7 +402,7 @@ public class ProgramDataManager implements MemoryManageable {
*
* <p>Prefetch should be enabled to call it.
*
- * @return {@link List} with Programs. It may includes dummy program if the entry needs DB
+ * @return {@link List} with Programs. It may includes stub program if the entry needs DB
* operations to get.
*/
public List<Program> getPrograms(long channelId, long startTime) {
@@ -425,7 +425,7 @@ public class ProgramDataManager implements MemoryManageable {
private int getProgramIndexAt(List<Program> programs, long time) {
Program key = mZeroLengthProgramCache.get(time);
if (key == null) {
- key = createDummyProgram(time, time);
+ key = createStubProgram(time, time);
mZeroLengthProgramCache.put(time, key);
}
int index = Collections.binarySearch(programs, key);
@@ -527,11 +527,11 @@ public class ProgramDataManager implements MemoryManageable {
continue;
}
- // Update dummy program around current program if any.
+ // Update stub program around current program if any.
if (cachedProgram.getStartTimeUtcMillis() < currentProgram.getStartTimeUtcMillis()) {
- // The dummy program starts earlier than the current program. Adjust its end time.
+ // The stub program starts earlier than the current program. Adjust its end time.
i.set(
- createDummyProgram(
+ createStubProgram(
cachedProgram.getStartTimeUtcMillis(),
currentProgram.getStartTimeUtcMillis()));
i.add(currentProgram);
@@ -539,9 +539,9 @@ public class ProgramDataManager implements MemoryManageable {
i.set(currentProgram);
}
if (currentProgram.getEndTimeUtcMillis() < cachedProgram.getEndTimeUtcMillis()) {
- // The dummy program ends later than the current program. Adjust its start time.
+ // The stub program ends later than the current program. Adjust its start time.
i.add(
- createDummyProgram(
+ createStubProgram(
currentProgram.getEndTimeUtcMillis(),
cachedProgram.getEndTimeUtcMillis()));
}
@@ -1010,8 +1010,8 @@ public class ProgramDataManager implements MemoryManageable {
}
}
- // Create dummy program which indicates data isn't loaded yet so DB query is required.
- private Program createDummyProgram(long startTimeMs, long endTimeMs) {
+ // Create stub program which indicates data isn't loaded yet so DB query is required.
+ private Program createStubProgram(long startTimeMs, long endTimeMs) {
return new ProgramImpl.Builder()
.setChannelId(Channel.INVALID_ID)
.setStartTimeUtcMillis(startTimeMs)
diff --git a/src/com/android/tv/data/ProgramImpl.java b/src/com/android/tv/data/ProgramImpl.java
index 5097e2d4..84e42fbe 100644
--- a/src/com/android/tv/data/ProgramImpl.java
+++ b/src/com/android/tv/data/ProgramImpl.java
@@ -419,7 +419,7 @@ public final class ProgramImpl extends BaseProgramImpl implements Parcelable, Pr
@Override
public int hashCode() {
- // Hash with all the properties because program ID can be invalid for the dummy programs.
+ // Hash with all the properties because program ID can be invalid for the stub programs.
return Objects.hash(
mChannelId,
mStartTimeUtcMillis,
@@ -446,7 +446,7 @@ public final class ProgramImpl extends BaseProgramImpl implements Parcelable, Pr
if (!(other instanceof ProgramImpl)) {
return false;
}
- // Compare all the properties because program ID can be invalid for the dummy programs.
+ // Compare all the properties because program ID can be invalid for the stub programs.
ProgramImpl program = (ProgramImpl) other;
return Objects.equals(mPackageName, program.mPackageName)
&& mChannelId == program.mChannelId
diff --git a/src/com/android/tv/dvr/data/RecordedProgram.java b/src/com/android/tv/dvr/data/RecordedProgram.java
index 61430551..230ec62d 100644
--- a/src/com/android/tv/dvr/data/RecordedProgram.java
+++ b/src/com/android/tv/dvr/data/RecordedProgram.java
@@ -113,7 +113,7 @@ public abstract class RecordedProgram extends BaseProgramImpl {
.setPosterArtUri(StringUtils.nullToEmpty(cursor.getString(index++)))
.setThumbnailUri(StringUtils.nullToEmpty(cursor.getString(index++)))
.setSearchable(cursor.getInt(index++) == 1)
- .setDataUri(cursor.getString(index++))
+ .setDataUri(StringUtils.nullToEmpty(cursor.getString(index++)))
.setDataBytes(cursor.getLong(index++))
.setDurationMillis(cursor.getLong(index++))
.setExpireTimeUtcMillis(cursor.getLong(index++))
diff --git a/src/com/android/tv/features/TvFeatures.java b/src/com/android/tv/features/TvFeatures.java
index a18d9c89..5282c28c 100644
--- a/src/com/android/tv/features/TvFeatures.java
+++ b/src/com/android/tv/features/TvFeatures.java
@@ -98,8 +98,8 @@ public final class TvFeatures extends CommonFeatures {
/** Enable a conflict dialog between currently watched channel and upcoming recording. */
public static final Feature SHOW_UPCOMING_CONFLICT_DIALOG = OFF;
- /** Use input blacklist to disable partner's tuner input. */
- public static final Feature USE_PARTNER_INPUT_BLACKLIST = ON;
+ /** Use input blocklist to disable partner's tuner input. */
+ public static final Feature USE_PARTNER_INPUT_BLOCKLIST = ON;
private TvFeatures() {}
}
diff --git a/src/com/android/tv/guide/ProgramManager.java b/src/com/android/tv/guide/ProgramManager.java
index 516a4d9c..9dfc05c0 100644
--- a/src/com/android/tv/guide/ProgramManager.java
+++ b/src/com/android/tv/guide/ProgramManager.java
@@ -417,8 +417,9 @@ public class ProgramManager {
/**
* Returns an entry as {@link ProgramImpl} for a given {@code channelId} and {@code index} of
- * entries within the currently managed time range. Returned {@link ProgramImpl} can be a dummy
- * one (e.g., whose channelId is INVALID_ID), when it corresponds to a gap between programs.
+ * entries within the currently managed time range. Returned {@link ProgramImpl} can be a
+ * placeholder (e.g., whose channelId is INVALID_ID), when it corresponds to a gap between
+ * programs.
*/
TableEntry getTableEntry(long channelId, int index) {
mProgramDataManager.prefetchChannel(channelId, index);
@@ -613,7 +614,7 @@ public class ProgramManager {
List<Program> programs = mProgramDataManager.getPrograms(channelId, mStartUtcMillis);
for (Program program : programs) {
if (program.getChannelId() == INVALID_ID) {
- // Dummy program.
+ // Placeholder program.
continue;
}
long programStartTime = Math.max(program.getStartTimeUtcMillis(), mStartUtcMillis);
diff --git a/src/com/android/tv/menu/MenuRowView.java b/src/com/android/tv/menu/MenuRowView.java
index e09a4ef0..27554c22 100644
--- a/src/com/android/tv/menu/MenuRowView.java
+++ b/src/com/android/tv/menu/MenuRowView.java
@@ -95,7 +95,8 @@ public abstract class MenuRowView extends LinearLayout {
@Override
public void sendAccessibilityEvent(View host, int eventType) {
super.sendAccessibilityEvent(host, eventType);
- if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED &&
+ if ((eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED ||
+ eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) &&
!mRow.isReselected()) {
requestChildFocus();
}
diff --git a/src/com/android/tv/menu/MenuView.java b/src/com/android/tv/menu/MenuView.java
index add4a774..e1a3379c 100644
--- a/src/com/android/tv/menu/MenuView.java
+++ b/src/com/android/tv/menu/MenuView.java
@@ -25,6 +25,8 @@ import android.view.View;
import android.view.ViewParent;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import com.android.tv.menu.Menu.MenuShowReason;
import java.util.ArrayList;
@@ -192,7 +194,12 @@ public class MenuView extends FrameLayout implements IMenuView {
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
int selectedPosition = mLayoutManager.getSelectedPosition();
// When the menu shows up, the selected row should have focus.
+ AccessibilityManager mAccessibilityManager =
+ getContext().getSystemService(AccessibilityManager.class);
if (selectedPosition >= 0 && selectedPosition < mMenuRowViews.size()) {
+ if(mAccessibilityManager.isEnabled())
+ mMenuRowViews.get(selectedPosition)
+ .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
return mMenuRowViews.get(selectedPosition).requestFocus();
}
return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
diff --git a/src/com/android/tv/parental/ContentRatingSystem.java b/src/com/android/tv/parental/ContentRatingSystem.java
index d85dd50e..b40c1133 100644
--- a/src/com/android/tv/parental/ContentRatingSystem.java
+++ b/src/com/android/tv/parental/ContentRatingSystem.java
@@ -288,7 +288,7 @@ public class ContentRatingSystem {
ratings.add(builder.build(subRatings));
}
- // Sanity check.
+ // Soundness check.
for (SubRating subRating : subRatings) {
boolean used = false;
for (Rating rating : ratings) {
diff --git a/src/com/android/tv/recommendation/NotificationService.java b/src/com/android/tv/recommendation/NotificationService.java
index 1652bd77..870b3c10 100644
--- a/src/com/android/tv/recommendation/NotificationService.java
+++ b/src/com/android/tv/recommendation/NotificationService.java
@@ -437,7 +437,8 @@ public class NotificationService extends Service
Intent intent = new Intent(Intent.ACTION_VIEW, channel.getUri());
intent.putExtra(TUNE_PARAMS_RECOMMENDATION_TYPE, mRecommendationType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final PendingIntent notificationIntent = PendingIntent.getActivity(this, 0, intent, 0);
+ final PendingIntent notificationIntent = PendingIntent.getActivity(this, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE);
// This callback will run on the main thread.
Bitmap largeIconBitmap =
diff --git a/src/com/android/tv/util/GtvUtils.java b/src/com/android/tv/util/GtvUtils.java
new file mode 100644
index 00000000..eb50e062
--- /dev/null
+++ b/src/com/android/tv/util/GtvUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 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.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+/** A utility class for Google TV */
+public class GtvUtils {
+ private static final String TAG = "GtvUtils";
+ private static final String AMATI_FEATURE = "com.google.android.feature.AMATI_EXPERIENCE";
+ private static final String PERMISSION_WRITE_EPG_DATA =
+ "com.android.providers.tv.permission.WRITE_EPG_DATA";
+ private static final String ACTION_INPUT_SELECTED = "android.apps.tv.launcherx.INPUT_SELECTED";
+ private static final String EXTRA_INPUT_ID = "extra_input_id";
+ private static final String LAUNCHERX_PACKAGE_NAME = "com.google.android.apps.tv.launcherx";
+ private static Boolean mEnabled = null;
+
+ private static boolean isEnabled(Context context) {
+ if (mEnabled == null) {
+ PackageManager pm = context.getPackageManager();
+ mEnabled = pm.hasSystemFeature(AMATI_FEATURE);
+ }
+ return mEnabled;
+ }
+
+ /** Broadcasts the intent with inputId to the Launcher */
+ public static void broadcastInputId(Context context, String inputId) {
+ if (isEnabled(context)) {
+ if (inputId == null) {
+ Log.e(TAG, "Will not broadcast inputId because it is null");
+ } else {
+ Intent intent = new Intent(ACTION_INPUT_SELECTED);
+ intent.putExtra(EXTRA_INPUT_ID, inputId);
+ intent.setPackage(LAUNCHERX_PACKAGE_NAME);
+ context.sendBroadcast(intent, PERMISSION_WRITE_EPG_DATA);
+ }
+ }
+ }
+}
diff --git a/src/com/android/tv/util/TvInputManagerHelper.java b/src/com/android/tv/util/TvInputManagerHelper.java
index 23c9b494..72d527a9 100644
--- a/src/com/android/tv/util/TvInputManagerHelper.java
+++ b/src/com/android/tv/util/TvInputManagerHelper.java
@@ -128,14 +128,14 @@ public class TvInputManagerHelper {
private static final String PERMISSION_ACCESS_ALL_EPG_DATA =
"com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA";
- private static final String[] mPhysicalTunerBlackList = {
+ private static final String[] mPhysicalTunerBlockList = {
"com.google.android.videos", // Play Movies
};
private static final String META_LABEL_SORT_KEY = "input_sort_key";
private static final String TV_INPUT_ALLOW_3RD_PARTY_INPUTS = "tv_input_allow_3rd_party_inputs";
- private static final String[] SYSTEM_INPUT_ID_BLACKLIST = {
+ private static final String[] SYSTEM_INPUT_ID_BLOCKLIST = {
"com.google.android.videos/" // Play Movies
};
@@ -160,7 +160,7 @@ public class TvInputManagerHelper {
DEFAULT_TV_INPUT_PRIORITY.add(TvInputInfo.TYPE_OTHER);
}
- private static final String[] PARTNER_TUNER_INPUT_PREFIX_BLACKLIST = {
+ private static final String[] PARTNER_TUNER_INPUT_PREFIX_BLOCKLIST = {
/* Begin_AOSP_Comment_Out
// Disabled partner's tuner input prefix list.
"com.mediatek.tvinput/.dtv"
@@ -597,7 +597,7 @@ public class TvInputManagerHelper {
private boolean isInputPhysicalTuner(TvInputInfo input) {
String packageName = input.getServiceInfo().packageName;
- if (Arrays.asList(mPhysicalTunerBlackList).contains(packageName)) {
+ if (Arrays.asList(mPhysicalTunerBlockList).contains(packageName)) {
return false;
}
@@ -628,9 +628,9 @@ public class TvInputManagerHelper {
return true;
}
- private boolean isInBlackList(String inputId) {
- if (TvFeatures.USE_PARTNER_INPUT_BLACKLIST.isEnabled(mContext)) {
- for (String disabledTunerInputPrefix : PARTNER_TUNER_INPUT_PREFIX_BLACKLIST) {
+ private boolean isBlocked(String inputId) {
+ if (TvFeatures.USE_PARTNER_INPUT_BLOCKLIST.isEnabled(mContext)) {
+ for (String disabledTunerInputPrefix : PARTNER_TUNER_INPUT_PREFIX_BLOCKLIST) {
if (inputId.contains(disabledTunerInputPrefix)) {
return true;
}
@@ -694,13 +694,13 @@ public class TvInputManagerHelper {
if (!isSystemInput(info)) {
return true;
}
- for (String id : SYSTEM_INPUT_ID_BLACKLIST) {
+ for (String id : SYSTEM_INPUT_ID_BLOCKLIST) {
if (info.getId().startsWith(id)) {
return true;
}
}
}
- return isInBlackList(info.getId());
+ return isBlocked(info.getId());
}
/**
diff --git a/tests/common/Android.bp b/tests/common/Android.bp
index 1abaaf76..5a65538d 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_library {
name: "tv-test-common",
diff --git a/tests/common/Android.mk b/tests/common/Android.mk
index ff0934b8..7a232ff7 100644
--- a/tests/common/Android.mk
+++ b/tests/common/Android.mk
@@ -2,6 +2,8 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := tv-test-common-robo
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_SRC_FILES := \
$(call all-java-files-under, src/com/android/tv/testing/robo) \
@@ -15,6 +17,10 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-robolectric-prebuilt \
tv-test-common \
+# Disable dexpreopt and <uses-library> check for test.
+LOCAL_ENFORCE_USES_LIBRARIES := false
+LOCAL_DEX_PREOPT := false
+
LOCAL_INSTRUMENTATION_FOR := LiveTv
LOCAL_MODULE_TAGS := optional
diff --git a/tests/func/Android.bp b/tests/func/Android.bp
new file mode 100644
index 00000000..d4085394
--- /dev/null
+++ b/tests/func/Android.bp
@@ -0,0 +1,21 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "TVFuncTests",
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.runner",
+ "tv-test-common",
+ "ub-uiautomator",
+ ],
+ libs: ["android.test.base.stubs"],
+ instrumentation_for: "LiveTv",
+ sdk_version: "system_current",
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/tests/func/Android.mk b/tests/func/Android.mk
deleted file mode 100644
index 53c869ee..00000000
--- a/tests/func/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := TVFuncTests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.runner \
- tv-test-common \
- ub-uiautomator \
-
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-
-LOCAL_INSTRUMENTATION_FOR := LiveTv
-
-LOCAL_SDK_VERSION := system_current
-
-LOCAL_PROGUARD_ENABLED := disabled
-include $(BUILD_PACKAGE)
diff --git a/tests/input/Android.bp b/tests/input/Android.bp
new file mode 100644
index 00000000..31e9e098
--- /dev/null
+++ b/tests/input/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 Google Inc.
+//
+// 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+}
+
+android_app {
+ name: "TVTestInput",
+ srcs: ["src/**/*.java"],
+ optimize: {
+ enabled: false,
+ },
+ // Overlay view related functionality requires system APIs.
+ sdk_version: "system_current",
+ static_libs: [
+ "tv-test-common",
+ "tv-common",
+ ],
+ // Disable dexpreopt and <uses-library> check for test.
+ enforce_uses_libs: false,
+ dex_preopt: {
+ enabled: false,
+ },
+ resource_dirs: [
+ "res",
+ ],
+ aaptflags: [
+ "--auto-add-overlay",
+ "--extra-packages com.android.tv.testing",
+ ],
+}
diff --git a/tests/input/Android.mk b/tests/input/Android.mk
deleted file mode 100644
index 46b5621c..00000000
--- a/tests/input/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := TVTestInput
-LOCAL_MODULE_TAGS := optional
-LOCAL_PROGUARD_ENABLED := disabled
-# Overlay view related functionality requires system APIs.
-LOCAL_SDK_VERSION := system_current
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- tv-test-common \
- tv-common
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../common/res $(LOCAL_PATH)/res
-LOCAL_AAPT_FLAGS := --auto-add-overlay \
- --extra-packages com.android.tv.testing
-
-include $(BUILD_PACKAGE)
-
-ifneq ($(filter TV,$(TARGET_BUILD_APPS)),)
- $(call dist-for-goals,apps_only,$(LOCAL_BUILT_MODULE):$(LOCAL_PACKAGE_NAME).apk)
-endif
-
diff --git a/tests/input/AndroidManifest.xml b/tests/input/AndroidManifest.xml
index e01b9131..b0c8aaac 100644
--- a/tests/input/AndroidManifest.xml
+++ b/tests/input/AndroidManifest.xml
@@ -16,55 +16,55 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tv.testinput">
+ package="com.android.tv.testinput">
- <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="23"/>
+ <uses-sdk android:targetSdkVersion="29"
+ android:minSdkVersion="23"/>
<!-- Required to update or read existing channel and program information in TvProvider. -->
- <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA"/>
<!-- Required to update channel and program information in TvProvider. -->
- <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/>
<application android:label="@string/sample_tv_input"
- android:icon="@drawable/android_48dp"
- android:theme="@android:style/Theme.Holo.Light.NoActionBar" >
+ android:icon="@drawable/android_48dp"
+ android:theme="@android:style/Theme.Holo.Light.NoActionBar">
<!-- Launched by the TV app before it uses TestTvInputService to set up channels for this
- input. -->
- <activity android:name=".TestTvInputSetupActivity" >
+ input. -->
+ <activity android:name=".TestTvInputSetupActivity"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<service android:name=".TestTvInputService"
- android:permission="android.permission.BIND_TV_INPUT"
- android:label="@string/simple_input_label">
+ android:permission="android.permission.BIND_TV_INPUT"
+ android:label="@string/simple_input_label"
+ android:exported="true">
<!-- Required filter used by the system to launch our account service. -->
<intent-filter>
- <action android:name="android.media.tv.TvInputService" />
+ <action android:name="android.media.tv.TvInputService"/>
</intent-filter>
<!-- An XML file which describes this input. This provides a pointer to the
- TestTvInputSetupActivity to the system/TV app. -->
+ TestTvInputSetupActivity to the system/TV app. -->
<meta-data android:name="android.media.tv.input"
- android:resource="@xml/testtvinputservice" />
+ android:resource="@xml/testtvinputservice"/>
</service>
- <service android:name=".TestInputControlService" android:exported="true"/>
+ <service android:name=".TestInputControlService"
+ android:exported="true"/>
</application>
- <instrumentation
- android:name=".instrument.TestSetupInstrumentation"
- android:label="Test Setup Instrument"
- android:targetPackage="com.android.tv.testinput" />
+ <instrumentation android:name=".instrument.TestSetupInstrumentation"
+ android:label="Test Setup Instrument"
+ android:targetPackage="com.android.tv.testinput"/>
- <uses-feature
- android:name="android.hardware.touchscreen"
- android:required="false" />
- <uses-feature
- android:name="android.software.leanback"
- android:required="true" />
+ <uses-feature android:name="android.hardware.touchscreen"
+ android:required="false"/>
+ <uses-feature android:name="android.software.leanback"
+ android:required="true"/>
<!-- Required to expose this app in the store only when the device has TV input framework
- with the TV app. -->
- <uses-feature
- android:name="android.software.live_tv"
- android:required="true" />
+ with the TV app. -->
+ <uses-feature android:name="android.software.live_tv"
+ android:required="true"/>
</manifest>
diff --git a/tests/jank/Android.bp b/tests/jank/Android.bp
new file mode 100644
index 00000000..1cea734b
--- /dev/null
+++ b/tests/jank/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "TVJankTests",
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.runner",
+ "tv-test-common",
+ "ub-janktesthelper",
+ "ub-uiautomator",
+ ],
+ libs: ["android.test.base.stubs"],
+ instrumentation_for: "LiveTv",
+ sdk_version: "system_current",
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/tests/jank/Android.mk b/tests/jank/Android.mk
deleted file mode 100644
index 7df77ea0..00000000
--- a/tests/jank/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := TVJankTests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.runner \
- tv-test-common \
- ub-janktesthelper \
- ub-uiautomator \
-
-LOCAL_JAVA_LIBRARIES := android.test.base.stubs
-
-LOCAL_INSTRUMENTATION_FOR := LiveTv
-
-LOCAL_SDK_VERSION := system_current
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/tests/robotests/Android.mk b/tests/robotests/Android.mk
index 2a6bdd15..c5341ab8 100644
--- a/tests/robotests/Android.mk
+++ b/tests/robotests/Android.mk
@@ -5,6 +5,8 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := TvRoboTests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
BASE_DIR = src/com/android/tv
@@ -50,6 +52,8 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
#############################################################
include $(CLEAR_VARS)
LOCAL_MODULE := RunTvRoboTests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
BASE_DIR = com/android/tv
EXCLUDE_FILES := \
diff --git a/tests/robotests/src/com/android/tv/SetupPassthroughActivityTest.java b/tests/robotests/src/com/android/tv/SetupPassthroughActivityTest.java
index efba4947..2b2bbe83 100644
--- a/tests/robotests/src/com/android/tv/SetupPassthroughActivityTest.java
+++ b/tests/robotests/src/com/android/tv/SetupPassthroughActivityTest.java
@@ -37,6 +37,7 @@ import com.android.tv.common.CommonConstants;
import com.android.tv.common.dagger.ApplicationModule;
import com.android.tv.common.flags.impl.DefaultLegacyFlags;
import com.android.tv.common.flags.impl.SettableFlagsModule;
+import com.android.tv.common.flags.proto.TypedFeatures.StringListParam;
import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.epg.EpgFetcher;
@@ -53,7 +54,6 @@ import com.android.tv.util.TvInputManagerHelper;
import com.google.android.tv.partner.support.EpgContract;
import com.google.common.base.Optional;
-import com.android.tv.common.flags.proto.TypedFeatures.StringListParam;
import dagger.Component;
import dagger.Module;
@@ -71,7 +71,6 @@ import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.android.util.concurrent.RoboExecutorService;
import org.robolectric.annotation.Config;
@@ -169,8 +168,7 @@ public class SetupPassthroughActivityTest {
CommonUtils.createSetupIntent(new Intent(), testInput.getId()));
SetupPassthroughActivity activity = activityController.get();
ShadowActivity shadowActivity = shadowOf(activity);
- shadowActivity.setCallingActivity(
- new ComponentName(CommonConstants.BASE_PACKAGE, "com.example.MyClass"));
+ shadowActivity.setCallingActivity(createTrustedComponent());
activityController.create();
ShadowActivity.IntentForResult shadowIntent =
@@ -205,6 +203,27 @@ public class SetupPassthroughActivityTest {
}
@Test
+ public void create_nullCallingPackage() {
+ testSingletonApp.tvInputManagerHelper.start();
+ testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1);
+
+ ActivityController<SetupPassthroughActivity> activityController =
+ Robolectric.buildActivity(
+ SetupPassthroughActivity.class,
+ CommonUtils.createSetupIntent(new Intent(), testInput.getId()));
+ SetupPassthroughActivity activity = activityController.get();
+ ShadowActivity shadowActivity = shadowOf(activity);
+ shadowActivity.setCallingActivity(null);
+ activityController.create();
+
+ ShadowActivity.IntentForResult shadowIntent =
+ shadowActivity.getNextStartedActivityForResult();
+ // Since the calling activity is null, the next activity should not be started.
+ assertThat(shadowIntent).isNull();
+ assertThat(activity.isFinishing()).isTrue();
+ }
+
+ @Test
public void onActivityResult_canceled() {
testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1);
SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId());
@@ -216,7 +235,7 @@ public class SetupPassthroughActivityTest {
@Test
public void onActivityResult_ok() {
- TestSetupUtils setupUtils = new TestSetupUtils(RuntimeEnvironment.application);
+ TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext());
testSingletonApp.setupUtils = setupUtils;
testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1);
SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId());
@@ -234,7 +253,7 @@ public class SetupPassthroughActivityTest {
@Test
public void onActivityResult_3rdPartyEpg_ok() {
TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.enableForTest();
- TestSetupUtils setupUtils = new TestSetupUtils(RuntimeEnvironment.application);
+ TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext());
testSingletonApp.setupUtils = setupUtils;
testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1);
testSingletonApp
@@ -257,9 +276,9 @@ public class SetupPassthroughActivityTest {
}
@Test
- public void onActivityResult_3rdPartyEpg_notWhiteListed() {
+ public void onActivityResult_3rdPartyEpg_notAllowed() {
TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.enableForTest();
- TestSetupUtils setupUtils = new TestSetupUtils(RuntimeEnvironment.application);
+ TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext());
testSingletonApp.setupUtils = setupUtils;
testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1);
SetupPassthroughActivity activity = createSetupActivityFor(testInput.getId());
@@ -280,7 +299,7 @@ public class SetupPassthroughActivityTest {
@Test
public void onActivityResult_3rdPartyEpg_disabled() {
TvFeatures.CLOUD_EPG_FOR_3RD_PARTY.disableForTests();
- TestSetupUtils setupUtils = new TestSetupUtils(RuntimeEnvironment.application);
+ TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext());
testSingletonApp.setupUtils = setupUtils;
testSingletonApp.tvInputManagerHelper.getFakeTvInputManager().add(testInput, -1);
testSingletonApp
@@ -305,7 +324,7 @@ public class SetupPassthroughActivityTest {
@Test
public void onActivityResult_ok_tvInputInfo_null() {
- TestSetupUtils setupUtils = new TestSetupUtils(RuntimeEnvironment.application);
+ TestSetupUtils setupUtils = new TestSetupUtils(ApplicationProvider.getApplicationContext());
testSingletonApp.setupUtils = setupUtils;
FakeTvInputManager tvInputManager =
testSingletonApp.tvInputManagerHelper.getFakeTvInputManager();
@@ -320,11 +339,15 @@ public class SetupPassthroughActivityTest {
}
private SetupPassthroughActivity createSetupActivityFor(String inputId) {
- return Robolectric.buildActivity(
+ ActivityController<SetupPassthroughActivity> activityController =
+ Robolectric.buildActivity(
SetupPassthroughActivity.class,
- CommonUtils.createSetupIntent(new Intent(), inputId))
- .create()
- .get();
+ CommonUtils.createSetupIntent(new Intent(), inputId));
+ SetupPassthroughActivity activity = activityController.get();
+ ShadowActivity shadowActivity = shadowOf(activity);
+ shadowActivity.setCallingActivity(createTrustedComponent());
+ activityController.create();
+ return activity;
}
private TvInputInfo createMockInput(String inputId) {
@@ -344,6 +367,10 @@ public class SetupPassthroughActivityTest {
return tvInputInfo;
}
+ private static ComponentName createTrustedComponent() {
+ return new ComponentName(CommonConstants.BASE_PACKAGE, "com.example.MyClass");
+ }
+
/**
* Test SetupUtils.
*
@@ -351,6 +378,7 @@ public class SetupPassthroughActivityTest {
* bypasses all of that.
*/
private static class TestSetupUtils extends SetupUtils {
+
public String finishedId;
public Runnable finishedRunnable;
@@ -413,6 +441,7 @@ public class SetupPassthroughActivityTest {
})
/** Module for {@link MyTestApp} */
static class TestModule {
+
private final MyTestApp myTestApp;
TestModule(MyTestApp test) {
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index 80dbfeef..6123af7b 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -21,6 +21,8 @@ LOCAL_JAVA_LIBRARIES := \
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../common/res
LOCAL_PACKAGE_NAME := TVUnitTests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_INSTRUMENTATION_FOR := LiveTv
@@ -28,4 +30,3 @@ LOCAL_SDK_VERSION := system_current
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
-
diff --git a/tuner/Android.bp b/tuner/Android.bp
index bbafef2e..d094c459 100644
--- a/tuner/Android.bp
+++ b/tuner/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_library {
name: "live-tv-tuner",
srcs: ["src/**/*.java"],
diff --git a/tuner/SampleDvbTuner/Android.bp b/tuner/SampleDvbTuner/Android.bp
index 29a177fe..578c051f 100644
--- a/tuner/SampleDvbTuner/Android.bp
+++ b/tuner/SampleDvbTuner/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_app {
name: "SampleDvbTuner",
diff --git a/tuner/SampleDvbTuner/AndroidManifest.xml b/tuner/SampleDvbTuner/AndroidManifest.xml
index 3e315175..5b4e12cf 100755
--- a/tuner/SampleDvbTuner/AndroidManifest.xml
+++ b/tuner/SampleDvbTuner/AndroidManifest.xml
@@ -14,79 +14,72 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.tv.tuner.sample.dvb" >
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.tv.tuner.sample.dvb">
- <uses-sdk
- android:minSdkVersion="23"
- android:targetSdkVersion="29" />
+ <uses-sdk android:minSdkVersion="23"
+ android:targetSdkVersion="29"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <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" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <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" />
+ <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" />
+ <uses-permission android:name="android.permission.DVB_DEVICE"/>
- <uses-feature
- android:name="android.hardware.usb.host"
- android:required="false" />
+ <uses-feature android:name="android.hardware.usb.host"
+ android:required="false"/>
<!-- Limit only for Android TV -->
- <uses-feature
- android:name="android.software.leanback"
- android:required="true" />
- <uses-feature
- android:name="android.software.live_tv"
- android:required="true" />
- <uses-feature
- android:name="android.hardware.touchscreen"
- android:required="false" />
+ <uses-feature android:name="android.software.leanback"
+ android:required="true"/>
+ <uses-feature android:name="android.software.live_tv"
+ android:required="true"/>
+ <uses-feature android:name="android.hardware.touchscreen"
+ android:required="false"/>
<application tools:replace="android:appComponentFactory"
- 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" >
+ 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.android.tv.tuner.sample.dvb.setup.SampleDvbTunerSetupActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:exported="true"
- android:label="@string/sample_dvb_tuner_app_name"
- android:launchMode="singleInstance"
- android:theme="@style/Theme.Setup.GuidedStep" >
+ <activity android:name="com.android.tv.tuner.sample.dvb.setup.SampleDvbTunerSetupActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="true"
+ android:label="@string/sample_dvb_tuner_app_name"
+ android:launchMode="singleInstance"
+ android:theme="@style/Theme.Setup.GuidedStep">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
- <service
- 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.android.tv.tuner.sample.dvb.tvinput" >
+ <service 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.android.tv.tuner.sample.dvb.tvinput"
+ android:exported="true">
<intent-filter>
- <action android:name="android.media.tv.TvInputService" />
+ <action android:name="android.media.tv.TvInputService"/>
</intent-filter>
- <meta-data
- android:name="android.media.tv.input"
- android:resource="@xml/sample_dvb_tvinputservice" />
+ <meta-data android:name="android.media.tv.input"
+ android:resource="@xml/sample_dvb_tvinputservice"/>
</service>
- <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.tuner.tvinput.TunerStorageCleanUpService"
+ android:exported="false"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:process="com.android.tv.tuner"/>
</application>
</manifest>
diff --git a/tuner/SampleNetworkTuner/Android.bp b/tuner/SampleNetworkTuner/Android.bp
index 4c2b344e..f4accb45 100644
--- a/tuner/SampleNetworkTuner/Android.bp
+++ b/tuner/SampleNetworkTuner/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_app {
name: "SampleNetworkTuner",
diff --git a/tuner/SampleNetworkTuner/AndroidManifest.xml b/tuner/SampleNetworkTuner/AndroidManifest.xml
index 348bfaae..60110a34 100755
--- a/tuner/SampleNetworkTuner/AndroidManifest.xml
+++ b/tuner/SampleNetworkTuner/AndroidManifest.xml
@@ -14,80 +14,73 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.tv.tuner.sample.network" >
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.tv.tuner.sample.network">
- <uses-sdk
- android:minSdkVersion="23"
- android:targetSdkVersion="29" />
+ <uses-sdk android:minSdkVersion="23"
+ android:targetSdkVersion="29"/>
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.INTERNET" />
- <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" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <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" />
+ <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" />
+ <uses-permission android:name="android.permission.DVB_DEVICE"/>
- <uses-feature
- android:name="android.hardware.usb.host"
- android:required="false" />
+ <uses-feature android:name="android.hardware.usb.host"
+ android:required="false"/>
<!-- Limit only for Android TV -->
- <uses-feature
- android:name="android.software.leanback"
- android:required="true" />
- <uses-feature
- android:name="android.software.live_tv"
- android:required="true" />
- <uses-feature
- android:name="android.hardware.touchscreen"
- android:required="false" />
+ <uses-feature android:name="android.software.leanback"
+ android:required="true"/>
+ <uses-feature android:name="android.software.live_tv"
+ android:required="true"/>
+ <uses-feature android:name="android.hardware.touchscreen"
+ android:required="false"/>
<application tools:replace="android:appComponentFactory"
- 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" >
+ 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.android.tv.tuner.sample.network.setup.SampleNetworkTunerSetupActivity"
- android:configChanges="keyboard|keyboardHidden"
- android:exported="true"
- android:label="@string/sample_network_tuner_app_name"
- android:launchMode="singleInstance"
- android:theme="@style/Theme.Setup.GuidedStep" >
+ <activity android:name="com.android.tv.tuner.sample.network.setup.SampleNetworkTunerSetupActivity"
+ android:configChanges="keyboard|keyboardHidden"
+ android:exported="true"
+ android:label="@string/sample_network_tuner_app_name"
+ android:launchMode="singleInstance"
+ android:theme="@style/Theme.Setup.GuidedStep">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
- <service
- 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.android.tv.tuner.sample.network.tvinput" >
+ <service 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.android.tv.tuner.sample.network.tvinput"
+ android:exported="true">
<intent-filter>
- <action android:name="android.media.tv.TvInputService" />
+ <action android:name="android.media.tv.TvInputService"/>
</intent-filter>
- <meta-data
- android:name="android.media.tv.input"
- android:resource="@xml/sample_network_tvinputservice" />
+ <meta-data android:name="android.media.tv.input"
+ android:resource="@xml/sample_network_tvinputservice"/>
</service>
- <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.tuner.tvinput.TunerStorageCleanUpService"
+ android:exported="false"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:process="com.android.tv.tuner"/>
</application>
</manifest>
diff --git a/tuner/lint-baseline.xml b/tuner/lint-baseline.xml
new file mode 100644
index 00000000..a0db5e0b
--- /dev/null
+++ b/tuner/lint-baseline.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 7.2.0-dev" client="cli" variant="all" version="4.1.0">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 26 (current min is 23): `android.app.NotificationManager#createNotificationChannel`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java"
+ line="399"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 26 (current min is 23): `new android.app.NotificationChannel`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java"
+ line="400"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 26 (current min is 23): `new android.app.Notification.Builder`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java"
+ line="406"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.app.job.JobScheduler#getPendingJob`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java"
+ line="94"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Extending RecordingSessionCompat requires API level 24 (current min is 23): `RecordingSessionCompat`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java"
+ line="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `RecordingSessionCompat`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java"
+ line="54"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputService.RecordingSession#notifyTuned`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java"
+ line="107"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `notifyRecordingStarted`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java"
+ line="116"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputService.RecordingSession#notifyRecordingStopped`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java"
+ line="125"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputService.RecordingSession#notifyError`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java"
+ line="131"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Extending RecordingSessionCompat requires API level 24 (current min is 23): `RecordingSessionCompat`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java"
+ line="39"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `RecordingSessionCompat`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java"
+ line="54"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputService.RecordingSession#notifyTuned`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java"
+ line="107"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `notifyRecordingStarted`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java"
+ line="116"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputService.RecordingSession#notifyRecordingStopped`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java"
+ line="125"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 24 (current min is 23): `android.media.tv.TvInputService.RecordingSession#notifyError`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java"
+ line="131"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Field requires API level 24 (current min is 23): `android.media.tv.TvContract.RecordedPrograms#CONTENT_URI`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java"
+ line="616"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Field requires API level 24 (current min is 23): `android.media.tv.TvContract.RecordedPrograms#CONTENT_URI`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java"
+ line="689"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Field requires API level 24 (current min is 23): `android.media.tv.TvContract.RecordedPrograms#CONTENT_URI`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorkerExoV2.java"
+ line="619"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Field requires API level 24 (current min is 23): `android.media.tv.TvContract.RecordedPrograms#CONTENT_URI`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorkerExoV2.java"
+ line="692"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Field requires API level 24 (current min is 23): `android.media.tv.TvContract.RecordedPrograms#CONTENT_URI`">
+ <location
+ file="packages/apps/TV/tuner/src/com/android/tv/tuner/tvinput/TunerStorageCleanUpService.java"
+ line="101"/>
+ </issue>
+
+</issues>
diff --git a/tuner/proto/Android.bp b/tuner/proto/Android.bp
index d1728a64..128eef95 100644
--- a/tuner/proto/Android.bp
+++ b/tuner/proto/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
java_library {
name: "live-tv-tuner-proto",
srcs: ["*.proto"],
diff --git a/tuner/sampletunertvinput/Android.bp b/tuner/sampletunertvinput/Android.bp
index 9d737c84..4e5900bb 100644
--- a/tuner/sampletunertvinput/Android.bp
+++ b/tuner/sampletunertvinput/Android.bp
@@ -14,6 +14,10 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
android_app {
name: "sampletunertvinput",
srcs: ["src/**/*.java"],
@@ -25,7 +29,6 @@ android_app {
platform_apis: true,
system_ext_specific: true,
-
privileged: true,
certificate: "platform",
// product_specific: true,
@@ -53,10 +56,19 @@ android_app {
"tv-lib-dagger-android",
"tv-test-common",
],
+ optional_uses_libs: ["com.android.libraries.tv.tvsystem"],
aaptflags: ["-0 .ts"],
plugins: [
"tv-auto-value",
"tv-auto-factory",
],
+ required: ["com.android.tv.samples.sampletunertvinput.xml"],
// min_sdk_version: "29",
}
+
+prebuilt_etc {
+ name: "com.android.tv.samples.sampletunertvinput.xml",
+ sub_dir: "permissions",
+ src: "com.android.tv.samples.sampletunertvinput.xml",
+ system_ext_specific: true,
+}
diff --git a/tuner/sampletunertvinput/AndroidManifest.xml b/tuner/sampletunertvinput/AndroidManifest.xml
index d282889a..8b25d0bf 100644
--- a/tuner/sampletunertvinput/AndroidManifest.xml
+++ b/tuner/sampletunertvinput/AndroidManifest.xml
@@ -43,7 +43,8 @@
android:theme="@android:style/Theme.Holo.Light.NoActionBar"
android:appComponentFactory="android.support.v4.app.CoreComponentFactory" >
<uses-library android:name="com.android.libraries.tv.tvsystem" android:required="false" />
- <activity android:name=".SampleTunerTvInputSetupActivity" >
+ <activity android:name=".SampleTunerTvInputSetupActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
@@ -51,7 +52,8 @@
<service android:name=".SampleTunerTvInputService"
android:permission="android.permission.BIND_TV_INPUT"
android:label="@string/sample_tuner_tv_input"
- android:process="com.android.tv.samples.sampletunertvinput">
+ android:process="com.android.tv.samples.sampletunertvinput"
+ android:exported="true">
<intent-filter>
<action android:name="android.media.tv.TvInputService" />
</intent-filter>
diff --git a/tuner/sampletunertvinput/com.android.tv.samples.sampletunertvinput.xml b/tuner/sampletunertvinput/com.android.tv.samples.sampletunertvinput.xml
new file mode 100644
index 00000000..d98e36ce
--- /dev/null
+++ b/tuner/sampletunertvinput/com.android.tv.samples.sampletunertvinput.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<permissions>
+ <privapp-permissions package="com.android.tv.samples.sampletunertvinput">
+ <permission name="android.permission.ACCESS_TV_DESCRAMBLER"/>
+ <permission name="android.permission.ACCESS_TV_TUNER"/>
+ <permission name="android.permission.TUNER_RESOURCE_ACCESS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml
index 8fc96b2a..909e2431 100644
--- a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml
+++ b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml
@@ -39,7 +39,8 @@
android:icon="@mipmap/ic_launcher"
android:theme="@android:style/Theme.Holo.Light.NoActionBar"
android:appComponentFactory="android.support.v4.app.CoreComponentFactory" >
- <activity android:name=".SampleTunerTvInputSetupActivity" >
+ <activity android:name=".SampleTunerTvInputSetupActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
@@ -47,7 +48,8 @@
<service android:name=".SampleTunerTvInputService"
android:permission="android.permission.BIND_TV_INPUT"
android:label="@string/sample_tuner_tv_input"
- android:process="com.android.tv.samples.sampletunertvinput">
+ android:process="com.android.tv.samples.sampletunertvinput"
+ android:exported="true">
<intent-filter>
<action android:name="android.media.tv.TvInputService" />
</intent-filter>
diff --git a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java
index 6ac95353..03e79650 100644
--- a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java
+++ b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java
@@ -1,13 +1,40 @@
package com.android.tv.samples.sampletunertvinput;
+import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN;
+
import android.content.Context;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodec.LinearBlock;
+import android.media.MediaFormat;
+import android.media.tv.tuner.dvr.DvrPlayback;
+import android.media.tv.tuner.dvr.DvrSettings;
+import android.media.tv.tuner.filter.AvSettings;
+import android.media.tv.tuner.filter.Filter;
+import android.media.tv.tuner.filter.FilterCallback;
+import android.media.tv.tuner.filter.FilterEvent;
+import android.media.tv.tuner.filter.MediaEvent;
+import android.media.tv.tuner.filter.TsFilterConfiguration;
import android.media.tv.tuner.frontend.AtscFrontendSettings;
+import android.media.tv.tuner.frontend.DvbtFrontendSettings;
import android.media.tv.tuner.frontend.FrontendSettings;
+import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.Tuner;
import android.media.tv.TvInputService;
import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.Surface;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
/** SampleTunerTvInputService */
@@ -15,8 +42,39 @@ public class SampleTunerTvInputService extends TvInputService {
private static final String TAG = "SampleTunerTvInput";
private static final boolean DEBUG = true;
+ private static final int AUDIO_TPID = 257;
+ private static final int VIDEO_TPID = 256;
+ private static final int STATUS_MASK = 0xf;
+ private static final int LOW_THRESHOLD = 0x1000;
+ private static final int HIGH_THRESHOLD = 0x07fff;
+ private static final int FREQUENCY = 578000;
+ private static final int FILTER_BUFFER_SIZE = 16000000;
+ private static final int DVR_BUFFER_SIZE = 4000000;
+ private static final int INPUT_FILE_MAX_SIZE = 700000;
+ private static final int PACKET_SIZE = 188;
+
+ private static final int TIMEOUT_US = 100000;
+ private static final boolean SAVE_DATA = false;
+ private static final String ES_FILE_NAME = "test.es";
+ private static final MediaFormat VIDEO_FORMAT;
+
+ static {
+ // format extracted for the specific input file
+ VIDEO_FORMAT = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 320, 240);
+ VIDEO_FORMAT.setInteger(MediaFormat.KEY_TRACK_ID, 1);
+ VIDEO_FORMAT.setLong(MediaFormat.KEY_DURATION, 9933333);
+ VIDEO_FORMAT.setInteger(MediaFormat.KEY_LEVEL, 32);
+ VIDEO_FORMAT.setInteger(MediaFormat.KEY_PROFILE, 65536);
+ ByteBuffer csd = ByteBuffer.wrap(
+ new byte[] {0, 0, 0, 1, 103, 66, -64, 20, -38, 5, 7, -24, 64, 0, 0, 3, 0, 64, 0,
+ 0, 15, 35, -59, 10, -88});
+ VIDEO_FORMAT.setByteBuffer("csd-0", csd);
+ csd = ByteBuffer.wrap(new byte[] {0, 0, 0, 1, 104, -50, 60, -128});
+ VIDEO_FORMAT.setByteBuffer("csd-1", csd);
+ }
+
public static final String INPUT_ID =
- "com.android.tv.samples.sampletunertvinput/.SampleTunerTvInputService";
+ "com.android.tv.samples.sampletunertvinput/.SampleTunerTvInputService";
private String mSessionId;
@Override
@@ -36,9 +94,19 @@ public class SampleTunerTvInputService extends TvInputService {
class TvInputSessionImpl extends Session {
- private Surface surface;
private final Context mContext;
- Tuner tuner;
+ private Handler mHandler;
+
+ private Surface mSurface;
+ private Filter mAudioFilter;
+ private Filter mVideoFilter;
+ private DvrPlayback mDvr;
+ private Tuner mTuner;
+ private MediaCodec mMediaCodec;
+ private Thread mDecoderThread;
+ private Deque<MediaEvent> mDataQueue;
+ private List<MediaEvent> mSavedData;
+ private boolean mDataReady = false;
public TvInputSessionImpl(Context context) {
@@ -51,6 +119,30 @@ public class SampleTunerTvInputService extends TvInputService {
if (DEBUG) {
Log.d(TAG, "onRelease");
}
+ if (mDecoderThread != null) {
+ mDecoderThread.interrupt();
+ mDecoderThread = null;
+ }
+ if (mMediaCodec != null) {
+ mMediaCodec.release();
+ mMediaCodec = null;
+ }
+ if (mAudioFilter != null) {
+ mAudioFilter.close();
+ }
+ if (mVideoFilter != null) {
+ mVideoFilter.close();
+ }
+ if (mDvr != null) {
+ mDvr.close();
+ mDvr = null;
+ }
+ if (mTuner != null) {
+ mTuner.close();
+ mTuner = null;
+ }
+ mDataQueue = null;
+ mSavedData = null;
}
@Override
@@ -58,7 +150,7 @@ public class SampleTunerTvInputService extends TvInputService {
if (DEBUG) {
Log.d(TAG, "onSetSurface");
}
- this.surface = surface;
+ this.mSurface = surface;
return true;
}
@@ -74,20 +166,16 @@ public class SampleTunerTvInputService extends TvInputService {
if (DEBUG) {
Log.d(TAG, "onTune " + uri);
}
- tuner = new Tuner(mContext, mSessionId,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
-
- int feCount = tuner.getFrontendIds().size();
- if (feCount <= 0) return false;
-
- AtscFrontendSettings settings =
- AtscFrontendSettings
- .builder()
- .setFrequency(2000)
- .setModulation(AtscFrontendSettings.MODULATION_AUTO)
- .build();
- tuner.tune(settings);
-
+ if (!initCodec()) {
+ Log.e(TAG, "null codec!");
+ return false;
+ }
+ mHandler = new Handler();
+ mDecoderThread =
+ new Thread(
+ this::decodeInternal,
+ "sample-tuner-tis-decoder-thread");
+ mDecoderThread.start();
return true;
}
@@ -97,5 +185,291 @@ public class SampleTunerTvInputService extends TvInputService {
Log.d(TAG, "onSetCaptionEnabled " + b);
}
}
+
+ private Filter audioFilter() {
+ Filter audioFilter = mTuner.openFilter(Filter.TYPE_TS, Filter.SUBTYPE_AUDIO,
+ FILTER_BUFFER_SIZE, new HandlerExecutor(mHandler),
+ new FilterCallback() {
+ @Override
+ public void onFilterEvent(Filter filter, FilterEvent[] events) {
+ if (DEBUG) {
+ Log.d(TAG, "onFilterEvent audio, size=" + events.length);
+ }
+ for (int i = 0; i < events.length; i++) {
+ if (DEBUG) {
+ Log.d(TAG, "events[" + i + "] is "
+ + events[i].getClass().getSimpleName());
+ }
+ }
+ }
+
+ @Override
+ public void onFilterStatusChanged(Filter filter, int status) {
+ if (DEBUG) {
+ Log.d(TAG, "onFilterEvent audio, status=" + status);
+ }
+ }
+ });
+ AvSettings settings =
+ AvSettings.builder(Filter.TYPE_TS, true).setPassthrough(false).build();
+ audioFilter.configure(
+ TsFilterConfiguration.builder().setTpid(AUDIO_TPID)
+ .setSettings(settings).build());
+ return audioFilter;
+ }
+
+ private Filter videoFilter() {
+ Filter videoFilter = mTuner.openFilter(Filter.TYPE_TS, Filter.SUBTYPE_VIDEO,
+ FILTER_BUFFER_SIZE, new HandlerExecutor(mHandler),
+ new FilterCallback() {
+ @Override
+ public void onFilterEvent(Filter filter, FilterEvent[] events) {
+ if (DEBUG) {
+ Log.d(TAG, "onFilterEvent video, size=" + events.length);
+ }
+ for (int i = 0; i < events.length; i++) {
+ if (DEBUG) {
+ Log.d(TAG, "events[" + i + "] is "
+ + events[i].getClass().getSimpleName());
+ }
+ if (events[i] instanceof MediaEvent) {
+ MediaEvent me = (MediaEvent) events[i];
+ mDataQueue.add(me);
+ if (SAVE_DATA) {
+ mSavedData.add(me);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFilterStatusChanged(Filter filter, int status) {
+ if (DEBUG) {
+ Log.d(TAG, "onFilterEvent video, status=" + status);
+ }
+ if (status == Filter.STATUS_DATA_READY) {
+ mDataReady = true;
+ }
+ }
+ });
+ AvSettings settings =
+ AvSettings.builder(Filter.TYPE_TS, false).setPassthrough(false).build();
+ videoFilter.configure(
+ TsFilterConfiguration.builder().setTpid(VIDEO_TPID)
+ .setSettings(settings).build());
+ return videoFilter;
+ }
+
+ private DvrPlayback dvrPlayback() {
+ DvrPlayback dvr = mTuner.openDvrPlayback(DVR_BUFFER_SIZE, new HandlerExecutor(mHandler),
+ status -> {
+ if (DEBUG) {
+ Log.d(TAG, "onPlaybackStatusChanged status=" + status);
+ }
+ });
+ int res = dvr.configure(
+ DvrSettings.builder()
+ .setStatusMask(STATUS_MASK)
+ .setLowThreshold(LOW_THRESHOLD)
+ .setHighThreshold(HIGH_THRESHOLD)
+ .setDataFormat(DvrSettings.DATA_FORMAT_ES)
+ .setPacketSize(PACKET_SIZE)
+ .build());
+ if (DEBUG) {
+ Log.d(TAG, "config res=" + res);
+ }
+ String testFile = mContext.getFilesDir().getAbsolutePath() + "/" + ES_FILE_NAME;
+ File file = new File(testFile);
+ if (file.exists()) {
+ try {
+ dvr.setFileDescriptor(
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Failed to create FD");
+ }
+ } else {
+ Log.w(TAG, "File not existing");
+ }
+ return dvr;
+ }
+
+ private void tune() {
+ DvbtFrontendSettings feSettings = DvbtFrontendSettings.builder()
+ .setFrequency(FREQUENCY)
+ .setTransmissionMode(DvbtFrontendSettings.TRANSMISSION_MODE_AUTO)
+ .setBandwidth(DvbtFrontendSettings.BANDWIDTH_8MHZ)
+ .setConstellation(DvbtFrontendSettings.CONSTELLATION_AUTO)
+ .setHierarchy(DvbtFrontendSettings.HIERARCHY_AUTO)
+ .setHighPriorityCodeRate(DvbtFrontendSettings.CODERATE_AUTO)
+ .setLowPriorityCodeRate(DvbtFrontendSettings.CODERATE_AUTO)
+ .setGuardInterval(DvbtFrontendSettings.GUARD_INTERVAL_AUTO)
+ .setHighPriority(true)
+ .setStandard(DvbtFrontendSettings.STANDARD_T)
+ .build();
+ mTuner.setOnTuneEventListener(new HandlerExecutor(mHandler), new OnTuneEventListener() {
+ @Override
+ public void onTuneEvent(int tuneEvent) {
+ if (DEBUG) {
+ Log.d(TAG, "onTuneEvent " + tuneEvent);
+ }
+ long read = mDvr.read(INPUT_FILE_MAX_SIZE);
+ if (DEBUG) {
+ Log.d(TAG, "read=" + read);
+ }
+ }
+ });
+ mTuner.tune(feSettings);
+ }
+
+ private boolean initCodec() {
+ if (mMediaCodec != null) {
+ mMediaCodec.release();
+ mMediaCodec = null;
+ }
+ try {
+ mMediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
+ mMediaCodec.configure(VIDEO_FORMAT, mSurface, null, 0);
+ } catch (IOException e) {
+ Log.e(TAG, "Error in initCodec: " + e.getMessage());
+ }
+
+ if (mMediaCodec == null) {
+ Log.e(TAG, "null codec!");
+ notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_UNKNOWN);
+ return false;
+ }
+ return true;
+ }
+
+ private void decodeInternal() {
+ mDataQueue = new ArrayDeque<>();
+ mSavedData = new ArrayList<>();
+ mTuner = new Tuner(mContext, mSessionId,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
+
+ mAudioFilter = audioFilter();
+ mVideoFilter = videoFilter();
+ mAudioFilter.start();
+ mVideoFilter.start();
+ // use dvr playback to feed the data on platform without physical tuner
+ mDvr = dvrPlayback();
+ tune();
+ mDvr.start();
+ mMediaCodec.start();
+
+ try {
+ while (!Thread.interrupted()) {
+ if (!mDataReady) {
+ Thread.sleep(100);
+ continue;
+ }
+ if (!mDataQueue.isEmpty()) {
+ if (handleDataBuffer(mDataQueue.getFirst())) {
+ // data consumed, remove.
+ mDataQueue.pollFirst();
+ }
+ }
+ if (SAVE_DATA) {
+ mDataQueue.addAll(mSavedData);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in decodeInternal: " + e.getMessage());
+ }
+ }
+
+ private boolean handleDataBuffer(MediaEvent mediaEvent) {
+ if (mediaEvent.getLinearBlock() == null) {
+ if (DEBUG) Log.d(TAG, "getLinearBlock() == null");
+ return true;
+ }
+ boolean success = false;
+ LinearBlock block = mediaEvent.getLinearBlock();
+ if (queueCodecInputBuffer(block, mediaEvent.getDataLength(), mediaEvent.getOffset(),
+ mediaEvent.getPts())) {
+ releaseCodecOutputBuffer();
+ success = true;
+ }
+ mediaEvent.release();
+ return success;
+ }
+
+ private boolean queueCodecInputBuffer(LinearBlock block, long sampleSize,
+ long offset, long pts) {
+ int res = mMediaCodec.dequeueInputBuffer(TIMEOUT_US);
+ if (res >= 0) {
+ ByteBuffer buffer = mMediaCodec.getInputBuffer(res);
+ if (buffer == null) {
+ throw new RuntimeException("Null decoder input buffer");
+ }
+
+ ByteBuffer data = block.map();
+ if (offset > 0 && offset < data.limit()) {
+ data.position((int) offset);
+ } else {
+ data.position(0);
+ }
+
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "Decoder: Send data to decoder."
+ + " Sample size="
+ + sampleSize
+ + " pts="
+ + pts
+ + " limit="
+ + data.limit()
+ + " pos="
+ + data.position()
+ + " size="
+ + (data.limit() - data.position()));
+ }
+ // fill codec input buffer
+ int size = sampleSize > data.limit() ? data.limit() : (int) sampleSize;
+ if (DEBUG) Log.d(TAG, "limit " + data.limit() + " sampleSize " + sampleSize);
+ if (data.hasArray()) {
+ Log.d(TAG, "hasArray");
+ buffer.put(data.array(), 0, size);
+ } else {
+ byte[] array = new byte[size];
+ data.get(array, 0, size);
+ buffer.put(array, 0, size);
+ }
+
+ mMediaCodec.queueInputBuffer(res, 0, (int) sampleSize, pts, 0);
+ } else {
+ if (DEBUG) Log.d(TAG, "queueCodecInputBuffer res=" + res);
+ return false;
+ }
+ return true;
+ }
+
+ private void releaseCodecOutputBuffer() {
+ // play frames
+ BufferInfo bufferInfo = new BufferInfo();
+ int res = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
+ if (res >= 0) {
+ mMediaCodec.releaseOutputBuffer(res, true);
+ notifyVideoAvailable();
+ if (DEBUG) {
+ Log.d(TAG, "notifyVideoAvailable");
+ }
+ } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat format = mMediaCodec.getOutputFormat();
+ if (DEBUG) {
+ Log.d(TAG, "releaseCodecOutputBuffer: Output format changed:" + format);
+ }
+ } else if (res == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ if (DEBUG) {
+ Log.d(TAG, "releaseCodecOutputBuffer: timeout");
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Return value of releaseCodecOutputBuffer:" + res);
+ }
+ }
+ }
+
}
-} \ No newline at end of file
+}
diff --git a/tuner/src/com/android/tv/tuner/data/Cea708Parser.java b/tuner/src/com/android/tv/tuner/data/Cea708Parser.java
index 7a5538c8..67683ccb 100644
--- a/tuner/src/com/android/tv/tuner/data/Cea708Parser.java
+++ b/tuner/src/com/android/tv/tuner/data/Cea708Parser.java
@@ -142,7 +142,7 @@ public class Cea708Parser {
private boolean mDtvCcPacking = false;
private boolean mFirstServiceNumberDiscovered;
- // Assign a dummy listener in order to avoid null checks.
+ // Assign an empty listener in order to avoid null checks.
private OnCea708ParserListener mListener =
new OnCea708ParserListener() {
@Override
diff --git a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java
index 89b55d65..b62c1867 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java
@@ -257,7 +257,7 @@ public class MpegTsPlayer
mBuilderCallback = null;
for (int i = 0; i < RENDERER_COUNT; i++) {
if (renderers[i] == null) {
- // Convert a null renderer to a dummy renderer.
+ // Convert a null renderer to an empty renderer.
renderers[i] = new DummyTrackRenderer();
}
}
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerExtractorsFactory.java b/tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerExtractorsFactory.java
deleted file mode 100644
index 6af8a26a..00000000
--- a/tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerExtractorsFactory.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2017 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.exoplayer2;
-
-import com.google.android.exoplayer2.extractor.Extractor;
-import com.google.android.exoplayer2.extractor.ExtractorsFactory;
-import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
-import com.google.android.exoplayer2.extractor.ts.TsExtractor;
-import com.google.android.exoplayer2.util.TimestampAdjuster;
-
-/**
- * Extractor factory, mainly aim at create TsExtractor with FLAG_ALLOW_NON_IDR_KEYFRAMES flags for
- * H.264 stream
- */
-public final class ExoPlayerExtractorsFactory implements ExtractorsFactory {
- @Override
- public Extractor[] createExtractors() {
- // Only create TsExtractor since we only target MPEG2TS stream.
- Extractor[] extractors = {
- new TsExtractor(
- TsExtractor.MODE_SINGLE_PMT,
- new TimestampAdjuster(0),
- new DefaultTsPayloadReaderFactory(
- DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES))
- };
- return extractors;
- }
-}
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerSampleExtractor.java
index 360f10c0..72462f25 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerSampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/ExoPlayerSampleExtractor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
@@ -20,29 +20,24 @@ import android.net.Uri;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Pair;
-import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer;
-import com.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener;
-import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer;
-import com.android.tv.tuner.exoplayer.buffer.SimpleSampleBuffer;
+import com.android.tv.tuner.exoplayer2.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer2.buffer.MemorySampleBuffer;
+import com.android.tv.tuner.exoplayer2.buffer.PlaybackBufferListener;
+import com.android.tv.tuner.exoplayer2.buffer.RecordingSampleBuffer;
-import com.google.android.exoplayer.MediaFormat;
-import com.google.android.exoplayer.MediaFormatHolder;
-import com.google.android.exoplayer.SampleHolder;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
-import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
@@ -65,9 +60,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
* For demux, this class relies on {@link com.google.android.exoplayer.extractor.ts.TsExtractor}.
*/
public class ExoPlayerSampleExtractor implements SampleExtractor {
- private static final String TAG = "ExoPlayerSampleExtracto";
+ private static final String TAG = "ExoPlayerSampleExtractor";
private static final int INVALID_TRACK_INDEX = -1;
+
+ // ATSC/53 allows sample rate to be only 48Khz.
+ // One AC3 sample has 1536 frames, and its duration is 32ms.
+ private static final long AC3_SAMPLE_DURATION_US = 32000;
+ // This is around 150ms, 150ms is big enough not to under-run AudioTrack,
+ // and 150ms is also small enough to fill the buffer rapidly.
+ private static final int BUFFERED_SAMPLES_IN_AUDIOTRACK = 5;
+ private static final long INITIAL_AUDIO_BUFFERING_TIME_US =
+ BUFFERED_SAMPLES_IN_AUDIOTRACK * AC3_SAMPLE_DURATION_US;
+
private final HandlerThread mSourceReaderThread;
private final long mId;
@@ -76,17 +81,20 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
private BufferManager.SampleBuffer mSampleBuffer;
private Handler mSourceReaderHandler;
private volatile boolean mPrepared;
- private AtomicBoolean mOnCompletionCalled = new AtomicBoolean();
+ private final AtomicBoolean mOnCompletionCalled = new AtomicBoolean();
private IOException mExceptionOnPrepare;
- private List<MediaFormat> mTrackFormats;
+ private List<Format> mTrackFormats;
+ private TrackGroupArray mTrackGroupArray;
private int mVideoTrackIndex = INVALID_TRACK_INDEX;
private boolean mVideoTrackMet;
private long mBaseSamplePts = Long.MIN_VALUE;
- private HashMap<Integer, Long> mLastExtractedPositionUsMap = new HashMap<>();
- private final List<Pair<Integer, SampleHolder>> mPendingSamples = new ArrayList<>();
+ private final HashMap<Integer, Long> mLastExtractedPositionUsMap = new HashMap<>();
+ private final List<Pair<Integer, DecoderInputBuffer>> mPendingSamples = new ArrayList<>();
private OnCompletionListener mOnCompletionListener;
private Handler mOnCompletionListenerHandler;
private IOException mError;
+ private MediaPeriod mMediaPeriod;
+ private Callback mCallback;
/**
* Factory for {@link ExoPlayerSampleExtractor}.
@@ -95,7 +103,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
* generated class.
*/
public interface Factory {
- public ExoPlayerSampleExtractor create(
+ ExoPlayerSampleExtractor create(
Uri uri,
DataSource source,
@Nullable BufferManager bufferManager,
@@ -117,20 +125,17 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
bufferManager,
bufferListener,
isRecording,
- Looper.myLooper(),
new HandlerThread("SourceReaderThread"),
recordingSampleBufferFactory);
}
@VisibleForTesting
- @SuppressWarnings("MissingOverride")
- public ExoPlayerSampleExtractor(
+ ExoPlayerSampleExtractor(
Uri uri,
DataSource source,
BufferManager bufferManager,
PlaybackBufferListener bufferListener,
boolean isRecording,
- Looper workerLooper,
HandlerThread sourceReaderThread,
RecordingSampleBuffer.Factory recordingSampleBufferFactory) {
// It'll be used as a timeshift file chunk name's prefix.
@@ -139,12 +144,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
mSourceReaderThread = sourceReaderThread;
mSourceReaderWorker =
new SourceReaderWorker(
- new ExtractorMediaSource(
- uri,
- /* dataSourceFactory= */ () -> source,
- new ExoPlayerExtractorsFactory(),
- new Handler(workerLooper),
- /* eventListener= */ error -> mError = error));
+ new ProgressiveMediaSource.Factory(() -> source).createMediaSource(uri));
if (isRecording) {
mSampleBuffer =
recordingSampleBufferFactory.create(
@@ -154,7 +154,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
RecordingSampleBuffer.BUFFER_REASON_RECORDING);
} else {
if (bufferManager == null) {
- mSampleBuffer = new SimpleSampleBuffer(bufferListener);
+ mSampleBuffer = new MemorySampleBuffer(bufferListener);
} else {
mSampleBuffer =
recordingSampleBufferFactory.create(
@@ -173,23 +173,22 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
}
private class SourceReaderWorker implements Handler.Callback, MediaPeriod.Callback {
- public static final int MSG_PREPARE = 1;
- public static final int MSG_FETCH_SAMPLES = 2;
- public static final int MSG_RELEASE = 3;
+ private static final int MSG_PREPARE = 1;
+ private static final int MSG_FETCH_SAMPLES = 2;
+ private static final int MSG_RELEASE = 3;
private static final int RETRY_INTERVAL_MS = 50;
private final MediaSource mSampleSource;
private final MediaSource.SourceInfoRefreshListener mSampleSourceListener;
- private MediaPeriod mMediaPeriod;
private SampleStream[] mStreams;
private boolean[] mTrackMetEos;
private boolean mMetEos = false;
private long mCurrentPosition;
- private DecoderInputBuffer mDecoderInputBuffer;
- private SampleHolder mSampleHolder;
+ private final DecoderInputBuffer mDecoderInputBuffer;
+ private final DecoderInputBuffer mDecoderInputBufferDuplicate;
private boolean mPrepareRequested;
- public SourceReaderWorker(MediaSource sampleSource) {
+ SourceReaderWorker(MediaSource sampleSource) {
mSampleSource = sampleSource;
mSampleSourceListener =
(source, timeline, manifest) -> {
@@ -199,53 +198,8 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
mSampleSource.prepareSource(mSampleSourceListener, null);
mDecoderInputBuffer =
new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
- mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
- }
-
- MediaFormat convertFormat(Format format) {
- if (format.sampleMimeType.startsWith("audio/")) {
- return MediaFormat.createAudioFormat(
- format.id,
- format.sampleMimeType,
- format.bitrate,
- format.maxInputSize,
- com.google.android.exoplayer.C.UNKNOWN_TIME_US,
- format.channelCount,
- format.sampleRate,
- format.initializationData,
- format.language,
- format.pcmEncoding);
- } else if (format.sampleMimeType.startsWith("video/")) {
- return MediaFormat.createVideoFormat(
- format.id,
- format.sampleMimeType,
- format.bitrate,
- format.maxInputSize,
- com.google.android.exoplayer.C.UNKNOWN_TIME_US,
- format.width,
- format.height,
- format.initializationData,
- format.rotationDegrees,
- format.pixelWidthHeightRatio,
- format.projectionData,
- format.stereoMode,
- null // colorInfo
- );
- } else if (format.sampleMimeType.endsWith("/cea-608")
- || format.sampleMimeType.startsWith("text/")) {
- return MediaFormat.createTextFormat(
- format.id,
- format.sampleMimeType,
- format.bitrate,
- com.google.android.exoplayer.C.UNKNOWN_TIME_US,
- format.language);
- } else {
- return MediaFormat.createFormatForMimeType(
- format.id,
- format.sampleMimeType,
- format.bitrate,
- com.google.android.exoplayer.C.UNKNOWN_TIME_US);
- }
+ mDecoderInputBufferDuplicate =
+ new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
}
@Override
@@ -255,6 +209,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
return;
}
TrackGroupArray trackGroupArray = mMediaPeriod.getTrackGroups();
+ mTrackGroupArray = trackGroupArray;
TrackSelection[] selections = new TrackSelection[trackGroupArray.length];
for (int i = 0; i < selections.length; ++i) {
selections[i] = new FixedTrackSelection(trackGroupArray.get(i), 0);
@@ -266,7 +221,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
if (mTrackFormats == null) {
int trackCount = trackGroupArray.length;
mTrackMetEos = new boolean[trackCount];
- List<MediaFormat> trackFormats = new ArrayList<>();
+ List<Format> trackFormats = new ArrayList<>();
int videoTrackCount = 0;
for (int i = 0; i < trackCount; i++) {
Format format = trackGroupArray.get(i).getFormat(0);
@@ -274,7 +229,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
videoTrackCount++;
mVideoTrackIndex = i;
}
- trackFormats.add(convertFormat(format));
+ trackFormats.add(format);
}
if (videoTrackCount > 1) {
// Disable dropping samples when there are multiple video tracks.
@@ -296,6 +251,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
}
mSourceReaderHandler.sendEmptyMessage(MSG_FETCH_SAMPLES);
mPrepared = true;
+ mCallback.onPrepared();
}
}
@@ -413,35 +369,26 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
if (mVideoTrackIndex != INVALID_TRACK_INDEX) {
if (!mVideoTrackMet) {
if (index != mVideoTrackIndex) {
- SampleHolder sample =
- new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL);
- mSampleHolder.flags =
- (mDecoderInputBuffer.isKeyFrame()
- ? com.google.android.exoplayer.C.SAMPLE_FLAG_SYNC
- : 0)
- | (mDecoderInputBuffer.isDecodeOnly()
- ? com.google
- .android
- .exoplayer
- .C
- .SAMPLE_FLAG_DECODE_ONLY
- : 0);
+ DecoderInputBuffer sample =
+ new DecoderInputBuffer(
+ DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
+ sample.setFlags(
+ (mDecoderInputBuffer.isDecodeOnly() ? C.BUFFER_FLAG_DECODE_ONLY : 0)
+ | (mDecoderInputBuffer.isEncrypted() ? C.BUFFER_FLAG_ENCRYPTED : 0)
+ | (mDecoderInputBuffer.isKeyFrame() ? C.BUFFER_FLAG_KEY_FRAME : 0));
sample.timeUs = mDecoderInputBuffer.timeUs;
- sample.size = mDecoderInputBuffer.data.position();
- sample.ensureSpaceForWrite(sample.size);
+ int size = mDecoderInputBuffer.data.position();
+ sample.ensureSpaceForWrite(size);
mDecoderInputBuffer.flip();
- sample.data.position(0);
+ sample.data.position(0).limit(size);
sample.data.put(mDecoderInputBuffer.data);
sample.data.flip();
mPendingSamples.add(Pair.create(index, sample));
return;
}
mVideoTrackMet = true;
- mBaseSamplePts =
- mDecoderInputBuffer.timeUs
- - MpegTsDefaultAudioTrackRenderer
- .INITIAL_AUDIO_BUFFERING_TIME_US;
- for (Pair<Integer, SampleHolder> pair : mPendingSamples) {
+ mBaseSamplePts = mDecoderInputBuffer.timeUs - INITIAL_AUDIO_BUFFERING_TIME_US;
+ for (Pair<Integer, DecoderInputBuffer> pair : mPendingSamples) {
if (pair.second.timeUs >= mBaseSamplePts) {
mSampleBuffer.writeSample(pair.first, pair.second, conditionVariable);
}
@@ -453,28 +400,24 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
}
}
}
- // Copy the decoder input to the sample holder.
- mSampleHolder.clearData();
- mSampleHolder.flags =
- (mDecoderInputBuffer.isKeyFrame()
- ? com.google.android.exoplayer.C.SAMPLE_FLAG_SYNC
- : 0)
- | (mDecoderInputBuffer.isDecodeOnly()
- ? com.google.android.exoplayer.C.SAMPLE_FLAG_DECODE_ONLY
- : 0);
- mSampleHolder.timeUs = mDecoderInputBuffer.timeUs;
- mSampleHolder.size = mDecoderInputBuffer.data.position();
- mSampleHolder.ensureSpaceForWrite(mSampleHolder.size);
+ // Copy the decoder input buffer to pass to sample buffer.
+ mDecoderInputBufferDuplicate.clear();
+ mDecoderInputBufferDuplicate.setFlags(
+ (mDecoderInputBuffer.isDecodeOnly() ? C.BUFFER_FLAG_DECODE_ONLY : 0)
+ | (mDecoderInputBuffer.isEncrypted() ? C.BUFFER_FLAG_ENCRYPTED : 0)
+ | (mDecoderInputBuffer.isKeyFrame() ? C.BUFFER_FLAG_KEY_FRAME : 0));
+ mDecoderInputBufferDuplicate.timeUs = mDecoderInputBuffer.timeUs;
+ int size = mDecoderInputBuffer.data.position();
+ mDecoderInputBufferDuplicate.ensureSpaceForWrite(size);
mDecoderInputBuffer.flip();
- mSampleHolder.data.position(0);
- mSampleHolder.data.put(mDecoderInputBuffer.data);
- mSampleHolder.data.flip();
+ mDecoderInputBufferDuplicate.data.position(0);
+ mDecoderInputBufferDuplicate.data.put(mDecoderInputBuffer.data);
long writeStartTimeNs = SystemClock.elapsedRealtimeNanos();
- mSampleBuffer.writeSample(index, mSampleHolder, conditionVariable);
-
+ mSampleBuffer.writeSample(index, mDecoderInputBufferDuplicate, conditionVariable);
// Checks whether the storage has enough bandwidth for recording samples.
if (mSampleBuffer.isWriteSpeedSlow(
- mSampleHolder.size, SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) {
+ mDecoderInputBufferDuplicate.data.position(),
+ SystemClock.elapsedRealtimeNanos() - writeStartTimeNs)) {
mSampleBuffer.handleWriteSpeedSlow();
}
}
@@ -490,7 +433,8 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
}
@Override
- public boolean prepare() throws IOException {
+ public void prepare(Callback callback) throws IOException {
+ mCallback = callback;
if (!mSourceReaderThread.isAlive()) {
mSourceReaderThread.start();
mSourceReaderHandler =
@@ -498,20 +442,15 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
mSourceReaderHandler.sendEmptyMessage(SourceReaderWorker.MSG_PREPARE);
}
if (mExceptionOnPrepare != null) {
- throw mExceptionOnPrepare;
+ IOException e = mExceptionOnPrepare;
+ mExceptionOnPrepare = null;
+ throw e;
}
- return mPrepared;
}
@Override
- public List<MediaFormat> getTrackFormats() {
- return mTrackFormats;
- }
-
- @Override
- public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) {
- outMediaFormatHolder.format = mTrackFormats.get(track);
- outMediaFormatHolder.drmInitData = null;
+ public void getTrackMediaFormat(int track, FormatHolder outMediaFormatHolder) {
+ outMediaFormatHolder.format = mTrackGroupArray.get(track).getFormat(0);
}
@Override
@@ -526,12 +465,17 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
@Override
public long getBufferedPositionUs() {
- return mSampleBuffer.getBufferedPositionUs();
+ return (mPrepared ? mMediaPeriod.getBufferedPositionUs() : 0);
}
@Override
- public boolean continueBuffering(long positionUs) {
- return mSampleBuffer.continueBuffering(positionUs);
+ public long getNextLoadPositionUs() {
+ return (mPrepared ? mMediaPeriod.getNextLoadPositionUs() : 0);
+ }
+
+ @Override
+ public boolean continueLoading(long positionUs) {
+ return mSampleBuffer.continueLoading(positionUs);
}
@Override
@@ -540,7 +484,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
}
@Override
- public int readSample(int track, SampleHolder sampleHolder) {
+ public int readSample(int track, DecoderInputBuffer sampleHolder) {
return mSampleBuffer.readSample(track, sampleHolder);
}
@@ -558,6 +502,11 @@ public class ExoPlayerSampleExtractor implements SampleExtractor {
}
}
+ @Override
+ public TrackGroupArray getTrackGroups() {
+ return mTrackGroupArray;
+ }
+
private void cleanUp() {
boolean result = true;
try {
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/FileSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer2/FileSampleExtractor.java
index c4deb583..9bd5d374 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer2/FileSampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/FileSampleExtractor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
@@ -16,20 +16,29 @@
package com.android.tv.tuner.exoplayer2;
+import android.media.MediaFormat;
import android.os.Handler;
+import android.support.annotation.Nullable;
-import com.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener;
-import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer;
+import com.android.tv.tuner.exoplayer2.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer2.buffer.PlaybackBufferListener;
+import com.android.tv.tuner.exoplayer2.buffer.RecordingSampleBuffer;
-import com.google.android.exoplayer.MediaFormat;
-import com.google.android.exoplayer.MediaFormatHolder;
-import com.google.android.exoplayer.MediaFormatUtil;
-import com.google.android.exoplayer.SampleHolder;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.FormatHolder;
+import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
+import com.google.android.exoplayer2.source.TrackGroup;
+import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.util.MimeTypes;
import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
@@ -40,15 +49,17 @@ import java.util.List;
public class FileSampleExtractor implements SampleExtractor {
private static final String TAG = "FileSampleExtractor";
private static final boolean DEBUG = false;
+ private final long mRecordingDurationMs;
+ private IOException mOnPrepareException = null;
- private int mTrackCount;
private boolean mReleased;
-
- private final List<MediaFormat> mTrackFormats = new ArrayList<>();
private final BufferManager mBufferManager;
private final PlaybackBufferListener mBufferListener;
private BufferManager.SampleBuffer mSampleBuffer;
private final RecordingSampleBuffer.Factory mRecordingSampleBufferFactory;
+ private TrackGroupArray mTrackGroupArray = new TrackGroupArray();
+ private final Runnable mRunnable;
+ private Callback mCallback;
/**
* Factory for {@link FileSampleExtractor}}.
@@ -57,59 +68,164 @@ public class FileSampleExtractor implements SampleExtractor {
* generated class.
*/
public interface Factory {
- public FileSampleExtractor create(
- BufferManager bufferManager, PlaybackBufferListener bufferListener);
+ FileSampleExtractor create(
+ BufferManager bufferManager,
+ PlaybackBufferListener bufferListener,
+ long durationMs);
}
@AutoFactory(implementing = Factory.class)
public FileSampleExtractor(
BufferManager bufferManager,
PlaybackBufferListener bufferListener,
+ long durationMs,
@Provided RecordingSampleBuffer.Factory recordingSampleBufferFactory) {
mBufferManager = bufferManager;
mBufferListener = bufferListener;
- mTrackCount = -1;
mRecordingSampleBufferFactory = recordingSampleBufferFactory;
+ mRecordingDurationMs = durationMs;
+ mRunnable = () -> {
+ try {
+ handlePrepare();
+ } catch (IOException e) {
+ mOnPrepareException = e;
+ }
+ };
}
@Override
public void maybeThrowError() throws IOException {
- // Do nothing.
+ if (mOnPrepareException != null) {
+ throw mOnPrepareException;
+ }
}
@Override
- public boolean prepare() throws IOException {
+ public void prepare(Callback callback) {
+ mCallback = callback;
+ mRunnable.run();
+ }
+
+ private void handlePrepare() throws IOException {
List<BufferManager.TrackFormat> trackFormatList = mBufferManager.readTrackInfoFiles();
if (trackFormatList == null || trackFormatList.isEmpty()) {
throw new IOException("Cannot find meta files for the recording.");
}
- mTrackCount = trackFormatList.size();
- List<String> ids = new ArrayList<>();
- mTrackFormats.clear();
- for (int i = 0; i < mTrackCount; ++i) {
- BufferManager.TrackFormat trackFormat = trackFormatList.get(i);
- ids.add(trackFormat.trackId);
- mTrackFormats.add(MediaFormatUtil.createMediaFormat(trackFormat.format));
+ List<Format> formats = ImmutableList.copyOf(
+ Lists.transform(trackFormatList, tf -> createFormat(tf.mediaFormat)));
+ Format videoFormat = Iterables.find(formats, f -> MimeTypes.isVideo(f.sampleMimeType));
+ Iterable<TrackGroup> captionTrackGroups = new ArrayList<>();
+ if (videoFormat != null) {
+ Format textFormat = Format.createTextSampleFormat(
+ /* id= */ null,
+ MimeTypes.APPLICATION_CEA708,
+ /* selectionFlags= */ 0,
+ videoFormat.language,
+ /* drmInitData= */ null);
+ captionTrackGroups = ImmutableList.of(new TrackGroup(textFormat));
}
+ Iterable<TrackGroup> trackGroups =
+ Iterables.concat(Iterables.transform(formats, TrackGroup::new), captionTrackGroups);
+ mTrackGroupArray = new TrackGroupArray(Iterables.toArray(trackGroups, TrackGroup.class));
mSampleBuffer =
mRecordingSampleBufferFactory.create(
mBufferManager,
mBufferListener,
true,
RecordingSampleBuffer.BUFFER_REASON_RECORDED_PLAYBACK);
- mSampleBuffer.init(ids, mTrackFormats);
- return true;
+ mSampleBuffer.init(Lists.transform(trackFormatList, tf -> tf.trackId), formats);
+ mCallback.onPrepared();
+ }
+
+ private Format createFormat(MediaFormat mediaFormat) {
+ String mimeType = mediaFormat.getString(android.media.MediaFormat.KEY_MIME);
+ String language = getOptionalStringV16(mediaFormat, android.media.MediaFormat.KEY_LANGUAGE);
+ int maxInputSize =
+ getOptionalIntegerV16(mediaFormat, android.media.MediaFormat.KEY_MAX_INPUT_SIZE);
+ int width = getOptionalIntegerV16(mediaFormat, android.media.MediaFormat.KEY_WIDTH);
+ int height = getOptionalIntegerV16(mediaFormat, android.media.MediaFormat.KEY_HEIGHT);
+ int rotationDegrees = getOptionalIntegerV16(mediaFormat, "rotation-degrees");
+ int channelCount =
+ getOptionalIntegerV16(mediaFormat, android.media.MediaFormat.KEY_CHANNEL_COUNT);
+ int sampleRate =
+ getOptionalIntegerV16(mediaFormat, android.media.MediaFormat.KEY_SAMPLE_RATE);
+ ArrayList<byte[]> initializationData = new ArrayList<>();
+ for (int i = 0; mediaFormat.containsKey("csd-" + i); i++) {
+ ByteBuffer buffer = mediaFormat.getByteBuffer("csd-" + i);
+ byte[] data = new byte[buffer.limit()];
+ buffer.get(data);
+ initializationData.add(data);
+ buffer.flip();
+ }
+ long durationUs =
+ mediaFormat.containsKey(android.media.MediaFormat.KEY_DURATION)
+ ? mediaFormat.getLong(android.media.MediaFormat.KEY_DURATION)
+ : C.TIME_UNSET;
+ int pcmEncoding =
+ MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT : Format.NO_VALUE;
+ if (MimeTypes.isAudio(mimeType)) {
+ return Format.createAudioSampleFormat(
+ null,
+ mimeType,
+ null,
+ Format.NO_VALUE,
+ maxInputSize,
+ channelCount,
+ sampleRate,
+ pcmEncoding,
+ initializationData,
+ null,
+ 0,
+ language);
+ } else if(MimeTypes.isVideo(mimeType)) {
+ return Format.createVideoSampleFormat(
+ null,
+ mimeType,
+ null,
+ Format.NO_VALUE,
+ maxInputSize,
+ width,
+ height,
+ Format.NO_VALUE,
+ initializationData,
+ rotationDegrees,
+ Format.NO_VALUE,
+ null);
+ } else if(MimeTypes.isText(mimeType)) {
+ return Format.createTextSampleFormat(
+ null,
+ mimeType,
+ null,
+ Format.NO_VALUE,
+ 0,
+ language,
+ Format.NO_VALUE,
+ null,
+ durationUs,
+ initializationData);
+ } else {
+ return Format.createSampleFormat(null, mimeType, durationUs);
+ }
+ }
+
+ @Nullable
+ private static String getOptionalStringV16(MediaFormat mediaFormat, String key) {
+ return mediaFormat.containsKey(key) ? mediaFormat.getString(key) : null;
+ }
+
+ private static int getOptionalIntegerV16(MediaFormat mediaFormat, String key) {
+ return mediaFormat.containsKey(key) ? mediaFormat.getInteger(key) : Format.NO_VALUE;
}
@Override
- public List<MediaFormat> getTrackFormats() {
- return mTrackFormats;
+ public TrackGroupArray getTrackGroups() {
+ return mTrackGroupArray;
}
@Override
- public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) {
- outMediaFormatHolder.format = mTrackFormats.get(track);
- outMediaFormatHolder.drmInitData = null;
+ public void getTrackMediaFormat(int track, FormatHolder outMediaFormatHolder) {
+ outMediaFormatHolder.format = mTrackGroupArray.get(track).getFormat(0);
+ outMediaFormatHolder.format.copyWithDrmInitData(null);
}
@Override
@@ -138,7 +254,12 @@ public class FileSampleExtractor implements SampleExtractor {
@Override
public long getBufferedPositionUs() {
- return mSampleBuffer.getBufferedPositionUs();
+ return C.msToUs(mRecordingDurationMs);
+ }
+
+ @Override
+ public long getNextLoadPositionUs() {
+ return C.TIME_END_OF_SOURCE;
}
@Override
@@ -147,13 +268,13 @@ public class FileSampleExtractor implements SampleExtractor {
}
@Override
- public int readSample(int track, SampleHolder sampleHolder) {
+ public int readSample(int track, DecoderInputBuffer sampleHolder) {
return mSampleBuffer.readSample(track, sampleHolder);
}
@Override
- public boolean continueBuffering(long positionUs) {
- return mSampleBuffer.continueBuffering(positionUs);
+ public boolean continueLoading(long positionUs) {
+ return mSampleBuffer.continueLoading(positionUs);
}
@Override
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaPeriod.java b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaPeriod.java
new file mode 100644
index 00000000..733b8f80
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaPeriod.java
@@ -0,0 +1,260 @@
+/*
+ * 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.exoplayer2;
+
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.FormatHolder;
+import com.google.android.exoplayer2.SeekParameters;
+import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
+import com.google.android.exoplayer2.source.MediaPeriod;
+import com.google.android.exoplayer2.source.SampleStream;
+import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.trackselection.TrackSelection;
+import com.google.android.exoplayer2.util.Assertions;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** A {@link MediaPeriod} that extracts data using an {@link SampleExtractor}. */
+/* package */ final class MpegTsMediaPeriod implements MediaPeriod, SampleExtractor.Callback {
+
+ private static final int TRACK_STATE_DISABLED = 0;
+ private static final int TRACK_STATE_ENABLED = 1;
+ private static final int TRACK_STATE_FORMAT_SENT = 2;
+
+ private final SampleExtractor mExtractor;
+ private final ArrayList<SampleStreamImpl> mSampleStreams = new ArrayList<>();
+ private final List<Integer> mTrackStates = new ArrayList<>();
+ private final List<Boolean> mPendingDiscontinuities = new ArrayList<>();
+
+ private boolean mPrepared;
+ private long mLastSeekPositionUs;
+ private long mPendingSeekPositionUs;
+ private Callback mCallback;
+ private IOException mExceptionOnPrepare;
+
+ public MpegTsMediaPeriod(SampleExtractor extractor) {
+ this.mExtractor = extractor;
+ }
+
+ @Override
+ public void prepare(Callback callback, long positionUs) {
+ mCallback = callback;
+ try {
+ mExtractor.prepare(this);
+ } catch (IOException e) {
+ mExceptionOnPrepare = e;
+ }
+ }
+
+ private void enable(int track) {
+ Assertions.checkState(mPrepared);
+ Assertions.checkState(mTrackStates.get(track) == TRACK_STATE_DISABLED);
+ mTrackStates.set(track, TRACK_STATE_ENABLED);
+ mExtractor.selectTrack(track);
+ }
+
+ private void disable(int track) {
+ Assertions.checkState(mPrepared);
+ Assertions.checkState(mTrackStates.get(track) != TRACK_STATE_DISABLED);
+ mExtractor.deselectTrack(track);
+ mPendingDiscontinuities.set(track, false);
+ mTrackStates.set(track, TRACK_STATE_DISABLED);
+ }
+
+ @Override
+ public void maybeThrowPrepareError() throws IOException {
+ if (mExceptionOnPrepare != null) {
+ IOException e = mExceptionOnPrepare;
+ mExceptionOnPrepare = null;
+ throw e;
+ }
+ }
+
+ @Override
+ public TrackGroupArray getTrackGroups() {
+ return mExtractor.getTrackGroups();
+ }
+
+ @Override
+ public long selectTracks(
+ TrackSelection[] selections,
+ boolean[] mayRetainStreamFlags,
+ SampleStream[] streams,
+ boolean[] streamResetFlags,
+ long positionUs) {
+ TrackGroupArray trackGroups = mExtractor.getTrackGroups();
+ for (int i = 0; i < selections.length; i++) {
+ if (streams[i] != null && (selections[i] == null || !mayRetainStreamFlags[i])) {
+ SampleStreamImpl stream = (SampleStreamImpl) streams[i];
+ disable(stream.mIndex);
+ mSampleStreams.remove(stream);
+ streams[i] = null;
+ }
+ if (streams[i] == null && selections[i] != null) {
+ int index = trackGroups.indexOf(selections[i].getTrackGroup());
+ SampleStreamImpl stream = new SampleStreamImpl(index);
+ mSampleStreams.add(stream);
+ streams[i] = stream;
+ streamResetFlags[i] = true;
+ enable(index);
+ }
+ }
+ seekToUsInternal(positionUs, positionUs != 0);
+ return positionUs;
+ }
+
+ @Override
+ public void discardBuffer(long positionUs, boolean toKeyframe) {
+ // Handled by extractor
+ }
+
+ @Override
+ public void reevaluateBuffer(long positionUs) {
+ // Do nothing.
+ }
+
+ @Override
+ public boolean continueLoading(long positionUs) {
+ return mExtractor.continueLoading(positionUs);
+ }
+
+ @Override
+ public long readDiscontinuity() {
+ boolean notifyDiscontinuity = false;
+ for (int i = 0; i < mPendingDiscontinuities.size(); i++) {
+ if (mPendingDiscontinuities.get(i)) {
+ mPendingDiscontinuities.set(i, false);
+ notifyDiscontinuity = true;
+ }
+ }
+ return (notifyDiscontinuity ? mLastSeekPositionUs : C.TIME_UNSET);
+ }
+
+ @Override
+ public long getNextLoadPositionUs() {
+ return mExtractor.getNextLoadPositionUs();
+ }
+
+ @Override
+ public long getBufferedPositionUs() {
+ return mExtractor.getBufferedPositionUs();
+ }
+
+ @Override
+ public long seekToUs(long positionUs) {
+ for (int i = 0; i < mSampleStreams.size(); i++) {
+ mSampleStreams.get(i).reset();
+ }
+ seekToUsInternal(positionUs, false);
+ return positionUs;
+ }
+
+ private void seekToUsInternal(long positionUs, boolean force) {
+ // Unless forced, avoid duplicate calls to the underlying extractor's seek method
+ // in the case that there have been no interleaving calls to readSample.
+ if (force || mPendingSeekPositionUs != positionUs) {
+ mLastSeekPositionUs = positionUs;
+ mPendingSeekPositionUs = positionUs;
+ mExtractor.seekTo(positionUs);
+ for (int i = 0; i < mTrackStates.size(); ++i) {
+ if (mTrackStates.get(i) != TRACK_STATE_DISABLED) {
+ mPendingDiscontinuities.set(i, true);
+ }
+ }
+ }
+ }
+
+ @Override
+ public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
+ return positionUs;
+ }
+
+ @Override
+ public void onPrepared() {
+ mPrepared = true;
+ int trackCount = mExtractor.getTrackGroups().length;
+ mTrackStates.clear();
+ mPendingDiscontinuities.clear();
+ for (int i = 0; i < trackCount; ++i) {
+ mTrackStates.add(i, TRACK_STATE_DISABLED);
+ mPendingDiscontinuities.add(i, false);
+ }
+ mCallback.onPrepared(this);
+ }
+
+ public void release() {
+ mExtractor.release();
+ }
+
+ private final class SampleStreamImpl implements SampleStream {
+
+ private static final int STREAM_STATE_SEND_FORMAT = 0;
+ private static final int STREAM_STATE_SEND_SAMPLE = 1;
+ private static final int STREAM_STATE_END_OF_STREAM = 2;
+ private final int mIndex;
+
+ private int streamState;
+
+ SampleStreamImpl(int index) {
+ mIndex = index;
+ }
+
+ void reset() {
+ if (streamState == STREAM_STATE_END_OF_STREAM) {
+ streamState = STREAM_STATE_SEND_SAMPLE;
+ }
+ }
+
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ @Override
+ public void maybeThrowError() throws IOException {
+ mExtractor.maybeThrowError();
+ }
+
+ @Override
+ public int readData(
+ FormatHolder formatHolder, DecoderInputBuffer buffer, boolean requireFormat) {
+ Assertions.checkState(mPrepared);
+ Assertions.checkState(mTrackStates.get(mIndex) != TRACK_STATE_DISABLED);
+ if (mPendingDiscontinuities.get(mIndex)) {
+ return C.RESULT_NOTHING_READ;
+ }
+ if (requireFormat || mTrackStates.get(mIndex) != TRACK_STATE_FORMAT_SENT) {
+ mExtractor.getTrackMediaFormat(mIndex, formatHolder);
+ mTrackStates.set(mIndex, TRACK_STATE_FORMAT_SENT);
+ return C.RESULT_FORMAT_READ;
+ }
+ mPendingSeekPositionUs = C.TIME_UNSET;
+ return mExtractor.readSample(mIndex, buffer);
+ }
+
+ @Override
+ public int skipData(long positionUs) {
+ if (positionUs > 0 && streamState != STREAM_STATE_END_OF_STREAM) {
+ streamState = STREAM_STATE_END_OF_STREAM;
+ return 1;
+ }
+ return 0;
+ }
+ }
+}
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaSource.java b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaSource.java
new file mode 100644
index 00000000..a9ab43b5
--- /dev/null
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsMediaSource.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 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.exoplayer2;
+
+import android.support.annotation.Nullable;
+
+import com.google.android.exoplayer2.source.BaseMediaSource;
+import com.google.android.exoplayer2.source.MediaPeriod;
+import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.source.SinglePeriodTimeline;
+import com.google.android.exoplayer2.upstream.Allocator;
+import com.google.android.exoplayer2.upstream.TransferListener;
+import com.google.android.exoplayer2.util.Assertions;
+
+/** {@link MediaSource} that extracts sample data using a {@link SampleExtractor}. */
+
+public final class MpegTsMediaSource extends BaseMediaSource {
+
+ private static final String TAG = "MpegTsMediaSource";
+
+ private final SampleExtractor mSampleExtractor;
+
+ /**
+ * Creates a new sample source that extracts samples using {@code mSampleExtractor}.
+ *
+ * @param sampleExtractor a sample extractor for accessing media samples
+ */
+ public MpegTsMediaSource(SampleExtractor sampleExtractor) {
+ mSampleExtractor = Assertions.checkNotNull(sampleExtractor);
+ }
+
+ @Override
+ protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
+ refreshSourceInfo(new SinglePeriodTimeline(0, false, false), null);
+ }
+
+ @Override
+ protected void releaseSourceInternal() {
+ // Do nothing
+ }
+
+ @Override
+ public void maybeThrowSourceInfoRefreshError() {
+ // Do nothing
+ }
+
+ @Override
+ public MpegTsMediaPeriod createPeriod(
+ MediaPeriodId id,
+ Allocator allocator,
+ long startPositionUs) {
+ return new MpegTsMediaPeriod(mSampleExtractor);
+ }
+
+ @Override
+ public void releasePeriod(MediaPeriod mediaPeriod) {
+ ((MpegTsMediaPeriod) mediaPeriod).release();
+ }
+}
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsPlayerV2.java b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsPlayerV2.java
index 4fa44c95..7cb4b9b8 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsPlayerV2.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsPlayerV2.java
@@ -17,20 +17,14 @@
package com.android.tv.tuner.exoplayer2;
import android.content.Context;
-import android.media.PlaybackParams;
-import android.net.Uri;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.view.Surface;
-import com.android.tv.common.SoftPreconditions;
import com.android.tv.tuner.data.Cea708Data;
import com.android.tv.tuner.data.Cea708Data.CaptionEvent;
import com.android.tv.tuner.data.Cea708Parser;
-import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.source.TsDataSource;
-import com.android.tv.tuner.source.TsDataSourceManager;
-import com.android.tv.tuner.ts.EventDetector;
import com.android.tv.tuner.tvinput.debug.TunerDebug;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
@@ -42,7 +36,6 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioListener;
import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.Cue;
@@ -96,12 +89,8 @@ public class MpegTsPlayerV2
/** Notifies the caption event. */
void onEmitCaptionEvent(CaptionEvent event);
- /** Notifies clearing up whole closed caption event. */
- void onClearCaptionEvent();
-
/** Notifies the discovered caption service number. */
void onDiscoverCaptionServiceNumber(int serviceNumber);
-
}
public static final int MIN_BUFFER_MS = 0;
@@ -124,33 +113,27 @@ public class MpegTsPlayerV2
public static final int STATE_READY = Player.STATE_READY;
public static final int STATE_ENDED = Player.STATE_ENDED;
- private static final float MAX_SMOOTH_TRICKPLAY_SPEED = 9.0f;
- private static final float MIN_SMOOTH_TRICKPLAY_SPEED = 0.1f;
-
- private int mCaptionServiceNumber = Cea708Data.EMPTY_SERVICE_NUMBER;
-
- private final Context mContext;
private final SimpleExoPlayer mPlayer;
private final DefaultTrackSelector mTrackSelector;
- private final TsDataSourceManager mSourceManager;
private DefaultTrackSelector.Parameters mTrackSelectorParameters;
private TrackGroupArray mLastSeenTrackGroupArray;
+ private TrackSelectionArray mLastSeenTrackSelections;
private Callback mCallback;
private TsDataSource mDataSource;
private VideoEventListener mVideoEventListener;
- private boolean mTrickplayRunning;
+ private boolean mCaptionsAvailable = false;
/**
* Creates MPEG2-TS stream player.
*
* @param context the application context
- * @param sourceManager the manager for {@link TsDataSource}
* @param callback callback for playback state changes
*/
- public MpegTsPlayerV2(Context context, TsDataSourceManager sourceManager, Callback callback) {
- mContext = context;
- mTrackSelectorParameters = new DefaultTrackSelector.ParametersBuilder().build();
+ public MpegTsPlayerV2(Context context, Callback callback) {
+ mTrackSelectorParameters = new DefaultTrackSelector.ParametersBuilder()
+ .setSelectUndeterminedTextLanguage(true)
+ .build();
mTrackSelector = new DefaultTrackSelector();
mTrackSelector.setParameters(mTrackSelectorParameters);
mLastSeenTrackGroupArray = null;
@@ -159,7 +142,6 @@ public class MpegTsPlayerV2
mPlayer.addVideoListener(this);
mPlayer.addAudioListener(this);
mPlayer.addTextOutput(this);
- mSourceManager = sourceManager;
mCallback = callback;
}
@@ -178,7 +160,6 @@ public class MpegTsPlayerV2
* @param captionServiceNumber the service number of CEA-708 closed caption
*/
public void setCaptionServiceNumber(int captionServiceNumber) {
- mCaptionServiceNumber = captionServiceNumber;
if (captionServiceNumber == Cea708Data.EMPTY_SERVICE_NUMBER) return;
MappedTrackInfo mappedTrackInfo = mTrackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo != null) {
@@ -207,6 +188,10 @@ public class MpegTsPlayerV2
*/
@Override
public void onCues(List<Cue> cues) {
+ if (!mCaptionsAvailable && cues != null && cues.size() != 0) {
+ mCaptionsAvailable = true;
+ onTracksChanged(mLastSeenTrackGroupArray, mLastSeenTrackSelections);
+ }
mVideoEventListener.onEmitCaptionEvent(
new CaptionEvent(
Cea708Parser.CAPTION_EMIT_TYPE_COMMAND_DFX,
@@ -225,8 +210,7 @@ public class MpegTsPlayerV2
/* penStyle= */ 0,
/* windowStyle= */ 2)));
mVideoEventListener.onEmitCaptionEvent(
- new CaptionEvent(Cea708Parser.CAPTION_EMIT_TYPE_BUFFER,
- cues));
+ new CaptionEvent(Cea708Parser.CAPTION_EMIT_TYPE_BUFFER, cues));
}
/**
@@ -239,26 +223,13 @@ public class MpegTsPlayerV2
}
/**
- * Creates renderers and {@link TsDataSource} and initializes player.
- *
- * @return true when everything is created and initialized well, false otherwise
+ * Prepares player.
*/
- public boolean prepare(TunerChannel channel, EventDetector.EventListener eventListener) {
- TsDataSource source = null;
- if (channel != null) {
- source = mSourceManager.createDataSource(mContext, channel, eventListener);
- if (source == null) {
- return false;
- }
- }
- mDataSource = source;
- MediaSource mediaSource =
- new ProgressiveMediaSource.Factory(() -> mDataSource).createMediaSource(Uri.EMPTY);
- mPlayer.prepare(mediaSource, true, false);
- return true;
+ public void prepare(TsDataSource dataSource, MediaSource mediaSource) {
+ mDataSource = dataSource;
+ mPlayer.prepare(mediaSource, false, false);
}
-
/** Returns {@link TsDataSource} which provides MPEG2-TS stream. */
public TsDataSource getDataSource() {
return mDataSource;
@@ -272,28 +243,6 @@ public class MpegTsPlayerV2
*/
public void setPlayWhenReady(boolean playWhenReady) {
mPlayer.setPlayWhenReady(playWhenReady);
- stopSmoothTrickplay(false);
- }
-
- /** Returns true, if trickplay is supported. */
- public boolean supportSmoothTrickPlay(float playbackSpeed) {
- return playbackSpeed > MIN_SMOOTH_TRICKPLAY_SPEED
- && playbackSpeed < MAX_SMOOTH_TRICKPLAY_SPEED;
- }
-
- /**
- * Starts trickplay. It'll be reset, if {@link #seekTo} or {@link #setPlayWhenReady} is called.
- */
- public void startSmoothTrickplay(PlaybackParams playbackParams) {
- SoftPreconditions.checkState(supportSmoothTrickPlay(playbackParams.getSpeed()));
- mPlayer.setPlayWhenReady(true);
- mTrickplayRunning = true;
- }
-
- private void stopSmoothTrickplay(boolean calledBySeek) {
- if (mTrickplayRunning) {
- mTrickplayRunning = false;
- }
}
/**
@@ -303,7 +252,6 @@ public class MpegTsPlayerV2
*/
public void seekTo(long positionMs) {
mPlayer.seekTo(positionMs);
- stopSmoothTrickplay(true);
}
/** Releases the player. */
@@ -311,6 +259,7 @@ public class MpegTsPlayerV2
if (mDataSource != null) {
mDataSource = null;
}
+ mCaptionsAvailable = false;
mCallback = null;
mPlayer.release();
}
@@ -358,7 +307,9 @@ public class MpegTsPlayerV2
*
* @param enable enables the audio and closed caption when {@code true}, disables otherwise.
*/
- public void setAudioTrackAndClosedCaption(boolean enable) {}
+ public void setAudioTrackAndClosedCaption(boolean enable) {
+ //TODO Add handling to enable/disable audio and captions
+ }
@Override
public void onTimelineChanged(
@@ -369,9 +320,19 @@ public class MpegTsPlayerV2
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
if (trackGroups != mLastSeenTrackGroupArray) {
+ MappedTrackInfo mappedTrackInfo = mTrackSelector.getCurrentMappedTrackInfo();
+ if (mCallback != null
+ && mappedTrackInfo != null
+ && mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_AUDIO)
+ == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) {
+ mCallback.onAudioUnplayable();
+ }
mLastSeenTrackGroupArray = trackGroups;
}
- if (mVideoEventListener != null) {
+ if (trackSelections != mLastSeenTrackSelections) {
+ mLastSeenTrackSelections = trackSelections;
+ }
+ if (mVideoEventListener != null && mCaptionsAvailable) {
MappedTrackInfo mappedTrackInfo = mTrackSelector.getCurrentMappedTrackInfo();
if (mappedTrackInfo != null) {
int rendererCount = mappedTrackInfo.getRendererCount();
@@ -523,7 +484,7 @@ public class MpegTsPlayerV2
@Override
public void onDroppedFrames(int count, long elapsed) {
TunerDebug.notifyVideoFrameDrop(count, elapsed);
- if (mTrickplayRunning && mCallback != null) {
+ if (mCallback != null) {
mCallback.onSmoothTrickplayForceStopped();
}
}
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsSampleExtractor.java
index 544e1894..d6640b5e 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsSampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/MpegTsSampleExtractor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
@@ -16,20 +16,22 @@
package com.android.tv.tuner.exoplayer2;
+import android.media.MediaFormat;
import android.net.Uri;
import android.os.Handler;
import android.support.annotation.Nullable;
-import com.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener;
-import com.android.tv.tuner.exoplayer.buffer.SamplePool;
+import com.android.tv.tuner.exoplayer2.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer2.buffer.InputBufferPool;
+import com.android.tv.tuner.exoplayer2.buffer.PlaybackBufferListener;
-import com.google.android.exoplayer.MediaFormat;
-import com.google.android.exoplayer.MediaFormatHolder;
-import com.google.android.exoplayer.SampleHolder;
-import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.util.MimeTypes;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.FormatHolder;
+import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
+import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.util.MimeTypes;
import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
@@ -39,23 +41,27 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
-/** Extracts samples from {@link DataSource} for MPEG-TS streams. */
-public final class MpegTsSampleExtractor implements SampleExtractor {
- public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
+/**
+ * Extracts samples from {@link DataSource} for MPEG-TS streams.
+ * Managed captions for live and recorded playback since exoplayer earlier version needed it.
+ * //TODO: Can be discarded from exoplayer2
+ */
+public final class MpegTsSampleExtractor implements SampleExtractor, SampleExtractor.Callback {
private static final int CC_BUFFER_SIZE_IN_BYTES = 9600 / 8;
private final SampleExtractor mSampleExtractor;
- private final List<MediaFormat> mTrackFormats = new ArrayList<>();
+ private final List<Format> mTrackFormats = new ArrayList<>();
private final List<Boolean> mReachedEos = new ArrayList<>();
private int mVideoTrackIndex;
- private final SamplePool mCcSamplePool = new SamplePool();
- private final List<SampleHolder> mPendingCcSamples = new LinkedList<>();
+ private final InputBufferPool mCcInputBufferPool = new InputBufferPool();
+ private final List<DecoderInputBuffer> mPendingCcSamples = new LinkedList<>();
private int mCea708TextTrackIndex;
private boolean mCea708TextTrackSelected;
private CcParser mCcParser;
+ private Callback mCallback;
private void init() {
mVideoTrackIndex = -1;
@@ -70,10 +76,12 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
* generated class.
*/
public interface Factory {
- public MpegTsSampleExtractor create(
- BufferManager bufferManager, PlaybackBufferListener bufferListener);
+ MpegTsSampleExtractor create(
+ BufferManager bufferManager,
+ PlaybackBufferListener bufferListener,
+ long durationMs);
- public MpegTsSampleExtractor create(
+ MpegTsSampleExtractor create(
DataSource source,
@Nullable BufferManager bufferManager,
PlaybackBufferListener bufferListener);
@@ -104,13 +112,16 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
* @param bufferManager the samples provider which is stored in physical storage
* @param bufferListener the {@link PlaybackBufferListener} to notify buffer storage status
* change
+ * @param durationMs the duration of recording in Milliseconds
*/
@AutoFactory(implementing = Factory.class)
public MpegTsSampleExtractor(
BufferManager bufferManager,
PlaybackBufferListener bufferListener,
+ long durationMs,
@Provided FileSampleExtractor.Factory fileSampleExtractorFactory) {
- mSampleExtractor = fileSampleExtractorFactory.create(bufferManager, bufferListener);
+ mSampleExtractor =
+ fileSampleExtractorFactory.create(bufferManager, bufferListener, durationMs);
init();
}
@@ -122,43 +133,14 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
}
@Override
- public boolean prepare() throws IOException {
- if (!mSampleExtractor.prepare()) {
- return false;
- }
- List<MediaFormat> formats = mSampleExtractor.getTrackFormats();
- int trackCount = formats.size();
- mTrackFormats.clear();
- mReachedEos.clear();
-
- for (int i = 0; i < trackCount; ++i) {
- mTrackFormats.add(formats.get(i));
- mReachedEos.add(false);
- String mime = formats.get(i).mimeType;
- if (MimeTypes.isVideo(mime) && mVideoTrackIndex == -1) {
- mVideoTrackIndex = i;
- if (android.media.MediaFormat.MIMETYPE_VIDEO_MPEG2.equals(mime)) {
- mCcParser = new Mpeg2CcParser();
- } else if (android.media.MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
- mCcParser = new H264CcParser();
- }
- }
- }
-
- if (mVideoTrackIndex != -1) {
- mCea708TextTrackIndex = trackCount;
- }
- if (mCea708TextTrackIndex >= 0) {
- mTrackFormats.add(
- MediaFormat.createTextFormat(
- null, MIMETYPE_TEXT_CEA_708, 0, mTrackFormats.get(0).durationUs, ""));
- }
- return true;
+ public void prepare(Callback callback) throws IOException {
+ mCallback = callback;
+ mSampleExtractor.prepare(this);
}
@Override
- public List<MediaFormat> getTrackFormats() {
- return mTrackFormats;
+ public TrackGroupArray getTrackGroups() {
+ return mSampleExtractor.getTrackGroups();
}
@Override
@@ -185,46 +167,51 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
}
@Override
+ public long getNextLoadPositionUs() {
+ return mSampleExtractor.getNextLoadPositionUs();
+ }
+
+ @Override
public void seekTo(long positionUs) {
mSampleExtractor.seekTo(positionUs);
- for (SampleHolder holder : mPendingCcSamples) {
- mCcSamplePool.releaseSample(holder);
+ for (DecoderInputBuffer holder : mPendingCcSamples) {
+ mCcInputBufferPool.releaseSample(holder);
}
mPendingCcSamples.clear();
}
@Override
- public void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder) {
- if (track != mCea708TextTrackIndex) {
- mSampleExtractor.getTrackMediaFormat(track, outMediaFormatHolder);
- }
+ public void getTrackMediaFormat(int track, FormatHolder outMediaFormatHolder) {
+ mSampleExtractor.getTrackMediaFormat(track, outMediaFormatHolder);
}
@Override
- public int readSample(int track, SampleHolder sampleHolder) {
+ public int readSample(int track, DecoderInputBuffer sampleHolder) {
if (track == mCea708TextTrackIndex) {
if (mCea708TextTrackSelected && !mPendingCcSamples.isEmpty()) {
- SampleHolder holder = mPendingCcSamples.remove(0);
+ DecoderInputBuffer holder = mPendingCcSamples.remove(0);
+ sampleHolder.ensureSpaceForWrite(CC_BUFFER_SIZE_IN_BYTES);
holder.data.flip();
sampleHolder.timeUs = holder.timeUs;
+ sampleHolder.data.clear();
sampleHolder.data.put(holder.data);
- mCcSamplePool.releaseSample(holder);
- return SampleSource.SAMPLE_READ;
+ mCcInputBufferPool.releaseSample(holder);
+ return C.RESULT_BUFFER_READ;
} else {
return mVideoTrackIndex < 0 || mReachedEos.get(mVideoTrackIndex)
- ? SampleSource.END_OF_STREAM
- : SampleSource.NOTHING_READ;
+ ? C.RESULT_END_OF_INPUT
+ : C.RESULT_NOTHING_READ;
}
}
int result = mSampleExtractor.readSample(track, sampleHolder);
switch (result) {
- case SampleSource.END_OF_STREAM:
+ case C.RESULT_END_OF_INPUT:
{
mReachedEos.set(track, true);
break;
}
- case SampleSource.SAMPLE_READ:
+ case C.RESULT_BUFFER_READ:
{
if (mCea708TextTrackSelected
&& track == mVideoTrackIndex
@@ -246,21 +233,46 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
}
@Override
- public boolean continueBuffering(long positionUs) {
- return mSampleExtractor.continueBuffering(positionUs);
+ public boolean continueLoading(long positionUs) {
+ return mSampleExtractor.continueLoading(positionUs);
}
@Override
public void setOnCompletionListener(OnCompletionListener listener, Handler handler) {}
+ @Override
+ public void onPrepared() {
+ int trackCount = mSampleExtractor.getTrackGroups().length;
+ mTrackFormats.clear();
+ mReachedEos.clear();
+
+ for (int i = 0; i < trackCount; ++i) {
+ Format format = mSampleExtractor.getTrackGroups().get(i).getFormat(0);
+ mTrackFormats.add(format);
+ mReachedEos.add(false);
+ String mime = format.sampleMimeType;
+ if (MimeTypes.isVideo(mime) && mVideoTrackIndex == -1) {
+ mVideoTrackIndex = i;
+ if (MediaFormat.MIMETYPE_VIDEO_MPEG2.equals(mime)) {
+ mCcParser = new Mpeg2CcParser();
+ } else if (MediaFormat.MIMETYPE_VIDEO_AVC.equals(mime)) {
+ mCcParser = new H264CcParser();
+ }
+ } else if (MimeTypes.APPLICATION_CEA708.equals(mime)) {
+ mCea708TextTrackIndex = i;
+ }
+ }
+ mCallback.onPrepared();
+ }
+
private abstract class CcParser {
// Interim buffer for reduce direct access to ByteBuffer which is expensive. Using
// relatively small buffer size in order to minimize memory footprint increase.
- protected final byte[] mBuffer = new byte[1024];
+ final byte[] mBuffer = new byte[1024];
abstract void mayParseClosedCaption(ByteBuffer buffer, long presentationTimeUs);
- protected int parseClosedCaption(ByteBuffer buffer, int offset, long presentationTimeUs) {
+ int parseClosedCaption(ByteBuffer buffer, int offset, long presentationTimeUs) {
// For the details of user_data_type_structure, see ATSC A/53 Part 4 - Table 6.9.
int pos = offset;
if (pos + 2 >= buffer.position()) {
@@ -272,7 +284,7 @@ public final class MpegTsSampleExtractor implements SampleExtractor {
if (!processCcDataFlag || pos + 3 * ccCount >= buffer.position() || ccCount == 0) {
return offset;
}
- SampleHolder holder = mCcSamplePool.acquireSample(CC_BUFFER_SIZE_IN_BYTES);
+ DecoderInputBuffer holder = mCcInputBufferPool.acquireSample(CC_BUFFER_SIZE_IN_BYTES);
for (int i = 0; i < 3 * ccCount; i++) {
holder.data.put(buffer.get(pos++));
}
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/SampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer2/SampleExtractor.java
index c3040e6e..dde44a7b 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer2/SampleExtractor.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/SampleExtractor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
@@ -16,19 +16,20 @@
package com.android.tv.tuner.exoplayer2;
import android.os.Handler;
-import com.google.android.exoplayer.MediaFormat;
-import com.google.android.exoplayer.MediaFormatHolder;
-import com.google.android.exoplayer.SampleHolder;
-import com.google.android.exoplayer.SampleSource;
-import com.google.android.exoplayer.TrackRenderer;
+
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.FormatHolder;
+import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
+import com.google.android.exoplayer2.source.TrackGroupArray;
+
import java.io.IOException;
-import java.util.List;
/**
* Extractor for reading track metadata and samples stored in tracks.
*
* <p>Call {@link #prepare} until it returns {@code true}, then access track metadata via {@link
- * #getTrackFormats} and {@link #getTrackMediaFormat}.
+ * #getTrackGroups and {@link #getTrackMediaFormat}.
*
* <p>Pass indices of tracks to read from to {@link #selectTrack}. A track can later be deselected
* by calling {@link #deselectTrack}. It is safe to select/deselect tracks after reading sample data
@@ -36,6 +37,7 @@ import java.util.List;
*
* <p>Call {@link #release()} when the extractor is no longer needed to free resources.
*/
+// TODO: Should be replaced by {@link com.google.android.exoplayer2.source.MediaPeriod}.
public interface SampleExtractor {
/**
@@ -49,13 +51,14 @@ public interface SampleExtractor {
/**
* Prepares the extractor for reading track metadata and samples.
*
- * @return whether the source is ready; if {@code false}, this method must be called again.
+ * @param callback Callback to receive updates from this sample extractor, including being
+ * notified when preparation completes.
* @throws IOException thrown if the source can't be read
*/
- boolean prepare() throws IOException;
+ void prepare(Callback callback) throws IOException;
/** Returns track information about all tracks that can be selected. */
- List<MediaFormat> getTrackFormats();
+ TrackGroupArray getTrackGroups();
/** Selects the track at {@code index} for reading sample data. */
void selectTrack(int index);
@@ -69,12 +72,20 @@ public interface SampleExtractor {
* <p>This method should not be called until after the extractor has been successfully prepared.
*
* @return an estimate of the absolute position in microseconds up to which data is buffered, or
- * {@link TrackRenderer#END_OF_TRACK_US} if data is buffered to the end of the stream, or
- * {@link TrackRenderer#UNKNOWN_TIME_US} if no estimate is available.
+ * {@link C#TIME_END_OF_SOURCE} if data is buffered to the end of the stream, or
+ * {@link C#TIME_UNSET} if no estimate is available.
*/
long getBufferedPositionUs();
/**
+ * Returns the next load time, or {@link C#TIME_END_OF_SOURCE} if loading has finished.
+ *
+ * <p>This method is only called after the period has been prepared. It may be called when no
+ * tracks are selected.
+ */
+ long getNextLoadPositionUs();
+
+ /**
* Seeks to the specified time in microseconds.
*
* <p>This method should not be called until after the extractor has been successfully prepared.
@@ -83,30 +94,30 @@ public interface SampleExtractor {
*/
void seekTo(long positionUs);
- /** Stores the {@link MediaFormat} of {@code track}. */
- void getTrackMediaFormat(int track, MediaFormatHolder outMediaFormatHolder);
+ /** Stores the {@link Format} of {@code track}. */
+ void getTrackMediaFormat(int track, FormatHolder outMediaFormatHolder);
/**
* Reads the next sample in the track at index {@code track} into {@code sampleHolder},
- * returning {@link SampleSource#SAMPLE_READ} if it is available.
+ * returning {@link C#RESULT_BUFFER_READ} if it is available.
*
* <p>Advances to the next sample if a sample was read.
*
* @param track the index of the track from which to read a sample
- * @param sampleHolder the holder for read sample data, if {@link SampleSource#SAMPLE_READ} is
+ * @param sampleHolder the holder for read sample data, if {@link C#RESULT_BUFFER_READ} is
* returned
- * @return {@link SampleSource#SAMPLE_READ} if a sample was read into {@code sampleHolder}, or
- * {@link SampleSource#END_OF_STREAM} if the last samples in all tracks have been read, or
- * {@link SampleSource#NOTHING_READ} if the sample cannot be read immediately as it is not
+ * @return {@link C#RESULT_BUFFER_READ} if a sample was read into {@code sampleHolder}, or
+ * {@link C#RESULT_END_OF_INPUT} if the last samples in all tracks have been read, or
+ * {@link C#RESULT_NOTHING_READ} if the sample cannot be read immediately as it is not
* loaded.
*/
- int readSample(int track, SampleHolder sampleHolder);
+ int readSample(int track, DecoderInputBuffer sampleHolder);
/** Releases resources associated with this extractor. */
void release();
/** Indicates to the source that it should still be buffering data. */
- boolean continueBuffering(long positionUs);
+ boolean continueLoading(long positionUs);
/**
* Sets OnCompletionListener for notifying the completion of SampleExtractor.
@@ -128,4 +139,11 @@ public interface SampleExtractor {
*/
void onCompletion(boolean result, long lastExtractedPositionUs);
}
+
+ /** A callback to be notified of {@link SampleExtractor} events. */
+ interface Callback {
+
+ /** Called when preparation completes. */
+ void onPrepared();
+ }
}
diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/buffer/SampleChunkIoHelper.java b/tuner/src/com/android/tv/tuner/exoplayer2/buffer/SampleChunkIoHelper.java
index 5a3f6682..93fc0552 100644
--- a/tuner/src/com/android/tv/tuner/exoplayer2/buffer/SampleChunkIoHelper.java
+++ b/tuner/src/com/android/tv/tuner/exoplayer2/buffer/SampleChunkIoHelper.java
@@ -259,8 +259,11 @@ public class SampleChunkIoHelper implements Handler.Callback {
mediaFormat.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
- // Set mediaFormat parameters that may be unset.
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
+ // Set mediaFormat parameters that may be unset.
+ if (format.language != null) {
+ mediaFormat.setString(MediaFormat.KEY_LANGUAGE, format.language);
+ }
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
if (Util.SDK_INT >= 23) {
diff --git a/tuner/src/com/android/tv/tuner/modules/TunerModule.java b/tuner/src/com/android/tv/tuner/modules/TunerModule.java
index f149aaa5..fe2482c1 100644
--- a/tuner/src/com/android/tv/tuner/modules/TunerModule.java
+++ b/tuner/src/com/android/tv/tuner/modules/TunerModule.java
@@ -32,8 +32,11 @@ import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBufferFactory;
import com.android.tv.tuner.exoplayer.buffer.SampleChunkIoHelper;
import com.android.tv.tuner.exoplayer.buffer.SampleChunkIoHelperFactory;
import com.android.tv.tuner.source.TunerSourceModule;
+import com.android.tv.tuner.tvinput.TunerRecordingSessionExoV2FactoryImpl;
import com.android.tv.tuner.tvinput.TunerRecordingSessionFactoryImpl;
import com.android.tv.tuner.tvinput.TunerRecordingSessionWorker;
+import com.android.tv.tuner.tvinput.TunerRecordingSessionWorkerExoV2;
+import com.android.tv.tuner.tvinput.TunerRecordingSessionWorkerExoV2Factory;
import com.android.tv.tuner.tvinput.TunerRecordingSessionWorkerFactory;
import com.android.tv.tuner.tvinput.TunerSessionExoV2Factory;
import com.android.tv.tuner.tvinput.TunerSessionOverlay;
@@ -64,11 +67,24 @@ public abstract class TunerModule {
return tunerFlags.useExoplayerV2() ? tunerSessionExoV2Factory : tunerSessionFactory;
}
+ @Provides
+ static TunerRecordingSessionFactory tunerRecordingSessionFactory(
+ TunerFlags tunerFlags,
+ TunerRecordingSessionFactoryImpl tunerRecordingSessionFactoryImpl,
+ TunerRecordingSessionExoV2FactoryImpl tunerRecordingSessionExoV2FactoryImpl) {
+ return tunerFlags.useExoplayerV2() ?
+ tunerRecordingSessionExoV2FactoryImpl : tunerRecordingSessionFactoryImpl;
+ }
+
@Binds
abstract TunerRecordingSessionWorker.Factory tunerRecordingSessionWorkerFactory(
TunerRecordingSessionWorkerFactory tunerRecordingSessionWorkerFactory);
@Binds
+ abstract TunerRecordingSessionWorkerExoV2.Factory tunerRecordingSessionWorkerExoV2Factory(
+ TunerRecordingSessionWorkerExoV2Factory tunerRecordingSessionWorkerExoV2Factory);
+
+ @Binds
abstract TunerSessionWorker.Factory tunerSessionWorkerFactory(
TunerSessionWorkerFactory tunerSessionWorkerFactory);
@@ -89,10 +105,6 @@ public abstract class TunerModule {
TunerSessionWorkerExoV2Factory tunerSessionWorkerExoV2Factory);
@Binds
- abstract TunerRecordingSessionFactory tunerRecordingSessionFactory(
- TunerRecordingSessionFactoryImpl impl);
-
- @Binds
abstract MpegTsRendererBuilder.Factory mpegTsRendererBuilderFactory(
MpegTsRendererBuilderFactory mpegTsRendererBuilderFactory);
@@ -115,4 +127,33 @@ public abstract class TunerModule {
@Binds
abstract SampleChunkIoHelper.Factory sampleChunkIoHelperFactory(
SampleChunkIoHelperFactory sampleChunkIoHelperFactory);
+
+ @Binds
+ abstract com.android.tv.tuner.exoplayer2.MpegTsSampleExtractor.Factory
+ mpegTsSampleExtractorFactoryV2(
+ com.android.tv.tuner.exoplayer2.MpegTsSampleExtractorFactory
+ mpegTsSampleExtractorFactory);
+
+ @Binds
+ abstract com.android.tv.tuner.exoplayer2.ExoPlayerSampleExtractor.Factory
+ exoPlayerSampleExtractorFactoryV2(
+ com.android.tv.tuner.exoplayer2.ExoPlayerSampleExtractorFactory
+ exoPlayerSampleExtractorFactory);
+
+ @Binds
+ abstract com.android.tv.tuner.exoplayer2.FileSampleExtractor.Factory
+ fileSampleExtractorFactoryV2(
+ com.android.tv.tuner.exoplayer2.FileSampleExtractorFactory fileSampleExtractorFactory);
+
+ @Binds
+ abstract com.android.tv.tuner.exoplayer2.buffer.RecordingSampleBuffer.Factory
+ recordingSampleBufferFactoryV2(
+ com.android.tv.tuner.exoplayer2.buffer.RecordingSampleBufferFactory
+ recordingSampleBufferFactory);
+
+ @Binds
+ abstract com.android.tv.tuner.exoplayer2.buffer.SampleChunkIoHelper.Factory
+ sampleChunkIoHelperFactoryV2(
+ com.android.tv.tuner.exoplayer2.buffer.SampleChunkIoHelperFactory
+ sampleChunkIoHelperFactory);
}
diff --git a/tuner/src/com/android/tv/tuner/setup/ScanFragment.java b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
index 8c14aaa1..9da14d95 100644
--- a/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
+++ b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java
@@ -62,7 +62,7 @@ public class ScanFragment extends SetupFragment {
private static final boolean DEBUG = false;
// In the fake mode, the connection to antenna or cable is not necessary.
- // Instead dummy channels are added.
+ // Instead fake channels are added.
private static final boolean FAKE_MODE = false;
private static final String VCTLESS_CHANNEL_NAME_FORMAT = "RF%d-%d";
diff --git a/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
index e47162ad..c1d10dc1 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java
@@ -25,6 +25,7 @@ import android.net.Uri;
import android.util.Log;
import com.android.tv.common.feature.CommonFeatures;
+import com.android.tv.common.flags.TunerFlags;
import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
import com.android.tv.tuner.tvinput.factory.TunerRecordingSessionFactory;
import com.android.tv.tuner.tvinput.factory.TunerSessionFactory;
@@ -55,6 +56,7 @@ public class BaseTunerTvInputService extends TvInputService {
Collections.newSetFromMap(new WeakHashMap<>());
@Inject TunerSessionFactory mTunerSessionFactory;
@Inject TunerRecordingSessionFactory mTunerRecordingSessionFactory;
+ @Inject TunerFlags mTunerFlags;
LoadingCache<String, ChannelDataManager> mChannelDataManagers;
RemovalListener<String, ChannelDataManager> mChannelDataManagerRemovalListener =
@@ -152,9 +154,16 @@ public class BaseTunerTvInputService extends TvInputService {
private Uri getRecordingUri(Uri channelUri) {
for (RecordingSession session : mTunerRecordingSession) {
- TunerRecordingSession tunerSession = (TunerRecordingSession) session;
- if (tunerSession.getChannelUri().equals(channelUri)) {
- return tunerSession.getRecordingUri();
+ if (mTunerFlags.useExoplayerV2()) {
+ TunerRecordingSessionExoV2 tunerSession = (TunerRecordingSessionExoV2) session;
+ if (tunerSession.getChannelUri().equals(channelUri)) {
+ return tunerSession.getRecordingUri();
+ }
+ } else {
+ TunerRecordingSession tunerSession = (TunerRecordingSession) session;
+ if (tunerSession.getChannelUri().equals(channelUri)) {
+ return tunerSession.getRecordingUri();
+ }
}
}
return null;
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java
index 9d689abc..47d2cc35 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionExoV2.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorkerExoV2.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorkerExoV2.java
index faf006a5..9481d1ae 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorkerExoV2.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorkerExoV2.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
@@ -50,17 +50,17 @@ import com.android.tv.tuner.data.PsipData.EitItem;
import com.android.tv.tuner.data.Track.AtscCaptionTrack;
import com.android.tv.tuner.data.TunerChannel;
import com.android.tv.tuner.dvb.DvbDeviceAccessor;
-import com.android.tv.tuner.exoplayer.ExoPlayerSampleExtractor;
-import com.android.tv.tuner.exoplayer.SampleExtractor;
-import com.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.DvrStorageManager;
-import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener;
+import com.android.tv.tuner.exoplayer2.ExoPlayerSampleExtractor;
+import com.android.tv.tuner.exoplayer2.SampleExtractor;
+import com.android.tv.tuner.exoplayer2.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer2.buffer.DvrStorageManager;
+import com.android.tv.tuner.exoplayer2.buffer.PlaybackBufferListener;
import com.android.tv.tuner.source.TsDataSource;
import com.android.tv.tuner.source.TsDataSourceManager;
import com.android.tv.tuner.ts.EventDetector.EventListener;
import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
-import com.google.android.exoplayer.C;
+import com.google.android.exoplayer2.C;
import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
@@ -83,8 +83,9 @@ public class TunerRecordingSessionWorkerExoV2
implements PlaybackBufferListener,
EventListener,
SampleExtractor.OnCompletionListener,
+ SampleExtractor.Callback,
Handler.Callback {
- private static final String TAG = "TunerRecordingSessionW";
+ private static final String TAG = "TunerRecordingSWExoV2";
private static final boolean DEBUG = false;
private static final String SORT_BY_TIME =
@@ -320,10 +321,7 @@ public class TunerRecordingSessionWorkerExoV2
return true;
}
try {
- if (!mRecorder.prepare()) {
- mHandler.sendEmptyMessageDelayed(
- MSG_PREPARE_RECODER, PREPARE_RECORDER_POLL_MS);
- }
+ mRecorder.prepare(this);
} catch (IOException e) {
Log.w(TAG, "Failed to start recording. Couldn't prepare an extractor");
mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN);
@@ -389,6 +387,11 @@ public class TunerRecordingSessionWorkerExoV2
return false;
}
+ @Override
+ public void onPrepared() {
+ // Do nothing
+ }
+
@Nullable
private TunerChannel getChannel(Uri channelUri) {
if (channelUri == null) {
@@ -657,7 +660,7 @@ public class TunerRecordingSessionWorkerExoV2
}
Log.i(TAG, "recording finished " + (success ? "completely" : "partially"));
long recordEndTime =
- (lastExtractedPositionUs == C.UNKNOWN_TIME_US)
+ (lastExtractedPositionUs == C.TIME_UNSET)
? System.currentTimeMillis()
: mRecordStartTime + lastExtractedPositionUs / 1000;
updateRecordedProgramStateFinished(recordEndTime, calculateRecordingSizeInBytes());
diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java
index a25c1d25..3bba28d6 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java
@@ -20,6 +20,7 @@ import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
+import android.media.MediaFormat;
import android.media.PlaybackParams;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
@@ -60,8 +61,15 @@ import com.android.tv.tuner.data.PsipData.TvTracksInterface;
import com.android.tv.tuner.data.Track.AtscAudioTrack;
import com.android.tv.tuner.data.Track.AtscCaptionTrack;
import com.android.tv.tuner.data.TunerChannel;
+import com.android.tv.tuner.exoplayer2.MpegTsMediaSource;
import com.android.tv.tuner.exoplayer2.MpegTsPlayerV2;
import com.android.tv.tuner.exoplayer2.MpegTsPlayerV2.PlayerState;
+import com.android.tv.tuner.exoplayer2.MpegTsSampleExtractor;
+import com.android.tv.tuner.exoplayer2.SampleExtractor;
+import com.android.tv.tuner.exoplayer2.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer2.buffer.DvrStorageManager;
+import com.android.tv.tuner.exoplayer2.buffer.PlaybackBufferListener;
+import com.android.tv.tuner.exoplayer2.buffer.TrickplayStorageManager;
import com.android.tv.tuner.prefs.TunerPreferences;
import com.android.tv.tuner.source.TsDataSource;
import com.android.tv.tuner.source.TsDataSourceManager;
@@ -69,8 +77,10 @@ import com.android.tv.tuner.ts.EventDetector.EventListener;
import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
import com.android.tv.tuner.tvinput.debug.TunerDebug;
import com.android.tv.tuner.util.StatusTextUtils;
+
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.AudioCapabilities;
+import com.google.android.exoplayer2.source.MediaSource;
import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
import com.google.common.collect.ImmutableList;
@@ -87,6 +97,7 @@ import java.util.concurrent.TimeUnit;
// TODO: Add PlaybackBufferListener,
@WorkerThread
public class TunerSessionWorkerExoV2 implements
+ PlaybackBufferListener,
MpegTsPlayerV2.VideoEventListener,
MpegTsPlayerV2.Callback,
EventListener,
@@ -119,7 +130,6 @@ public class TunerSessionWorkerExoV2 implements
private static final int MSG_SCHEDULE_OF_PROGRAMS = 1009;
private static final int MSG_UPDATE_CHANNEL_INFO = 1010;
private static final int MSG_TRICKPLAY_BY_SEEK = 1011;
- private static final int MSG_SMOOTH_TRICKPLAY_MONITOR = 1012;
private static final int MSG_PARENTAL_CONTROLS = 1015;
private static final int MSG_RESCHEDULE_PROGRAMS = 1016;
private static final int MSG_BUFFER_START_TIME_CHANGED = 1017;
@@ -164,7 +174,6 @@ public class TunerSessionWorkerExoV2 implements
// Actual interval would be divided by the speed.
private static final int EXPECTED_KEY_FRAME_INTERVAL_MS = 500;
private static final int MIN_TRICKPLAY_SEEK_INTERVAL_MS = 20;
- private static final int TRICKPLAY_MONITOR_INTERVAL_MS = 250;
private static final int RELEASE_WAIT_INTERVAL_MS = 50;
private static final long TRICKPLAY_OFF_DURATION_MS = TimeUnit.DAYS.toMillis(14);
private static final long SEEK_MARGIN_MS = TimeUnit.SECONDS.toMillis(2);
@@ -183,13 +192,13 @@ public class TunerSessionWorkerExoV2 implements
private final int mMaxTrickplayBufferSizeMb;
private final File mTrickplayBufferDir;
private final @TRICKPLAY_MODE int mTrickplayModeCustomization;
-
+ private final MpegTsSampleExtractor.Factory mMpegTsSampleExtractorFactory;
private volatile Surface mSurface;
private volatile float mVolume = 1.0f;
private volatile boolean mCaptionEnabled;
private volatile MpegTsPlayerV2 mPlayer;
private volatile TunerChannel mChannel;
- private volatile Long mRecordingDuration;
+ private volatile Long mRecordingDuration = 0L;
private volatile long mRecordStartTimeMs;
private volatile long mBufferStartTimeMs;
private volatile boolean mTrickplayDisabledByStorageIssue;
@@ -232,6 +241,7 @@ public class TunerSessionWorkerExoV2 implements
private int mSignalStrength;
private long mRecordedProgramStartTimeMs;
+ private BufferManager mBufferManager;
/**
* Factory for {@link TunerSessionWorkerExoV2}.
@@ -254,6 +264,7 @@ public class TunerSessionWorkerExoV2 implements
TunerSessionExoV2 tunerSession,
TunerSessionOverlay tunerSessionOverlay,
@Provided LegacyFlags legacyFlags,
+ @Provided MpegTsSampleExtractor.Factory mpegTsSampleExtractorFactory,
@Provided TsDataSourceManager.Factory tsDataSourceManagerFactory) {
this(
context,
@@ -262,6 +273,7 @@ public class TunerSessionWorkerExoV2 implements
tunerSessionOverlay,
null,
legacyFlags,
+ mpegTsSampleExtractorFactory,
tsDataSourceManagerFactory);
}
@@ -273,6 +285,7 @@ public class TunerSessionWorkerExoV2 implements
TunerSessionOverlay tunerSessionOverlay,
@Nullable Handler handler,
LegacyFlags legacyFlags,
+ MpegTsSampleExtractor.Factory mpegTsSampleExtractorFactory,
TsDataSourceManager.Factory tsDataSourceManagerFactory) {
mLegacyFlags = legacyFlags;
if (DEBUG) {
@@ -291,6 +304,7 @@ public class TunerSessionWorkerExoV2 implements
mSession = tunerSession;
mTunerSessionOverlay = tunerSessionOverlay;
mChannelDataManager = channelDataManager;
+ mMpegTsSampleExtractorFactory = mpegTsSampleExtractorFactory;
mRecordingUri = null;
mChannelDataManager.setListener(this);
mChannelDataManager.checkDataVersion(mContext);
@@ -401,9 +415,21 @@ public class TunerSessionWorkerExoV2 implements
return Uri.parse(mRecordingId).getPath();
}
- private Long getDurationForRecording(String recordingId) {
- // TODO: Get recording duration
- return null;
+ private Long getDurationForRecording() {
+ DvrStorageManager storageManager =
+ new DvrStorageManager(new File(getRecordingPath()), false);
+ List<BufferManager.TrackFormat> trackFormatList = storageManager.readTrackInfoFiles(false);
+ if (trackFormatList.isEmpty()) {
+ trackFormatList = storageManager.readTrackInfoFiles(true);
+ }
+ if (!trackFormatList.isEmpty()) {
+ BufferManager.TrackFormat trackFormat = trackFormatList.get(0);
+ Long durationUs = trackFormat.mediaFormat.getLong(MediaFormat.KEY_DURATION);
+ // we need duration by milli for trickplay notification.
+ return durationUs != null ? durationUs / 1000 : 0L;
+ }
+ Log.e(TAG, "meta file for recording was not found: " + mRecordingId);
+ return 0L;
}
@MainThread
@@ -454,6 +480,9 @@ public class TunerSessionWorkerExoV2 implements
synchronized (mReleaseLock) {
mReleaseRequested = true;
}
+ if (mIsActiveSession) {
+ sActiveSessionSemaphore.release();
+ }
if (mHasSoftwareAudioDecoder) {
// TODO reimplement for google3
// Here disconnect ffmpeg
@@ -474,9 +503,7 @@ public class TunerSessionWorkerExoV2 implements
mReadyStartTimeMs = INVALID_TIME;
mBufferingStartTimeMs = INVALID_TIME;
if (playbackState == MpegTsPlayerV2.STATE_READY) {
- if (DEBUG) {
- Log.d(TAG, "ExoPlayerV2 ready");
- }
+ if (DEBUG) Log.d(TAG, "ExoPlayerV2 ready");
if (!mPlayerStarted) {
sendMessage(MSG_START_PLAYBACK, System.identityHashCode(mPlayer));
}
@@ -529,10 +556,8 @@ public class TunerSessionWorkerExoV2 implements
@Override
public void onRenderedFirstFrame() {
- if (mSurface != null && mPlayerStarted) {
- if (DEBUG) {
- Log.d(TAG, "MSG_DRAWN_TO_SURFACE");
- }
+ if (!mReportedDrawnToSurface && mSurface != null && mPlayerStarted) {
+ if (DEBUG) Log.d(TAG, "MSG_DRAWN_TO_SURFACE");
if (mRecordingId != null) {
// Workaround of b/33298048: set it to 1 instead of 0.
mBufferStartTimeMs = mRecordStartTimeMs = 1;
@@ -562,10 +587,9 @@ public class TunerSessionWorkerExoV2 implements
@Override
public void onSmoothTrickplayForceStopped() {
- if (mPlayer == null || !mHandler.hasMessages(MSG_SMOOTH_TRICKPLAY_MONITOR)) {
+ if (mPlayer == null) {
return;
}
- mHandler.removeMessages(MSG_SMOOTH_TRICKPLAY_MONITOR);
doTrickplayBySeek((int) mPlayer.getCurrentPosition());
}
@@ -585,11 +609,6 @@ public class TunerSessionWorkerExoV2 implements
}
@Override
- public void onClearCaptionEvent() {
- mTunerSessionOverlay.sendUiMessage(TunerSessionOverlay.MSG_UI_CLEAR_CAPTION_RENDERER);
- }
-
- @Override
public void onDiscoverCaptionServiceNumber(int serviceNumber) {
sendMessage(MSG_DISCOVER_CAPTION_SERVICE_NUMBER, serviceNumber);
}
@@ -615,6 +634,22 @@ public class TunerSessionWorkerExoV2 implements
sendMessage(MSG_PROGRAM_DATA_RESULT, Pair.create(channel, programs));
}
+ // PlaybackBufferListener
+ @Override
+ public void onBufferStartTimeChanged(long startTimeMs) {
+ sendMessage(MSG_BUFFER_START_TIME_CHANGED, startTimeMs);
+ }
+
+ @Override
+ public void onBufferStateChanged(boolean available) {
+ sendMessage(MSG_BUFFER_STATE_CHANGED, available);
+ }
+
+ @Override
+ public void onDiskTooSlow() {
+ mTrickplayDisabledByStorageIssue = true;
+ sendMessage(MSG_RETRY_PLAYBACK, System.identityHashCode(mPlayer));
+ }
// EventDetector.EventListener
@Override
@@ -745,8 +780,6 @@ public class TunerSessionWorkerExoV2 implements
return handleMessageProgramDataResult(msg);
case MSG_TRICKPLAY_BY_SEEK:
return handleMessageTrickplayBySeek(msg.arg1);
- case MSG_SMOOTH_TRICKPLAY_MONITOR:
- return handleMessageSmoothTrickplayMonitor();
case MSG_RESCHEDULE_PROGRAMS:
return handleMessageReschedulePrograms();
case MSG_PARENTAL_CONTROLS:
@@ -793,9 +826,7 @@ public class TunerSessionWorkerExoV2 implements
Log.d(TAG, "MSG_TUNE");
}
- // When sequential tuning messages arrived, it skips middle tuning messages in
- // order
- // to change to the last requested channel quickly.
+ // There's a pending tune which will override this one, so we ignore the current message.
if (mHandler.hasMessages(MSG_TUNE)) {
return true;
}
@@ -849,9 +880,8 @@ public class TunerSessionWorkerExoV2 implements
mChannelDataManager.requestProgramsData(channel);
}
prepareTune(channel, recording);
- // TODO: Need to refactor. notifyContentAllowed() should not be called if
- // parental
- // control is turned on.
+ // TODO: Need to refactor. notifyContentAllowed() should not be called if parental control
+ // is turned on.
mSession.notifyContentAllowed();
resetTvTracks();
resetPlayback();
@@ -881,9 +911,6 @@ public class TunerSessionWorkerExoV2 implements
stopCaptionTrack();
mSourceManager.release();
mHandler.getLooper().quitSafely();
- if (mIsActiveSession) {
- sActiveSessionSemaphore.release();
- }
return true;
}
@@ -902,8 +929,7 @@ public class TunerSessionWorkerExoV2 implements
resetPlayback();
} else {
// When it reaches this point, it may be due to an error that occurred
- // in
- // the tuner device. Calling stopPlayback() resets the tuner device
+ // in the tuner device. Calling stopPlayback() resets the tuner device
// to recover from the error.
stopPlayback(false);
stopCaptionTrack();
@@ -912,8 +938,7 @@ public class TunerSessionWorkerExoV2 implements
Log.i(TAG, "Notify weak signal since fail to retry playback");
// After MAX_IMMEDIATE_RETRY_COUNT, give some delay of an empirically
- // chosen
- // value before recovering the playback.
+ // chosen value before recovering the playback.
mHandler.sendEmptyMessageDelayed(
MSG_RESET_PLAYBACK, RECOVER_STOPPED_PLAYBACK_PERIOD_MS);
}
@@ -1029,39 +1054,6 @@ public class TunerSessionWorkerExoV2 implements
return true;
}
- private boolean handleMessageSmoothTrickplayMonitor() {
- if (mPlayer == null) {
- return true;
- }
- long systemCurrentTime = System.currentTimeMillis();
- long position = getCurrentPosition();
- if (mRecordingId == null) {
- // Checks if the position exceeds the upper bound when forwarding,
- // or exceed the lower bound when rewinding.
- // If the direction is not checked, there can be some issues.
- // (See b/29939781 for more details.)
- if ((position > systemCurrentTime && mPlaybackParams.getSpeed() > 0L)
- || (position < mBufferStartTimeMs && mPlaybackParams.getSpeed() < 0L)) {
- doTimeShiftResume();
- return true;
- }
- } else {
- if (position > mRecordingDuration || position < 0) {
- doTimeShiftPause();
- return true;
- }
- long systemBufferTime =
- systemCurrentTime - SEEK_MARGIN_MS - mRecordedProgramStartTimeMs;
- if (position > systemBufferTime) {
- doTimeShiftResume();
- return true;
- }
- }
- mHandler.sendEmptyMessageDelayed(
- MSG_SMOOTH_TRICKPLAY_MONITOR, TRICKPLAY_MONITOR_INTERVAL_MS);
- return true;
- }
-
private boolean handleMessageReschedulePrograms() {
if (mHandler.hasMessages(MSG_SCHEDULE_OF_PROGRAMS)) {
mHandler.sendEmptyMessage(MSG_RESCHEDULE_PROGRAMS);
@@ -1411,10 +1403,23 @@ public class TunerSessionWorkerExoV2 implements
}
}
}
-
- // TODO: Add support for BufferManager
-
- MpegTsPlayerV2 player = new MpegTsPlayerV2(mContext, mSourceManager, this);
+ mBufferManager = null;
+ if (mRecordingId != null) {
+ BufferManager.StorageManager storageManager =
+ new DvrStorageManager(new File(getRecordingPath()), false);
+ mBufferManager = new BufferManager(storageManager);
+ updateCaptionTracks(((DvrStorageManager) storageManager).readCaptionInfoFiles());
+ } else if (!mTrickplayDisabledByStorageIssue
+ && mTrickplaySetting != TunerPreferences.TRICKPLAY_SETTING_DISABLED
+ && mMaxTrickplayBufferSizeMb >= MIN_BUFFER_SIZE_DEF) {
+ mBufferManager =
+ new BufferManager(
+ new TrickplayStorageManager(
+ mContext,
+ mTrickplayBufferDir,
+ 1024L * 1024 * mMaxTrickplayBufferSizeMb));
+ }
+ MpegTsPlayerV2 player = new MpegTsPlayerV2(mContext,this);
player.setVideoEventListener(this);
player.setCaptionServiceNumber(
mCaptionTrack != null
@@ -1692,31 +1697,42 @@ public class TunerSessionWorkerExoV2 implements
@VisibleForTesting
protected void preparePlayback() {
MpegTsPlayerV2 player = createPlayer(mAudioCapabilities);
- if (!player.prepare(mChannel, this)) {
- mSourceManager.setKeepTuneStatus(false);
- player.release();
- if (!mHandler.hasMessages(MSG_TUNE)) {
- // When prepare failed, there may be some errors related to hardware. In that
- // case, retry playback immediately may not help.
- notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
- Log.i(TAG, "Notify weak signal due to player preparation failure");
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(
- MSG_RETRY_PLAYBACK, System.identityHashCode(mPlayer)),
- PLAYBACK_RETRY_DELAY_MS);
- }
- } else {
- mPlayer = player;
- mPlayerStarted = false;
- mHandler.removeMessages(MSG_CHECK_SIGNAL);
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_SIGNAL, CHECK_NO_SIGNAL_INITIAL_DELAY_MS);
- if (mHandler.hasMessages(MSG_CHECK_SIGNAL_STRENGTH)) {
- mHandler.removeMessages(MSG_CHECK_SIGNAL_STRENGTH);
- }
- if (CommonFeatures.TUNER_SIGNAL_STRENGTH.isEnabled(mContext)) {
- mHandler.sendEmptyMessage(MSG_CHECK_SIGNAL_STRENGTH);
+ TsDataSource source = null;
+ MediaSource mediaSource;
+ if (mChannel != null) {
+ source = mSourceManager.createDataSource(mContext, mChannel, this);
+ if (source == null) {
+ mSourceManager.setKeepTuneStatus(false);
+ player.release();
+ if (!mHandler.hasMessages(MSG_TUNE)) {
+ // When prepare failed, there may be some errors related to hardware. In that
+ // case, retry playback immediately may not help.
+ notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
+ Log.i(TAG, "Notify weak signal due to player preparation failure");
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(
+ MSG_RETRY_PLAYBACK, System.identityHashCode(mPlayer)),
+ PLAYBACK_RETRY_DELAY_MS);
+ }
+ return;
}
}
+ SampleExtractor extractor =
+ source == null ?
+ mMpegTsSampleExtractorFactory.create(
+ mBufferManager, this, mRecordingDuration) :
+ mMpegTsSampleExtractorFactory.create(source, mBufferManager, this);
+ mediaSource = new MpegTsMediaSource(extractor);
+ player.prepare(source, mediaSource);
+ mPlayer = player;
+ mHandler.removeMessages(MSG_CHECK_SIGNAL);
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_SIGNAL, CHECK_NO_SIGNAL_INITIAL_DELAY_MS);
+ if (mHandler.hasMessages(MSG_CHECK_SIGNAL_STRENGTH)) {
+ mHandler.removeMessages(MSG_CHECK_SIGNAL_STRENGTH);
+ }
+ if (CommonFeatures.TUNER_SIGNAL_STRENGTH.isEnabled(mContext)) {
+ mHandler.sendEmptyMessage(MSG_CHECK_SIGNAL_STRENGTH);
+ }
}
private void resetPlayback() {
@@ -1743,7 +1759,8 @@ public class TunerSessionWorkerExoV2 implements
mRetryCount = 0;
mChannel = channel;
mRecordingId = recording;
- mRecordingDuration = recording != null ? getDurationForRecording(recording) : null;
+ // TODO: Use asynchronous task to update this value.
+ mRecordingDuration = recording != null ? getDurationForRecording() : 0L;
mProgram = null;
mPrograms = null;
if (mRecordingId != null) {
@@ -1845,7 +1862,6 @@ public class TunerSessionWorkerExoV2 implements
}
private void doTimeShiftPause() {
- mHandler.removeMessages(MSG_SMOOTH_TRICKPLAY_MONITOR);
mHandler.removeMessages(MSG_TRICKPLAY_BY_SEEK);
if (!hasEnoughBackwardBuffer()) {
return;
@@ -1856,7 +1872,6 @@ public class TunerSessionWorkerExoV2 implements
}
private void doTimeShiftResume() {
- mHandler.removeMessages(MSG_SMOOTH_TRICKPLAY_MONITOR);
mHandler.removeMessages(MSG_TRICKPLAY_BY_SEEK);
mPlaybackParams.setSpeed(1.0f);
mPlayer.setPlayWhenReady(true);
@@ -1864,7 +1879,6 @@ public class TunerSessionWorkerExoV2 implements
}
private void doTimeShiftSeekTo(long timeMs) {
- mHandler.removeMessages(MSG_SMOOTH_TRICKPLAY_MONITOR);
mHandler.removeMessages(MSG_TRICKPLAY_BY_SEEK);
mPlayer.seekTo((int) (timeMs - mRecordStartTimeMs));
}
@@ -1876,17 +1890,9 @@ public class TunerSessionWorkerExoV2 implements
mPlaybackParams = params;
float speed = mPlaybackParams.getSpeed();
if (speed == 1.0f) {
- mHandler.removeMessages(MSG_SMOOTH_TRICKPLAY_MONITOR);
mHandler.removeMessages(MSG_TRICKPLAY_BY_SEEK);
doTimeShiftResume();
- } else if (mPlayer.supportSmoothTrickPlay(speed)) {
- mHandler.removeMessages(MSG_TRICKPLAY_BY_SEEK);
- mPlayer.setAudioTrackAndClosedCaption(false);
- mPlayer.startSmoothTrickplay(mPlaybackParams);
- mHandler.sendEmptyMessageDelayed(
- MSG_SMOOTH_TRICKPLAY_MONITOR, TRICKPLAY_MONITOR_INTERVAL_MS);
} else {
- mHandler.removeMessages(MSG_SMOOTH_TRICKPLAY_MONITOR);
if (!mHandler.hasMessages(MSG_TRICKPLAY_BY_SEEK)) {
mPlayer.setAudioTrackAndClosedCaption(false);
mPlayer.setPlayWhenReady(false);
diff --git a/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java b/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java
index 447618a4..b06d15ca 100644
--- a/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java
+++ b/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java
@@ -283,7 +283,7 @@ public class ChannelDataManager implements Handler.Callback {
* obsolete channels, which are previously scanned but are not in the current scanned result.
*/
public void notifyScanCompleted() {
- // Send a dummy message to check whether there is any MSG_HANDLE_CHANNEL in queue
+ // Send an empty message to check whether there is any MSG_HANDLE_CHANNEL in queue
// and avoid race conditions.
scanCompleted.set(true);
mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_HANDLE_CHANNEL, null));
@@ -635,7 +635,7 @@ public class ChannelDataManager implements Handler.Callback {
private void clearChannels() {
int count = mContext.getContentResolver().delete(mChannelsUri, null, null);
if (count > 0) {
- // We have just deleted obsolete data. Now tell the user that he or she needs
+ // We have just deleted obsolete data. Now tell the user that they need
// to perform the auto-scan again.
if (mListener != null) {
mListener.onRescanNeeded();
diff --git a/tuner/tests/robotests/Android.mk b/tuner/tests/robotests/Android.mk
index 16af9e9c..bf2f24c8 100644
--- a/tuner/tests/robotests/Android.mk
+++ b/tuner/tests/robotests/Android.mk
@@ -5,6 +5,8 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := TvTunerRoboTests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-java-files-under, javatests)
@@ -42,6 +44,8 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
#############################################################
include $(CLEAR_VARS)
LOCAL_MODULE := RunTvTunerRoboTests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
BASE_DIR = com/android/tv/tuner
EXCLUDE_FILES := \
diff --git a/tuner/tests/robotests/javatests/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2Test.java b/tuner/tests/robotests/javatests/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2Test.java
index 6ef09182..e34ef00b 100644
--- a/tuner/tests/robotests/javatests/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2Test.java
+++ b/tuner/tests/robotests/javatests/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2Test.java
@@ -25,6 +25,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.support.annotation.Nullable;
import android.view.accessibility.CaptioningManager;
import com.android.tv.common.CommonConstants;
@@ -37,6 +38,9 @@ import com.android.tv.testing.TestSingletonApp;
import com.android.tv.testing.constants.ConfigConstants;
import com.android.tv.tuner.cc.CaptionTrackRenderer;
import com.android.tv.tuner.exoplayer2.MpegTsPlayerV2;
+import com.android.tv.tuner.exoplayer2.MpegTsSampleExtractor;
+import com.android.tv.tuner.exoplayer2.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer2.buffer.PlaybackBufferListener;
import com.android.tv.tuner.source.TsDataSourceManager;
import com.android.tv.tuner.source.TunerTsStreamerManager;
import com.android.tv.tuner.testing.TvTunerRobolectricTestRunner;
@@ -44,6 +48,7 @@ import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager;
import com.android.tv.tuner.tvinput.TunerSessionOverlay;
import com.google.android.exoplayer2.audio.AudioCapabilities;
+import com.google.android.exoplayer2.upstream.DataSource;
import org.junit.Before;
import org.junit.Ignore;
@@ -108,6 +113,28 @@ public class TunerSessionWorkerExoV2Test {
captionLayout,
context2 -> null,
mTunerFlags));
+ MpegTsSampleExtractor.Factory mpegTsSampleExtractorFactory =
+ new MpegTsSampleExtractor.Factory() {
+ @Override
+ public MpegTsSampleExtractor create(
+ BufferManager bufferManager,
+ PlaybackBufferListener bufferListener,
+ long durationMs) {
+ return new MpegTsSampleExtractor(
+ bufferManager,
+ bufferListener,
+ durationMs,
+ (bufferManager1, bufferListener1, durationMs1) -> null);
+ }
+
+ @Override
+ public MpegTsSampleExtractor create(
+ DataSource source,
+ @Nullable BufferManager bufferManager,
+ PlaybackBufferListener bufferListener) {
+ return null;
+ }
+ };
new TunerSessionExoV2(
context,
@@ -123,6 +150,7 @@ public class TunerSessionWorkerExoV2Test {
tunerSessionOverlay,
mHandler,
mLegacyFlags,
+ mpegTsSampleExtractorFactory,
tsDataSourceManagerFactory) {
@Override
protected void notifySignal(int signal) {
@@ -182,30 +210,10 @@ public class TunerSessionWorkerExoV2Test {
}
@Test
- public void preparePlayback_playerIsNotReady() {
- Mockito.when(
- mPlayer.prepare(
- ArgumentMatchers.any(),
- ArgumentMatchers.any()))
- .thenReturn(false);
- tunerSessionWorker.preparePlayback();
- assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_TUNE)).isFalse();
- assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_RETRY_PLAYBACK)).isTrue();
- assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_CHECK_SIGNAL_STRENGTH)).isFalse();
- assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_CHECK_SIGNAL)).isFalse();
- }
-
- @Test
- @Ignore
public void preparePlayback_playerIsReady() {
- Mockito.when(
- mPlayer.prepare(
- ArgumentMatchers.any(),
- ArgumentMatchers.any()))
- .thenReturn(true);
tunerSessionWorker.preparePlayback();
assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_RETRY_PLAYBACK)).isFalse();
- assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_CHECK_SIGNAL_STRENGTH)).isTrue();
+ assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_CHECK_SIGNAL_STRENGTH)).isFalse();
assertThat(mHandler.hasMessages(TunerSessionWorker.MSG_CHECK_SIGNAL)).isTrue();
}
}
diff --git a/tuner/tests/testing/Android.mk b/tuner/tests/testing/Android.mk
index 864f5f3e..38f7342a 100644
--- a/tuner/tests/testing/Android.mk
+++ b/tuner/tests/testing/Android.mk
@@ -19,6 +19,8 @@ LOCAL_JAVA_LIBRARIES := tv-common
LOCAL_INSTRUMENTATION_FOR := LiveTv
LOCAL_MODULE := tv-tuner-testing
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_TAGS := optional
LOCAL_SDK_VERSION := system_current
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTestExoV2.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTestExoV2.java
index b2478eb6..359e7bbc 100644
--- a/tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTestExoV2.java
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/ZappingTimeTestExoV2.java
@@ -21,32 +21,39 @@ import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
-import android.support.annotation.Nullable;
import android.test.InstrumentationTestCase;
import android.util.Log;
+import android.util.Pair;
import android.view.Surface;
import androidx.test.filters.LargeTest;
+import com.android.tv.tuner.api.Tuner;
+import com.android.tv.tuner.api.TunerFactory;
import com.android.tv.tuner.data.Cea708Data;
import com.android.tv.tuner.data.Channel.AudioStreamType;
import com.android.tv.tuner.data.Channel.VideoStreamType;
import com.android.tv.tuner.data.PsiData;
import com.android.tv.tuner.data.PsipData;
import com.android.tv.tuner.data.TunerChannel;
-import com.android.tv.tuner.exoplayer.MpegTsPlayer;
-import com.android.tv.tuner.exoplayer.MpegTsRendererBuilder;
-import com.android.tv.tuner.exoplayer.MpegTsSampleExtractor;
-import com.android.tv.tuner.exoplayer.buffer.BufferManager;
-import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener;
-import com.android.tv.tuner.exoplayer.buffer.TrickplayStorageManager;
+import com.android.tv.tuner.exoplayer2.ExoPlayerSampleExtractor;
+import com.android.tv.tuner.exoplayer2.MpegTsMediaSource;
+import com.android.tv.tuner.exoplayer2.MpegTsPlayerV2;
+import com.android.tv.tuner.exoplayer2.MpegTsSampleExtractor;
+import com.android.tv.tuner.exoplayer2.SampleExtractor;
+import com.android.tv.tuner.exoplayer2.buffer.BufferManager;
+import com.android.tv.tuner.exoplayer2.buffer.PlaybackBufferListener;
+import com.android.tv.tuner.exoplayer2.buffer.RecordingSampleBuffer;
+import com.android.tv.tuner.exoplayer2.buffer.SampleChunkIoHelper;
+import com.android.tv.tuner.exoplayer2.buffer.TrickplayStorageManager;
+import com.android.tv.tuner.source.TsDataSource;
import com.android.tv.tuner.source.TsDataSourceManager;
import com.android.tv.tuner.source.TsDataSourceManager.Factory;
import com.android.tv.tuner.source.TunerTsStreamerManager;
import com.android.tv.tuner.ts.EventDetector.EventListener;
-import com.google.android.exoplayer.ExoPlayer;
-import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.source.MediaSource;
import javax.inject.Provider;
import org.junit.Ignore;
@@ -86,7 +93,7 @@ public class ZappingTimeTestExoV2 extends InstrumentationTestCase {
private TunerChannel mChannel;
private FileTunerHal mTunerHal;
- private MpegTsPlayer mPlayer;
+ private MpegTsPlayerV2 mPlayer;
private TsDataSourceManager mSourceManager;
private Handler mHandler;
private Context mTargetContext;
@@ -99,6 +106,7 @@ public class ZappingTimeTestExoV2 extends InstrumentationTestCase {
private MockMpegTsPlayerListener mMpegTsPlayerListener = new MockMpegTsPlayerListener();
private MockPlaybackBufferListener mPlaybackBufferListener = new MockPlaybackBufferListener();
private MockChannelScanListener mEventListener = new MockChannelScanListener();
+ private ExoPlayerSampleExtractor.Factory mExoPlayerSampleExtractorFactory;
@Override
protected void setUp() throws Exception {
@@ -126,31 +134,27 @@ public class ZappingTimeTestExoV2 extends InstrumentationTestCase {
mChannel.setModulation(MODULATION);
mTunerHal = new FileTunerHal(context, tsCacheFile);
mTunerHal.openFirstAvailable();
+ TunerFactory tunerFactory = new TunerFactory() {
+ @Override
+ public Tuner createInstance(Context context) {
+ return null;
+ }
+
+ @Override
+ public boolean useBuiltInTuner(Context context) {
+ return false;
+ }
+
+ @Override
+ public Pair<Integer, Integer> getTunerTypeAndCount(Context context) {
+ return null;
+ }
+ };
Provider<TunerTsStreamerManager> tsStreamerManagerProvider =
- () -> new TunerTsStreamerManager(null);
+ () -> new TunerTsStreamerManager(tunerFactory);
TsDataSourceManager.Factory tsFactory = new Factory(tsStreamerManagerProvider);
mSourceManager = tsFactory.create(false);
mSourceManager.addTunerHalForTest(mTunerHal);
- MpegTsSampleExtractor.Factory mpegTsSampleExtractorFactory =
- new MpegTsSampleExtractor.Factory() {
- @Override
- public MpegTsSampleExtractor create(
- BufferManager bufferManager, PlaybackBufferListener bufferListener) {
- return null;
- }
-
- @Override
- public MpegTsSampleExtractor create(
- DataSource source,
- @Nullable BufferManager bufferManager,
- PlaybackBufferListener bufferListener) {
- return new MpegTsSampleExtractor(
- source,
- bufferManager,
- bufferListener,
- (uri, source1, manager, listener, isRecording) -> null);
- }
- };
mHandler =
new Handler(
handlerThread.getLooper(),
@@ -160,48 +164,7 @@ public class ZappingTimeTestExoV2 extends InstrumentationTestCase {
switch (msg.what) {
case MSG_START_PLAYBACK:
{
- mHandler.removeCallbacksAndMessages(null);
- stopPlayback();
- mOnDrawnToSurfaceTimeMs.set(0);
- mDrawnToSurfaceLatch = new CountDownLatch(1);
- if (mWaitTuneExecuteLatch != null) {
- mWaitTuneExecuteLatch.countDown();
- }
- int frequency = msg.arg1;
- boolean useSimpleSampleBuffer = (msg.arg2 == 1);
- BufferManager bufferManager = null;
- if (!useSimpleSampleBuffer) {
- bufferManager =
- new BufferManager(
- new TrickplayStorageManager(
- mTargetContext,
- mTrickplayBufferDir,
- 1024L
- * 1024L
- * BUFFER_SIZE_DEF));
- }
- mChannel.setFrequency(frequency);
- mSourceManager.setKeepTuneStatus(true);
-
- mPlayer =
- new MpegTsPlayer(
- new MpegTsRendererBuilder(
- mTargetContext,
- bufferManager,
- mPlaybackBufferListener,
- mpegTsSampleExtractorFactory),
- mHandler,
- mSourceManager,
- null,
- mMpegTsPlayerListener);
- mPlayer.setCaptionServiceNumber(
- Cea708Data.EMPTY_SERVICE_NUMBER);
- mPlayer.prepare(
- mTargetContext,
- mChannel,
- false,
- mEventListener);
- return true;
+ handleMessageStartPlayback(msg.arg1, msg.arg2 == 1);
}
default:
{
@@ -211,6 +174,70 @@ public class ZappingTimeTestExoV2 extends InstrumentationTestCase {
}
}
});
+ SampleChunkIoHelper.Factory sampleChunkIoHelperFactory =
+ (ids, mediaFormats, bufferReason, bufferManager, samplePool, ioCallback) ->
+ new SampleChunkIoHelper(
+ ids,
+ mediaFormats,
+ bufferReason,
+ bufferManager,
+ samplePool,
+ ioCallback);
+ RecordingSampleBuffer.Factory recordingSampleBufferFactory =
+ (bufferManager, bufferListener, enableTrickplay, bufferReason) ->
+ new RecordingSampleBuffer(
+ bufferManager,
+ bufferListener,
+ enableTrickplay,
+ bufferReason,
+ sampleChunkIoHelperFactory);
+ mExoPlayerSampleExtractorFactory =
+ (uri, source, bufferManager, bufferListener, isRecording) ->
+ new ExoPlayerSampleExtractor(
+ uri,
+ source,
+ bufferManager,
+ bufferListener,
+ isRecording,
+ recordingSampleBufferFactory);
+ }
+
+ private boolean handleMessageStartPlayback(int frequency, boolean useSimpleSampleBuffer) {
+ mHandler.removeCallbacksAndMessages(null);
+ stopPlayback();
+ mOnDrawnToSurfaceTimeMs.set(0);
+ mDrawnToSurfaceLatch = new CountDownLatch(1);
+ if (mWaitTuneExecuteLatch != null) {
+ mWaitTuneExecuteLatch.countDown();
+ }
+ BufferManager bufferManager = null;
+ if (!useSimpleSampleBuffer) {
+ bufferManager =
+ new BufferManager(
+ new TrickplayStorageManager(
+ mTargetContext,
+ mTrickplayBufferDir,
+ 1024L * 1024L * BUFFER_SIZE_DEF));
+ }
+ mChannel.setFrequency(frequency);
+ mSourceManager.setKeepTuneStatus(true);
+ mPlayer = new MpegTsPlayerV2(mTargetContext, mMpegTsPlayerListener);
+ mPlayer.setCaptionServiceNumber(
+ Cea708Data.EMPTY_SERVICE_NUMBER);
+ TsDataSource source =
+ mSourceManager.createDataSource(mTargetContext, mChannel, mEventListener);
+ if (source == null) {
+ return false;
+ }
+ SampleExtractor extractor =
+ new MpegTsSampleExtractor(
+ source,
+ bufferManager,
+ mPlaybackBufferListener,
+ mExoPlayerSampleExtractorFactory);
+ MediaSource mediaSource = new MpegTsMediaSource(extractor);
+ mPlayer.prepare(source, mediaSource);
+ return true;
}
@Override
@@ -345,12 +372,12 @@ public class ZappingTimeTestExoV2 extends InstrumentationTestCase {
}
}
- private class MockMpegTsPlayerListener implements MpegTsPlayer.Listener {
+ private class MockMpegTsPlayerListener implements MpegTsPlayerV2.Callback {
@Override
- public void onStateChanged(boolean playWhenReady, int playbackState) {
+ public void onStateChanged(int playbackState) {
if (DEBUG) {
- Log.d(TAG, "ExoPlayer state change: " + playbackState + " " + playWhenReady);
+ Log.d(TAG, "ExoPlayer state change: " + playbackState);
}
if (playbackState == ExoPlayer.STATE_READY) {
mPlayer.setSurface(mSurface);
@@ -377,7 +404,7 @@ public class ZappingTimeTestExoV2 extends InstrumentationTestCase {
}
@Override
- public void onDrawnToSurface(MpegTsPlayer player, Surface surface) {
+ public void onRenderedFirstFrame() {
if (DEBUG) {
Log.d(TAG, "onDrawnToSurface");
}