Google is committed to advancing racial equity for Black communities. See how.

Tuner Framework

For Android 11 or higher, you can use the Android Tuner framework to deliver A/V content. The framework uses the hardware pipeline from vendors, making it suitable for both low-end and high-end SoC. The framework provides a secure way to deliver A/V content protected by a trusted execution environment (TEE) and secure media path (SMP), allowing it to be used in a highly restricted, content protection environment.

The standardized interface between Tuner and Android CAS results in a faster integration between Tuner vendors and CAS vendors. The Tuner interface works with MediaCodec and AudioTrack to build a one world solution for Android TV. The Tuner interface supports both digital TV and analog TV based on major broadcast standards.

Components

For Android 11, three components are specifically designed for the TV platform.

  • Tuner HAL: An interface between the framework and vendors
  • Tuner SDK API: An interface between the framework and apps
  • Tuner Resource Manager (TRM): Coordinates Tuner HW resources

For Android 11, the following components have been enhanced.

  • CAS V2
  • TvInputService or TV Input Service (TIS)
  • TvInputManagerService or TV Input Manager Service (TIMS)
  • MediaCodec or media codec
  • AudioTrack or audio track
  • MediaResourceManager or media resource manager (MRM)

Flow diagram of Tuner framework components.

Figure 1. Interactions between Android TV components

Features

Frontend supports the DTV standards below.

  • ATSC
  • ATSC3
  • DVB C/S/T
  • ISDB S/S3/T
  • Analog

Demux supports the stream protocols below.

  • Transport stream (TS)
  • MPEG media transport protocol (MMTP)
  • Internet protocol (IP)
  • Type length value (TLV)
  • ATSC link-layer protocol (ALP)

Descrambler supports the content protections below.

  • Secure media path
  • Clear media path
  • Secure local record
  • Secure local playback

Tuner APIs support the use cases below.

  • Scan
  • Live
  • Playback
  • Record

Tuner, MediaCodec, and AudioTrack support the data flow modes below.

  • ES payload with clear memory buffer
  • ES payload with secure memory handle
  • Passthrough

Overall design

The Tuner HAL is defined between the Android framework and the vendor's hardware.

  • Describes what the framework expects from the vendor and how the vendor might do it.
  • Exports the functionalities of frontend, demux and descrambler to the framework through IFrontend, IDemux, IDescrambler, IFilter, IDvr, and ILnb interfaces.
  • Includes the functions to integrate the Tuner HAL with other framework components, such as MediaCodec and AudioTrack.

A Tuner Java class and native class are created.

  • The Tuner Java API allows apps to access the Tuner HAL through public APIs.
  • Native class allows permission control and handling of large amounts of recording or playback data with the Tuner HAL.
  • Native Tuner module is a bridge between the Tuner Java class and the Tuner HAL.

A TRM class is created.

  • Manages limited Tuner resources, such as Frontend, LNB, CAS sessions, and a TV input device from the TV input HAL.
  • Applies rules to reclaim insufficient resources from apps. The default rule is the foreground win.

Media CAS and the CAS HAL are enhanced with the features below.

  • Opens CAS sessions for different usages and algorithms.
  • Supports dynamic CAS systems, such as CICAM removal and insertion.
  • Integrates with the Tuner HAL by providing key tokens.

MediaCodec and AudioTrack are enhanced with the features below.

  • Takes secure A/V memory as content input.
  • Configured to do hardware A/V sync in tunneled playback.
  • Configured support for ES_payload and passthrough mode.

Overall design of the Tuner HAL.

Figure 2. Diagram of the components within the Tuner HAL

Overall workflow

The diagrams below illustrate call sequences for live broadcast playback.

Setup

Setup sequence of live broadcast playback diagram.

Figure 3. Setup sequence for live broadcast playback

Handling A/V

Handling A/V for live broadcast playback diagram.

Figure 4. Handling A/V for live broadcast playback

Handling scrambled content

Handling scrambled content for live broadcast playback diagram.

Figure 5. Handling scrambled content for live broadcast playback

