/* * Copyright (C) 2014 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 android.media.tv; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.AttributionSource; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.media.AudioDeviceInfo; import android.media.AudioFormat.Encoding; import android.media.AudioPresentation; import android.media.PlaybackParams; import android.media.tv.ad.TvAdManager; import android.media.tv.flags.Flags; import android.media.tv.interactive.TvInteractiveAppManager; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Pools.Pool; import android.util.Pools.SimplePool; import android.util.SparseArray; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventSender; import android.view.KeyEvent; import android.view.Surface; import android.view.View; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; /** * Central system API to the overall TV input framework (TIF) architecture, which arbitrates * interaction between applications and the selected TV inputs. * *
There are three primary parties involved in the TV input framework (TIF) architecture: * *
Type: String */ public static final String TV_MESSAGE_KEY_STREAM_ID = "android.media.tv.TvInputManager.stream_id"; /** * This value for {@link #TV_MESSAGE_KEY_GROUP_ID} denotes that the message doesn't * belong to any group. */ public static final long TV_MESSAGE_GROUP_ID_NONE = -1; /** * This constant is used as a {@link Bundle} key for TV messages. This is used to * optionally identify messages that belong together, such as headers and bodies * of the same event. For messages that do not have a group, this value * should be {@link #TV_MESSAGE_GROUP_ID_NONE}. * *
As -1 is a reserved value, -1 should not be used as a valid groupId. * *
Type: long */ public static final String TV_MESSAGE_KEY_GROUP_ID = "android.media.tv.TvInputManager.group_id"; /** * This is a subtype for TV messages that can be potentially found as a value * at {@link #TV_MESSAGE_KEY_SUBTYPE}. It identifies the subtype of the message * as the watermarking format ATSC A/335. */ public static final String TV_MESSAGE_SUBTYPE_WATERMARKING_A335 = "ATSC A/335"; /** * This is a subtype for TV messages that can be potentially found as a value * at {@link #TV_MESSAGE_KEY_SUBTYPE}. It identifies the subtype of the message * as the CC format CTA 608-E. */ public static final String TV_MESSAGE_SUBTYPE_CC_608E = "CTA 608-E"; /** * This constant is used as a {@link Bundle} key for TV messages. The value of the key * identifies the subtype of the data, such as the format of the CC data. The format * found at this key can then be used to identify how to parse the data at * {@link #TV_MESSAGE_KEY_RAW_DATA}. * *
To parse the raw data based on the subtype, please refer to the official * documentation of the concerning subtype. For example, for the subtype * {@link #TV_MESSAGE_SUBTYPE_WATERMARKING_A335}, the document for A/335 from the ATSC * standard details how this data is formatted. Similarly, the subtype * {@link #TV_MESSAGE_SUBTYPE_CC_608E} is documented in the ANSI/CTA standard for * 608-E. These subtypes are examples of common formats for their respective uses * and other subtypes may exist. * *
Type: String */ public static final String TV_MESSAGE_KEY_SUBTYPE = "android.media.tv.TvInputManager.subtype"; /** * This constant is used as a {@link Bundle} key for TV messages. The value of the key * stores the raw data contained in this TV message. The format of this data is determined * by the format defined by the subtype, found using the key at * {@link #TV_MESSAGE_KEY_SUBTYPE}. See {@link #TV_MESSAGE_KEY_SUBTYPE} for more * information on how to parse this data. * *
Type: byte[] */ public static final String TV_MESSAGE_KEY_RAW_DATA = "android.media.tv.TvInputManager.raw_data"; static final int VIDEO_UNAVAILABLE_REASON_START = 0; static final int VIDEO_UNAVAILABLE_REASON_END = 18; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable due to * an unspecified error. */ public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = VIDEO_UNAVAILABLE_REASON_START; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the corresponding TV input is in the middle of tuning to a new channel. */ public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable due to * weak TV signal. */ public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the corresponding TV input has stopped playback temporarily to buffer more data. */ public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the current TV program is audio-only. */ public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the source is not physically connected, for example the HDMI cable is not connected. */ public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the resource is not enough to meet requirement. */ public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the output protection level enabled on the device is not sufficient to meet the requirements * in the license policy. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION = 7; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the PVR record is not allowed by the license policy. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED = 8; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * no license keys have been provided. * @hide */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE = 9; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * Using a license in whhich the keys have expired. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED = 10; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the device need be activated. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION = 11; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the device need be paired. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING = 12; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * smart card is missed. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD = 13; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * smart card is muted. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE = 14; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * smart card is invalid. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID = 15; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * of a geographical blackout. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * CAS system is rebooting. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING = 17; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * of unknown CAS error. */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * it has been stopped by {@link TvView#stopPlayback(int)}. */ @FlaggedApi(Flags.FLAG_TIAF_V_APIS) public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED, TIME_SHIFT_STATUS_UNAVAILABLE, TIME_SHIFT_STATUS_AVAILABLE}) public @interface TimeShiftStatus {} /** * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Unknown status. Also * the status prior to calling {@code notifyTimeShiftStatusChanged}. */ public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; /** * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: The current TV input * does not support time shifting. */ public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; /** * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Time shifting is * currently unavailable but might work again later. */ public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; /** * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Time shifting is * currently available. In this status, the application assumes it can pause/resume playback, * seek to a specified time position and set playback rate and audio mode. */ public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; /** * Value returned by {@link TvInputService.Session#onTimeShiftGetCurrentPosition()} and * {@link TvInputService.Session#onTimeShiftGetStartPosition()} when time shifting has not * yet started. */ public static final long TIME_SHIFT_INVALID_TIME = Long.MIN_VALUE; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = false, prefix = "TIME_SHIFT_MODE_", value = { TIME_SHIFT_MODE_OFF, TIME_SHIFT_MODE_LOCAL, TIME_SHIFT_MODE_NETWORK, TIME_SHIFT_MODE_AUTO}) public @interface TimeShiftMode {} /** * Time shift mode: off. *
Time shift is disabled. */ public static final int TIME_SHIFT_MODE_OFF = 1; /** * Time shift mode: local. *
Time shift is handle locally, using on-device data. E.g. playing a local file. */ public static final int TIME_SHIFT_MODE_LOCAL = 2; /** * Time shift mode: network. *
Time shift is handle remotely via network. E.g. online streaming. */ public static final int TIME_SHIFT_MODE_NETWORK = 3; /** * Time shift mode: auto. *
Time shift mode is handled automatically.
*/
public static final int TIME_SHIFT_MODE_AUTO = 4;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
RECORDING_ERROR_RESOURCE_BUSY})
public @interface RecordingError {}
static final int RECORDING_ERROR_START = 0;
static final int RECORDING_ERROR_END = 2;
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: The requested operation cannot be
* completed due to a problem that does not fit under any other error codes, or the error code
* for the problem is defined on the higher version than application's
* android:targetSdkVersion
.
*/
public static final int RECORDING_ERROR_UNKNOWN = RECORDING_ERROR_START;
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed due to
* insufficient storage space.
*/
public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1;
/**
* Error for {@link TvInputService.RecordingSession#notifyError(int)} and
* {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
* a required recording resource was not able to be allocated.
*/
public static final int RECORDING_ERROR_RESOURCE_BUSY = RECORDING_ERROR_END;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({INPUT_STATE_CONNECTED, INPUT_STATE_CONNECTED_STANDBY, INPUT_STATE_DISCONNECTED})
public @interface InputState {}
/**
* State for {@link #getInputState(String)} and
* {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is connected.
*
*
This state indicates that a source device is connected to the input port and is in the * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input. * Non-hardware inputs are considered connected all the time. */ public static final int INPUT_STATE_CONNECTED = 0; /** * State for {@link #getInputState(String)} and * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is connected but * in standby mode. * *
This state indicates that a source device is connected to the input port but is in standby * or low power mode. It is mostly relevant to hardware inputs such as HDMI input and Component * inputs. */ public static final int INPUT_STATE_CONNECTED_STANDBY = 1; /** * State for {@link #getInputState(String)} and * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is disconnected. * *
This state indicates that a source device is disconnected from the input port. It is * mostly relevant to hardware inputs such as HDMI input. * */ public static final int INPUT_STATE_DISCONNECTED = 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = "BROADCAST_INFO_TYPE_", value = { BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION, BROADCAST_INFO_TYPE_PES, BROADCAST_INFO_STREAM_EVENT, BROADCAST_INFO_TYPE_DSMCC, BROADCAST_INFO_TYPE_COMMAND, BROADCAST_INFO_TYPE_TIMELINE, BROADCAST_INFO_TYPE_SIGNALING_DATA }) public @interface BroadcastInfoType {} public static final int BROADCAST_INFO_TYPE_TS = 1; public static final int BROADCAST_INFO_TYPE_TABLE = 2; public static final int BROADCAST_INFO_TYPE_SECTION = 3; public static final int BROADCAST_INFO_TYPE_PES = 4; public static final int BROADCAST_INFO_STREAM_EVENT = 5; public static final int BROADCAST_INFO_TYPE_DSMCC = 6; public static final int BROADCAST_INFO_TYPE_COMMAND = 7; public static final int BROADCAST_INFO_TYPE_TIMELINE = 8; /** @hide */ public static final int BROADCAST_INFO_TYPE_SIGNALING_DATA = 9; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "SIGNAL_STRENGTH_", value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG}) public @interface SignalStrength {} /** * Signal lost. */ public static final int SIGNAL_STRENGTH_LOST = 1; /** * Weak signal. */ public static final int SIGNAL_STRENGTH_WEAK = 2; /** * Strong signal. */ public static final int SIGNAL_STRENGTH_STRONG = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @StringDef(prefix = "SESSION_DATA_TYPE_", value = { SESSION_DATA_TYPE_TUNED, SESSION_DATA_TYPE_TRACK_SELECTED, SESSION_DATA_TYPE_TRACKS_CHANGED, SESSION_DATA_TYPE_VIDEO_AVAILABLE, SESSION_DATA_TYPE_VIDEO_UNAVAILABLE, SESSION_DATA_TYPE_BROADCAST_INFO_RESPONSE, SESSION_DATA_TYPE_AD_RESPONSE, SESSION_DATA_TYPE_AD_BUFFER_CONSUMED, SESSION_DATA_TYPE_TV_MESSAGE}) public @interface SessionDataType {} /** * Informs the application that the session has been tuned to the given channel. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see SESSION_DATA_KEY_CHANNEL_URI */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_TUNED = "tuned"; /** * Sends the type and ID of a selected track. This is used to inform the application that a * specific track is selected. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see SESSION_DATA_KEY_TRACK_TYPE * @see SESSION_DATA_KEY_TRACK_ID */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_TRACK_SELECTED = "track_selected"; /** * Sends the list of all audio/video/subtitle tracks. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see SESSION_DATA_KEY_TRACKS */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_TRACKS_CHANGED = "tracks_changed"; /** * Informs the application that the video is now available for watching. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_VIDEO_AVAILABLE = "video_available"; /** * Informs the application that the video became unavailable for some reason. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_VIDEO_UNAVAILABLE = "video_unavailable"; /** * Notifies response for broadcast info. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_BROADCAST_INFO_RESPONSE = "broadcast_info_response"; /** * Notifies response for advertisement. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see SESSION_DATA_KEY_AD_RESPONSE */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_AD_RESPONSE = "ad_response"; /** * Notifies the advertisement buffer is consumed. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see SESSION_DATA_KEY_AD_BUFFER */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_AD_BUFFER_CONSUMED = "ad_buffer_consumed"; /** * Sends the TV message. * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) * @see TvInputService.Session#notifyTvMessage(int, Bundle) * @see SESSION_DATA_KEY_TV_MESSAGE_TYPE */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_TYPE_TV_MESSAGE = "tv_message"; /** @hide */ @Retention(RetentionPolicy.SOURCE) @StringDef(prefix = "SESSION_DATA_KEY_", value = { SESSION_DATA_KEY_CHANNEL_URI, SESSION_DATA_KEY_TRACK_TYPE, SESSION_DATA_KEY_TRACK_ID, SESSION_DATA_KEY_TRACKS, SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON, SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE, SESSION_DATA_KEY_AD_RESPONSE, SESSION_DATA_KEY_AD_BUFFER, SESSION_DATA_KEY_TV_MESSAGE_TYPE}) public @interface SessionDataKey {} /** * The URI of a channel. * *
Type: android.net.Uri * * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_KEY_CHANNEL_URI = "channel_uri"; /** * The type of the track. * *
One of {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO}, * {@link TvTrackInfo#TYPE_SUBTITLE}. * *
Type: Integer * * @see TvTrackInfo#getType() * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_KEY_TRACK_TYPE = "track_type"; /** * The ID of the track. * *
Type: String * * @see TvTrackInfo#getId() * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) */ @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) public static final String SESSION_DATA_KEY_TRACK_ID = "track_id"; /** * A list which includes track information. * *
Type: {@code java.util.List The value can be {@link VIDEO_UNAVAILABLE_REASON_BUFFERING},
* {@link VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}, etc.
*
* Type: Integer
*
* @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
*/
@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
public static final String SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON =
"video_unavailable_reason";
/**
* An object of {@link BroadcastInfoResponse}.
*
* Type: android.media.tv.BroadcastInfoResponse
*
* @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
*/
@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
public static final String SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE = "broadcast_info_response";
/**
* An object of {@link AdResponse}.
*
* Type: android.media.tv.AdResponse
*
* @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
*/
@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
public static final String SESSION_DATA_KEY_AD_RESPONSE = "ad_response";
/**
* An object of {@link AdBuffer}.
*
* Type: android.media.tv.AdBuffer
*
* @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
*/
@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
public static final String SESSION_DATA_KEY_AD_BUFFER = "ad_buffer";
/**
* The type of TV message.
* It can be one of {@link TV_MESSAGE_TYPE_WATERMARK},
* {@link TV_MESSAGE_TYPE_CLOSED_CAPTION}, {@link TV_MESSAGE_TYPE_OTHER}
*
* Type: Integer
*
* @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
*/
@FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
public static final String SESSION_DATA_KEY_TV_MESSAGE_TYPE = "tv_message_type";
/**
* An unknown state of the client pid gets from the TvInputManager. Client gets this value when
* query through {@link getClientPid(String sessionId)} fails.
*
* @hide
*/
public static final int UNKNOWN_CLIENT_PID = -1;
/**
* Broadcast intent action when the user blocked content ratings change. For use with the
* {@link #isRatingBlocked}.
*/
public static final String ACTION_BLOCKED_RATINGS_CHANGED =
"android.media.tv.action.BLOCKED_RATINGS_CHANGED";
/**
* Broadcast intent action when the parental controls enabled state changes. For use with the
* {@link #isParentalControlsEnabled}.
*/
public static final String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED =
"android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
/**
* Broadcast intent action used to query available content rating systems.
*
* The TV input manager service locates available content rating systems by querying
* broadcast receivers that are registered for this action. An application can offer additional
* content rating systems to the user by declaring a suitable broadcast receiver in its
* manifest.
*
* Here is an example broadcast receiver declaration that an application might include in its
* AndroidManifest.xml to advertise custom content rating systems. The meta-data specifies a
* resource that contains a description of each content rating system that is provided by the
* application.
*
* In the above example, the Specifies the resource ID of an XML resource that describes the content rating systems
* that are provided by the application.
*/
public static final String META_DATA_CONTENT_RATING_SYSTEMS =
"android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
/**
* Activity action to set up channel sources i.e. TV inputs of type
* {@link TvInputInfo#TYPE_TUNER}. When invoked, the system will display an appropriate UI for
* the user to initiate the individual setup flow provided by
* {@link android.R.attr#setupActivity} of each TV input service.
*/
public static final String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
/**
* Activity action to display the recording schedules. When invoked, the system will display an
* appropriate UI to browse the schedules.
*/
public static final String ACTION_VIEW_RECORDING_SCHEDULES =
"android.media.tv.action.VIEW_RECORDING_SCHEDULES";
private final ITvInputManager mService;
private final Object mLock = new Object();
// @GuardedBy("mLock")
private final List Normally it happens when the user installs a new TV input package that implements
* {@link TvInputService} interface.
*
* @param inputId The ID of the TV input.
*/
public void onInputAdded(String inputId) {
}
/**
* This is called when a TV input is removed from the system.
*
* Normally it happens when the user uninstalls the previously installed TV input
* package.
*
* @param inputId The ID of the TV input.
*/
public void onInputRemoved(String inputId) {
}
/**
* This is called when a TV input is updated on the system.
*
* Normally it happens when a previously installed TV input package is re-installed or
* the media on which a newer version of the package exists becomes available/unavailable.
*
* @param inputId The ID of the TV input.
*/
public void onInputUpdated(String inputId) {
}
/**
* This is called when the information about an existing TV input has been updated.
*
* Because the system automatically creates a The system automatically creates a Attempting to change information about a TV input that the calling package does not own
* does nothing.
*
* @param inputInfo The The state is one of the following:
* The number of sessions that can be created at the same time is limited by the capability
* of the given TV input.
*
* @param inputId The ID of the TV input.
* @param tvAppAttributionSource The Attribution Source of the TV App.
* @param callback A callback used to receive the created session.
* @param handler A {@link Handler} that the session creation will be delivered to.
* @hide
*/
public void createSession(@NonNull String inputId,
@NonNull AttributionSource tvAppAttributionSource,
@NonNull final SessionCallback callback, @NonNull Handler handler) {
createSessionInternal(inputId, tvAppAttributionSource, false, callback, handler);
}
/**
* Get a the client pid when creating the session with the session id provided.
*
* @param sessionId a String of session id that is used to query the client pid.
* @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_PID}
* if the call fails.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public int getClientPid(@NonNull String sessionId) {
return getClientPidInternal(sessionId);
};
/**
* Returns a priority for the given use case type and the client's foreground or background
* status.
*
* @param useCase the use case type of the client.
* {@see TvInputService#PriorityHintUseCaseType}.
* @param sessionId the unique id of the session owned by the client.
* {@see TvInputService#onCreateSession(String, String, AttributionSource)}.
*
* @return the use case priority value for the given use case type and the client's foreground
* or background status.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase,
@NonNull String sessionId) {
Preconditions.checkNotNull(sessionId);
if (!isValidUseCase(useCase)) {
throw new IllegalArgumentException("Invalid use case: " + useCase);
}
return getClientPriorityInternal(useCase, sessionId);
};
/**
* Returns a priority for the given use case type and the caller's foreground or background
* status.
*
* @param useCase the use case type of the caller.
* {@see TvInputService#PriorityHintUseCaseType}.
*
* @return the use case priority value for the given use case type and the caller's foreground
* or background status.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase) {
if (!isValidUseCase(useCase)) {
throw new IllegalArgumentException("Invalid use case: " + useCase);
}
return getClientPriorityInternal(useCase, null);
};
/**
* Creates a recording {@link Session} for a given TV input.
*
* The number of sessions that can be created at the same time is limited by the capability
* of the given TV input.
*
* @param inputId The ID of the TV input.
* @param callback A callback used to receive the created session.
* @param handler A {@link Handler} that the session creation will be delivered to.
* @hide
*/
public void createRecordingSession(@NonNull String inputId,
@NonNull final SessionCallback callback, @NonNull Handler handler) {
createSessionInternal(inputId, null, true, callback, handler);
}
private void createSessionInternal(String inputId, AttributionSource tvAppAttributionSource,
boolean isRecordingSession, SessionCallback callback, Handler handler) {
Preconditions.checkNotNull(inputId);
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(handler);
SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
synchronized (mSessionCallbackRecordMap) {
int seq = mNextSeq++;
mSessionCallbackRecordMap.put(seq, record);
try {
mService.createSession(
mClient, inputId, tvAppAttributionSource, isRecordingSession, seq, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
private int getClientPidInternal(String sessionId) {
Preconditions.checkNotNull(sessionId);
int clientPid = UNKNOWN_CLIENT_PID;
try {
clientPid = mService.getClientPid(sessionId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return clientPid;
}
private int getClientPriorityInternal(int useCase, String sessionId) {
try {
return mService.getClientPriority(useCase, sessionId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private boolean isValidUseCase(int useCase) {
return useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND
|| useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN
|| useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK
|| useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE
|| useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD;
}
/**
* Returns the TvStreamConfig list of the given TV input.
*
* If you are using {@link Hardware} object from {@link
* #acquireTvInputHardware}, you should get the list of available streams
* from {@link HardwareCallback#onStreamConfigChanged} method, not from
* here. This method is designed to be used with {@link #captureFrame} in
* capture scenarios specifically and not suitable for any other use.
*
* @param inputId The ID of the TV input.
* @return List of {@link TvStreamConfig} which is available for capturing
* of the given TV input.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT)
public List A subsequent call to this method on the same {@code deviceId} will release the currently
* acquired Hardware.
*
* @param deviceId The device ID to acquire Hardware for.
* @param callback A callback to receive updates on Hardware.
* @param info The TV input which will use the acquired Hardware.
* @return Hardware on success, {@code null} otherwise.
*
* @hide
* @removed
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback,
TvInputInfo info) {
return acquireTvInputHardware(deviceId, info, callback);
}
/**
* Acquires {@link Hardware} object for the given device ID.
*
* A subsequent call to this method on the same {@code deviceId} could release the currently
* acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current
* request.
*
* If the client would like to provide information for the TRM to compare, use
* {@link #acquireTvInputHardware(int, TvInputInfo, HardwareCallback, String, int)} instead.
*
* Otherwise default priority will be applied.
*
* @param deviceId The device ID to acquire Hardware for.
* @param info The TV input which will use the acquired Hardware.
* @param callback A callback to receive updates on Hardware.
* @return Hardware on success, {@code null} otherwise.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info,
@NonNull final HardwareCallback callback) {
Preconditions.checkNotNull(info);
Preconditions.checkNotNull(callback);
return acquireTvInputHardwareInternal(deviceId, info, null,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, new Executor() {
public void execute(Runnable r) {
r.run();
}
}, callback);
}
/**
* Acquires {@link Hardware} object for the given device ID.
*
* A subsequent call to this method on the same {@code deviceId} could release the currently
* acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current
* request.
*
* @param deviceId The device ID to acquire Hardware for.
* @param info The TV input which will use the acquired Hardware.
* @param tvInputSessionId a String returned to TIS when the session was created.
* {@see TvInputService#onCreateSession(String, String, AttributionSource)}. If null, the
* client will be treated as a background app.
* @param priorityHint The use case of the client. {@see TvInputService#PriorityHintUseCaseType}
* @param executor the executor on which the listener would be invoked.
* @param callback A callback to receive updates on Hardware.
* @return Hardware on success, {@code null} otherwise. When the TRM decides to not grant
* resource, null is returned and the {@link IllegalStateException} is thrown with
* "No enough resources".
*
* @hide
*/
@SystemApi
@Nullable
@RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info,
@Nullable String tvInputSessionId,
@TvInputService.PriorityHintUseCaseType int priorityHint,
@NonNull @CallbackExecutor Executor executor,
@NonNull final HardwareCallback callback) {
Preconditions.checkNotNull(info);
Preconditions.checkNotNull(callback);
return acquireTvInputHardwareInternal(deviceId, info, tvInputSessionId, priorityHint,
executor, callback);
}
/**
* API to add a hardware device in the TvInputHardwareManager for CTS testing
* purpose.
*
* @param deviceId Id of the adding hardware device.
*
* @hide
*/
@TestApi
public void addHardwareDevice(int deviceId) {
try {
mService.addHardwareDevice(deviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* API to remove a hardware device in the TvInputHardwareManager for CTS testing
* purpose.
*
* @param deviceId Id of the removing hardware device.
*
* @hide
*/
@TestApi
public void removeHardwareDevice(int deviceId) {
try {
mService.removeHardwareDevice(deviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private Hardware acquireTvInputHardwareInternal(int deviceId, TvInputInfo info,
String tvInputSessionId, int priorityHint,
Executor executor, final HardwareCallback callback) {
try {
ITvInputHardware hardware =
mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() {
@Override
public void onReleased() {
final long identity = Binder.clearCallingIdentity();
try {
executor.execute(() -> callback.onReleased());
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void onStreamConfigChanged(TvStreamConfig[] configs) {
final long identity = Binder.clearCallingIdentity();
try {
executor.execute(() -> callback.onStreamConfigChanged(configs));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}, info, mUserId, tvInputSessionId, priorityHint);
if (hardware == null) {
return null;
}
return new Hardware(hardware);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Releases previously acquired hardware object.
*
* @param deviceId The device ID this Hardware was acquired for
* @param hardware Hardware to release.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public void releaseTvInputHardware(int deviceId, Hardware hardware) {
try {
mService.releaseTvInputHardware(deviceId, hardware.getInterface(), mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the list of currently available DVB frontend devices on the system.
*
* @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.DVB_DEVICE)
@NonNull
public List Once called, the system will review the request and make the channel browsable based on
* its policy. The first request from a package is guaranteed to be approved.
*
* @param channelUri The URI for the channel to be browsable.
* @hide
*/
public void requestChannelBrowsable(Uri channelUri) {
try {
mService.requestChannelBrowsable(channelUri, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the list of session information for {@link TvInputService.Session} that are
* currently in use.
* Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get
* the channel URIs. If the permission is not granted,
* {@link TunedInfo#getChannelUri()} returns {@code null}.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO)
@NonNull
public List Normally, the position is given within range between the start and the current time,
* inclusively.
*
* @param timeMs The time position to seek to, in milliseconds since the epoch.
* @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged
*/
void timeShiftSeekTo(long timeMs) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.timeShiftSeekTo(mToken, timeMs, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets playback rate using {@link android.media.PlaybackParams}.
*
* @param params The playback params.
*/
void timeShiftSetPlaybackParams(PlaybackParams params) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.timeShiftSetPlaybackParams(mToken, params, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets time shift mode.
*
* @param mode The time shift mode. The value is one of the following:
* {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
* {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
* {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
* @hide
*/
void timeShiftSetMode(@TimeShiftMode int mode) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.timeShiftSetMode(mToken, mode, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Enable/disable position tracking.
*
* @param enable {@code true} to enable tracking, {@code false} otherwise.
*/
void timeShiftEnablePositionTracking(boolean enable) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.timeShiftEnablePositionTracking(mToken, enable, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
void stopPlayback(int mode) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.stopPlayback(mToken, mode, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
void resumePlayback() {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.resumePlayback(mToken, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
void setVideoFrozen(boolean isFrozen) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.setVideoFrozen(mToken, isFrozen, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sends TV messages to the service for testing purposes
*/
public void notifyTvMessage(int type, Bundle data) {
try {
mService.notifyTvMessage(mToken, type, data, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets whether the TV message of the specific type should be enabled.
*/
public void setTvMessageEnabled(int type, boolean enabled) {
try {
mService.setTvMessageEnabled(mToken, type, enabled, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Starts TV program recording in the current recording session.
*
* @param programUri The URI for the TV program to record as a hint, built by
* {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
*/
void startRecording(@Nullable Uri programUri) {
startRecording(programUri, null);
}
/**
* Starts TV program recording in the current recording session.
*
* @param programUri The URI for the TV program to record as a hint, built by
* {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
* @param params A set of extra parameters which might be handled with this event.
*/
void startRecording(@Nullable Uri programUri, @Nullable Bundle params) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.startRecording(mToken, programUri, params, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Stops TV program recording in the current recording session.
*/
void stopRecording() {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.stopRecording(mToken, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Pauses TV program recording in the current recording session.
*
* @param params Domain-specific data for this request. Keys must be a scoped
* name, i.e. prefixed with a package name you own, so that different developers
* will not create conflicting keys.
* {@link TvRecordingClient#pauseRecording(Bundle)}.
*/
void pauseRecording(@NonNull Bundle params) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.pauseRecording(mToken, params, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Resumes TV program recording in the current recording session.
*
* @param params Domain-specific data for this request. Keys must be a scoped
* name, i.e. prefixed with a package name you own, so that different developers
* will not create conflicting keys.
* {@link TvRecordingClient#resumeRecording(Bundle)}.
*/
void resumeRecording(@NonNull Bundle params) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.resumeRecording(mToken, params, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle)
* TvInputService.Session.appPrivateCommand()} on the current TvView.
*
* @param action Name of the command to be performed. This must be a scoped name,
* i.e. prefixed with a package name you own, so that different developers will
* not create conflicting commands.
* @param data Any data to include with the command.
*/
public void sendAppPrivateCommand(String action, Bundle data) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.sendAppPrivateCommand(mToken, action, data, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
* should be called whenever the layout of its containing view is changed.
* {@link #removeOverlayView()} should be called to remove the overlay view.
* Since a session can have only one overlay view, this method should be called only once
* or it can be called again after calling {@link #removeOverlayView()}.
*
* @param view A view playing TV.
* @param frame A position of the overlay view.
* @throws IllegalStateException if {@code view} is not attached to a window.
*/
void createOverlayView(@NonNull View view, @NonNull Rect frame) {
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(frame);
if (view.getWindowToken() == null) {
throw new IllegalStateException("view must be attached to a window");
}
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Relayouts the current overlay view.
*
* @param frame A new position of the overlay view.
*/
void relayoutOverlayView(@NonNull Rect frame) {
Preconditions.checkNotNull(frame);
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.relayoutOverlayView(mToken, frame, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes the current overlay view.
*/
void removeOverlayView() {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.removeOverlayView(mToken, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Requests to unblock content blocked by parental controls.
*/
void unblockContent(@NonNull TvContentRating unblockedRating) {
Preconditions.checkNotNull(unblockedRating);
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.unblockContent(mToken, unblockedRating.flattenToString(), mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Dispatches an input event to this session.
*
* @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
* @param token A token used to identify the input event later in the callback.
* @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
* @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
* {@code null}.
* @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
* {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
* {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
* be invoked later.
* @hide
*/
public int dispatchInputEvent(@NonNull InputEvent event, Object token,
@NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
Preconditions.checkNotNull(event);
Preconditions.checkNotNull(callback);
Preconditions.checkNotNull(handler);
synchronized (mHandler) {
if (mChannel == null) {
return DISPATCH_NOT_HANDLED;
}
PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
if (Looper.myLooper() == Looper.getMainLooper()) {
// Already running on the main thread so we can send the event immediately.
return sendInputEventOnMainLooperLocked(p);
}
// Post the event to the main thread.
Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
return DISPATCH_IN_PROGRESS;
}
}
/**
* Callback that is invoked when an input event that was dispatched to this session has been
* finished.
*
* @hide
*/
public interface FinishedInputEventCallback {
/**
* Called when the dispatched input event is finished.
*
* @param token A token passed to {@link #dispatchInputEvent}.
* @param handled {@code true} if the dispatched input event was handled properly.
* {@code false} otherwise.
*/
void onFinishedInputEvent(Object token, boolean handled);
}
// Must be called on the main looper
private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
synchronized (mHandler) {
int result = sendInputEventOnMainLooperLocked(p);
if (result == DISPATCH_IN_PROGRESS) {
return;
}
}
invokeFinishedInputEventCallback(p, false);
}
private int sendInputEventOnMainLooperLocked(PendingEvent p) {
if (mChannel != null) {
if (mSender == null) {
mSender = new TvInputEventSender(mChannel, mHandler.getLooper());
}
final InputEvent event = p.mEvent;
final int seq = event.getSequenceNumber();
if (mSender.sendInputEvent(seq, event)) {
mPendingEvents.put(seq, p);
Message msg = mHandler.obtainMessage(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
return DISPATCH_IN_PROGRESS;
}
Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
+ event);
}
return DISPATCH_NOT_HANDLED;
}
void finishedInputEvent(int seq, boolean handled, boolean timeout) {
final PendingEvent p;
synchronized (mHandler) {
int index = mPendingEvents.indexOfKey(seq);
if (index < 0) {
return; // spurious, event already finished or timed out
}
p = mPendingEvents.valueAt(index);
mPendingEvents.removeAt(index);
if (timeout) {
Log.w(TAG, "Timeout waiting for session to handle input event after "
+ INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
} else {
mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
}
}
invokeFinishedInputEventCallback(p, handled);
}
// Assumes the event has already been removed from the queue.
void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
p.mHandled = handled;
if (p.mEventHandler.getLooper().isCurrentThread()) {
// Already running on the callback handler thread so we can send the callback
// immediately.
p.run();
} else {
// Post the event to the callback handler thread.
// In this case, the callback will be responsible for recycling the event.
Message msg = Message.obtain(p.mEventHandler, p);
msg.setAsynchronous(true);
msg.sendToTarget();
}
}
private void flushPendingEventsLocked() {
mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
final int count = mPendingEvents.size();
for (int i = 0; i < count; i++) {
int seq = mPendingEvents.keyAt(i);
Message msg = mHandler.obtainMessage(InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
msg.setAsynchronous(true);
msg.sendToTarget();
}
}
private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
FinishedInputEventCallback callback, Handler handler) {
PendingEvent p = mPendingEventPool.acquire();
if (p == null) {
p = new PendingEvent();
}
p.mEvent = event;
p.mEventToken = token;
p.mCallback = callback;
p.mEventHandler = handler;
return p;
}
private void recyclePendingEventLocked(PendingEvent p) {
p.recycle();
mPendingEventPool.release(p);
}
IBinder getToken() {
return mToken;
}
private void releaseInternal() {
mToken = null;
synchronized (mHandler) {
if (mChannel != null) {
if (mSender != null) {
flushPendingEventsLocked();
mSender.dispose();
mSender = null;
}
mChannel.dispose();
mChannel = null;
}
}
synchronized (mSessionCallbackRecordMap) {
mSessionCallbackRecordMap.delete(mSeq);
}
}
public void requestBroadcastInfo(BroadcastInfoRequest request) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.requestBroadcastInfo(mToken, request, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes broadcast info.
* @param requestId the corresponding request ID sent from
* {@link #requestBroadcastInfo(android.media.tv.BroadcastInfoRequest)}
*/
public void removeBroadcastInfo(int requestId) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.removeBroadcastInfo(mToken, requestId, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
public void requestAd(AdRequest request) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.requestAd(mToken, request, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Notifies when the advertisement buffer is filled and ready to be read.
*/
public void notifyAdBufferReady(AdBuffer buffer) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.notifyAdBufferReady(mToken, buffer, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
if (buffer != null) {
buffer.getSharedMemory().close();
}
}
}
/**
* Notifies data from session of linked TvAdService.
*/
public void notifyTvAdSessionData(String type, Bundle data) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
mService.notifyTvAdSessionData(mToken, type, data, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private final class InputEventHandler extends Handler {
public static final int MSG_SEND_INPUT_EVENT = 1;
public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
public static final int MSG_FLUSH_INPUT_EVENT = 3;
InputEventHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SEND_INPUT_EVENT: {
sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
return;
}
case MSG_TIMEOUT_INPUT_EVENT: {
finishedInputEvent(msg.arg1, false, true);
return;
}
case MSG_FLUSH_INPUT_EVENT: {
finishedInputEvent(msg.arg1, false, false);
return;
}
}
}
}
private final class TvInputEventSender extends InputEventSender {
public TvInputEventSender(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEventFinished(int seq, boolean handled) {
finishedInputEvent(seq, handled, false);
}
}
private final class PendingEvent implements Runnable {
public InputEvent mEvent;
public Object mEventToken;
public FinishedInputEventCallback mCallback;
public Handler mEventHandler;
public boolean mHandled;
public void recycle() {
mEvent = null;
mEventToken = null;
mCallback = null;
mEventHandler = null;
mHandled = false;
}
@Override
public void run() {
mCallback.onFinishedInputEvent(mEventToken, mHandled);
synchronized (mEventHandler) {
recyclePendingEventLocked(this);
}
}
}
}
/**
* The Hardware provides the per-hardware functionality of TV hardware.
*
* TV hardware is physical hardware attached to the Android device; for example, HDMI ports,
* Component/Composite ports, etc. Specifically, logical devices such as HDMI CEC logical
* devices don't fall into this category.
*
* @hide
*/
@SystemApi
public final static class Hardware {
private final ITvInputHardware mInterface;
private Hardware(ITvInputHardware hardwareInterface) {
mInterface = hardwareInterface;
}
private ITvInputHardware getInterface() {
return mInterface;
}
public boolean setSurface(Surface surface, TvStreamConfig config) {
try {
return mInterface.setSurface(surface, config);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
public void setStreamVolume(float volume) {
try {
mInterface.setStreamVolume(volume);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/** @removed */
@SystemApi
public boolean dispatchKeyEventToHdmi(KeyEvent event) {
return false;
}
/**
* Override default audio sink from audio policy.
*
* @param audioType device type of the audio sink to override with.
* @param audioAddress device address of the audio sink to override with.
* @param samplingRate desired sampling rate. Use default when it's 0.
* @param channelMask desired channel mask. Use default when it's
* AudioFormat.CHANNEL_OUT_DEFAULT.
* @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
*/
public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
int channelMask, int format) {
try {
mInterface.overrideAudioSink(audioType, audioAddress, samplingRate, channelMask,
format);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
* Override default audio sink from audio policy.
*
* @param device {@link android.media.AudioDeviceInfo} to use.
* @param samplingRate desired sampling rate. Use default when it's 0.
* @param channelMask desired channel mask. Use default when it's
* AudioFormat.CHANNEL_OUT_DEFAULT.
* @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
*/
public void overrideAudioSink(@NonNull AudioDeviceInfo device,
@IntRange(from = 0) int samplingRate,
int channelMask, @Encoding int format) {
Objects.requireNonNull(device);
try {
mInterface.overrideAudioSink(
AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
device.getAddress(), samplingRate, channelMask, format);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}
}
* {@literal
*
*
* @xml/tv_content_rating_systems
resource refers to an
* XML resource whose root element is <rating-system-definitions>
that
* contains zero or more <rating-system-definition>
elements. Each
* <rating-system-definition>
element specifies the ratings, sub-ratings and rating
* orders of a particular content rating system.
*
* @see TvContentRating
*/
public static final String ACTION_QUERY_CONTENT_RATING_SYSTEMS =
"android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
/**
* Content rating systems metadata associated with {@link #ACTION_QUERY_CONTENT_RATING_SYSTEMS}.
*
*
*
*/
public void onVideoUnavailable(Session session, int reason) {
}
/**
* This is called when the video freeze state has been updated.
* If {@code true}, the video is frozen on the last frame while audio playback continues.
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param isFrozen Whether the video is frozen
*/
public void onVideoFreezeUpdated(Session session, boolean isFrozen) {
}
/**
* This is called when the current program content turns out to be allowed to watch since
* its content rating is not blocked by parental controls.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
*/
public void onContentAllowed(Session session) {
}
/**
* This is called when the current program content turns out to be not allowed to watch
* since its content rating is blocked by parental controls.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param rating The content ration of the blocked program.
*/
public void onContentBlocked(Session session, TvContentRating rating) {
}
/**
* This is called when {@link TvInputService.Session#layoutSurface} is called to change the
* layout of surface.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param left Left position.
* @param top Top position.
* @param right Right position.
* @param bottom Bottom position.
*/
public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
}
/**
* This is called when a custom event has been sent from this session.
*
* @param session A {@link TvInputManager.Session} associated with this callback
* @param eventType The type of the event.
* @param eventArgs Optional arguments of the event.
*/
public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
}
/**
* This is called when the time shift status is changed.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param status The current time shift status. Should be one of the followings.
*
*
*/
public void onTimeShiftStatusChanged(Session session, int status) {
}
/**
* This is called when the start position for time shifting has changed.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param timeMs The start position for time shifting, in milliseconds since the epoch.
*/
public void onTimeShiftStartPositionChanged(Session session, long timeMs) {
}
/**
* This is called when the current position for time shifting is changed.
*
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param timeMs The current position for time shifting, in milliseconds since the epoch.
*/
public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) {
}
/**
* This is called when AIT info is updated.
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param aitInfo The current AIT info.
*/
public void onAitInfoUpdated(Session session, AitInfo aitInfo) {
}
/**
* This is called when signal strength is updated.
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param strength The current signal strength.
*/
public void onSignalStrengthUpdated(Session session, @SignalStrength int strength) {
}
/**
* This is called when cueing message becomes available or unavailable.
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param available The current availability of cueing message. {@code true} if cueing
* message is available; {@code false} if it becomes unavailable.
*/
public void onCueingMessageAvailability(Session session, boolean available) {
}
/**
* This is called when time shift mode is set or updated.
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param mode The current time shift mode. The value is one of the following:
* {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
* {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
* {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
*/
public void onTimeShiftMode(Session session, @TimeShiftMode int mode) {
}
/**
* Informs the app available speeds for time-shifting.
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param speeds An ordered array of playback speeds, expressed as values relative to the
* normal playback speed (1.0), at which the current content can be played as
* a time-shifted broadcast. This is an empty array if the supported playback
* speeds are unknown or the video/broadcast is not in time shift mode. If
* currently in time shift mode, this array will normally include at least
* the values 1.0 (normal speed) and 0.0 (paused).
* @see PlaybackParams#getSpeed()
*/
public void onAvailableSpeeds(Session session, float[] speeds) {
}
/**
* This is called when the session has been tuned to the given channel.
*
* @param channelUri The URI of a channel.
*/
public void onTuned(Session session, Uri channelUri) {
}
/**
* This is called when the session receives a new TV Message
*
* @param session A {@link TvInputManager.Session} associated with this callback.
* @param type The type of message received, such as {@link #TV_MESSAGE_TYPE_WATERMARK}
* @param data The raw data of the message. The bundle keys are:
* {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID},
* {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID},
* {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE},
* {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}.
* See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on
* how to parse this data.
*
*/
public void onTvMessage(Session session, @TvInputManager.TvMessageType int type,
Bundle data) {
}
// For the recording session only
/**
* This is called when the current recording session has stopped recording and created a
* new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly
* recorded program.
*
* @param recordedProgramUri The URI for the newly recorded program.
**/
void onRecordingStopped(Session session, Uri recordedProgramUri) {
}
// For the recording session only
/**
* This is called when an issue has occurred. It may be called at any time after the current
* recording session is created until it is released.
*
* @param error The error code.
*/
void onError(Session session, @TvInputManager.RecordingError int error) {
}
}
private static final class SessionCallbackRecord {
private final SessionCallback mSessionCallback;
private final Handler mHandler;
private Session mSession;
SessionCallbackRecord(SessionCallback sessionCallback,
Handler handler) {
mSessionCallback = sessionCallback;
mHandler = handler;
}
void postSessionCreated(final Session session) {
mSession = session;
mHandler.post(new Runnable() {
@Override
public void run() {
mSessionCallback.onSessionCreated(session);
}
});
}
void postSessionReleased() {
mHandler.post(new Runnable() {
@Override
public void run() {
mSessionCallback.onSessionReleased(mSession);
}
});
}
void postChannelRetuned(final Uri channelUri) {
mHandler.post(new Runnable() {
@Override
public void run() {
mSessionCallback.onChannelRetuned(mSession, channelUri);
}
});
}
void postAudioPresentationsChanged(final List
*
*/
public void onInputStateChanged(String inputId, @InputState int state) {
}
/**
* This is called when a TV input is added to the system.
*
* TvInputInfo
object for each TV
* input based on the information collected from the AndroidManifest.xml
, this
* method is only called back when such information has changed dynamically.
*
* @param inputInfo The TvInputInfo
object that contains new information.
*/
public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
}
/**
* This is called when the information about current tuned information has been updated.
*
* @param tunedInfos a list of {@link TunedInfo} objects of new tuned information.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO)
public void onCurrentTunedInfosUpdated(@NonNull ListTvInputInfo
for an existing TV input. A TV input service
* implementation may call this method to pass the application and system an up-to-date
* TvInputInfo
object that describes itself.
*
* TvInputInfo
object for each TV input,
* based on the information collected from the AndroidManifest.xml
, thus it is not
* necessary to call this method unless such information has changed dynamically.
* Use {@link TvInputInfo.Builder} to build a new TvInputInfo
object.
*
* TvInputInfo
object that contains new information.
* @throws IllegalArgumentException if the argument is {@code null}.
* @see TvInputCallback#onTvInputInfoUpdated(TvInputInfo)
*/
public void updateTvInputInfo(@NonNull TvInputInfo inputInfo) {
Preconditions.checkNotNull(inputInfo);
try {
mService.updateTvInputInfo(inputInfo, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the state of a given TV input.
*
*
*
*
* @param inputId The ID of the TV input.
* @throws IllegalArgumentException if the argument is {@code null}.
*/
@InputState
public int getInputState(@NonNull String inputId) {
Preconditions.checkNotNull(inputId);
synchronized (mLock) {
Integer state = mStateMap.get(inputId);
if (state == null) {
Log.w(TAG, "Unrecognized input ID: " + inputId);
return INPUT_STATE_DISCONNECTED;
}
return state;
}
}
/**
* Returns available extension interfaces of a given hardware TV input. This can be used to
* provide domain-specific features that are only known between certain hardware TV inputs
* and their clients.
*
* @param inputId The ID of the TV input.
* @return a non-null list of extension interface names available to the caller. An empty
* list indicates the given TV input is not found, or the given TV input is not a
* hardware TV input, or the given TV input doesn't support any extension
* interfaces, or the caller doesn't hold the required permission for the extension
* interfaces supported by the given TV input.
* @see #getExtensionInterface
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE)
@NonNull
public List