Processing A/V data

Process A/V data for live broadcast playback diagram.

Figure 6. Processing A/V for live broadcast playback

Tuner SDK API

The Tuner SDK API handles the interactions with the Tuner JNI, the Tuner HAL, and TunerResourceManager. The TIS app uses the Tuner SDK API to access Tuner resources and subcomponents such as the filter and descrambler. Frontend and demux are internal components.

Flow diagram of the Tuner SDK API.

Figure 7. Interactions with the Tuner SDK API

Packages

The Tuner SDK API provides the four packages below.

  • android.media.tv.tuner
  • android.media.tv.tuner.frontend
  • android.media.tv.tuner.filter
  • android.media.tv.tuner.dvr

Flow diagram of the Tuner SDK API packages.

Figure 8. Tuner SDK API packages

Android.media.tv.tuner

The Tuner package is an entry point to use the Tuner framework. The TIS app uses the package to initialize and acquire resource instances by specifying the initial setting and callback.

  • tuner(): Initializes a Tuner instance by specifying the useCase and sessionId parameters.
  • tune(): Acquires a frontend resource and tune by specifying the FrontendSetting parameter.
  • openFilter(): Acquires a filter instance by specifying the filter type.
  • openDvrRecorder(): Acquires a recording instance by specifying the buffer size.
  • openDvrPlayback(): Acquires a playback instance by specifying the buffer size.
  • openDescrambler(): Acquires a descrambler instance.
  • openLnb(): Acquires an internal LNB instance.
  • openLnbByName(): Acquires an external LNB instance.
  • openTimeFilter(): Acquires a time filter instance.

The Tuner package provides functionalities that aren't covered under the filter, DVR, and frontend packages. The functionalities are listed below.

  • cancelTuning
  • scan / cancelScanning
  • getAvSyncHwId
  • getAvSyncTime
  • connectCiCam1 / disconnectCiCam
  • shareFrontendFromTuner
  • updateResourcePriority
  • setOnTuneEventListener
  • setResourceLostListener

Android.media.tv.tuner.frontend

The frontend package includes collections of frontend-related settings, information, statuses, events, and capabilities.

Classes

FrontendSettings is derived for different DTV standards by the classes below.

  • AnalogFrontendSettings
  • Atsc3FrontendSettings
  • AtscFrontendSettings
  • DvbcFrontendSettings
  • DvbsFrontendSettings
  • DvbtFrontendSettings
  • Isdbs3FrontendSettings
  • IsdbsFrontendSettings
  • IsdbtFrontendSettings

FrontendCapabilities is derived for different DTV standards by the classes below.

  • AnalogFrontendCapabilities
  • Atsc3FrontendCapabilities
  • AtscFrontendCapabilities
  • DvbcFrontendCapabilities
  • DvbsFrontendCapabilities
  • DvbtFrontendCapabilities
  • Isdbs3FrontendCapabilities
  • IsdbsFrontendCapabilities
  • IsdbtFrontendCapabilities

FrontendInfo retrieves the frontend's information. FrontendStatus retrieves the current status of the frontend. OnTuneEventListener listens to the events on the frontend. The TIS app uses ScanCallback to process scan messages from the frontend.

Channel scan

To set up a TV, the app scans possible frequencies and builds up a channel lineup for users to access. TIS might use Tuner.tune, Tuner.scan(BLIND_SCAN), or Tuner.scan(AUTO_SCAN) to complete channel scanning.

If TIS has accurate delivery information for the signal, such as frequency, standard (for example, T/T2, S/S2), and additional necessary information (for example, PLD ID), then Tuner.tune is recommended as the faster option.

When the user calls Tuner.tune, the following actions happen:

  • TIS populates FrontendSettings with required information using Tuner.tune.
  • The HAL reports tune LOCKED messages if the signal is locked.
  • TIS uses Frontend.getStatus to collect the necessary information.
  • TIS moves to the next available frequency in its frequency list.

TIS calls Tuner.tune again until all frequencies are exhausted.

During tuning, you can call stopTune() or close() to pause or end the Tuner.tune call.

Tuner.scan(AUTO_SCAN)

If TIS doesn't have enough information to use Tuner.tune, but has a frequency list and standard type (for example, DVB T/C/S), then Tuner.scan(AUTO_SCAN) is recommended.

When the user calls Tuner.scan(AUTO_SCAN), the following actions happen:

  • TIS uses Tuner.scan(AUTO_SCAN) with FrontendSettings filled with frequency.

  • The HAL reports scan LOCKED messages if the signal is locked. The HAL might also report other scan messages to provide additional information about the signal.

  • TIS uses Frontend.getStatus to collect necessary information.

  • TIS calls Tuner.scan for the HAL to continue to the next setting on the same frequency. If the FrontendSettings structure is empty, the HAL uses the next available setting. Otherwise, HAL uses FrontendSettings for a one-time scan and sends END to indicate that the scan operation is finished.

  • TIS repeats the actions above until all settings on the frequency are exhausted.

  • The HAL sends END to indicate that the scan operation is finished.

  • TIS moves to the next available frequency in its frequency list.

TIS calls Tuner.scan(AUTO_SCAN) again until all frequencies are exhausted.

During scanning, you can call stopScan() or close() to pause or end the scan.

Tuner.scan(BLIND_SCAN)

If TIS doesn't have a frequency list and the Vendor HAL can search for the frequency of the user-specified frontend to get the frontend resource, then Tuner.scan(BLIND_SCAN) is recommended.

  • TIS uses Tuner.scan(BLIND_SCAN). A frequency can be specified in FrontendSettings for start frequency, but TIS ignores other settings in FrontendSettings.
  • The HAL reports a scan LOCKED message if signal is locked.
  • TIS uses Frontend.getStatus to collect necessary information.
  • TIS calls Tuner.scan again to continue scanning. (FrontendSettings is ignored.)
  • TIS repeats the actions above until all settings on the frequency are exhausted. The HAL increments frequency with no action needed from TIS. The HAL reports PROGRESS.

TIS calls Tuner.scan(AUTO_SCAN) again until all frequencies are exhausted. The HAL reports END to indicate that the scan operation is finished.

During scanning, you can call stopScan() or close() to pause or end the scan.

Flow diagram of the TIS Scan process.

Figure 9. Flow diagram of a TIS scan

Android.media.tv.tuner.filter

The filter package is a collection of filter operations along with configuration, settings, callbacks, and events. The package includes the operations below. Refer to the Android source code for the complete list of operations.

  • configure()
  • start()
  • stop()
  • flush()
  • read()

Refer to the Android source code for the complete list.

FilterConfiguration is derived from the classes below. The configurations are for the main filter type and they specify which protocol the filter uses to extract data.

  • AlpFilterConfiguration
  • IpFilterConfiguration
  • MmtpFilterConfiguration
  • TlvFilterConfiguration
  • TsFilterConfiguration

The settings are derived from the classes below. The settings are for the filter subtype and they specify what kinds of data the filter can exclude.

  • SectionSettings
  • AvSettings
  • PesSettings
  • RecordSettings
  • DownloadSettings

FilterEvent is derived from the classes below to report events for different kinds of data.

  • SectionEvent
  • MediaEvent
  • PesEvent
  • TsRecordEvent
  • MmtpRecordEvent
  • TemiEvent
  • DownloadEvent
  • IpPayloadEvent
Events and data format from filter
Filter type Flags Events Data operation Data format
TS.SECTION
MMTP.SECTION
IP.SECTION
TLV.SECTION
ALP.SECTION
isRaw:
true
Mandatory:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Recommended:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
According to the event and internal schedule, run
Filter.read(buffer, offset, adjustedSize) one or more times.

Data is copied from HAL's MQ to the client buffer.
One assembled session package is filled in FMQ by another session package.
isRaw:
false
Mandatory:
DemuxFilterEvent::DemuxFilterSectionEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Optional:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterSectionEven[i].size)


Data is copied from HAL's MQ to the client buffer.
TS.PES isRaw:
true
Mandatory:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Recommended:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
According to the event and internal schedule, run
Filter.read(buffer, offset, adjustedSize) one or more times.

Data is copied from the HAL's MQ to the client buffer.
One assembled PES package is filled in FMQ by another PES package.
isRaw:
false
Mandatory:
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Optional:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


Data is copied from HAL's MQ to the client buffer.
MMTP.PES isRaw:
true
Mandatory:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Recommended:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
According to the event and internal schedule, run
Filter.read(buffer, offset, adjustedSize) one or more times.

Data is copied from the HAL's MQ to the client buffer.
One assembled MFU package is filled in FMQ by another MFU package.
isRaw:
false
Mandatory:
DemuxFilterEvent::DemuxFilterPesEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Optional:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
Filter.read(buffer, offset, DemuxFilterPesEven[i].size)


Data is copied from the HAL's MQ to the client buffer.
TS.TS
N/A Mandatory:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Recommended:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
According to the event and internal schedule, run
Filter.read(buffer, offset, adjustedSize) one or more times.

Data is copied from the HAL's MQ to the client buffer.
Filtered out ts with ts header
is filled in FMQ.
TS.Audio
TS.Video
MMTP.Audio
MMTP.Video
isPassthrough:
true
Optional:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
The client can start MediaCodec after receiving DemuxFilterStatus::DATA_READY.
The client can call Filter.flush after receiving DemuxFilterStatus::DATA_OVERFLOW.
N/A
isPassthrough:
false
Mandatory:
DemuxFilterEvent::DemuxFilterMediaEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Optional:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
To use MediaCodec:
for i=0; i<n; i++
linearblock = MediaEven[i].getLinaraBlock();
codec.startQueueLinearBlock(linearblock)
linearblock.recycle()


To use Direct Audio of AudioTrack:
for i=0; i<n; i++
audioHandle = MediaEven[i].getAudioHandle();
audiotrack.write(encapsulated(audiohandle))
ES or partial ES data in ION memory.
TS.PCR
IP.NTP
ALP.PTP
N/A Mandatory: N/A
Optional: N/A
N/A N/A
TS.RECORD N/A Mandatory:
DemuxFilterEvent::DemuxFilterTsRecordEvent[n]
RecordStatus::DATA_READY
RecordStatus::DATA_OVERFLOW
RecordStatus::LOW_WATER
RecordStatus::HIGH_WATER

Optional:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
For index data:
for i=0; i<n; i++
DemuxFilterTsRecordEvent[i];


For recorded content, according to RecordStatus::* and internal schedule, do one of the following:
  • Run DvrRecord.write(adustedSize) one or more times to storage.
    The data is transferred from the HAL's MQ to storage.
  • Run DvrRecord.write(buffer, adustedSize) one or more times to buffer.
    The data is copied from the HAL's MQ to the client buffer.
For index data: Carried in event payload.

For recorded content: Muxed TS stream filled in FMQ.
TS.TEMI N/A Mandatory:
DemuxFilterEvent::DemuxFilterTemiEvent[n]

Optional:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++
DemuxFilterTemiEvent[i];
N/A
MMTP.MMTP N/A Mandatory:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Recommended:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
According to the event and internal schedule, run
Filter.read(buffer, offset, adjustedSize) one or more times.

Data is copied from the HAL's MQ to the client buffer.
Filtered out mmtp with mmtp header
is filled in FMQ.
MMTP.RECORD N/A Mandatory:
DemuxFilterEvent::DemuxFilterMmtpRecordEvent[n]
RecordStatus::DATA_READY
RecordStatus::DATA_OVERFLOW
RecordStatus::LOW_WATER
RecordStatus::HIGH_WATER

Optional:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
For index data: for i=0; i<n; i++
DemuxFilterMmtpRecordEvent[i];


For recorded content, according to RecordStatus::* and internal schedule, do one of the following:
  • Run DvrRecord.write(adjustedSize) one or more times to storage.
    The data is transferred from the HAL's MQ to storage.
  • Run DvrRecord.write(buffer, adjustedSize)one or more times to buffer.
    The data is copied from the HAL's MQ to the client buffer.
For index data: Carried in event payload.

For recorded content: Muxed recorded stream filled in FMQ.

If the filter source for recording is TLV.TLV to IP.IP with passthrough, the recorded stream has a TLV and IP header.
MMTP.DOWNLOAD N/A Mandatory:
DemuxFilterEvent::DemuxFilterDownloadEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Optional:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++ Filter.read(buffer, offset, DemuxFilterDownloadEvent[i].size)

Data is copied from HAL's MQ to the client buffer.
Download package is filled in FMQ by another IP download package.
IP.IP_PAYLOAD N/A Mandatory:
DemuxFilterEvent::DemuxFilterIpPayloadEvent[n]
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Optional:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
for i=0; i<n; i++ Filter.read(buffer, offset, DemuxFilterIpPayloadEvent[i].size)

Data is copied from HAL's MQ to the client buffer.
IP payload package is filled in FMQ by another IP payload package.
IP.IP
TLV.TLV
ALP.ALP
isPassthrough:
true
Optional:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
Filtered out protocol sub stream feeds the next filter in filter chain. N/A
isPassthrough:
false
Mandatory:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW

Recommended:
DemuxFilterStatus::LOW_WATER
DemuxFilterStatus::HIGH_WATER
According to the event and internal schedule, run
Filter.read(buffer, offset, adjustedSize) one or more times.

Data is copied from the HAL's MQ to the client buffer.
Filtered out protocol sub stream with protocol header is filled in FMQ.
IP.PAYLOAD_THROUGH
TLV.PAYLOAD_THROUGH
ALP.PAYLOAD_THROUGH
N/A Optional:
DemuxFilterStatus::DATA_READY
DemuxFilterStatus::DATA_OVERFLOW
Filtered out protocol payload feeds the next filter in filter chain. N/A
Example flow to use filter to build PSI/SI

Example flow for using filter to build PSI/SI.

Figure 10. Flow to build PSI/SI

  1. Open a filter.

    Filter filter = tuner.openFilter(
      Filter.TYPE_TS,
      Filter.SUBTYPE_SECTION,
      /* bufferSize */1000,
      executor,
      filterCallback
    );
    
  2. Configure and start the filter.

    Settings settings = SectionSettingsWithTableInfo
        .builder(Filter.TYPE_TS)
        .setTableId(2)
        .setVersion(1)
        .setCrcEnabled(true)
        .setRaw(false)
        .setRepeat(false)
        .build();
      FilterConfiguration config = TsFilterConfiguration
        .builder()
        .setTpid(10)
        .setSettings(settings)
        .build();
      filter.configure(config);
      filter.start();
    
  3. Process SectionEvent.

    FilterCallback filterCallback = new FilterCallback() {
      @Override
      public void onFilterEvent(Filter filter, FilterEvent[] events) {
        for (FilterEvent event : events) {
          if (event instanceof SectionEvent) {
            SectionEvent sectionEvent = (SectionEvent) event;
            int tableId = sectionEvent.getTableId();
            int version = sectionEvent.getVersion();
            int dataLength = sectionEvent.getDataLength();
            int sectionNumber = sectionEvent.getSectionNumber();
            filter.read(buffer, 0, dataLength); }
          }
        }
    };
    
Example flow to use MediaEvent from filter

Example flow to use MediaEvent from filter.

Figure 11. Flow to use MediaEvent from filter

  1. Open, configure, and start the A/V filters.
  2. Process MediaEvent.
  3. Receive MediaEvent.
  4. Queue the linear block to codec.
  5. Release the A/V handle when the data has been consumed.

Android.media.tv.tuner.dvr

DvrRecorder provides these methods for recording.

  • configure
  • attachFilter
  • detachFilter
  • start
  • flush
  • stop
  • setFileDescriptor
  • write

DvrPlayback provides these methods for playback.

  • configure
  • start
  • flush
  • stop
  • setFileDescriptor
  • read

DvrSettings is used to configure DvrRecorder and DvrPlayback. OnPlaybackStatusChangedListener and OnRecordStatusChangedListener are used to report the status of a DVR instance.

Example flow to start a record

Example flow to start a record.

Figure 12. Flow to start a record

  1. Open, configure, and start DvrRecorder.

    DvrRecorder recorder = openDvrRecorder(/* bufferSize */ 1000, executor, listener);
    DvrSettings dvrSettings = DvrSettings
    .builder()
    .setDataFormat(DvrSettings.DATA_FORMAT_TS)
    .setLowThreshold(100)
    .setHighThreshold(900)
    .setPacketSize(188)
    .build();
    recorder.configure(dvrSettings);
    recorder.attachFilter(filter);
    recorder.setFileDescriptor(fd);
    recorder.start();
    
  2. Receive RecordEvent and retrieve the index information.

    FilterCallback filterCallback = new FilterCallback() {
      @Override
      public void onFilterEvent(Filter filter, FilterEvent[] events) {
        for (FilterEvent event : events) {
          if (event instanceof TsRecordEvent) {
            TsRecordEvent recordEvent = (TsRecordEvent) event;
            int tsMask = recordEvent.getTsIndexMask();
            int scMask = recordEvent.getScIndexMask();
            int packetId = recordEvent.getPacketId();
            long dataLength = recordEvent.getDataLength();
            // handle the masks etc. }
          }
        }
    };
    
  3. Initialize OnRecordStatusChangedListener and store the record data.

      OnRecordStatusChangedListener listener = new OnRecordStatusChangedListener() {
        @Override
        public void onRecordStatusChanged(int status) {
          // a customized way to consume data efficiently by using status as a hint.
          if (status == Filter.STATUS_DATA_READY) {
            recorder.write(size);
          }
        }
      };
    

Tuner HAL

The Tuner HAL follows HIDL and defines the interface between the framework and vendor hardware. Vendors use the interface to implement the Tuner HAL and the framework uses it to communicate with the Tuner HAL implementation.

Modules

Modules Basic controls Module-specific controls HAL files
ITuner N/A frontend(open, getIds, getInfo), openDemux, openDescrambler, openLnb, getDemuxCaps ITuner.hal
IFrontend setCallback, getStatus, close tune, stopTune, scan, stopScan, setLnb IFrontend.hal
IFrontendCallback.hal
IDemux close setFrontendDataSource, openFilter, openDvr, getAvSyncHwId, getAvSyncTime, connect / disconnectCiCam IDemux.hal
IDvr close, start, stop, configure attach/detachFilters, flush, getQueueDesc IDvr.hal
IDvrCallback.hal
IFilter close, start, stop, configure, getId flush, getQueueDesc, releaseAvHandle, setDataSource IFilter.hal
IFilterCallback.hal
ILnb close, setCallback setVoltage, setTone, setSatellitePosition, sendDiseqcMessage ILnb.hal
ILnbCallback.hal
IDescrambler close setDemuxSource, setKeyToken, addPid, removePid IDescrambler.hal

Flow diagram of interactions between the modules of the Tuner HAL.

Figure 13. Diagram of the interactions between the Tuner HAL modules

Filter linkage

The Tuner HAL supports filter linkage such that filters can be linked to other filters for multiple layers. The filters follow the rules below.

  • Filters are linked as a tree, close path is not allowed.
  • The root node is demux.
  • Filters operate independently.
  • All filters start to get data.
  • The filter linkage flushes on the last filter.

The code block below and Figure 14 illustrate an example of filtering multiple layers.

demuxCaps = ITuner.getDemuxCap;
If (demuxCaps[IP][MMTP] == true) {
        ipFilter = ITuner.openFilter(<IP, ..>)
        mmtpFilter1 = ITuner.openFilter(<MMTP ..>)
        mmtpFilter2 = ITuner.openFilter(<MMTP ..>)
        mmtpFilter1.setDataSource(<ipFilter>)
        mmtpFilter2.setDataSource(<ipFilter>)
}

Diagram of filter linkage example.

Figure 14. Flow diagram of a filter linkage for multiple layers

Tuner Resource Manager

Before Tuner Resource Manager (TRM), switching between two apps required the same Tuner hardware. TV Input Framework (TIF) used a "first-to-acquire win" mechanism, which means whichever app gets the resource first keeps the resource. However, this mechanism might not be ideal for some complicated use cases.

TRM runs as a system service to manage the Tuner, TVInput, and CAS hardware resources for apps. TRM uses a "foreground win" mechanism, which calculates the app's priority based on the app's foreground or background status and use case type. TRM grants or revokes the resource based on the priority. TRM centralizes ATV resource management for broadcast, OTT, and DVR.

TRM interface

TRM exposes AIDL interfaces in ITunerResourceManager.aidl for the Tuner framework, MediaCas, and TvInputHardwareManager to register, request, or release resources.

Interfaces for client management are listed below.

  • registerClientProfile(in ResourceClientProfile profile, IResourcesReclaimListener listener, out int[] clientId)
  • unregisterClientProfile(in int clientId)

The interfaces to request and release resources are listed below.

  • requestFrontend(TunerFrontendRequest request, int[] frontendHandle) / releaseFrontend
  • requestDemux(TunerDemuxRequest request, int[] demuxHandle) / releaseDemux
  • requestDescrambler(TunerDescramblerRequest request, int[] descramblerHandle) / releaseDescrambler
  • requestCasSession(CasSessionRequest request, int[] casSessionHandle) / releaseCasSession
  • requestLnb(TunerLnbRequest request, int[] lnbHandle) / releaseLnb

Client and request classes are listed below.

  • ResourceClientProfile
  • ResourcesReclaimListener
  • TunerFrontendRequest
  • TunerDemuxRequest
  • TunerDescramblerRequest
  • CasSessionRequest
  • TunerLnbRequest

Client priority

TRM calculates the client's priority by using parameters from the client's profile and the priority value from the configuration file. The priority might also be updated by an arbitrary priority value from the client.

Parameters in client's profile

TRM retrieves the process ID from mTvInputSessionId to decide whether an app is a foreground or background app. To create mTvInputSessionId, TvInputService.onCreateSession, or TvInputService.onCreateRecordingSession initializes a TIS session.

mUseCase indicates the session's use case. The predefined use cases are listed below.

TvInputService.PriorityHintUseCaseType  {
  PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK
  PRIORITY_HINT_USE_CASE_TYPE_LIVE
  PRIORITY_HINT_USE_CASE_TYPE_RECORD,
  PRIORITY_HINT_USE_CASE_TYPE_SCAN,
  PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND
}

Configuration file

Default configuration file

The default configuration file below provides priority values for predefined use cases. Users can change the values using a customized configuration file.

Use case Foreground Background
LIVE 490 400
PLAYBACK 480 300
RECORD 600 500
SCAN 450 200
BACKGROUND 180 100
Customized configuration file

Vendors can customize the configuration file /vendor/etc/tunerResourceManagerUseCaseConfig.xml. This file is used to add, remove, or update the use case types and the use case priority values. The customized file can use platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfigSample.xml as a template.

For example, a new vendor use case is VENDOR_USE_CASE__[A-Z0-9]+, [0 - 1000]. The format should follow platform/hardware/interfaces/tv/tuner/1.0/config/tunerResourceManagerUseCaseConfig.xsd.

Arbitrary priority value and nice value

TRM provides updateClientPriority for the client to update the arbitrary priority value and nice value. The arbitrary priority value overwrites the priority value calculated from use case type and session ID.

The nice value indicates how lenient the client's behavior is when it's in conflict with another client. The nice value decreases the client's priority value before its priority value is compared to the challenging client.

Reclaim mechanism

The diagram below shows how resources are reclaimed and assigned when a resource conflict happens.

Diagram of reclaim mechanism process.

Figure 15. Diagram of the reclaim mechanism for a conflict between Tuner resources