1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.tv; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.FlaggedApi; 21 import android.annotation.IntDef; 22 import android.annotation.IntRange; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.StringDef; 27 import android.annotation.SystemApi; 28 import android.annotation.SystemService; 29 import android.annotation.TestApi; 30 import android.content.AttributionSource; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.graphics.Rect; 34 import android.media.AudioDeviceInfo; 35 import android.media.AudioFormat.Encoding; 36 import android.media.AudioPresentation; 37 import android.media.PlaybackParams; 38 import android.media.tv.ad.TvAdManager; 39 import android.media.tv.flags.Flags; 40 import android.media.tv.interactive.TvInteractiveAppManager; 41 import android.net.Uri; 42 import android.os.Binder; 43 import android.os.Bundle; 44 import android.os.Handler; 45 import android.os.IBinder; 46 import android.os.Looper; 47 import android.os.Message; 48 import android.os.ParcelFileDescriptor; 49 import android.os.RemoteException; 50 import android.text.TextUtils; 51 import android.util.ArrayMap; 52 import android.util.Log; 53 import android.util.Pools.Pool; 54 import android.util.Pools.SimplePool; 55 import android.util.SparseArray; 56 import android.view.InputChannel; 57 import android.view.InputEvent; 58 import android.view.InputEventSender; 59 import android.view.KeyEvent; 60 import android.view.Surface; 61 import android.view.View; 62 63 import com.android.internal.util.Preconditions; 64 65 import java.lang.annotation.Retention; 66 import java.lang.annotation.RetentionPolicy; 67 import java.util.ArrayList; 68 import java.util.Iterator; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.Objects; 72 import java.util.concurrent.Executor; 73 74 /** 75 * Central system API to the overall TV input framework (TIF) architecture, which arbitrates 76 * interaction between applications and the selected TV inputs. 77 * 78 * <p>There are three primary parties involved in the TV input framework (TIF) architecture: 79 * 80 * <ul> 81 * <li>The <strong>TV input manager</strong> as expressed by this class is the central point of the 82 * system that manages interaction between all other parts. It is expressed as the client-side API 83 * here which exists in each application context and communicates with a global system service that 84 * manages the interaction across all processes. 85 * <li>A <strong>TV input</strong> implemented by {@link TvInputService} represents an input source 86 * of TV, which can be a pass-through input such as HDMI, or a tuner input which provides broadcast 87 * TV programs. The system binds to the TV input per application’s request. 88 * on implementing TV inputs. 89 * <li><strong>Applications</strong> talk to the TV input manager to list TV inputs and check their 90 * status. Once an application find the input to use, it uses {@link TvView} or 91 * {@link TvRecordingClient} for further interaction such as watching and recording broadcast TV 92 * programs. 93 * </ul> 94 */ 95 @SystemService(Context.TV_INPUT_SERVICE) 96 public final class TvInputManager { 97 private static final String TAG = "TvInputManager"; 98 99 static final int DVB_DEVICE_START = 0; 100 static final int DVB_DEVICE_END = 2; 101 102 /** 103 * A demux device of DVB API for controlling the filters of DVB hardware/software. 104 * @hide 105 */ 106 public static final int DVB_DEVICE_DEMUX = DVB_DEVICE_START; 107 /** 108 * A DVR device of DVB API for reading transport streams. 109 * @hide 110 */ 111 public static final int DVB_DEVICE_DVR = 1; 112 /** 113 * A frontend device of DVB API for controlling the tuner and DVB demodulator hardware. 114 * @hide 115 */ 116 public static final int DVB_DEVICE_FRONTEND = DVB_DEVICE_END; 117 118 /** @hide */ 119 @Retention(RetentionPolicy.SOURCE) 120 @IntDef({DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_FRONTEND}) 121 public @interface DvbDeviceType {} 122 123 124 /** @hide */ 125 @Retention(RetentionPolicy.SOURCE) 126 @IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING, 127 VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING, 128 VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY, VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED, 129 VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE, 130 VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION, 131 VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED, 132 VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE, VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED, 133 VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION, VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING, 134 VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD, VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE, 135 VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID, VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT, 136 VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN, 137 VIDEO_UNAVAILABLE_REASON_STOPPED}) 138 public @interface VideoUnavailableReason {} 139 140 /** Indicates that this TV message contains watermarking data */ 141 public static final int TV_MESSAGE_TYPE_WATERMARK = 1; 142 143 /** Indicates that this TV message contains Closed Captioning data */ 144 public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2; 145 146 /** Indicates that this TV message contains other data */ 147 public static final int TV_MESSAGE_TYPE_OTHER = 1000; 148 149 /** @hide */ 150 @Retention(RetentionPolicy.SOURCE) 151 @IntDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION, TV_MESSAGE_TYPE_OTHER}) 152 public @interface TvMessageType {} 153 154 /** 155 * This constant is used as a {@link Bundle} key for TV messages. The value of the key 156 * identifies the stream on the TV input source for which the watermark event is relevant to. 157 * 158 * <p> Type: String 159 */ 160 public static final String TV_MESSAGE_KEY_STREAM_ID = 161 "android.media.tv.TvInputManager.stream_id"; 162 163 /** 164 * This value for {@link #TV_MESSAGE_KEY_GROUP_ID} denotes that the message doesn't 165 * belong to any group. 166 */ 167 public static final long TV_MESSAGE_GROUP_ID_NONE = -1; 168 169 /** 170 * This constant is used as a {@link Bundle} key for TV messages. This is used to 171 * optionally identify messages that belong together, such as headers and bodies 172 * of the same event. For messages that do not have a group, this value 173 * should be {@link #TV_MESSAGE_GROUP_ID_NONE}. 174 * 175 * <p> As -1 is a reserved value, -1 should not be used as a valid groupId. 176 * 177 * <p> Type: long 178 */ 179 public static final String TV_MESSAGE_KEY_GROUP_ID = 180 "android.media.tv.TvInputManager.group_id"; 181 182 /** 183 * This is a subtype for TV messages that can be potentially found as a value 184 * at {@link #TV_MESSAGE_KEY_SUBTYPE}. It identifies the subtype of the message 185 * as the watermarking format ATSC A/335. 186 */ 187 public static final String TV_MESSAGE_SUBTYPE_WATERMARKING_A335 = "ATSC A/335"; 188 189 /** 190 * This is a subtype for TV messages that can be potentially found as a value 191 * at {@link #TV_MESSAGE_KEY_SUBTYPE}. It identifies the subtype of the message 192 * as the CC format CTA 608-E. 193 */ 194 public static final String TV_MESSAGE_SUBTYPE_CC_608E = "CTA 608-E"; 195 196 /** 197 * This constant is used as a {@link Bundle} key for TV messages. The value of the key 198 * identifies the subtype of the data, such as the format of the CC data. The format 199 * found at this key can then be used to identify how to parse the data at 200 * {@link #TV_MESSAGE_KEY_RAW_DATA}. 201 * 202 * <p> To parse the raw data based on the subtype, please refer to the official 203 * documentation of the concerning subtype. For example, for the subtype 204 * {@link #TV_MESSAGE_SUBTYPE_WATERMARKING_A335}, the document for A/335 from the ATSC 205 * standard details how this data is formatted. Similarly, the subtype 206 * {@link #TV_MESSAGE_SUBTYPE_CC_608E} is documented in the ANSI/CTA standard for 207 * 608-E. These subtypes are examples of common formats for their respective uses 208 * and other subtypes may exist. 209 * 210 * <p> Type: String 211 */ 212 public static final String TV_MESSAGE_KEY_SUBTYPE = 213 "android.media.tv.TvInputManager.subtype"; 214 215 /** 216 * This constant is used as a {@link Bundle} key for TV messages. The value of the key 217 * stores the raw data contained in this TV message. The format of this data is determined 218 * by the format defined by the subtype, found using the key at 219 * {@link #TV_MESSAGE_KEY_SUBTYPE}. See {@link #TV_MESSAGE_KEY_SUBTYPE} for more 220 * information on how to parse this data. 221 * 222 * <p> Type: byte[] 223 */ 224 public static final String TV_MESSAGE_KEY_RAW_DATA = 225 "android.media.tv.TvInputManager.raw_data"; 226 227 static final int VIDEO_UNAVAILABLE_REASON_START = 0; 228 static final int VIDEO_UNAVAILABLE_REASON_END = 18; 229 230 /** 231 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 232 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable due to 233 * an unspecified error. 234 */ 235 public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = VIDEO_UNAVAILABLE_REASON_START; 236 /** 237 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 238 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 239 * the corresponding TV input is in the middle of tuning to a new channel. 240 */ 241 public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; 242 /** 243 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 244 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable due to 245 * weak TV signal. 246 */ 247 public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; 248 /** 249 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 250 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 251 * the corresponding TV input has stopped playback temporarily to buffer more data. 252 */ 253 public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; 254 /** 255 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 256 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 257 * the current TV program is audio-only. 258 */ 259 public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; 260 /** 261 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 262 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 263 * the source is not physically connected, for example the HDMI cable is not connected. 264 */ 265 public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5; 266 /** 267 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 268 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 269 * the resource is not enough to meet requirement. 270 */ 271 public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6; 272 /** 273 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 274 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 275 * the output protection level enabled on the device is not sufficient to meet the requirements 276 * in the license policy. 277 */ 278 public static final int VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION = 7; 279 /** 280 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 281 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 282 * the PVR record is not allowed by the license policy. 283 */ 284 public static final int VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED = 8; 285 /** 286 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 287 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 288 * no license keys have been provided. 289 * @hide 290 */ 291 public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE = 9; 292 /** 293 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 294 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 295 * Using a license in whhich the keys have expired. 296 */ 297 public static final int VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED = 10; 298 /** 299 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 300 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 301 * the device need be activated. 302 */ 303 public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION = 11; 304 /** 305 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 306 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 307 * the device need be paired. 308 */ 309 public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING = 12; 310 /** 311 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 312 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 313 * smart card is missed. 314 */ 315 public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD = 13; 316 /** 317 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 318 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 319 * smart card is muted. 320 */ 321 public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE = 14; 322 /** 323 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 324 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 325 * smart card is invalid. 326 */ 327 public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID = 15; 328 /** 329 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 330 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 331 * of a geographical blackout. 332 */ 333 public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16; 334 /** 335 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 336 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 337 * CAS system is rebooting. 338 */ 339 public static final int VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING = 17; 340 /** 341 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 342 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 343 * of unknown CAS error. 344 */ 345 public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END; 346 347 /** 348 * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and 349 * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because 350 * it has been stopped by {@link TvView#stopPlayback(int)}. 351 */ 352 @FlaggedApi(Flags.FLAG_TIAF_V_APIS) 353 public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19; 354 355 /** @hide */ 356 @Retention(RetentionPolicy.SOURCE) 357 @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED, 358 TIME_SHIFT_STATUS_UNAVAILABLE, TIME_SHIFT_STATUS_AVAILABLE}) 359 public @interface TimeShiftStatus {} 360 361 /** 362 * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and 363 * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Unknown status. Also 364 * the status prior to calling {@code notifyTimeShiftStatusChanged}. 365 */ 366 public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; 367 368 /** 369 * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and 370 * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: The current TV input 371 * does not support time shifting. 372 */ 373 public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; 374 375 /** 376 * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and 377 * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Time shifting is 378 * currently unavailable but might work again later. 379 */ 380 public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; 381 382 /** 383 * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and 384 * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Time shifting is 385 * currently available. In this status, the application assumes it can pause/resume playback, 386 * seek to a specified time position and set playback rate and audio mode. 387 */ 388 public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; 389 390 /** 391 * Value returned by {@link TvInputService.Session#onTimeShiftGetCurrentPosition()} and 392 * {@link TvInputService.Session#onTimeShiftGetStartPosition()} when time shifting has not 393 * yet started. 394 */ 395 public static final long TIME_SHIFT_INVALID_TIME = Long.MIN_VALUE; 396 397 /** @hide */ 398 @Retention(RetentionPolicy.SOURCE) 399 @IntDef(flag = false, prefix = "TIME_SHIFT_MODE_", value = { 400 TIME_SHIFT_MODE_OFF, 401 TIME_SHIFT_MODE_LOCAL, 402 TIME_SHIFT_MODE_NETWORK, 403 TIME_SHIFT_MODE_AUTO}) 404 public @interface TimeShiftMode {} 405 /** 406 * Time shift mode: off. 407 * <p>Time shift is disabled. 408 */ 409 public static final int TIME_SHIFT_MODE_OFF = 1; 410 /** 411 * Time shift mode: local. 412 * <p>Time shift is handle locally, using on-device data. E.g. playing a local file. 413 */ 414 public static final int TIME_SHIFT_MODE_LOCAL = 2; 415 /** 416 * Time shift mode: network. 417 * <p>Time shift is handle remotely via network. E.g. online streaming. 418 */ 419 public static final int TIME_SHIFT_MODE_NETWORK = 3; 420 /** 421 * Time shift mode: auto. 422 * <p>Time shift mode is handled automatically. 423 */ 424 public static final int TIME_SHIFT_MODE_AUTO = 4; 425 426 /** @hide */ 427 @Retention(RetentionPolicy.SOURCE) 428 @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE, 429 RECORDING_ERROR_RESOURCE_BUSY}) 430 public @interface RecordingError {} 431 432 static final int RECORDING_ERROR_START = 0; 433 static final int RECORDING_ERROR_END = 2; 434 435 /** 436 * Error for {@link TvInputService.RecordingSession#notifyError(int)} and 437 * {@link TvRecordingClient.RecordingCallback#onError(int)}: The requested operation cannot be 438 * completed due to a problem that does not fit under any other error codes, or the error code 439 * for the problem is defined on the higher version than application's 440 * <code>android:targetSdkVersion</code>. 441 */ 442 public static final int RECORDING_ERROR_UNKNOWN = RECORDING_ERROR_START; 443 444 /** 445 * Error for {@link TvInputService.RecordingSession#notifyError(int)} and 446 * {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed due to 447 * insufficient storage space. 448 */ 449 public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1; 450 451 /** 452 * Error for {@link TvInputService.RecordingSession#notifyError(int)} and 453 * {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because 454 * a required recording resource was not able to be allocated. 455 */ 456 public static final int RECORDING_ERROR_RESOURCE_BUSY = RECORDING_ERROR_END; 457 458 /** @hide */ 459 @Retention(RetentionPolicy.SOURCE) 460 @IntDef({INPUT_STATE_CONNECTED, INPUT_STATE_CONNECTED_STANDBY, INPUT_STATE_DISCONNECTED}) 461 public @interface InputState {} 462 463 /** 464 * State for {@link #getInputState(String)} and 465 * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is connected. 466 * 467 * <p>This state indicates that a source device is connected to the input port and is in the 468 * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input. 469 * Non-hardware inputs are considered connected all the time. 470 */ 471 public static final int INPUT_STATE_CONNECTED = 0; 472 473 /** 474 * State for {@link #getInputState(String)} and 475 * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is connected but 476 * in standby mode. 477 * 478 * <p>This state indicates that a source device is connected to the input port but is in standby 479 * or low power mode. It is mostly relevant to hardware inputs such as HDMI input and Component 480 * inputs. 481 */ 482 public static final int INPUT_STATE_CONNECTED_STANDBY = 1; 483 484 /** 485 * State for {@link #getInputState(String)} and 486 * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is disconnected. 487 * 488 * <p>This state indicates that a source device is disconnected from the input port. It is 489 * mostly relevant to hardware inputs such as HDMI input. 490 * 491 */ 492 public static final int INPUT_STATE_DISCONNECTED = 2; 493 494 /** @hide */ 495 @Retention(RetentionPolicy.SOURCE) 496 @IntDef( 497 prefix = "BROADCAST_INFO_TYPE_", 498 value = { 499 BROADCAST_INFO_TYPE_TS, 500 BROADCAST_INFO_TYPE_TABLE, 501 BROADCAST_INFO_TYPE_SECTION, 502 BROADCAST_INFO_TYPE_PES, 503 BROADCAST_INFO_STREAM_EVENT, 504 BROADCAST_INFO_TYPE_DSMCC, 505 BROADCAST_INFO_TYPE_COMMAND, 506 BROADCAST_INFO_TYPE_TIMELINE, 507 BROADCAST_INFO_TYPE_SIGNALING_DATA 508 }) 509 public @interface BroadcastInfoType {} 510 511 public static final int BROADCAST_INFO_TYPE_TS = 1; 512 public static final int BROADCAST_INFO_TYPE_TABLE = 2; 513 public static final int BROADCAST_INFO_TYPE_SECTION = 3; 514 public static final int BROADCAST_INFO_TYPE_PES = 4; 515 public static final int BROADCAST_INFO_STREAM_EVENT = 5; 516 public static final int BROADCAST_INFO_TYPE_DSMCC = 6; 517 public static final int BROADCAST_INFO_TYPE_COMMAND = 7; 518 public static final int BROADCAST_INFO_TYPE_TIMELINE = 8; 519 520 /** @hide */ 521 public static final int BROADCAST_INFO_TYPE_SIGNALING_DATA = 9; 522 523 /** @hide */ 524 @Retention(RetentionPolicy.SOURCE) 525 @IntDef(prefix = "SIGNAL_STRENGTH_", 526 value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG}) 527 public @interface SignalStrength {} 528 529 /** 530 * Signal lost. 531 */ 532 public static final int SIGNAL_STRENGTH_LOST = 1; 533 /** 534 * Weak signal. 535 */ 536 public static final int SIGNAL_STRENGTH_WEAK = 2; 537 /** 538 * Strong signal. 539 */ 540 public static final int SIGNAL_STRENGTH_STRONG = 3; 541 542 /** @hide */ 543 @Retention(RetentionPolicy.SOURCE) 544 @StringDef(prefix = "SESSION_DATA_TYPE_", value = { 545 SESSION_DATA_TYPE_TUNED, 546 SESSION_DATA_TYPE_TRACK_SELECTED, 547 SESSION_DATA_TYPE_TRACKS_CHANGED, 548 SESSION_DATA_TYPE_VIDEO_AVAILABLE, 549 SESSION_DATA_TYPE_VIDEO_UNAVAILABLE, 550 SESSION_DATA_TYPE_BROADCAST_INFO_RESPONSE, 551 SESSION_DATA_TYPE_AD_RESPONSE, 552 SESSION_DATA_TYPE_AD_BUFFER_CONSUMED, 553 SESSION_DATA_TYPE_TV_MESSAGE}) 554 public @interface SessionDataType {} 555 556 /** 557 * Informs the application that the session has been tuned to the given channel. 558 * 559 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 560 * @see SESSION_DATA_KEY_CHANNEL_URI 561 */ 562 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 563 public static final String SESSION_DATA_TYPE_TUNED = "tuned"; 564 565 /** 566 * Sends the type and ID of a selected track. This is used to inform the application that a 567 * specific track is selected. 568 * 569 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 570 * @see SESSION_DATA_KEY_TRACK_TYPE 571 * @see SESSION_DATA_KEY_TRACK_ID 572 */ 573 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 574 public static final String SESSION_DATA_TYPE_TRACK_SELECTED = "track_selected"; 575 576 /** 577 * Sends the list of all audio/video/subtitle tracks. 578 * 579 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 580 * @see SESSION_DATA_KEY_TRACKS 581 */ 582 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 583 public static final String SESSION_DATA_TYPE_TRACKS_CHANGED = "tracks_changed"; 584 585 /** 586 * Informs the application that the video is now available for watching. 587 * 588 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 589 */ 590 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 591 public static final String SESSION_DATA_TYPE_VIDEO_AVAILABLE = "video_available"; 592 593 /** 594 * Informs the application that the video became unavailable for some reason. 595 * 596 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 597 * @see SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON 598 */ 599 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 600 public static final String SESSION_DATA_TYPE_VIDEO_UNAVAILABLE = "video_unavailable"; 601 602 /** 603 * Notifies response for broadcast info. 604 * 605 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 606 * @see SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE 607 */ 608 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 609 public static final String SESSION_DATA_TYPE_BROADCAST_INFO_RESPONSE = 610 "broadcast_info_response"; 611 612 /** 613 * Notifies response for advertisement. 614 * 615 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 616 * @see SESSION_DATA_KEY_AD_RESPONSE 617 */ 618 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 619 public static final String SESSION_DATA_TYPE_AD_RESPONSE = "ad_response"; 620 621 /** 622 * Notifies the advertisement buffer is consumed. 623 * 624 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 625 * @see SESSION_DATA_KEY_AD_BUFFER 626 */ 627 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 628 public static final String SESSION_DATA_TYPE_AD_BUFFER_CONSUMED = "ad_buffer_consumed"; 629 630 /** 631 * Sends the TV message. 632 * 633 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 634 * @see TvInputService.Session#notifyTvMessage(int, Bundle) 635 * @see SESSION_DATA_KEY_TV_MESSAGE_TYPE 636 */ 637 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 638 public static final String SESSION_DATA_TYPE_TV_MESSAGE = "tv_message"; 639 640 641 /** @hide */ 642 @Retention(RetentionPolicy.SOURCE) 643 @StringDef(prefix = "SESSION_DATA_KEY_", value = { 644 SESSION_DATA_KEY_CHANNEL_URI, 645 SESSION_DATA_KEY_TRACK_TYPE, 646 SESSION_DATA_KEY_TRACK_ID, 647 SESSION_DATA_KEY_TRACKS, 648 SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON, 649 SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE, 650 SESSION_DATA_KEY_AD_RESPONSE, 651 SESSION_DATA_KEY_AD_BUFFER, 652 SESSION_DATA_KEY_TV_MESSAGE_TYPE}) 653 public @interface SessionDataKey {} 654 655 /** 656 * The URI of a channel. 657 * 658 * <p> Type: android.net.Uri 659 * 660 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 661 */ 662 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 663 public static final String SESSION_DATA_KEY_CHANNEL_URI = "channel_uri"; 664 665 /** 666 * The type of the track. 667 * 668 * <p>One of {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO}, 669 * {@link TvTrackInfo#TYPE_SUBTITLE}. 670 * 671 * <p> Type: Integer 672 * 673 * @see TvTrackInfo#getType() 674 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 675 */ 676 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 677 public static final String SESSION_DATA_KEY_TRACK_TYPE = "track_type"; 678 679 /** 680 * The ID of the track. 681 * 682 * <p> Type: String 683 * 684 * @see TvTrackInfo#getId() 685 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 686 */ 687 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 688 public static final String SESSION_DATA_KEY_TRACK_ID = "track_id"; 689 690 /** 691 * A list which includes track information. 692 * 693 * <p> Type: {@code java.util.List<android.media.tv.TvTrackInfo> } 694 * 695 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 696 */ 697 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 698 public static final String SESSION_DATA_KEY_TRACKS = "tracks"; 699 700 /** 701 * The reason why the video became unavailable. 702 * <p>The value can be {@link VIDEO_UNAVAILABLE_REASON_BUFFERING}, 703 * {@link VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}, etc. 704 * 705 * <p> Type: Integer 706 * 707 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 708 */ 709 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 710 public static final String SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON = 711 "video_unavailable_reason"; 712 713 /** 714 * An object of {@link BroadcastInfoResponse}. 715 * 716 * <p> Type: android.media.tv.BroadcastInfoResponse 717 * 718 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 719 */ 720 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 721 public static final String SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE = "broadcast_info_response"; 722 723 /** 724 * An object of {@link AdResponse}. 725 * 726 * <p> Type: android.media.tv.AdResponse 727 * 728 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 729 */ 730 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 731 public static final String SESSION_DATA_KEY_AD_RESPONSE = "ad_response"; 732 733 /** 734 * An object of {@link AdBuffer}. 735 * 736 * <p> Type: android.media.tv.AdBuffer 737 * 738 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 739 */ 740 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 741 public static final String SESSION_DATA_KEY_AD_BUFFER = "ad_buffer"; 742 743 /** 744 * The type of TV message. 745 * <p>It can be one of {@link TV_MESSAGE_TYPE_WATERMARK}, 746 * {@link TV_MESSAGE_TYPE_CLOSED_CAPTION}, {@link TV_MESSAGE_TYPE_OTHER} 747 * 748 * <p> Type: Integer 749 * 750 * @see TvInputService.Session#sendTvInputSessionData(String, Bundle) 751 */ 752 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) 753 public static final String SESSION_DATA_KEY_TV_MESSAGE_TYPE = "tv_message_type"; 754 755 756 /** 757 * An unknown state of the client pid gets from the TvInputManager. Client gets this value when 758 * query through {@link getClientPid(String sessionId)} fails. 759 * 760 * @hide 761 */ 762 public static final int UNKNOWN_CLIENT_PID = -1; 763 764 /** 765 * Broadcast intent action when the user blocked content ratings change. For use with the 766 * {@link #isRatingBlocked}. 767 */ 768 public static final String ACTION_BLOCKED_RATINGS_CHANGED = 769 "android.media.tv.action.BLOCKED_RATINGS_CHANGED"; 770 771 /** 772 * Broadcast intent action when the parental controls enabled state changes. For use with the 773 * {@link #isParentalControlsEnabled}. 774 */ 775 public static final String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED = 776 "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED"; 777 778 /** 779 * Broadcast intent action used to query available content rating systems. 780 * 781 * <p>The TV input manager service locates available content rating systems by querying 782 * broadcast receivers that are registered for this action. An application can offer additional 783 * content rating systems to the user by declaring a suitable broadcast receiver in its 784 * manifest. 785 * 786 * <p>Here is an example broadcast receiver declaration that an application might include in its 787 * AndroidManifest.xml to advertise custom content rating systems. The meta-data specifies a 788 * resource that contains a description of each content rating system that is provided by the 789 * application. 790 * 791 * <p><pre class="prettyprint"> 792 * {@literal 793 * <receiver android:name=".TvInputReceiver"> 794 * <intent-filter> 795 * <action android:name= 796 * "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS" /> 797 * </intent-filter> 798 * <meta-data 799 * android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS" 800 * android:resource="@xml/tv_content_rating_systems" /> 801 * </receiver>}</pre> 802 * 803 * <p>In the above example, the <code>@xml/tv_content_rating_systems</code> resource refers to an 804 * XML resource whose root element is <code><rating-system-definitions></code> that 805 * contains zero or more <code><rating-system-definition></code> elements. Each <code> 806 * <rating-system-definition></code> element specifies the ratings, sub-ratings and rating 807 * orders of a particular content rating system. 808 * 809 * @see TvContentRating 810 */ 811 public static final String ACTION_QUERY_CONTENT_RATING_SYSTEMS = 812 "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS"; 813 814 /** 815 * Content rating systems metadata associated with {@link #ACTION_QUERY_CONTENT_RATING_SYSTEMS}. 816 * 817 * <p>Specifies the resource ID of an XML resource that describes the content rating systems 818 * that are provided by the application. 819 */ 820 public static final String META_DATA_CONTENT_RATING_SYSTEMS = 821 "android.media.tv.metadata.CONTENT_RATING_SYSTEMS"; 822 823 /** 824 * Activity action to set up channel sources i.e. TV inputs of type 825 * {@link TvInputInfo#TYPE_TUNER}. When invoked, the system will display an appropriate UI for 826 * the user to initiate the individual setup flow provided by 827 * {@link android.R.attr#setupActivity} of each TV input service. 828 */ 829 public static final String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS"; 830 831 /** 832 * Activity action to display the recording schedules. When invoked, the system will display an 833 * appropriate UI to browse the schedules. 834 */ 835 public static final String ACTION_VIEW_RECORDING_SCHEDULES = 836 "android.media.tv.action.VIEW_RECORDING_SCHEDULES"; 837 838 private final ITvInputManager mService; 839 840 private final Object mLock = new Object(); 841 842 // @GuardedBy("mLock") 843 private final List<TvInputCallbackRecord> mCallbackRecords = new ArrayList<>(); 844 845 // A mapping from TV input ID to the state of corresponding input. 846 // @GuardedBy("mLock") 847 private final Map<String, Integer> mStateMap = new ArrayMap<>(); 848 849 // A mapping from the sequence number of a session to its SessionCallbackRecord. 850 private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap = 851 new SparseArray<>(); 852 853 // A sequence number for the next session to be created. Should be protected by a lock 854 // {@code mSessionCallbackRecordMap}. 855 private int mNextSeq; 856 857 private final ITvInputClient mClient; 858 859 private final int mUserId; 860 861 /** 862 * Interface used to receive the created session. 863 * @hide 864 */ 865 public abstract static class SessionCallback { 866 /** 867 * This is called after {@link TvInputManager#createSession} has been processed. 868 * 869 * @param session A {@link TvInputManager.Session} instance created. This can be 870 * {@code null} if the creation request failed. 871 */ onSessionCreated(@ullable Session session)872 public void onSessionCreated(@Nullable Session session) { 873 } 874 875 /** 876 * This is called when {@link TvInputManager.Session} is released. 877 * This typically happens when the process hosting the session has crashed or been killed. 878 * 879 * @param session A {@link TvInputManager.Session} instance released. 880 */ onSessionReleased(Session session)881 public void onSessionReleased(Session session) { 882 } 883 884 /** 885 * This is called when the channel of this session is changed by the underlying TV input 886 * without any {@link TvInputManager.Session#tune(Uri)} request. 887 * 888 * @param session A {@link TvInputManager.Session} associated with this callback. 889 * @param channelUri The URI of a channel. 890 */ onChannelRetuned(Session session, Uri channelUri)891 public void onChannelRetuned(Session session, Uri channelUri) { 892 } 893 894 /** 895 * This is called when the audio presentation information of the session has been changed. 896 * 897 * @param session A {@link TvInputManager.Session} associated with this callback. 898 * @param audioPresentations An updated list of selectable audio presentations. 899 */ onAudioPresentationsChanged(Session session, List<AudioPresentation> audioPresentations)900 public void onAudioPresentationsChanged(Session session, 901 List<AudioPresentation> audioPresentations) { 902 } 903 904 /** 905 * This is called when an audio presentation is selected. 906 * 907 * @param session A {@link TvInputManager.Session} associated with this callback. 908 * @param presentationId The ID of the selected audio presentation. 909 * @param programId The ID of the program providing the selected audio presentation. 910 */ onAudioPresentationSelected(Session session, int presentationId, int programId)911 public void onAudioPresentationSelected(Session session, int presentationId, 912 int programId) { 913 } 914 915 /** 916 * This is called when the track information of the session has been changed. 917 * 918 * @param session A {@link TvInputManager.Session} associated with this callback. 919 * @param tracks A list which includes track information. 920 */ onTracksChanged(Session session, List<TvTrackInfo> tracks)921 public void onTracksChanged(Session session, List<TvTrackInfo> tracks) { 922 } 923 924 /** 925 * This is called when a track for a given type is selected. 926 * 927 * @param session A {@link TvInputManager.Session} associated with this callback. 928 * @param type The type of the selected track. The type can be 929 * {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or 930 * {@link TvTrackInfo#TYPE_SUBTITLE}. 931 * @param trackId The ID of the selected track. When {@code null} the currently selected 932 * track for a given type should be unselected. 933 */ onTrackSelected(Session session, int type, @Nullable String trackId)934 public void onTrackSelected(Session session, int type, @Nullable String trackId) { 935 } 936 937 /** 938 * This is invoked when the video size has been changed. It is also called when the first 939 * time video size information becomes available after the session is tuned to a specific 940 * channel. 941 * 942 * @param session A {@link TvInputManager.Session} associated with this callback. 943 * @param width The width of the video. 944 * @param height The height of the video. 945 */ onVideoSizeChanged(Session session, int width, int height)946 public void onVideoSizeChanged(Session session, int width, int height) { 947 } 948 949 /** 950 * This is called when the video is available, so the TV input starts the playback. 951 * 952 * @param session A {@link TvInputManager.Session} associated with this callback. 953 */ onVideoAvailable(Session session)954 public void onVideoAvailable(Session session) { 955 } 956 957 /** 958 * This is called when the video is not available, so the TV input stops the playback. 959 * 960 * @param session A {@link TvInputManager.Session} associated with this callback. 961 * @param reason The reason why the TV input stopped the playback: 962 * <ul> 963 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} 964 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING} 965 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} 966 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} 967 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY} 968 * </ul> 969 */ onVideoUnavailable(Session session, int reason)970 public void onVideoUnavailable(Session session, int reason) { 971 } 972 973 /** 974 * This is called when the video freeze state has been updated. 975 * If {@code true}, the video is frozen on the last frame while audio playback continues. 976 * @param session A {@link TvInputManager.Session} associated with this callback. 977 * @param isFrozen Whether the video is frozen 978 */ onVideoFreezeUpdated(Session session, boolean isFrozen)979 public void onVideoFreezeUpdated(Session session, boolean isFrozen) { 980 } 981 982 /** 983 * This is called when the current program content turns out to be allowed to watch since 984 * its content rating is not blocked by parental controls. 985 * 986 * @param session A {@link TvInputManager.Session} associated with this callback. 987 */ onContentAllowed(Session session)988 public void onContentAllowed(Session session) { 989 } 990 991 /** 992 * This is called when the current program content turns out to be not allowed to watch 993 * since its content rating is blocked by parental controls. 994 * 995 * @param session A {@link TvInputManager.Session} associated with this callback. 996 * @param rating The content ration of the blocked program. 997 */ onContentBlocked(Session session, TvContentRating rating)998 public void onContentBlocked(Session session, TvContentRating rating) { 999 } 1000 1001 /** 1002 * This is called when {@link TvInputService.Session#layoutSurface} is called to change the 1003 * layout of surface. 1004 * 1005 * @param session A {@link TvInputManager.Session} associated with this callback. 1006 * @param left Left position. 1007 * @param top Top position. 1008 * @param right Right position. 1009 * @param bottom Bottom position. 1010 */ onLayoutSurface(Session session, int left, int top, int right, int bottom)1011 public void onLayoutSurface(Session session, int left, int top, int right, int bottom) { 1012 } 1013 1014 /** 1015 * This is called when a custom event has been sent from this session. 1016 * 1017 * @param session A {@link TvInputManager.Session} associated with this callback 1018 * @param eventType The type of the event. 1019 * @param eventArgs Optional arguments of the event. 1020 */ onSessionEvent(Session session, String eventType, Bundle eventArgs)1021 public void onSessionEvent(Session session, String eventType, Bundle eventArgs) { 1022 } 1023 1024 /** 1025 * This is called when the time shift status is changed. 1026 * 1027 * @param session A {@link TvInputManager.Session} associated with this callback. 1028 * @param status The current time shift status. Should be one of the followings. 1029 * <ul> 1030 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED} 1031 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE} 1032 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} 1033 * </ul> 1034 */ onTimeShiftStatusChanged(Session session, int status)1035 public void onTimeShiftStatusChanged(Session session, int status) { 1036 } 1037 1038 /** 1039 * This is called when the start position for time shifting has changed. 1040 * 1041 * @param session A {@link TvInputManager.Session} associated with this callback. 1042 * @param timeMs The start position for time shifting, in milliseconds since the epoch. 1043 */ onTimeShiftStartPositionChanged(Session session, long timeMs)1044 public void onTimeShiftStartPositionChanged(Session session, long timeMs) { 1045 } 1046 1047 /** 1048 * This is called when the current position for time shifting is changed. 1049 * 1050 * @param session A {@link TvInputManager.Session} associated with this callback. 1051 * @param timeMs The current position for time shifting, in milliseconds since the epoch. 1052 */ onTimeShiftCurrentPositionChanged(Session session, long timeMs)1053 public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) { 1054 } 1055 1056 /** 1057 * This is called when AIT info is updated. 1058 * @param session A {@link TvInputManager.Session} associated with this callback. 1059 * @param aitInfo The current AIT info. 1060 */ onAitInfoUpdated(Session session, AitInfo aitInfo)1061 public void onAitInfoUpdated(Session session, AitInfo aitInfo) { 1062 } 1063 1064 /** 1065 * This is called when signal strength is updated. 1066 * @param session A {@link TvInputManager.Session} associated with this callback. 1067 * @param strength The current signal strength. 1068 */ onSignalStrengthUpdated(Session session, @SignalStrength int strength)1069 public void onSignalStrengthUpdated(Session session, @SignalStrength int strength) { 1070 } 1071 1072 /** 1073 * This is called when cueing message becomes available or unavailable. 1074 * @param session A {@link TvInputManager.Session} associated with this callback. 1075 * @param available The current availability of cueing message. {@code true} if cueing 1076 * message is available; {@code false} if it becomes unavailable. 1077 */ onCueingMessageAvailability(Session session, boolean available)1078 public void onCueingMessageAvailability(Session session, boolean available) { 1079 } 1080 1081 /** 1082 * This is called when time shift mode is set or updated. 1083 * @param session A {@link TvInputManager.Session} associated with this callback. 1084 * @param mode The current time shift mode. The value is one of the following: 1085 * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL}, 1086 * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK}, 1087 * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}. 1088 */ onTimeShiftMode(Session session, @TimeShiftMode int mode)1089 public void onTimeShiftMode(Session session, @TimeShiftMode int mode) { 1090 } 1091 1092 /** 1093 * Informs the app available speeds for time-shifting. 1094 * @param session A {@link TvInputManager.Session} associated with this callback. 1095 * @param speeds An ordered array of playback speeds, expressed as values relative to the 1096 * normal playback speed (1.0), at which the current content can be played as 1097 * a time-shifted broadcast. This is an empty array if the supported playback 1098 * speeds are unknown or the video/broadcast is not in time shift mode. If 1099 * currently in time shift mode, this array will normally include at least 1100 * the values 1.0 (normal speed) and 0.0 (paused). 1101 * @see PlaybackParams#getSpeed() 1102 */ onAvailableSpeeds(Session session, float[] speeds)1103 public void onAvailableSpeeds(Session session, float[] speeds) { 1104 } 1105 1106 /** 1107 * This is called when the session has been tuned to the given channel. 1108 * 1109 * @param channelUri The URI of a channel. 1110 */ onTuned(Session session, Uri channelUri)1111 public void onTuned(Session session, Uri channelUri) { 1112 } 1113 1114 /** 1115 * This is called when the session receives a new TV Message 1116 * 1117 * @param session A {@link TvInputManager.Session} associated with this callback. 1118 * @param type The type of message received, such as {@link #TV_MESSAGE_TYPE_WATERMARK} 1119 * @param data The raw data of the message. The bundle keys are: 1120 * {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID}, 1121 * {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID}, 1122 * {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE}, 1123 * {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}. 1124 * See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on 1125 * how to parse this data. 1126 * 1127 */ onTvMessage(Session session, @TvInputManager.TvMessageType int type, Bundle data)1128 public void onTvMessage(Session session, @TvInputManager.TvMessageType int type, 1129 Bundle data) { 1130 } 1131 1132 // For the recording session only 1133 /** 1134 * This is called when the current recording session has stopped recording and created a 1135 * new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly 1136 * recorded program. 1137 * 1138 * @param recordedProgramUri The URI for the newly recorded program. 1139 **/ onRecordingStopped(Session session, Uri recordedProgramUri)1140 void onRecordingStopped(Session session, Uri recordedProgramUri) { 1141 } 1142 1143 // For the recording session only 1144 /** 1145 * This is called when an issue has occurred. It may be called at any time after the current 1146 * recording session is created until it is released. 1147 * 1148 * @param error The error code. 1149 */ onError(Session session, @TvInputManager.RecordingError int error)1150 void onError(Session session, @TvInputManager.RecordingError int error) { 1151 } 1152 } 1153 1154 private static final class SessionCallbackRecord { 1155 private final SessionCallback mSessionCallback; 1156 private final Handler mHandler; 1157 private Session mSession; 1158 SessionCallbackRecord(SessionCallback sessionCallback, Handler handler)1159 SessionCallbackRecord(SessionCallback sessionCallback, 1160 Handler handler) { 1161 mSessionCallback = sessionCallback; 1162 mHandler = handler; 1163 } 1164 postSessionCreated(final Session session)1165 void postSessionCreated(final Session session) { 1166 mSession = session; 1167 mHandler.post(new Runnable() { 1168 @Override 1169 public void run() { 1170 mSessionCallback.onSessionCreated(session); 1171 } 1172 }); 1173 } 1174 postSessionReleased()1175 void postSessionReleased() { 1176 mHandler.post(new Runnable() { 1177 @Override 1178 public void run() { 1179 mSessionCallback.onSessionReleased(mSession); 1180 } 1181 }); 1182 } 1183 postChannelRetuned(final Uri channelUri)1184 void postChannelRetuned(final Uri channelUri) { 1185 mHandler.post(new Runnable() { 1186 @Override 1187 public void run() { 1188 mSessionCallback.onChannelRetuned(mSession, channelUri); 1189 } 1190 }); 1191 } 1192 postAudioPresentationsChanged(final List<AudioPresentation> audioPresentations)1193 void postAudioPresentationsChanged(final List<AudioPresentation> audioPresentations) { 1194 mHandler.post(new Runnable() { 1195 @Override 1196 public void run() { 1197 mSessionCallback.onAudioPresentationsChanged(mSession, audioPresentations); 1198 } 1199 }); 1200 } 1201 postAudioPresentationSelected(final int presentationId, final int programId)1202 void postAudioPresentationSelected(final int presentationId, final int programId) { 1203 mHandler.post(new Runnable() { 1204 @Override 1205 public void run() { 1206 mSessionCallback.onAudioPresentationSelected(mSession, presentationId, 1207 programId); 1208 } 1209 }); 1210 } 1211 postTracksChanged(final List<TvTrackInfo> tracks)1212 void postTracksChanged(final List<TvTrackInfo> tracks) { 1213 mHandler.post(new Runnable() { 1214 @Override 1215 public void run() { 1216 mSessionCallback.onTracksChanged(mSession, tracks); 1217 if (mSession.mIAppNotificationEnabled 1218 && mSession.getInteractiveAppSession() != null) { 1219 mSession.getInteractiveAppSession().notifyTracksChanged(tracks); 1220 } 1221 } 1222 }); 1223 } 1224 postTrackSelected(final int type, final String trackId)1225 void postTrackSelected(final int type, final String trackId) { 1226 mHandler.post(new Runnable() { 1227 @Override 1228 public void run() { 1229 mSessionCallback.onTrackSelected(mSession, type, trackId); 1230 if (mSession.mIAppNotificationEnabled 1231 && mSession.getInteractiveAppSession() != null) { 1232 mSession.getInteractiveAppSession().notifyTrackSelected(type, trackId); 1233 } 1234 } 1235 }); 1236 } 1237 postVideoSizeChanged(final int width, final int height)1238 void postVideoSizeChanged(final int width, final int height) { 1239 mHandler.post(new Runnable() { 1240 @Override 1241 public void run() { 1242 mSessionCallback.onVideoSizeChanged(mSession, width, height); 1243 } 1244 }); 1245 } 1246 postVideoAvailable()1247 void postVideoAvailable() { 1248 mHandler.post(new Runnable() { 1249 @Override 1250 public void run() { 1251 mSessionCallback.onVideoAvailable(mSession); 1252 if (mSession.mIAppNotificationEnabled 1253 && mSession.getInteractiveAppSession() != null) { 1254 mSession.getInteractiveAppSession().notifyVideoAvailable(); 1255 } 1256 } 1257 }); 1258 } 1259 postVideoUnavailable(final int reason)1260 void postVideoUnavailable(final int reason) { 1261 mHandler.post(new Runnable() { 1262 @Override 1263 public void run() { 1264 mSessionCallback.onVideoUnavailable(mSession, reason); 1265 if (mSession.mIAppNotificationEnabled 1266 && mSession.getInteractiveAppSession() != null) { 1267 mSession.getInteractiveAppSession().notifyVideoUnavailable(reason); 1268 } 1269 } 1270 }); 1271 } 1272 postVideoFreezeUpdated(boolean isFrozen)1273 void postVideoFreezeUpdated(boolean isFrozen) { 1274 mHandler.post(new Runnable() { 1275 @Override 1276 public void run() { 1277 mSessionCallback.onVideoFreezeUpdated(mSession, isFrozen); 1278 if (mSession.mIAppNotificationEnabled 1279 && mSession.getInteractiveAppSession() != null) { 1280 mSession.getInteractiveAppSession().notifyVideoFreezeUpdated(isFrozen); 1281 } 1282 } 1283 }); 1284 } 1285 postContentAllowed()1286 void postContentAllowed() { 1287 mHandler.post(new Runnable() { 1288 @Override 1289 public void run() { 1290 mSessionCallback.onContentAllowed(mSession); 1291 if (mSession.mIAppNotificationEnabled 1292 && mSession.getInteractiveAppSession() != null) { 1293 mSession.getInteractiveAppSession().notifyContentAllowed(); 1294 } 1295 } 1296 }); 1297 } 1298 postContentBlocked(final TvContentRating rating)1299 void postContentBlocked(final TvContentRating rating) { 1300 mHandler.post(new Runnable() { 1301 @Override 1302 public void run() { 1303 mSessionCallback.onContentBlocked(mSession, rating); 1304 if (mSession.mIAppNotificationEnabled 1305 && mSession.getInteractiveAppSession() != null) { 1306 mSession.getInteractiveAppSession().notifyContentBlocked(rating); 1307 } 1308 } 1309 }); 1310 } 1311 postLayoutSurface(final int left, final int top, final int right, final int bottom)1312 void postLayoutSurface(final int left, final int top, final int right, 1313 final int bottom) { 1314 mHandler.post(new Runnable() { 1315 @Override 1316 public void run() { 1317 mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom); 1318 } 1319 }); 1320 } 1321 postSessionEvent(final String eventType, final Bundle eventArgs)1322 void postSessionEvent(final String eventType, final Bundle eventArgs) { 1323 mHandler.post(new Runnable() { 1324 @Override 1325 public void run() { 1326 mSessionCallback.onSessionEvent(mSession, eventType, eventArgs); 1327 } 1328 }); 1329 } 1330 postTimeShiftStatusChanged(final int status)1331 void postTimeShiftStatusChanged(final int status) { 1332 mHandler.post(new Runnable() { 1333 @Override 1334 public void run() { 1335 mSessionCallback.onTimeShiftStatusChanged(mSession, status); 1336 } 1337 }); 1338 } 1339 postTimeShiftStartPositionChanged(final long timeMs)1340 void postTimeShiftStartPositionChanged(final long timeMs) { 1341 mHandler.post(new Runnable() { 1342 @Override 1343 public void run() { 1344 mSessionCallback.onTimeShiftStartPositionChanged(mSession, timeMs); 1345 } 1346 }); 1347 } 1348 postTimeShiftCurrentPositionChanged(final long timeMs)1349 void postTimeShiftCurrentPositionChanged(final long timeMs) { 1350 mHandler.post(new Runnable() { 1351 @Override 1352 public void run() { 1353 mSessionCallback.onTimeShiftCurrentPositionChanged(mSession, timeMs); 1354 } 1355 }); 1356 } 1357 postAitInfoUpdated(final AitInfo aitInfo)1358 void postAitInfoUpdated(final AitInfo aitInfo) { 1359 mHandler.post(new Runnable() { 1360 @Override 1361 public void run() { 1362 mSessionCallback.onAitInfoUpdated(mSession, aitInfo); 1363 } 1364 }); 1365 } 1366 postSignalStrength(final int strength)1367 void postSignalStrength(final int strength) { 1368 mHandler.post(new Runnable() { 1369 @Override 1370 public void run() { 1371 mSessionCallback.onSignalStrengthUpdated(mSession, strength); 1372 if (mSession.mIAppNotificationEnabled 1373 && mSession.getInteractiveAppSession() != null) { 1374 mSession.getInteractiveAppSession().notifySignalStrength(strength); 1375 } 1376 } 1377 }); 1378 } 1379 postCueingMessageAvailability(final boolean available)1380 void postCueingMessageAvailability(final boolean available) { 1381 mHandler.post(new Runnable() { 1382 @Override 1383 public void run() { 1384 mSessionCallback.onCueingMessageAvailability(mSession, available); 1385 } 1386 }); 1387 } 1388 postTimeShiftMode(final int mode)1389 void postTimeShiftMode(final int mode) { 1390 mHandler.post(new Runnable() { 1391 @Override 1392 public void run() { 1393 mSessionCallback.onTimeShiftMode(mSession, mode); 1394 } 1395 }); 1396 } 1397 postAvailableSpeeds(float[] speeds)1398 void postAvailableSpeeds(float[] speeds) { 1399 mHandler.post(new Runnable() { 1400 @Override 1401 public void run() { 1402 mSessionCallback.onAvailableSpeeds(mSession, speeds); 1403 } 1404 }); 1405 } 1406 postTuned(final Uri channelUri)1407 void postTuned(final Uri channelUri) { 1408 mHandler.post(new Runnable() { 1409 @Override 1410 public void run() { 1411 mSessionCallback.onTuned(mSession, channelUri); 1412 if (mSession.mIAppNotificationEnabled 1413 && mSession.getInteractiveAppSession() != null) { 1414 mSession.getInteractiveAppSession().notifyTuned(channelUri); 1415 } 1416 } 1417 }); 1418 } 1419 postTvMessage(int type, Bundle data)1420 void postTvMessage(int type, Bundle data) { 1421 mHandler.post(new Runnable() { 1422 @Override 1423 public void run() { 1424 mSessionCallback.onTvMessage(mSession, type, data); 1425 if (mSession.mIAppNotificationEnabled 1426 && mSession.getInteractiveAppSession() != null) { 1427 mSession.getInteractiveAppSession().notifyTvMessage(type, data); 1428 } 1429 } 1430 }); 1431 } 1432 1433 // For the recording session only postRecordingStopped(final Uri recordedProgramUri)1434 void postRecordingStopped(final Uri recordedProgramUri) { 1435 mHandler.post(new Runnable() { 1436 @Override 1437 public void run() { 1438 mSessionCallback.onRecordingStopped(mSession, recordedProgramUri); 1439 } 1440 }); 1441 } 1442 1443 // For the recording session only postError(final int error)1444 void postError(final int error) { 1445 mHandler.post(new Runnable() { 1446 @Override 1447 public void run() { 1448 mSessionCallback.onError(mSession, error); 1449 } 1450 }); 1451 } 1452 postBroadcastInfoResponse(final BroadcastInfoResponse response)1453 void postBroadcastInfoResponse(final BroadcastInfoResponse response) { 1454 if (mSession.mIAppNotificationEnabled) { 1455 mHandler.post(new Runnable() { 1456 @Override 1457 public void run() { 1458 if (mSession.getInteractiveAppSession() != null) { 1459 mSession.getInteractiveAppSession() 1460 .notifyBroadcastInfoResponse(response); 1461 } 1462 } 1463 }); 1464 } 1465 } 1466 postAdResponse(final AdResponse response)1467 void postAdResponse(final AdResponse response) { 1468 if (mSession.mIAppNotificationEnabled) { 1469 mHandler.post(new Runnable() { 1470 @Override 1471 public void run() { 1472 if (mSession.getInteractiveAppSession() != null) { 1473 mSession.getInteractiveAppSession().notifyAdResponse(response); 1474 } 1475 } 1476 }); 1477 } 1478 } 1479 postAdBufferConsumed(AdBuffer buffer)1480 void postAdBufferConsumed(AdBuffer buffer) { 1481 if (mSession.mIAppNotificationEnabled) { 1482 mHandler.post(new Runnable() { 1483 @Override 1484 public void run() { 1485 if (mSession.getInteractiveAppSession() != null) { 1486 mSession.getInteractiveAppSession().notifyAdBufferConsumed(buffer); 1487 } 1488 } 1489 }); 1490 } 1491 } 1492 postTvInputSessionData(String type, Bundle data)1493 void postTvInputSessionData(String type, Bundle data) { 1494 mHandler.post(new Runnable() { 1495 @Override 1496 public void run() { 1497 if (mSession.getAdSession() != null) { 1498 mSession.getAdSession().notifyTvInputSessionData(type, data); 1499 } 1500 } 1501 }); 1502 } 1503 } 1504 1505 /** 1506 * Callback used to monitor status of the TV inputs. 1507 */ 1508 public abstract static class TvInputCallback { 1509 /** 1510 * This is called when the state of a given TV input is changed. 1511 * 1512 * @param inputId The ID of the TV input. 1513 * @param state State of the TV input. The value is one of the following: 1514 * <ul> 1515 * <li>{@link TvInputManager#INPUT_STATE_CONNECTED} 1516 * <li>{@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY} 1517 * <li>{@link TvInputManager#INPUT_STATE_DISCONNECTED} 1518 * </ul> 1519 */ onInputStateChanged(String inputId, @InputState int state)1520 public void onInputStateChanged(String inputId, @InputState int state) { 1521 } 1522 1523 /** 1524 * This is called when a TV input is added to the system. 1525 * 1526 * <p>Normally it happens when the user installs a new TV input package that implements 1527 * {@link TvInputService} interface. 1528 * 1529 * @param inputId The ID of the TV input. 1530 */ onInputAdded(String inputId)1531 public void onInputAdded(String inputId) { 1532 } 1533 1534 /** 1535 * This is called when a TV input is removed from the system. 1536 * 1537 * <p>Normally it happens when the user uninstalls the previously installed TV input 1538 * package. 1539 * 1540 * @param inputId The ID of the TV input. 1541 */ onInputRemoved(String inputId)1542 public void onInputRemoved(String inputId) { 1543 } 1544 1545 /** 1546 * This is called when a TV input is updated on the system. 1547 * 1548 * <p>Normally it happens when a previously installed TV input package is re-installed or 1549 * the media on which a newer version of the package exists becomes available/unavailable. 1550 * 1551 * @param inputId The ID of the TV input. 1552 */ onInputUpdated(String inputId)1553 public void onInputUpdated(String inputId) { 1554 } 1555 1556 /** 1557 * This is called when the information about an existing TV input has been updated. 1558 * 1559 * <p>Because the system automatically creates a <code>TvInputInfo</code> object for each TV 1560 * input based on the information collected from the <code>AndroidManifest.xml</code>, this 1561 * method is only called back when such information has changed dynamically. 1562 * 1563 * @param inputInfo The <code>TvInputInfo</code> object that contains new information. 1564 */ onTvInputInfoUpdated(TvInputInfo inputInfo)1565 public void onTvInputInfoUpdated(TvInputInfo inputInfo) { 1566 } 1567 1568 /** 1569 * This is called when the information about current tuned information has been updated. 1570 * 1571 * @param tunedInfos a list of {@link TunedInfo} objects of new tuned information. 1572 * @hide 1573 */ 1574 @SystemApi 1575 @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) onCurrentTunedInfosUpdated(@onNull List<TunedInfo> tunedInfos)1576 public void onCurrentTunedInfosUpdated(@NonNull List<TunedInfo> tunedInfos) { 1577 } 1578 } 1579 1580 private static final class TvInputCallbackRecord { 1581 private final TvInputCallback mCallback; 1582 private final Handler mHandler; 1583 TvInputCallbackRecord(TvInputCallback callback, Handler handler)1584 public TvInputCallbackRecord(TvInputCallback callback, Handler handler) { 1585 mCallback = callback; 1586 mHandler = handler; 1587 } 1588 getCallback()1589 public TvInputCallback getCallback() { 1590 return mCallback; 1591 } 1592 postInputAdded(final String inputId)1593 public void postInputAdded(final String inputId) { 1594 mHandler.post(new Runnable() { 1595 @Override 1596 public void run() { 1597 mCallback.onInputAdded(inputId); 1598 } 1599 }); 1600 } 1601 postInputRemoved(final String inputId)1602 public void postInputRemoved(final String inputId) { 1603 mHandler.post(new Runnable() { 1604 @Override 1605 public void run() { 1606 mCallback.onInputRemoved(inputId); 1607 } 1608 }); 1609 } 1610 postInputUpdated(final String inputId)1611 public void postInputUpdated(final String inputId) { 1612 mHandler.post(new Runnable() { 1613 @Override 1614 public void run() { 1615 mCallback.onInputUpdated(inputId); 1616 } 1617 }); 1618 } 1619 postInputStateChanged(final String inputId, final int state)1620 public void postInputStateChanged(final String inputId, final int state) { 1621 mHandler.post(new Runnable() { 1622 @Override 1623 public void run() { 1624 mCallback.onInputStateChanged(inputId, state); 1625 } 1626 }); 1627 } 1628 postTvInputInfoUpdated(final TvInputInfo inputInfo)1629 public void postTvInputInfoUpdated(final TvInputInfo inputInfo) { 1630 mHandler.post(new Runnable() { 1631 @Override 1632 public void run() { 1633 mCallback.onTvInputInfoUpdated(inputInfo); 1634 } 1635 }); 1636 } 1637 postCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos)1638 public void postCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos) { 1639 mHandler.post(new Runnable() { 1640 @Override 1641 public void run() { 1642 mCallback.onCurrentTunedInfosUpdated(currentTunedInfos); 1643 } 1644 }); 1645 } 1646 } 1647 1648 /** 1649 * Interface used to receive events from Hardware objects. 1650 * 1651 * @hide 1652 */ 1653 @SystemApi 1654 public abstract static class HardwareCallback { 1655 /** 1656 * This is called when {@link Hardware} is no longer available for the client. 1657 */ onReleased()1658 public abstract void onReleased(); 1659 1660 /** 1661 * This is called when the underlying {@link TvStreamConfig} has been changed. 1662 * 1663 * @param configs The new {@link TvStreamConfig}s. 1664 */ onStreamConfigChanged(TvStreamConfig[] configs)1665 public abstract void onStreamConfigChanged(TvStreamConfig[] configs); 1666 } 1667 1668 /** 1669 * @hide 1670 */ TvInputManager(ITvInputManager service, int userId)1671 public TvInputManager(ITvInputManager service, int userId) { 1672 mService = service; 1673 mUserId = userId; 1674 mClient = new ITvInputClient.Stub() { 1675 @Override 1676 public void onSessionCreated(String inputId, IBinder token, InputChannel channel, 1677 int seq) { 1678 synchronized (mSessionCallbackRecordMap) { 1679 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1680 if (record == null) { 1681 Log.e(TAG, "Callback not found for " + token); 1682 return; 1683 } 1684 Session session = null; 1685 if (token != null) { 1686 session = new Session(token, channel, mService, mUserId, seq, 1687 mSessionCallbackRecordMap); 1688 } else { 1689 mSessionCallbackRecordMap.delete(seq); 1690 } 1691 record.postSessionCreated(session); 1692 } 1693 } 1694 1695 @Override 1696 public void onSessionReleased(int seq) { 1697 synchronized (mSessionCallbackRecordMap) { 1698 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1699 mSessionCallbackRecordMap.delete(seq); 1700 if (record == null) { 1701 Log.e(TAG, "Callback not found for seq:" + seq); 1702 return; 1703 } 1704 record.mSession.releaseInternal(); 1705 record.postSessionReleased(); 1706 } 1707 } 1708 1709 @Override 1710 public void onChannelRetuned(Uri channelUri, int seq) { 1711 synchronized (mSessionCallbackRecordMap) { 1712 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1713 if (record == null) { 1714 Log.e(TAG, "Callback not found for seq " + seq); 1715 return; 1716 } 1717 record.postChannelRetuned(channelUri); 1718 } 1719 } 1720 @Override 1721 public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations, 1722 int seq) { 1723 synchronized (mSessionCallbackRecordMap) { 1724 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1725 if (record == null) { 1726 Log.e(TAG, "Callback not found for seq " + seq); 1727 return; 1728 } 1729 if (record.mSession.updateAudioPresentations(audioPresentations)) { 1730 record.postAudioPresentationsChanged(audioPresentations); 1731 } 1732 } 1733 } 1734 1735 @Override 1736 public void onAudioPresentationSelected(int presentationId, int programId, int seq) { 1737 synchronized (mSessionCallbackRecordMap) { 1738 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1739 if (record == null) { 1740 Log.e(TAG, "Callback not found for seq " + seq); 1741 return; 1742 } 1743 if (record.mSession.updateAudioPresentationSelection(presentationId, 1744 programId)) { 1745 record.postAudioPresentationSelected(presentationId, programId); 1746 } 1747 } 1748 } 1749 1750 1751 @Override 1752 public void onTracksChanged(List<TvTrackInfo> tracks, int seq) { 1753 synchronized (mSessionCallbackRecordMap) { 1754 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1755 if (record == null) { 1756 Log.e(TAG, "Callback not found for seq " + seq); 1757 return; 1758 } 1759 if (record.mSession.updateTracks(tracks)) { 1760 record.postTracksChanged(tracks); 1761 postVideoSizeChangedIfNeededLocked(record); 1762 } 1763 } 1764 } 1765 1766 @Override 1767 public void onTrackSelected(int type, String trackId, int seq) { 1768 synchronized (mSessionCallbackRecordMap) { 1769 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1770 if (record == null) { 1771 Log.e(TAG, "Callback not found for seq " + seq); 1772 return; 1773 } 1774 if (record.mSession.updateTrackSelection(type, trackId)) { 1775 record.postTrackSelected(type, trackId); 1776 postVideoSizeChangedIfNeededLocked(record); 1777 } 1778 } 1779 } 1780 1781 private void postVideoSizeChangedIfNeededLocked(SessionCallbackRecord record) { 1782 TvTrackInfo track = record.mSession.getVideoTrackToNotify(); 1783 if (track != null) { 1784 record.postVideoSizeChanged(track.getVideoWidth(), track.getVideoHeight()); 1785 } 1786 } 1787 1788 @Override 1789 public void onVideoAvailable(int seq) { 1790 synchronized (mSessionCallbackRecordMap) { 1791 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1792 if (record == null) { 1793 Log.e(TAG, "Callback not found for seq " + seq); 1794 return; 1795 } 1796 record.postVideoAvailable(); 1797 } 1798 } 1799 1800 @Override 1801 public void onVideoUnavailable(int reason, int seq) { 1802 synchronized (mSessionCallbackRecordMap) { 1803 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1804 if (record == null) { 1805 Log.e(TAG, "Callback not found for seq " + seq); 1806 return; 1807 } 1808 record.postVideoUnavailable(reason); 1809 } 1810 } 1811 1812 @Override 1813 public void onVideoFreezeUpdated(boolean isFrozen, int seq) { 1814 synchronized (mSessionCallbackRecordMap) { 1815 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1816 if (record == null) { 1817 Log.e(TAG, "Callback not found for seq " + seq); 1818 return; 1819 } 1820 record.postVideoFreezeUpdated(isFrozen); 1821 } 1822 } 1823 1824 @Override 1825 public void onContentAllowed(int seq) { 1826 synchronized (mSessionCallbackRecordMap) { 1827 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1828 if (record == null) { 1829 Log.e(TAG, "Callback not found for seq " + seq); 1830 return; 1831 } 1832 record.postContentAllowed(); 1833 } 1834 } 1835 1836 @Override 1837 public void onContentBlocked(String rating, int seq) { 1838 synchronized (mSessionCallbackRecordMap) { 1839 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1840 if (record == null) { 1841 Log.e(TAG, "Callback not found for seq " + seq); 1842 return; 1843 } 1844 record.postContentBlocked(TvContentRating.unflattenFromString(rating)); 1845 } 1846 } 1847 1848 @Override 1849 public void onLayoutSurface(int left, int top, int right, int bottom, int seq) { 1850 synchronized (mSessionCallbackRecordMap) { 1851 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1852 if (record == null) { 1853 Log.e(TAG, "Callback not found for seq " + seq); 1854 return; 1855 } 1856 record.postLayoutSurface(left, top, right, bottom); 1857 } 1858 } 1859 1860 @Override 1861 public void onSessionEvent(String eventType, Bundle eventArgs, int seq) { 1862 synchronized (mSessionCallbackRecordMap) { 1863 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1864 if (record == null) { 1865 Log.e(TAG, "Callback not found for seq " + seq); 1866 return; 1867 } 1868 record.postSessionEvent(eventType, eventArgs); 1869 } 1870 } 1871 1872 @Override 1873 public void onTimeShiftStatusChanged(int status, int seq) { 1874 synchronized (mSessionCallbackRecordMap) { 1875 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1876 if (record == null) { 1877 Log.e(TAG, "Callback not found for seq " + seq); 1878 return; 1879 } 1880 record.postTimeShiftStatusChanged(status); 1881 } 1882 } 1883 1884 @Override 1885 public void onTimeShiftStartPositionChanged(long timeMs, int seq) { 1886 synchronized (mSessionCallbackRecordMap) { 1887 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1888 if (record == null) { 1889 Log.e(TAG, "Callback not found for seq " + seq); 1890 return; 1891 } 1892 record.postTimeShiftStartPositionChanged(timeMs); 1893 } 1894 } 1895 1896 @Override 1897 public void onTimeShiftCurrentPositionChanged(long timeMs, int seq) { 1898 synchronized (mSessionCallbackRecordMap) { 1899 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1900 if (record == null) { 1901 Log.e(TAG, "Callback not found for seq " + seq); 1902 return; 1903 } 1904 record.postTimeShiftCurrentPositionChanged(timeMs); 1905 } 1906 } 1907 1908 @Override 1909 public void onAitInfoUpdated(AitInfo aitInfo, int seq) { 1910 synchronized (mSessionCallbackRecordMap) { 1911 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1912 if (record == null) { 1913 Log.e(TAG, "Callback not found for seq " + seq); 1914 return; 1915 } 1916 record.postAitInfoUpdated(aitInfo); 1917 } 1918 } 1919 1920 @Override 1921 public void onSignalStrength(int strength, int seq) { 1922 synchronized (mSessionCallbackRecordMap) { 1923 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1924 if (record == null) { 1925 Log.e(TAG, "Callback not found for seq " + seq); 1926 return; 1927 } 1928 record.postSignalStrength(strength); 1929 } 1930 } 1931 1932 @Override 1933 public void onCueingMessageAvailability(boolean available, int seq) { 1934 synchronized (mSessionCallbackRecordMap) { 1935 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1936 if (record == null) { 1937 Log.e(TAG, "Callback not found for seq " + seq); 1938 return; 1939 } 1940 record.postCueingMessageAvailability(available); 1941 } 1942 } 1943 1944 @Override 1945 public void onTimeShiftMode(int mode, int seq) { 1946 synchronized (mSessionCallbackRecordMap) { 1947 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1948 if (record == null) { 1949 Log.e(TAG, "Callback not found for seq " + seq); 1950 return; 1951 } 1952 record.postTimeShiftMode(mode); 1953 } 1954 } 1955 1956 @Override 1957 public void onAvailableSpeeds(float[] speeds, int seq) { 1958 synchronized (mSessionCallbackRecordMap) { 1959 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1960 if (record == null) { 1961 Log.e(TAG, "Callback not found for seq " + seq); 1962 return; 1963 } 1964 record.postAvailableSpeeds(speeds); 1965 } 1966 } 1967 1968 @Override 1969 public void onTuned(Uri channelUri, int seq) { 1970 synchronized (mSessionCallbackRecordMap) { 1971 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1972 if (record == null) { 1973 Log.e(TAG, "Callback not found for seq " + seq); 1974 return; 1975 } 1976 record.postTuned(channelUri); 1977 // TODO: synchronized and wrap the channelUri 1978 } 1979 } 1980 1981 @Override 1982 public void onTvMessage(int type, Bundle data, int seq) { 1983 synchronized (mSessionCallbackRecordMap) { 1984 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1985 if (record == null) { 1986 Log.e(TAG, "Callback not found for seq " + seq); 1987 return; 1988 } 1989 record.postTvMessage(type, data); 1990 } 1991 } 1992 1993 @Override 1994 public void onRecordingStopped(Uri recordedProgramUri, int seq) { 1995 synchronized (mSessionCallbackRecordMap) { 1996 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 1997 if (record == null) { 1998 Log.e(TAG, "Callback not found for seq " + seq); 1999 return; 2000 } 2001 record.postRecordingStopped(recordedProgramUri); 2002 } 2003 } 2004 2005 @Override 2006 public void onError(int error, int seq) { 2007 synchronized (mSessionCallbackRecordMap) { 2008 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 2009 if (record == null) { 2010 Log.e(TAG, "Callback not found for seq " + seq); 2011 return; 2012 } 2013 record.postError(error); 2014 } 2015 } 2016 2017 @Override 2018 public void onBroadcastInfoResponse(BroadcastInfoResponse response, int seq) { 2019 synchronized (mSessionCallbackRecordMap) { 2020 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 2021 if (record == null) { 2022 Log.e(TAG, "Callback not found for seq " + seq); 2023 return; 2024 } 2025 record.postBroadcastInfoResponse(response); 2026 } 2027 } 2028 2029 @Override 2030 public void onAdResponse(AdResponse response, int seq) { 2031 synchronized (mSessionCallbackRecordMap) { 2032 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 2033 if (record == null) { 2034 Log.e(TAG, "Callback not found for seq " + seq); 2035 return; 2036 } 2037 record.postAdResponse(response); 2038 } 2039 } 2040 2041 @Override 2042 public void onAdBufferConsumed(AdBuffer buffer, int seq) { 2043 synchronized (mSessionCallbackRecordMap) { 2044 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 2045 if (record == null) { 2046 Log.e(TAG, "Callback not found for seq " + seq); 2047 return; 2048 } 2049 record.postAdBufferConsumed(buffer); 2050 } 2051 } 2052 2053 @Override 2054 public void onTvInputSessionData(String type, Bundle data, int seq) { 2055 synchronized (mSessionCallbackRecordMap) { 2056 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 2057 if (record == null) { 2058 Log.e(TAG, "Callback not found for seq " + seq); 2059 return; 2060 } 2061 record.postTvInputSessionData(type, data); 2062 } 2063 } 2064 }; 2065 ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() { 2066 @Override 2067 public void onInputAdded(String inputId) { 2068 synchronized (mLock) { 2069 mStateMap.put(inputId, INPUT_STATE_CONNECTED); 2070 for (TvInputCallbackRecord record : mCallbackRecords) { 2071 record.postInputAdded(inputId); 2072 } 2073 } 2074 } 2075 2076 @Override 2077 public void onInputRemoved(String inputId) { 2078 synchronized (mLock) { 2079 mStateMap.remove(inputId); 2080 for (TvInputCallbackRecord record : mCallbackRecords) { 2081 record.postInputRemoved(inputId); 2082 } 2083 } 2084 } 2085 2086 @Override 2087 public void onInputUpdated(String inputId) { 2088 synchronized (mLock) { 2089 for (TvInputCallbackRecord record : mCallbackRecords) { 2090 record.postInputUpdated(inputId); 2091 } 2092 } 2093 } 2094 2095 @Override 2096 public void onInputStateChanged(String inputId, int state) { 2097 synchronized (mLock) { 2098 mStateMap.put(inputId, state); 2099 for (TvInputCallbackRecord record : mCallbackRecords) { 2100 record.postInputStateChanged(inputId, state); 2101 } 2102 } 2103 } 2104 2105 @Override 2106 public void onTvInputInfoUpdated(TvInputInfo inputInfo) { 2107 synchronized (mLock) { 2108 for (TvInputCallbackRecord record : mCallbackRecords) { 2109 record.postTvInputInfoUpdated(inputInfo); 2110 } 2111 } 2112 } 2113 2114 @Override 2115 public void onCurrentTunedInfosUpdated(List<TunedInfo> currentTunedInfos) { 2116 synchronized (mLock) { 2117 for (TvInputCallbackRecord record : mCallbackRecords) { 2118 record.postCurrentTunedInfosUpdated(currentTunedInfos); 2119 } 2120 } 2121 } 2122 }; 2123 try { 2124 if (mService != null) { 2125 mService.registerCallback(managerCallback, mUserId); 2126 List<TvInputInfo> infos = mService.getTvInputList(mUserId); 2127 synchronized (mLock) { 2128 for (TvInputInfo info : infos) { 2129 String inputId = info.getId(); 2130 mStateMap.put(inputId, mService.getTvInputState(inputId, mUserId)); 2131 } 2132 } 2133 } 2134 } catch (RemoteException e) { 2135 throw e.rethrowFromSystemServer(); 2136 } 2137 } 2138 2139 /** 2140 * Returns the complete list of TV inputs on the system. 2141 * 2142 * @return List of {@link TvInputInfo} for each TV input that describes its meta information. 2143 */ getTvInputList()2144 public List<TvInputInfo> getTvInputList() { 2145 try { 2146 return mService.getTvInputList(mUserId); 2147 } catch (RemoteException e) { 2148 throw e.rethrowFromSystemServer(); 2149 } 2150 } 2151 2152 /** 2153 * Returns the {@link TvInputInfo} for a given TV input. 2154 * 2155 * @param inputId The ID of the TV input. 2156 * @return the {@link TvInputInfo} for a given TV input. {@code null} if not found. 2157 */ 2158 @Nullable getTvInputInfo(@onNull String inputId)2159 public TvInputInfo getTvInputInfo(@NonNull String inputId) { 2160 Preconditions.checkNotNull(inputId); 2161 try { 2162 return mService.getTvInputInfo(inputId, mUserId); 2163 } catch (RemoteException e) { 2164 throw e.rethrowFromSystemServer(); 2165 } 2166 } 2167 2168 /** 2169 * Updates the <code>TvInputInfo</code> for an existing TV input. A TV input service 2170 * implementation may call this method to pass the application and system an up-to-date 2171 * <code>TvInputInfo</code> object that describes itself. 2172 * 2173 * <p>The system automatically creates a <code>TvInputInfo</code> object for each TV input, 2174 * based on the information collected from the <code>AndroidManifest.xml</code>, thus it is not 2175 * necessary to call this method unless such information has changed dynamically. 2176 * Use {@link TvInputInfo.Builder} to build a new <code>TvInputInfo</code> object. 2177 * 2178 * <p>Attempting to change information about a TV input that the calling package does not own 2179 * does nothing. 2180 * 2181 * @param inputInfo The <code>TvInputInfo</code> object that contains new information. 2182 * @throws IllegalArgumentException if the argument is {@code null}. 2183 * @see TvInputCallback#onTvInputInfoUpdated(TvInputInfo) 2184 */ updateTvInputInfo(@onNull TvInputInfo inputInfo)2185 public void updateTvInputInfo(@NonNull TvInputInfo inputInfo) { 2186 Preconditions.checkNotNull(inputInfo); 2187 try { 2188 mService.updateTvInputInfo(inputInfo, mUserId); 2189 } catch (RemoteException e) { 2190 throw e.rethrowFromSystemServer(); 2191 } 2192 } 2193 2194 /** 2195 * Returns the state of a given TV input. 2196 * 2197 * <p>The state is one of the following: 2198 * <ul> 2199 * <li>{@link #INPUT_STATE_CONNECTED} 2200 * <li>{@link #INPUT_STATE_CONNECTED_STANDBY} 2201 * <li>{@link #INPUT_STATE_DISCONNECTED} 2202 * </ul> 2203 * 2204 * @param inputId The ID of the TV input. 2205 * @throws IllegalArgumentException if the argument is {@code null}. 2206 */ 2207 @InputState getInputState(@onNull String inputId)2208 public int getInputState(@NonNull String inputId) { 2209 Preconditions.checkNotNull(inputId); 2210 synchronized (mLock) { 2211 Integer state = mStateMap.get(inputId); 2212 if (state == null) { 2213 Log.w(TAG, "Unrecognized input ID: " + inputId); 2214 return INPUT_STATE_DISCONNECTED; 2215 } 2216 return state; 2217 } 2218 } 2219 2220 /** 2221 * Returns available extension interfaces of a given hardware TV input. This can be used to 2222 * provide domain-specific features that are only known between certain hardware TV inputs 2223 * and their clients. 2224 * 2225 * @param inputId The ID of the TV input. 2226 * @return a non-null list of extension interface names available to the caller. An empty 2227 * list indicates the given TV input is not found, or the given TV input is not a 2228 * hardware TV input, or the given TV input doesn't support any extension 2229 * interfaces, or the caller doesn't hold the required permission for the extension 2230 * interfaces supported by the given TV input. 2231 * @see #getExtensionInterface 2232 * @hide 2233 */ 2234 @SystemApi 2235 @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE) 2236 @NonNull getAvailableExtensionInterfaceNames(@onNull String inputId)2237 public List<String> getAvailableExtensionInterfaceNames(@NonNull String inputId) { 2238 Preconditions.checkNotNull(inputId); 2239 try { 2240 return mService.getAvailableExtensionInterfaceNames(inputId, mUserId); 2241 } catch (RemoteException e) { 2242 throw e.rethrowFromSystemServer(); 2243 } 2244 } 2245 2246 /** 2247 * Returns an extension interface of a given hardware TV input. This can be used to provide 2248 * domain-specific features that are only known between certain hardware TV inputs and 2249 * their clients. 2250 * 2251 * @param inputId The ID of the TV input. 2252 * @param name The extension interface name. 2253 * @return an {@link IBinder} for the given extension interface, {@code null} if the given TV 2254 * input is not found, or if the given TV input is not a hardware TV input, or if the 2255 * given TV input doesn't support the given extension interface, or if the caller 2256 * doesn't hold the required permission for the given extension interface. 2257 * @see #getAvailableExtensionInterfaceNames 2258 * @hide 2259 */ 2260 @SystemApi 2261 @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE) 2262 @Nullable getExtensionInterface(@onNull String inputId, @NonNull String name)2263 public IBinder getExtensionInterface(@NonNull String inputId, @NonNull String name) { 2264 Preconditions.checkNotNull(inputId); 2265 Preconditions.checkNotNull(name); 2266 try { 2267 return mService.getExtensionInterface(inputId, name, mUserId); 2268 } catch (RemoteException e) { 2269 throw e.rethrowFromSystemServer(); 2270 } 2271 } 2272 2273 /** 2274 * Registers a {@link TvInputCallback}. 2275 * 2276 * @param callback A callback used to monitor status of the TV inputs. 2277 * @param handler A {@link Handler} that the status change will be delivered to. 2278 */ registerCallback(@onNull TvInputCallback callback, @NonNull Handler handler)2279 public void registerCallback(@NonNull TvInputCallback callback, @NonNull Handler handler) { 2280 Preconditions.checkNotNull(callback); 2281 Preconditions.checkNotNull(handler); 2282 synchronized (mLock) { 2283 mCallbackRecords.add(new TvInputCallbackRecord(callback, handler)); 2284 } 2285 } 2286 2287 /** 2288 * Unregisters the existing {@link TvInputCallback}. 2289 * 2290 * @param callback The existing callback to remove. 2291 */ unregisterCallback(@onNull final TvInputCallback callback)2292 public void unregisterCallback(@NonNull final TvInputCallback callback) { 2293 Preconditions.checkNotNull(callback); 2294 synchronized (mLock) { 2295 for (Iterator<TvInputCallbackRecord> it = mCallbackRecords.iterator(); 2296 it.hasNext(); ) { 2297 TvInputCallbackRecord record = it.next(); 2298 if (record.getCallback() == callback) { 2299 it.remove(); 2300 break; 2301 } 2302 } 2303 } 2304 } 2305 2306 /** 2307 * Returns the user's parental controls enabled state. 2308 * 2309 * @return {@code true} if the user enabled the parental controls, {@code false} otherwise. 2310 */ isParentalControlsEnabled()2311 public boolean isParentalControlsEnabled() { 2312 try { 2313 return mService.isParentalControlsEnabled(mUserId); 2314 } catch (RemoteException e) { 2315 throw e.rethrowFromSystemServer(); 2316 } 2317 } 2318 2319 /** 2320 * Sets the user's parental controls enabled state. 2321 * 2322 * @param enabled The user's parental controls enabled state. {@code true} if the user enabled 2323 * the parental controls, {@code false} otherwise. 2324 * @see #isParentalControlsEnabled 2325 * @hide 2326 */ 2327 @SystemApi 2328 @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) setParentalControlsEnabled(boolean enabled)2329 public void setParentalControlsEnabled(boolean enabled) { 2330 try { 2331 mService.setParentalControlsEnabled(enabled, mUserId); 2332 } catch (RemoteException e) { 2333 throw e.rethrowFromSystemServer(); 2334 } 2335 } 2336 2337 /** 2338 * Checks whether a given TV content rating is blocked by the user. 2339 * 2340 * @param rating The TV content rating to check. Can be {@link TvContentRating#UNRATED}. 2341 * @return {@code true} if the given TV content rating is blocked, {@code false} otherwise. 2342 */ isRatingBlocked(@onNull TvContentRating rating)2343 public boolean isRatingBlocked(@NonNull TvContentRating rating) { 2344 Preconditions.checkNotNull(rating); 2345 try { 2346 return mService.isRatingBlocked(rating.flattenToString(), mUserId); 2347 } catch (RemoteException e) { 2348 throw e.rethrowFromSystemServer(); 2349 } 2350 } 2351 2352 /** 2353 * Returns the list of blocked content ratings. 2354 * 2355 * @return the list of content ratings blocked by the user. 2356 */ getBlockedRatings()2357 public List<TvContentRating> getBlockedRatings() { 2358 try { 2359 List<TvContentRating> ratings = new ArrayList<>(); 2360 for (String rating : mService.getBlockedRatings(mUserId)) { 2361 ratings.add(TvContentRating.unflattenFromString(rating)); 2362 } 2363 return ratings; 2364 } catch (RemoteException e) { 2365 throw e.rethrowFromSystemServer(); 2366 } 2367 } 2368 2369 /** 2370 * Adds a user blocked content rating. 2371 * 2372 * @param rating The content rating to block. 2373 * @see #isRatingBlocked 2374 * @see #removeBlockedRating 2375 * @hide 2376 */ 2377 @SystemApi 2378 @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) addBlockedRating(@onNull TvContentRating rating)2379 public void addBlockedRating(@NonNull TvContentRating rating) { 2380 Preconditions.checkNotNull(rating); 2381 try { 2382 mService.addBlockedRating(rating.flattenToString(), mUserId); 2383 } catch (RemoteException e) { 2384 throw e.rethrowFromSystemServer(); 2385 } 2386 } 2387 2388 /** 2389 * Removes a user blocked content rating. 2390 * 2391 * @param rating The content rating to unblock. 2392 * @see #isRatingBlocked 2393 * @see #addBlockedRating 2394 * @hide 2395 */ 2396 @SystemApi 2397 @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) removeBlockedRating(@onNull TvContentRating rating)2398 public void removeBlockedRating(@NonNull TvContentRating rating) { 2399 Preconditions.checkNotNull(rating); 2400 try { 2401 mService.removeBlockedRating(rating.flattenToString(), mUserId); 2402 } catch (RemoteException e) { 2403 throw e.rethrowFromSystemServer(); 2404 } 2405 } 2406 2407 /** 2408 * Returns the list of all TV content rating systems defined. 2409 * @hide 2410 */ 2411 @SystemApi 2412 @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) getTvContentRatingSystemList()2413 public List<TvContentRatingSystemInfo> getTvContentRatingSystemList() { 2414 try { 2415 return mService.getTvContentRatingSystemList(mUserId); 2416 } catch (RemoteException e) { 2417 throw e.rethrowFromSystemServer(); 2418 } 2419 } 2420 2421 /** 2422 * Notifies the TV input of the given preview program that the program's browsable state is 2423 * disabled. 2424 * @hide 2425 */ 2426 @SystemApi 2427 @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) notifyPreviewProgramBrowsableDisabled(String packageName, long programId)2428 public void notifyPreviewProgramBrowsableDisabled(String packageName, long programId) { 2429 Intent intent = new Intent(); 2430 intent.setAction(TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED); 2431 intent.putExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, programId); 2432 intent.setPackage(packageName); 2433 try { 2434 mService.sendTvInputNotifyIntent(intent, mUserId); 2435 } catch (RemoteException e) { 2436 throw e.rethrowFromSystemServer(); 2437 } 2438 } 2439 2440 /** 2441 * Notifies the TV input of the given watch next program that the program's browsable state is 2442 * disabled. 2443 * @hide 2444 */ 2445 @SystemApi 2446 @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) notifyWatchNextProgramBrowsableDisabled(String packageName, long programId)2447 public void notifyWatchNextProgramBrowsableDisabled(String packageName, long programId) { 2448 Intent intent = new Intent(); 2449 intent.setAction(TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED); 2450 intent.putExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, programId); 2451 intent.setPackage(packageName); 2452 try { 2453 mService.sendTvInputNotifyIntent(intent, mUserId); 2454 } catch (RemoteException e) { 2455 throw e.rethrowFromSystemServer(); 2456 } 2457 } 2458 2459 /** 2460 * Notifies the TV input of the given preview program that the program is added to watch next. 2461 * @hide 2462 */ 2463 @SystemApi 2464 @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) notifyPreviewProgramAddedToWatchNext(String packageName, long previewProgramId, long watchNextProgramId)2465 public void notifyPreviewProgramAddedToWatchNext(String packageName, long previewProgramId, 2466 long watchNextProgramId) { 2467 Intent intent = new Intent(); 2468 intent.setAction(TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT); 2469 intent.putExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, previewProgramId); 2470 intent.putExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, watchNextProgramId); 2471 intent.setPackage(packageName); 2472 try { 2473 mService.sendTvInputNotifyIntent(intent, mUserId); 2474 } catch (RemoteException e) { 2475 throw e.rethrowFromSystemServer(); 2476 } 2477 } 2478 2479 /** 2480 * Creates a {@link Session} for a given TV input. 2481 * 2482 * <p>The number of sessions that can be created at the same time is limited by the capability 2483 * of the given TV input. 2484 * 2485 * @param inputId The ID of the TV input. 2486 * @param tvAppAttributionSource The Attribution Source of the TV App. 2487 * @param callback A callback used to receive the created session. 2488 * @param handler A {@link Handler} that the session creation will be delivered to. 2489 * @hide 2490 */ createSession(@onNull String inputId, @NonNull AttributionSource tvAppAttributionSource, @NonNull final SessionCallback callback, @NonNull Handler handler)2491 public void createSession(@NonNull String inputId, 2492 @NonNull AttributionSource tvAppAttributionSource, 2493 @NonNull final SessionCallback callback, @NonNull Handler handler) { 2494 createSessionInternal(inputId, tvAppAttributionSource, false, callback, handler); 2495 } 2496 2497 /** 2498 * Get a the client pid when creating the session with the session id provided. 2499 * 2500 * @param sessionId a String of session id that is used to query the client pid. 2501 * @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_PID} 2502 * if the call fails. 2503 * 2504 * @hide 2505 */ 2506 @SystemApi 2507 @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) getClientPid(@onNull String sessionId)2508 public int getClientPid(@NonNull String sessionId) { 2509 return getClientPidInternal(sessionId); 2510 }; 2511 2512 /** 2513 * Returns a priority for the given use case type and the client's foreground or background 2514 * status. 2515 * 2516 * @param useCase the use case type of the client. 2517 * {@see TvInputService#PriorityHintUseCaseType}. 2518 * @param sessionId the unique id of the session owned by the client. 2519 * {@see TvInputService#onCreateSession(String, String, AttributionSource)}. 2520 * 2521 * @return the use case priority value for the given use case type and the client's foreground 2522 * or background status. 2523 * 2524 * @hide 2525 */ 2526 @SystemApi 2527 @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) getClientPriority(@vInputService.PriorityHintUseCaseType int useCase, @NonNull String sessionId)2528 public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase, 2529 @NonNull String sessionId) { 2530 Preconditions.checkNotNull(sessionId); 2531 if (!isValidUseCase(useCase)) { 2532 throw new IllegalArgumentException("Invalid use case: " + useCase); 2533 } 2534 return getClientPriorityInternal(useCase, sessionId); 2535 }; 2536 2537 /** 2538 * Returns a priority for the given use case type and the caller's foreground or background 2539 * status. 2540 * 2541 * @param useCase the use case type of the caller. 2542 * {@see TvInputService#PriorityHintUseCaseType}. 2543 * 2544 * @return the use case priority value for the given use case type and the caller's foreground 2545 * or background status. 2546 * 2547 * @hide 2548 */ 2549 @SystemApi 2550 @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) getClientPriority(@vInputService.PriorityHintUseCaseType int useCase)2551 public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase) { 2552 if (!isValidUseCase(useCase)) { 2553 throw new IllegalArgumentException("Invalid use case: " + useCase); 2554 } 2555 return getClientPriorityInternal(useCase, null); 2556 }; 2557 /** 2558 * Creates a recording {@link Session} for a given TV input. 2559 * 2560 * <p>The number of sessions that can be created at the same time is limited by the capability 2561 * of the given TV input. 2562 * 2563 * @param inputId The ID of the TV input. 2564 * @param callback A callback used to receive the created session. 2565 * @param handler A {@link Handler} that the session creation will be delivered to. 2566 * @hide 2567 */ createRecordingSession(@onNull String inputId, @NonNull final SessionCallback callback, @NonNull Handler handler)2568 public void createRecordingSession(@NonNull String inputId, 2569 @NonNull final SessionCallback callback, @NonNull Handler handler) { 2570 createSessionInternal(inputId, null, true, callback, handler); 2571 } 2572 createSessionInternal(String inputId, AttributionSource tvAppAttributionSource, boolean isRecordingSession, SessionCallback callback, Handler handler)2573 private void createSessionInternal(String inputId, AttributionSource tvAppAttributionSource, 2574 boolean isRecordingSession, SessionCallback callback, Handler handler) { 2575 Preconditions.checkNotNull(inputId); 2576 Preconditions.checkNotNull(callback); 2577 Preconditions.checkNotNull(handler); 2578 SessionCallbackRecord record = new SessionCallbackRecord(callback, handler); 2579 synchronized (mSessionCallbackRecordMap) { 2580 int seq = mNextSeq++; 2581 mSessionCallbackRecordMap.put(seq, record); 2582 try { 2583 mService.createSession( 2584 mClient, inputId, tvAppAttributionSource, isRecordingSession, seq, mUserId); 2585 } catch (RemoteException e) { 2586 throw e.rethrowFromSystemServer(); 2587 } 2588 } 2589 } 2590 getClientPidInternal(String sessionId)2591 private int getClientPidInternal(String sessionId) { 2592 Preconditions.checkNotNull(sessionId); 2593 int clientPid = UNKNOWN_CLIENT_PID; 2594 try { 2595 clientPid = mService.getClientPid(sessionId); 2596 } catch (RemoteException e) { 2597 throw e.rethrowFromSystemServer(); 2598 } 2599 return clientPid; 2600 } 2601 getClientPriorityInternal(int useCase, String sessionId)2602 private int getClientPriorityInternal(int useCase, String sessionId) { 2603 try { 2604 return mService.getClientPriority(useCase, sessionId); 2605 } catch (RemoteException e) { 2606 throw e.rethrowFromSystemServer(); 2607 } 2608 } 2609 isValidUseCase(int useCase)2610 private boolean isValidUseCase(int useCase) { 2611 return useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND 2612 || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN 2613 || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK 2614 || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE 2615 || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD; 2616 } 2617 2618 /** 2619 * Returns the TvStreamConfig list of the given TV input. 2620 * 2621 * If you are using {@link Hardware} object from {@link 2622 * #acquireTvInputHardware}, you should get the list of available streams 2623 * from {@link HardwareCallback#onStreamConfigChanged} method, not from 2624 * here. This method is designed to be used with {@link #captureFrame} in 2625 * capture scenarios specifically and not suitable for any other use. 2626 * 2627 * @param inputId The ID of the TV input. 2628 * @return List of {@link TvStreamConfig} which is available for capturing 2629 * of the given TV input. 2630 * @hide 2631 */ 2632 @SystemApi 2633 @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) getAvailableTvStreamConfigList(String inputId)2634 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId) { 2635 try { 2636 return mService.getAvailableTvStreamConfigList(inputId, mUserId); 2637 } catch (RemoteException e) { 2638 throw e.rethrowFromSystemServer(); 2639 } 2640 } 2641 2642 /** 2643 * Take a snapshot of the given TV input into the provided Surface. 2644 * 2645 * @param inputId The ID of the TV input. 2646 * @param surface the {@link Surface} to which the snapshot is captured. 2647 * @param config the {@link TvStreamConfig} which is used for capturing. 2648 * @return true when the {@link Surface} is ready to be captured. 2649 * @hide 2650 */ 2651 @SystemApi 2652 @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) captureFrame(String inputId, Surface surface, TvStreamConfig config)2653 public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config) { 2654 try { 2655 return mService.captureFrame(inputId, surface, config, mUserId); 2656 } catch (RemoteException e) { 2657 throw e.rethrowFromSystemServer(); 2658 } 2659 } 2660 2661 /** 2662 * Returns true if there is only a single TV input session. 2663 * 2664 * @hide 2665 */ 2666 @SystemApi 2667 @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) isSingleSessionActive()2668 public boolean isSingleSessionActive() { 2669 try { 2670 return mService.isSingleSessionActive(mUserId); 2671 } catch (RemoteException e) { 2672 throw e.rethrowFromSystemServer(); 2673 } 2674 } 2675 2676 /** 2677 * Returns a list of TvInputHardwareInfo objects representing available hardware. 2678 * 2679 * @hide 2680 */ 2681 @SystemApi 2682 @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) getHardwareList()2683 public List<TvInputHardwareInfo> getHardwareList() { 2684 try { 2685 return mService.getHardwareList(); 2686 } catch (RemoteException e) { 2687 throw e.rethrowFromSystemServer(); 2688 } 2689 } 2690 2691 /** 2692 * Acquires {@link Hardware} object for the given device ID. 2693 * 2694 * <p>A subsequent call to this method on the same {@code deviceId} will release the currently 2695 * acquired Hardware. 2696 * 2697 * @param deviceId The device ID to acquire Hardware for. 2698 * @param callback A callback to receive updates on Hardware. 2699 * @param info The TV input which will use the acquired Hardware. 2700 * @return Hardware on success, {@code null} otherwise. 2701 * 2702 * @hide 2703 * @removed 2704 */ 2705 @SystemApi 2706 @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) acquireTvInputHardware(int deviceId, final HardwareCallback callback, TvInputInfo info)2707 public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback, 2708 TvInputInfo info) { 2709 return acquireTvInputHardware(deviceId, info, callback); 2710 } 2711 2712 /** 2713 * Acquires {@link Hardware} object for the given device ID. 2714 * 2715 * <p>A subsequent call to this method on the same {@code deviceId} could release the currently 2716 * acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current 2717 * request. 2718 * 2719 * <p>If the client would like to provide information for the TRM to compare, use 2720 * {@link #acquireTvInputHardware(int, TvInputInfo, HardwareCallback, String, int)} instead. 2721 * 2722 * <p>Otherwise default priority will be applied. 2723 * 2724 * @param deviceId The device ID to acquire Hardware for. 2725 * @param info The TV input which will use the acquired Hardware. 2726 * @param callback A callback to receive updates on Hardware. 2727 * @return Hardware on success, {@code null} otherwise. 2728 * 2729 * @hide 2730 */ 2731 @SystemApi 2732 @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info, @NonNull final HardwareCallback callback)2733 public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info, 2734 @NonNull final HardwareCallback callback) { 2735 Preconditions.checkNotNull(info); 2736 Preconditions.checkNotNull(callback); 2737 return acquireTvInputHardwareInternal(deviceId, info, null, 2738 TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, new Executor() { 2739 public void execute(Runnable r) { 2740 r.run(); 2741 } 2742 }, callback); 2743 } 2744 2745 /** 2746 * Acquires {@link Hardware} object for the given device ID. 2747 * 2748 * <p>A subsequent call to this method on the same {@code deviceId} could release the currently 2749 * acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current 2750 * request. 2751 * 2752 * @param deviceId The device ID to acquire Hardware for. 2753 * @param info The TV input which will use the acquired Hardware. 2754 * @param tvInputSessionId a String returned to TIS when the session was created. 2755 * {@see TvInputService#onCreateSession(String, String, AttributionSource)}. If null, the 2756 * client will be treated as a background app. 2757 * @param priorityHint The use case of the client. {@see TvInputService#PriorityHintUseCaseType} 2758 * @param executor the executor on which the listener would be invoked. 2759 * @param callback A callback to receive updates on Hardware. 2760 * @return Hardware on success, {@code null} otherwise. When the TRM decides to not grant 2761 * resource, null is returned and the {@link IllegalStateException} is thrown with 2762 * "No enough resources". 2763 * 2764 * @hide 2765 */ 2766 @SystemApi 2767 @Nullable 2768 @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2769 public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info, 2770 @Nullable String tvInputSessionId, 2771 @TvInputService.PriorityHintUseCaseType int priorityHint, 2772 @NonNull @CallbackExecutor Executor executor, 2773 @NonNull final HardwareCallback callback) { 2774 Preconditions.checkNotNull(info); 2775 Preconditions.checkNotNull(callback); 2776 return acquireTvInputHardwareInternal(deviceId, info, tvInputSessionId, priorityHint, 2777 executor, callback); 2778 } 2779 2780 /** 2781 * API to add a hardware device in the TvInputHardwareManager for CTS testing 2782 * purpose. 2783 * 2784 * @param deviceId Id of the adding hardware device. 2785 * 2786 * @hide 2787 */ 2788 @TestApi 2789 public void addHardwareDevice(int deviceId) { 2790 try { 2791 mService.addHardwareDevice(deviceId); 2792 } catch (RemoteException e) { 2793 throw e.rethrowFromSystemServer(); 2794 } 2795 } 2796 2797 /** 2798 * API to remove a hardware device in the TvInputHardwareManager for CTS testing 2799 * purpose. 2800 * 2801 * @param deviceId Id of the removing hardware device. 2802 * 2803 * @hide 2804 */ 2805 @TestApi 2806 public void removeHardwareDevice(int deviceId) { 2807 try { 2808 mService.removeHardwareDevice(deviceId); 2809 } catch (RemoteException e) { 2810 throw e.rethrowFromSystemServer(); 2811 } 2812 } 2813 2814 private Hardware acquireTvInputHardwareInternal(int deviceId, TvInputInfo info, 2815 String tvInputSessionId, int priorityHint, 2816 Executor executor, final HardwareCallback callback) { 2817 try { 2818 ITvInputHardware hardware = 2819 mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() { 2820 @Override 2821 public void onReleased() { 2822 final long identity = Binder.clearCallingIdentity(); 2823 try { 2824 executor.execute(() -> callback.onReleased()); 2825 } finally { 2826 Binder.restoreCallingIdentity(identity); 2827 } 2828 } 2829 2830 @Override 2831 public void onStreamConfigChanged(TvStreamConfig[] configs) { 2832 final long identity = Binder.clearCallingIdentity(); 2833 try { 2834 executor.execute(() -> callback.onStreamConfigChanged(configs)); 2835 } finally { 2836 Binder.restoreCallingIdentity(identity); 2837 } 2838 } 2839 }, info, mUserId, tvInputSessionId, priorityHint); 2840 if (hardware == null) { 2841 return null; 2842 } 2843 return new Hardware(hardware); 2844 } catch (RemoteException e) { 2845 throw e.rethrowFromSystemServer(); 2846 } 2847 } 2848 2849 /** 2850 * Releases previously acquired hardware object. 2851 * 2852 * @param deviceId The device ID this Hardware was acquired for 2853 * @param hardware Hardware to release. 2854 * 2855 * @hide 2856 */ 2857 @SystemApi 2858 @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) 2859 public void releaseTvInputHardware(int deviceId, Hardware hardware) { 2860 try { 2861 mService.releaseTvInputHardware(deviceId, hardware.getInterface(), mUserId); 2862 } catch (RemoteException e) { 2863 throw e.rethrowFromSystemServer(); 2864 } 2865 } 2866 2867 /** 2868 * Returns the list of currently available DVB frontend devices on the system. 2869 * 2870 * @return the list of {@link DvbDeviceInfo} objects representing available DVB devices. 2871 * @hide 2872 */ 2873 @SystemApi 2874 @RequiresPermission(android.Manifest.permission.DVB_DEVICE) 2875 @NonNull 2876 public List<DvbDeviceInfo> getDvbDeviceList() { 2877 try { 2878 return mService.getDvbDeviceList(); 2879 } catch (RemoteException e) { 2880 throw e.rethrowFromSystemServer(); 2881 } 2882 } 2883 2884 /** 2885 * Returns a {@link ParcelFileDescriptor} of a specified DVB device of a given type for a given 2886 * {@link DvbDeviceInfo}. 2887 * 2888 * @param info A {@link DvbDeviceInfo} to open a DVB device. 2889 * @param deviceType A DVB device type. 2890 * @return a {@link ParcelFileDescriptor} of a specified DVB device for a given 2891 * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} 2892 * failed to open. 2893 * @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found. 2894 2895 * @see <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB API v3</a> 2896 * @hide 2897 */ 2898 @SystemApi 2899 @RequiresPermission(android.Manifest.permission.DVB_DEVICE) 2900 @Nullable 2901 public ParcelFileDescriptor openDvbDevice(@NonNull DvbDeviceInfo info, 2902 @DvbDeviceType int deviceType) { 2903 try { 2904 if (DVB_DEVICE_START > deviceType || DVB_DEVICE_END < deviceType) { 2905 throw new IllegalArgumentException("Invalid DVB device: " + deviceType); 2906 } 2907 return mService.openDvbDevice(info, deviceType); 2908 } catch (RemoteException e) { 2909 throw e.rethrowFromSystemServer(); 2910 } 2911 } 2912 2913 /** 2914 * Requests to make a channel browsable. 2915 * 2916 * <p>Once called, the system will review the request and make the channel browsable based on 2917 * its policy. The first request from a package is guaranteed to be approved. 2918 * 2919 * @param channelUri The URI for the channel to be browsable. 2920 * @hide 2921 */ 2922 public void requestChannelBrowsable(Uri channelUri) { 2923 try { 2924 mService.requestChannelBrowsable(channelUri, mUserId); 2925 } catch (RemoteException e) { 2926 throw e.rethrowFromSystemServer(); 2927 } 2928 } 2929 2930 /** 2931 * Returns the list of session information for {@link TvInputService.Session} that are 2932 * currently in use. 2933 * <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get 2934 * the channel URIs. If the permission is not granted, 2935 * {@link TunedInfo#getChannelUri()} returns {@code null}. 2936 * @hide 2937 */ 2938 @SystemApi 2939 @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) 2940 @NonNull 2941 public List<TunedInfo> getCurrentTunedInfos() { 2942 try { 2943 return mService.getCurrentTunedInfos(mUserId); 2944 } catch (RemoteException e) { 2945 throw e.rethrowFromSystemServer(); 2946 } 2947 } 2948 2949 /** 2950 * The Session provides the per-session functionality of TV inputs. 2951 * @hide 2952 */ 2953 public static final class Session { 2954 static final int DISPATCH_IN_PROGRESS = -1; 2955 static final int DISPATCH_NOT_HANDLED = 0; 2956 static final int DISPATCH_HANDLED = 1; 2957 2958 private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500; 2959 2960 private final ITvInputManager mService; 2961 private final int mUserId; 2962 private final int mSeq; 2963 2964 // For scheduling input event handling on the main thread. This also serves as a lock to 2965 // protect pending input events and the input channel. 2966 private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper()); 2967 2968 private final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20); 2969 private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20); 2970 private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap; 2971 2972 private IBinder mToken; 2973 private TvInputEventSender mSender; 2974 private InputChannel mChannel; 2975 2976 private final Object mMetadataLock = new Object(); 2977 // @GuardedBy("mMetadataLock") 2978 private final List<AudioPresentation> mAudioPresentations = new ArrayList<>(); 2979 // @GuardedBy("mMetadataLock") 2980 private final List<TvTrackInfo> mAudioTracks = new ArrayList<>(); 2981 // @GuardedBy("mMetadataLock") 2982 private final List<TvTrackInfo> mVideoTracks = new ArrayList<>(); 2983 // @GuardedBy("mMetadataLock") 2984 private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<>(); 2985 // @GuardedBy("mMetadataLock") 2986 private int mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN; 2987 // @GuardedBy("mMetadataLock") 2988 private int mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN; 2989 // @GuardedBy("mMetadataLock") 2990 private String mSelectedAudioTrackId; 2991 // @GuardedBy("mMetadataLock") 2992 private String mSelectedVideoTrackId; 2993 // @GuardedBy("mMetadataLock") 2994 private String mSelectedSubtitleTrackId; 2995 // @GuardedBy("mMetadataLock") 2996 private int mVideoWidth; 2997 // @GuardedBy("mMetadataLock") 2998 private int mVideoHeight; 2999 3000 private TvInteractiveAppManager.Session mIAppSession; 3001 private TvAdManager.Session mAdSession; 3002 private boolean mIAppNotificationEnabled = false; 3003 3004 private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId, 3005 int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) { 3006 mToken = token; 3007 mChannel = channel; 3008 mService = service; 3009 mUserId = userId; 3010 mSeq = seq; 3011 mSessionCallbackRecordMap = sessionCallbackRecordMap; 3012 } 3013 3014 public TvInteractiveAppManager.Session getInteractiveAppSession() { 3015 return mIAppSession; 3016 } 3017 3018 public void setInteractiveAppSession(TvInteractiveAppManager.Session iAppSession) { 3019 this.mIAppSession = iAppSession; 3020 } 3021 3022 public TvAdManager.Session getAdSession() { 3023 return mAdSession; 3024 } 3025 3026 public void setAdSession(TvAdManager.Session adSession) { 3027 this.mAdSession = adSession; 3028 } 3029 3030 /** 3031 * Releases this session. 3032 */ 3033 public void release() { 3034 if (mToken == null) { 3035 Log.w(TAG, "The session has been already released"); 3036 return; 3037 } 3038 try { 3039 mService.releaseSession(mToken, mUserId); 3040 } catch (RemoteException e) { 3041 throw e.rethrowFromSystemServer(); 3042 } 3043 3044 releaseInternal(); 3045 } 3046 3047 /** 3048 * Sets this as the main session. The main session is a session whose corresponding TV 3049 * input determines the HDMI-CEC active source device. 3050 * 3051 * @see TvView#setMain 3052 */ 3053 void setMain() { 3054 if (mToken == null) { 3055 Log.w(TAG, "The session has been already released"); 3056 return; 3057 } 3058 try { 3059 mService.setMainSession(mToken, mUserId); 3060 } catch (RemoteException e) { 3061 throw e.rethrowFromSystemServer(); 3062 } 3063 } 3064 3065 /** 3066 * Sets the {@link android.view.Surface} for this session. 3067 * 3068 * @param surface A {@link android.view.Surface} used to render video. 3069 */ 3070 public void setSurface(Surface surface) { 3071 if (mToken == null) { 3072 Log.w(TAG, "The session has been already released"); 3073 return; 3074 } 3075 // surface can be null. 3076 try { 3077 mService.setSurface(mToken, surface, mUserId); 3078 } catch (RemoteException e) { 3079 throw e.rethrowFromSystemServer(); 3080 } 3081 } 3082 3083 /** 3084 * Notifies of any structural changes (format or size) of the surface passed in 3085 * {@link #setSurface}. 3086 * 3087 * @param format The new PixelFormat of the surface. 3088 * @param width The new width of the surface. 3089 * @param height The new height of the surface. 3090 */ 3091 public void dispatchSurfaceChanged(int format, int width, int height) { 3092 if (mToken == null) { 3093 Log.w(TAG, "The session has been already released"); 3094 return; 3095 } 3096 try { 3097 mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId); 3098 } catch (RemoteException e) { 3099 throw e.rethrowFromSystemServer(); 3100 } 3101 } 3102 3103 /** 3104 * Sets the relative stream volume of this session to handle a change of audio focus. 3105 * 3106 * @param volume A volume value between 0.0f to 1.0f. 3107 * @throws IllegalArgumentException if the volume value is out of range. 3108 */ 3109 public void setStreamVolume(float volume) { 3110 if (mToken == null) { 3111 Log.w(TAG, "The session has been already released"); 3112 return; 3113 } 3114 try { 3115 if (volume < 0.0f || volume > 1.0f) { 3116 throw new IllegalArgumentException("volume should be between 0.0f and 1.0f"); 3117 } 3118 mService.setVolume(mToken, volume, mUserId); 3119 } catch (RemoteException e) { 3120 throw e.rethrowFromSystemServer(); 3121 } 3122 } 3123 3124 /** 3125 * Tunes to a given channel. 3126 * 3127 * @param channelUri The URI of a channel. 3128 */ 3129 public void tune(Uri channelUri) { 3130 tune(channelUri, null); 3131 } 3132 3133 /** 3134 * Tunes to a given channel. 3135 * 3136 * @param channelUri The URI of a channel. 3137 * @param params A set of extra parameters which might be handled with this tune event. 3138 */ 3139 public void tune(@NonNull Uri channelUri, Bundle params) { 3140 Preconditions.checkNotNull(channelUri); 3141 if (mToken == null) { 3142 Log.w(TAG, "The session has been already released"); 3143 return; 3144 } 3145 synchronized (mMetadataLock) { 3146 mAudioPresentations.clear(); 3147 mAudioTracks.clear(); 3148 mVideoTracks.clear(); 3149 mSubtitleTracks.clear(); 3150 mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN; 3151 mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN; 3152 mSelectedAudioTrackId = null; 3153 mSelectedVideoTrackId = null; 3154 mSelectedSubtitleTrackId = null; 3155 mVideoWidth = 0; 3156 mVideoHeight = 0; 3157 } 3158 try { 3159 mService.tune(mToken, channelUri, params, mUserId); 3160 } catch (RemoteException e) { 3161 throw e.rethrowFromSystemServer(); 3162 } 3163 } 3164 3165 /** 3166 * Enables or disables the caption for this session. 3167 * 3168 * @param enabled {@code true} to enable, {@code false} to disable. 3169 */ 3170 public void setCaptionEnabled(boolean enabled) { 3171 if (mToken == null) { 3172 Log.w(TAG, "The session has been already released"); 3173 return; 3174 } 3175 try { 3176 mService.setCaptionEnabled(mToken, enabled, mUserId); 3177 } catch (RemoteException e) { 3178 throw e.rethrowFromSystemServer(); 3179 } 3180 } 3181 3182 /** 3183 * Selects an audio presentation 3184 * 3185 * @param presentationId The ID of the audio presentation to select. 3186 * @param programId The ID of the program offering the selected audio presentation. 3187 * @see #getAudioPresentations 3188 */ 3189 public void selectAudioPresentation(int presentationId, int programId) { 3190 synchronized (mMetadataLock) { 3191 if (presentationId != AudioPresentation.PRESENTATION_ID_UNKNOWN 3192 && !containsAudioPresentation(mAudioPresentations, presentationId)) { 3193 Log.w(TAG, "Invalid audio presentation id: " + presentationId); 3194 return; 3195 } 3196 } 3197 if (mToken == null) { 3198 Log.w(TAG, "The session has been already released"); 3199 return; 3200 } 3201 try { 3202 mService.selectAudioPresentation(mToken, presentationId, programId, mUserId); 3203 } catch (RemoteException e) { 3204 throw e.rethrowFromSystemServer(); 3205 } 3206 } 3207 3208 private boolean containsAudioPresentation(List<AudioPresentation> audioPresentations, 3209 int presentationId) { 3210 synchronized (mMetadataLock) { 3211 for (AudioPresentation audioPresentation : audioPresentations) { 3212 if (audioPresentation.getPresentationId() == presentationId) { 3213 return true; 3214 } 3215 } 3216 return false; 3217 } 3218 } 3219 3220 /** 3221 * Returns a list of audio presentations. 3222 * 3223 * @return the list of audio presentations. 3224 * Returns empty AudioPresentation list if no presentations are available. 3225 */ 3226 public List<AudioPresentation> getAudioPresentations() { 3227 synchronized (mMetadataLock) { 3228 if (mAudioPresentations == null) { 3229 return new ArrayList<AudioPresentation>(); 3230 } 3231 return new ArrayList<AudioPresentation>(mAudioPresentations); 3232 } 3233 } 3234 3235 /** 3236 * Returns the program ID of the selected audio presentation. 3237 * 3238 * @return The ID of the program providing the selected audio presentation. 3239 * Returns {@value AudioPresentation.PROGRAM_ID_UNKNOWN} if no audio presentation has 3240 * been selected from a program. 3241 * @see #selectAudioPresentation 3242 */ 3243 public int getSelectedProgramId() { 3244 synchronized (mMetadataLock) { 3245 return mSelectedAudioProgramId; 3246 } 3247 } 3248 3249 /** 3250 * Returns the presentation ID of the selected audio presentation. 3251 * 3252 * @return The ID of the selected audio presentation. 3253 * Returns {@value AudioPresentation.PRESENTATION_ID_UNKNOWN} if no audio presentation 3254 * has been selected. 3255 * @see #selectAudioPresentation 3256 */ 3257 public int getSelectedAudioPresentationId() { 3258 synchronized (mMetadataLock) { 3259 return mSelectedAudioPresentationId; 3260 } 3261 } 3262 3263 /** 3264 * Responds to onAudioPresentationsChanged() and updates the internal audio presentation 3265 * information. 3266 * @return true if there is an update. 3267 */ 3268 boolean updateAudioPresentations(List<AudioPresentation> audioPresentations) { 3269 synchronized (mMetadataLock) { 3270 mAudioPresentations.clear(); 3271 for (AudioPresentation presentation : audioPresentations) { 3272 mAudioPresentations.add(presentation); 3273 } 3274 return !mAudioPresentations.isEmpty(); 3275 } 3276 } 3277 3278 /** 3279 * Responds to onAudioPresentationSelected() and updates the internal audio presentation 3280 * selection information. 3281 * @return true if there is an update. 3282 */ 3283 boolean updateAudioPresentationSelection(int presentationId, int programId) { 3284 synchronized (mMetadataLock) { 3285 if ((programId != mSelectedAudioProgramId) 3286 || (presentationId != mSelectedAudioPresentationId)) { 3287 mSelectedAudioPresentationId = presentationId; 3288 mSelectedAudioProgramId = programId; 3289 return true; 3290 } 3291 } 3292 return false; 3293 } 3294 3295 /** 3296 * Selects a track. 3297 * 3298 * @param type The type of the track to select. The type can be 3299 * {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or 3300 * {@link TvTrackInfo#TYPE_SUBTITLE}. 3301 * @param trackId The ID of the track to select. When {@code null}, the currently selected 3302 * track of the given type will be unselected. 3303 * @see #getTracks 3304 */ 3305 public void selectTrack(int type, @Nullable String trackId) { 3306 synchronized (mMetadataLock) { 3307 if (type == TvTrackInfo.TYPE_AUDIO) { 3308 if (trackId != null && !containsTrack(mAudioTracks, trackId)) { 3309 Log.w(TAG, "Invalid audio trackId: " + trackId); 3310 return; 3311 } 3312 } else if (type == TvTrackInfo.TYPE_VIDEO) { 3313 if (trackId != null && !containsTrack(mVideoTracks, trackId)) { 3314 Log.w(TAG, "Invalid video trackId: " + trackId); 3315 return; 3316 } 3317 } else if (type == TvTrackInfo.TYPE_SUBTITLE) { 3318 if (trackId != null && !containsTrack(mSubtitleTracks, trackId)) { 3319 Log.w(TAG, "Invalid subtitle trackId: " + trackId); 3320 return; 3321 } 3322 } else { 3323 throw new IllegalArgumentException("invalid type: " + type); 3324 } 3325 } 3326 if (mToken == null) { 3327 Log.w(TAG, "The session has been already released"); 3328 return; 3329 } 3330 try { 3331 mService.selectTrack(mToken, type, trackId, mUserId); 3332 } catch (RemoteException e) { 3333 throw e.rethrowFromSystemServer(); 3334 } 3335 } 3336 3337 private boolean containsTrack(List<TvTrackInfo> tracks, String trackId) { 3338 for (TvTrackInfo track : tracks) { 3339 if (track.getId().equals(trackId)) { 3340 return true; 3341 } 3342 } 3343 return false; 3344 } 3345 3346 /** 3347 * Returns the list of tracks for a given type. Returns {@code null} if the information is 3348 * not available. 3349 * 3350 * @param type The type of the tracks. The type can be {@link TvTrackInfo#TYPE_AUDIO}, 3351 * {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}. 3352 * @return the list of tracks for the given type. 3353 */ 3354 @Nullable 3355 public List<TvTrackInfo> getTracks(int type) { 3356 synchronized (mMetadataLock) { 3357 if (type == TvTrackInfo.TYPE_AUDIO) { 3358 if (mAudioTracks == null) { 3359 return null; 3360 } 3361 return new ArrayList<>(mAudioTracks); 3362 } else if (type == TvTrackInfo.TYPE_VIDEO) { 3363 if (mVideoTracks == null) { 3364 return null; 3365 } 3366 return new ArrayList<>(mVideoTracks); 3367 } else if (type == TvTrackInfo.TYPE_SUBTITLE) { 3368 if (mSubtitleTracks == null) { 3369 return null; 3370 } 3371 return new ArrayList<>(mSubtitleTracks); 3372 } 3373 } 3374 throw new IllegalArgumentException("invalid type: " + type); 3375 } 3376 3377 /** 3378 * Returns the selected track for a given type. Returns {@code null} if the information is 3379 * not available or any of the tracks for the given type is not selected. 3380 * 3381 * @return The ID of the selected track. 3382 * @see #selectTrack 3383 */ 3384 @Nullable 3385 public String getSelectedTrack(int type) { 3386 synchronized (mMetadataLock) { 3387 if (type == TvTrackInfo.TYPE_AUDIO) { 3388 return mSelectedAudioTrackId; 3389 } else if (type == TvTrackInfo.TYPE_VIDEO) { 3390 return mSelectedVideoTrackId; 3391 } else if (type == TvTrackInfo.TYPE_SUBTITLE) { 3392 return mSelectedSubtitleTrackId; 3393 } 3394 } 3395 throw new IllegalArgumentException("invalid type: " + type); 3396 } 3397 3398 /** 3399 * Enables interactive app notification. 3400 * 3401 * @param enabled {@code true} if you want to enable interactive app notifications. 3402 * {@code false} otherwise. 3403 */ 3404 public void setInteractiveAppNotificationEnabled(boolean enabled) { 3405 if (mToken == null) { 3406 Log.w(TAG, "The session has been already released"); 3407 return; 3408 } 3409 try { 3410 mService.setInteractiveAppNotificationEnabled(mToken, enabled, mUserId); 3411 mIAppNotificationEnabled = enabled; 3412 } catch (RemoteException e) { 3413 throw e.rethrowFromSystemServer(); 3414 } 3415 } 3416 3417 /** 3418 * Responds to onTracksChanged() and updates the internal track information. Returns true if 3419 * there is an update. 3420 */ 3421 boolean updateTracks(List<TvTrackInfo> tracks) { 3422 synchronized (mMetadataLock) { 3423 mAudioTracks.clear(); 3424 mVideoTracks.clear(); 3425 mSubtitleTracks.clear(); 3426 for (TvTrackInfo track : tracks) { 3427 if (track.getType() == TvTrackInfo.TYPE_AUDIO) { 3428 mAudioTracks.add(track); 3429 } else if (track.getType() == TvTrackInfo.TYPE_VIDEO) { 3430 mVideoTracks.add(track); 3431 } else if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) { 3432 mSubtitleTracks.add(track); 3433 } 3434 } 3435 return !mAudioTracks.isEmpty() || !mVideoTracks.isEmpty() 3436 || !mSubtitleTracks.isEmpty(); 3437 } 3438 } 3439 3440 /** 3441 * Responds to onTrackSelected() and updates the internal track selection information. 3442 * Returns true if there is an update. 3443 */ 3444 boolean updateTrackSelection(int type, String trackId) { 3445 synchronized (mMetadataLock) { 3446 if (type == TvTrackInfo.TYPE_AUDIO 3447 && !TextUtils.equals(trackId, mSelectedAudioTrackId)) { 3448 mSelectedAudioTrackId = trackId; 3449 return true; 3450 } else if (type == TvTrackInfo.TYPE_VIDEO 3451 && !TextUtils.equals(trackId, mSelectedVideoTrackId)) { 3452 mSelectedVideoTrackId = trackId; 3453 return true; 3454 } else if (type == TvTrackInfo.TYPE_SUBTITLE 3455 && !TextUtils.equals(trackId, mSelectedSubtitleTrackId)) { 3456 mSelectedSubtitleTrackId = trackId; 3457 return true; 3458 } 3459 } 3460 return false; 3461 } 3462 3463 /** 3464 * Returns the new/updated video track that contains new video size information. Returns 3465 * null if there is no video track to notify. Subsequent calls of this method results in a 3466 * non-null video track returned only by the first call and null returned by following 3467 * calls. The caller should immediately notify of the video size change upon receiving the 3468 * track. 3469 */ 3470 TvTrackInfo getVideoTrackToNotify() { 3471 synchronized (mMetadataLock) { 3472 if (!mVideoTracks.isEmpty() && mSelectedVideoTrackId != null) { 3473 for (TvTrackInfo track : mVideoTracks) { 3474 if (track.getId().equals(mSelectedVideoTrackId)) { 3475 int videoWidth = track.getVideoWidth(); 3476 int videoHeight = track.getVideoHeight(); 3477 if (mVideoWidth != videoWidth || mVideoHeight != videoHeight) { 3478 mVideoWidth = videoWidth; 3479 mVideoHeight = videoHeight; 3480 return track; 3481 } 3482 } 3483 } 3484 } 3485 } 3486 return null; 3487 } 3488 3489 /** 3490 * Plays a given recorded TV program. 3491 */ 3492 void timeShiftPlay(Uri recordedProgramUri) { 3493 if (mToken == null) { 3494 Log.w(TAG, "The session has been already released"); 3495 return; 3496 } 3497 try { 3498 mService.timeShiftPlay(mToken, recordedProgramUri, mUserId); 3499 } catch (RemoteException e) { 3500 throw e.rethrowFromSystemServer(); 3501 } 3502 } 3503 3504 /** 3505 * Pauses the playback. Call {@link #timeShiftResume()} to restart the playback. 3506 */ 3507 void timeShiftPause() { 3508 if (mToken == null) { 3509 Log.w(TAG, "The session has been already released"); 3510 return; 3511 } 3512 try { 3513 mService.timeShiftPause(mToken, mUserId); 3514 } catch (RemoteException e) { 3515 throw e.rethrowFromSystemServer(); 3516 } 3517 } 3518 3519 /** 3520 * Resumes the playback. No-op if it is already playing the channel. 3521 */ 3522 void timeShiftResume() { 3523 if (mToken == null) { 3524 Log.w(TAG, "The session has been already released"); 3525 return; 3526 } 3527 try { 3528 mService.timeShiftResume(mToken, mUserId); 3529 } catch (RemoteException e) { 3530 throw e.rethrowFromSystemServer(); 3531 } 3532 } 3533 3534 /** 3535 * Seeks to a specified time position. 3536 * 3537 * <p>Normally, the position is given within range between the start and the current time, 3538 * inclusively. 3539 * 3540 * @param timeMs The time position to seek to, in milliseconds since the epoch. 3541 * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged 3542 */ 3543 void timeShiftSeekTo(long timeMs) { 3544 if (mToken == null) { 3545 Log.w(TAG, "The session has been already released"); 3546 return; 3547 } 3548 try { 3549 mService.timeShiftSeekTo(mToken, timeMs, mUserId); 3550 } catch (RemoteException e) { 3551 throw e.rethrowFromSystemServer(); 3552 } 3553 } 3554 3555 /** 3556 * Sets playback rate using {@link android.media.PlaybackParams}. 3557 * 3558 * @param params The playback params. 3559 */ 3560 void timeShiftSetPlaybackParams(PlaybackParams params) { 3561 if (mToken == null) { 3562 Log.w(TAG, "The session has been already released"); 3563 return; 3564 } 3565 try { 3566 mService.timeShiftSetPlaybackParams(mToken, params, mUserId); 3567 } catch (RemoteException e) { 3568 throw e.rethrowFromSystemServer(); 3569 } 3570 } 3571 3572 /** 3573 * Sets time shift mode. 3574 * 3575 * @param mode The time shift mode. The value is one of the following: 3576 * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL}, 3577 * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK}, 3578 * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}. 3579 * @hide 3580 */ 3581 void timeShiftSetMode(@TimeShiftMode int mode) { 3582 if (mToken == null) { 3583 Log.w(TAG, "The session has been already released"); 3584 return; 3585 } 3586 try { 3587 mService.timeShiftSetMode(mToken, mode, mUserId); 3588 } catch (RemoteException e) { 3589 throw e.rethrowFromSystemServer(); 3590 } 3591 } 3592 3593 /** 3594 * Enable/disable position tracking. 3595 * 3596 * @param enable {@code true} to enable tracking, {@code false} otherwise. 3597 */ 3598 void timeShiftEnablePositionTracking(boolean enable) { 3599 if (mToken == null) { 3600 Log.w(TAG, "The session has been already released"); 3601 return; 3602 } 3603 try { 3604 mService.timeShiftEnablePositionTracking(mToken, enable, mUserId); 3605 } catch (RemoteException e) { 3606 throw e.rethrowFromSystemServer(); 3607 } 3608 } 3609 3610 void stopPlayback(int mode) { 3611 if (mToken == null) { 3612 Log.w(TAG, "The session has been already released"); 3613 return; 3614 } 3615 try { 3616 mService.stopPlayback(mToken, mode, mUserId); 3617 } catch (RemoteException e) { 3618 throw e.rethrowFromSystemServer(); 3619 } 3620 } 3621 3622 void resumePlayback() { 3623 if (mToken == null) { 3624 Log.w(TAG, "The session has been already released"); 3625 return; 3626 } 3627 try { 3628 mService.resumePlayback(mToken, mUserId); 3629 } catch (RemoteException e) { 3630 throw e.rethrowFromSystemServer(); 3631 } 3632 } 3633 3634 void setVideoFrozen(boolean isFrozen) { 3635 if (mToken == null) { 3636 Log.w(TAG, "The session has been already released"); 3637 return; 3638 } 3639 try { 3640 mService.setVideoFrozen(mToken, isFrozen, mUserId); 3641 } catch (RemoteException e) { 3642 throw e.rethrowFromSystemServer(); 3643 } 3644 } 3645 3646 /** 3647 * Sends TV messages to the service for testing purposes 3648 */ 3649 public void notifyTvMessage(int type, Bundle data) { 3650 try { 3651 mService.notifyTvMessage(mToken, type, data, mUserId); 3652 } catch (RemoteException e) { 3653 throw e.rethrowFromSystemServer(); 3654 } 3655 } 3656 3657 /** 3658 * Sets whether the TV message of the specific type should be enabled. 3659 */ 3660 public void setTvMessageEnabled(int type, boolean enabled) { 3661 try { 3662 mService.setTvMessageEnabled(mToken, type, enabled, mUserId); 3663 } catch (RemoteException e) { 3664 throw e.rethrowFromSystemServer(); 3665 } 3666 } 3667 3668 /** 3669 * Starts TV program recording in the current recording session. 3670 * 3671 * @param programUri The URI for the TV program to record as a hint, built by 3672 * {@link TvContract#buildProgramUri(long)}. Can be {@code null}. 3673 */ 3674 void startRecording(@Nullable Uri programUri) { 3675 startRecording(programUri, null); 3676 } 3677 3678 /** 3679 * Starts TV program recording in the current recording session. 3680 * 3681 * @param programUri The URI for the TV program to record as a hint, built by 3682 * {@link TvContract#buildProgramUri(long)}. Can be {@code null}. 3683 * @param params A set of extra parameters which might be handled with this event. 3684 */ 3685 void startRecording(@Nullable Uri programUri, @Nullable Bundle params) { 3686 if (mToken == null) { 3687 Log.w(TAG, "The session has been already released"); 3688 return; 3689 } 3690 try { 3691 mService.startRecording(mToken, programUri, params, mUserId); 3692 } catch (RemoteException e) { 3693 throw e.rethrowFromSystemServer(); 3694 } 3695 } 3696 3697 /** 3698 * Stops TV program recording in the current recording session. 3699 */ 3700 void stopRecording() { 3701 if (mToken == null) { 3702 Log.w(TAG, "The session has been already released"); 3703 return; 3704 } 3705 try { 3706 mService.stopRecording(mToken, mUserId); 3707 } catch (RemoteException e) { 3708 throw e.rethrowFromSystemServer(); 3709 } 3710 } 3711 3712 /** 3713 * Pauses TV program recording in the current recording session. 3714 * 3715 * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped 3716 * name, i.e. prefixed with a package name you own, so that different developers 3717 * will not create conflicting keys. 3718 * {@link TvRecordingClient#pauseRecording(Bundle)}. 3719 */ 3720 void pauseRecording(@NonNull Bundle params) { 3721 if (mToken == null) { 3722 Log.w(TAG, "The session has been already released"); 3723 return; 3724 } 3725 try { 3726 mService.pauseRecording(mToken, params, mUserId); 3727 } catch (RemoteException e) { 3728 throw e.rethrowFromSystemServer(); 3729 } 3730 } 3731 3732 /** 3733 * Resumes TV program recording in the current recording session. 3734 * 3735 * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped 3736 * name, i.e. prefixed with a package name you own, so that different developers 3737 * will not create conflicting keys. 3738 * {@link TvRecordingClient#resumeRecording(Bundle)}. 3739 */ 3740 void resumeRecording(@NonNull Bundle params) { 3741 if (mToken == null) { 3742 Log.w(TAG, "The session has been already released"); 3743 return; 3744 } 3745 try { 3746 mService.resumeRecording(mToken, params, mUserId); 3747 } catch (RemoteException e) { 3748 throw e.rethrowFromSystemServer(); 3749 } 3750 } 3751 3752 /** 3753 * Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle) 3754 * TvInputService.Session.appPrivateCommand()} on the current TvView. 3755 * 3756 * @param action Name of the command to be performed. This <em>must</em> be a scoped name, 3757 * i.e. prefixed with a package name you own, so that different developers will 3758 * not create conflicting commands. 3759 * @param data Any data to include with the command. 3760 */ 3761 public void sendAppPrivateCommand(String action, Bundle data) { 3762 if (mToken == null) { 3763 Log.w(TAG, "The session has been already released"); 3764 return; 3765 } 3766 try { 3767 mService.sendAppPrivateCommand(mToken, action, data, mUserId); 3768 } catch (RemoteException e) { 3769 throw e.rethrowFromSystemServer(); 3770 } 3771 } 3772 3773 /** 3774 * Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView} 3775 * should be called whenever the layout of its containing view is changed. 3776 * {@link #removeOverlayView()} should be called to remove the overlay view. 3777 * Since a session can have only one overlay view, this method should be called only once 3778 * or it can be called again after calling {@link #removeOverlayView()}. 3779 * 3780 * @param view A view playing TV. 3781 * @param frame A position of the overlay view. 3782 * @throws IllegalStateException if {@code view} is not attached to a window. 3783 */ 3784 void createOverlayView(@NonNull View view, @NonNull Rect frame) { 3785 Preconditions.checkNotNull(view); 3786 Preconditions.checkNotNull(frame); 3787 if (view.getWindowToken() == null) { 3788 throw new IllegalStateException("view must be attached to a window"); 3789 } 3790 if (mToken == null) { 3791 Log.w(TAG, "The session has been already released"); 3792 return; 3793 } 3794 try { 3795 mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId); 3796 } catch (RemoteException e) { 3797 throw e.rethrowFromSystemServer(); 3798 } 3799 } 3800 3801 /** 3802 * Relayouts the current overlay view. 3803 * 3804 * @param frame A new position of the overlay view. 3805 */ 3806 void relayoutOverlayView(@NonNull Rect frame) { 3807 Preconditions.checkNotNull(frame); 3808 if (mToken == null) { 3809 Log.w(TAG, "The session has been already released"); 3810 return; 3811 } 3812 try { 3813 mService.relayoutOverlayView(mToken, frame, mUserId); 3814 } catch (RemoteException e) { 3815 throw e.rethrowFromSystemServer(); 3816 } 3817 } 3818 3819 /** 3820 * Removes the current overlay view. 3821 */ 3822 void removeOverlayView() { 3823 if (mToken == null) { 3824 Log.w(TAG, "The session has been already released"); 3825 return; 3826 } 3827 try { 3828 mService.removeOverlayView(mToken, mUserId); 3829 } catch (RemoteException e) { 3830 throw e.rethrowFromSystemServer(); 3831 } 3832 } 3833 3834 /** 3835 * Requests to unblock content blocked by parental controls. 3836 */ 3837 void unblockContent(@NonNull TvContentRating unblockedRating) { 3838 Preconditions.checkNotNull(unblockedRating); 3839 if (mToken == null) { 3840 Log.w(TAG, "The session has been already released"); 3841 return; 3842 } 3843 try { 3844 mService.unblockContent(mToken, unblockedRating.flattenToString(), mUserId); 3845 } catch (RemoteException e) { 3846 throw e.rethrowFromSystemServer(); 3847 } 3848 } 3849 3850 /** 3851 * Dispatches an input event to this session. 3852 * 3853 * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}. 3854 * @param token A token used to identify the input event later in the callback. 3855 * @param callback A callback used to receive the dispatch result. Cannot be {@code null}. 3856 * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be 3857 * {@code null}. 3858 * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns 3859 * {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns 3860 * {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will 3861 * be invoked later. 3862 * @hide 3863 */ 3864 public int dispatchInputEvent(@NonNull InputEvent event, Object token, 3865 @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) { 3866 Preconditions.checkNotNull(event); 3867 Preconditions.checkNotNull(callback); 3868 Preconditions.checkNotNull(handler); 3869 synchronized (mHandler) { 3870 if (mChannel == null) { 3871 return DISPATCH_NOT_HANDLED; 3872 } 3873 PendingEvent p = obtainPendingEventLocked(event, token, callback, handler); 3874 if (Looper.myLooper() == Looper.getMainLooper()) { 3875 // Already running on the main thread so we can send the event immediately. 3876 return sendInputEventOnMainLooperLocked(p); 3877 } 3878 3879 // Post the event to the main thread. 3880 Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p); 3881 msg.setAsynchronous(true); 3882 mHandler.sendMessage(msg); 3883 return DISPATCH_IN_PROGRESS; 3884 } 3885 } 3886 3887 /** 3888 * Callback that is invoked when an input event that was dispatched to this session has been 3889 * finished. 3890 * 3891 * @hide 3892 */ 3893 public interface FinishedInputEventCallback { 3894 /** 3895 * Called when the dispatched input event is finished. 3896 * 3897 * @param token A token passed to {@link #dispatchInputEvent}. 3898 * @param handled {@code true} if the dispatched input event was handled properly. 3899 * {@code false} otherwise. 3900 */ 3901 void onFinishedInputEvent(Object token, boolean handled); 3902 } 3903 3904 // Must be called on the main looper 3905 private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) { 3906 synchronized (mHandler) { 3907 int result = sendInputEventOnMainLooperLocked(p); 3908 if (result == DISPATCH_IN_PROGRESS) { 3909 return; 3910 } 3911 } 3912 3913 invokeFinishedInputEventCallback(p, false); 3914 } 3915 3916 private int sendInputEventOnMainLooperLocked(PendingEvent p) { 3917 if (mChannel != null) { 3918 if (mSender == null) { 3919 mSender = new TvInputEventSender(mChannel, mHandler.getLooper()); 3920 } 3921 3922 final InputEvent event = p.mEvent; 3923 final int seq = event.getSequenceNumber(); 3924 if (mSender.sendInputEvent(seq, event)) { 3925 mPendingEvents.put(seq, p); 3926 Message msg = mHandler.obtainMessage(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p); 3927 msg.setAsynchronous(true); 3928 mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT); 3929 return DISPATCH_IN_PROGRESS; 3930 } 3931 3932 Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:" 3933 + event); 3934 } 3935 return DISPATCH_NOT_HANDLED; 3936 } 3937 3938 void finishedInputEvent(int seq, boolean handled, boolean timeout) { 3939 final PendingEvent p; 3940 synchronized (mHandler) { 3941 int index = mPendingEvents.indexOfKey(seq); 3942 if (index < 0) { 3943 return; // spurious, event already finished or timed out 3944 } 3945 3946 p = mPendingEvents.valueAt(index); 3947 mPendingEvents.removeAt(index); 3948 3949 if (timeout) { 3950 Log.w(TAG, "Timeout waiting for session to handle input event after " 3951 + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken); 3952 } else { 3953 mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p); 3954 } 3955 } 3956 3957 invokeFinishedInputEventCallback(p, handled); 3958 } 3959 3960 // Assumes the event has already been removed from the queue. 3961 void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) { 3962 p.mHandled = handled; 3963 if (p.mEventHandler.getLooper().isCurrentThread()) { 3964 // Already running on the callback handler thread so we can send the callback 3965 // immediately. 3966 p.run(); 3967 } else { 3968 // Post the event to the callback handler thread. 3969 // In this case, the callback will be responsible for recycling the event. 3970 Message msg = Message.obtain(p.mEventHandler, p); 3971 msg.setAsynchronous(true); 3972 msg.sendToTarget(); 3973 } 3974 } 3975 3976 private void flushPendingEventsLocked() { 3977 mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT); 3978 3979 final int count = mPendingEvents.size(); 3980 for (int i = 0; i < count; i++) { 3981 int seq = mPendingEvents.keyAt(i); 3982 Message msg = mHandler.obtainMessage(InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0); 3983 msg.setAsynchronous(true); 3984 msg.sendToTarget(); 3985 } 3986 } 3987 3988 private PendingEvent obtainPendingEventLocked(InputEvent event, Object token, 3989 FinishedInputEventCallback callback, Handler handler) { 3990 PendingEvent p = mPendingEventPool.acquire(); 3991 if (p == null) { 3992 p = new PendingEvent(); 3993 } 3994 p.mEvent = event; 3995 p.mEventToken = token; 3996 p.mCallback = callback; 3997 p.mEventHandler = handler; 3998 return p; 3999 } 4000 4001 private void recyclePendingEventLocked(PendingEvent p) { 4002 p.recycle(); 4003 mPendingEventPool.release(p); 4004 } 4005 4006 IBinder getToken() { 4007 return mToken; 4008 } 4009 4010 private void releaseInternal() { 4011 mToken = null; 4012 synchronized (mHandler) { 4013 if (mChannel != null) { 4014 if (mSender != null) { 4015 flushPendingEventsLocked(); 4016 mSender.dispose(); 4017 mSender = null; 4018 } 4019 mChannel.dispose(); 4020 mChannel = null; 4021 } 4022 } 4023 synchronized (mSessionCallbackRecordMap) { 4024 mSessionCallbackRecordMap.delete(mSeq); 4025 } 4026 } 4027 4028 public void requestBroadcastInfo(BroadcastInfoRequest request) { 4029 if (mToken == null) { 4030 Log.w(TAG, "The session has been already released"); 4031 return; 4032 } 4033 try { 4034 mService.requestBroadcastInfo(mToken, request, mUserId); 4035 } catch (RemoteException e) { 4036 throw e.rethrowFromSystemServer(); 4037 } 4038 } 4039 4040 /** 4041 * Removes broadcast info. 4042 * @param requestId the corresponding request ID sent from 4043 * {@link #requestBroadcastInfo(android.media.tv.BroadcastInfoRequest)} 4044 */ 4045 public void removeBroadcastInfo(int requestId) { 4046 if (mToken == null) { 4047 Log.w(TAG, "The session has been already released"); 4048 return; 4049 } 4050 try { 4051 mService.removeBroadcastInfo(mToken, requestId, mUserId); 4052 } catch (RemoteException e) { 4053 throw e.rethrowFromSystemServer(); 4054 } 4055 } 4056 4057 public void requestAd(AdRequest request) { 4058 if (mToken == null) { 4059 Log.w(TAG, "The session has been already released"); 4060 return; 4061 } 4062 try { 4063 mService.requestAd(mToken, request, mUserId); 4064 } catch (RemoteException e) { 4065 throw e.rethrowFromSystemServer(); 4066 } 4067 } 4068 4069 /** 4070 * Notifies when the advertisement buffer is filled and ready to be read. 4071 */ 4072 public void notifyAdBufferReady(AdBuffer buffer) { 4073 if (mToken == null) { 4074 Log.w(TAG, "The session has been already released"); 4075 return; 4076 } 4077 try { 4078 mService.notifyAdBufferReady(mToken, buffer, mUserId); 4079 } catch (RemoteException e) { 4080 throw e.rethrowFromSystemServer(); 4081 } finally { 4082 if (buffer != null) { 4083 buffer.getSharedMemory().close(); 4084 } 4085 } 4086 } 4087 4088 /** 4089 * Notifies data from session of linked TvAdService. 4090 */ 4091 public void notifyTvAdSessionData(String type, Bundle data) { 4092 if (mToken == null) { 4093 Log.w(TAG, "The session has been already released"); 4094 return; 4095 } 4096 try { 4097 mService.notifyTvAdSessionData(mToken, type, data, mUserId); 4098 } catch (RemoteException e) { 4099 throw e.rethrowFromSystemServer(); 4100 } 4101 } 4102 4103 private final class InputEventHandler extends Handler { 4104 public static final int MSG_SEND_INPUT_EVENT = 1; 4105 public static final int MSG_TIMEOUT_INPUT_EVENT = 2; 4106 public static final int MSG_FLUSH_INPUT_EVENT = 3; 4107 4108 InputEventHandler(Looper looper) { 4109 super(looper, null, true); 4110 } 4111 4112 @Override 4113 public void handleMessage(Message msg) { 4114 switch (msg.what) { 4115 case MSG_SEND_INPUT_EVENT: { 4116 sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj); 4117 return; 4118 } 4119 case MSG_TIMEOUT_INPUT_EVENT: { 4120 finishedInputEvent(msg.arg1, false, true); 4121 return; 4122 } 4123 case MSG_FLUSH_INPUT_EVENT: { 4124 finishedInputEvent(msg.arg1, false, false); 4125 return; 4126 } 4127 } 4128 } 4129 } 4130 4131 private final class TvInputEventSender extends InputEventSender { 4132 public TvInputEventSender(InputChannel inputChannel, Looper looper) { 4133 super(inputChannel, looper); 4134 } 4135 4136 @Override 4137 public void onInputEventFinished(int seq, boolean handled) { 4138 finishedInputEvent(seq, handled, false); 4139 } 4140 } 4141 4142 private final class PendingEvent implements Runnable { 4143 public InputEvent mEvent; 4144 public Object mEventToken; 4145 public FinishedInputEventCallback mCallback; 4146 public Handler mEventHandler; 4147 public boolean mHandled; 4148 4149 public void recycle() { 4150 mEvent = null; 4151 mEventToken = null; 4152 mCallback = null; 4153 mEventHandler = null; 4154 mHandled = false; 4155 } 4156 4157 @Override 4158 public void run() { 4159 mCallback.onFinishedInputEvent(mEventToken, mHandled); 4160 4161 synchronized (mEventHandler) { 4162 recyclePendingEventLocked(this); 4163 } 4164 } 4165 } 4166 } 4167 4168 /** 4169 * The Hardware provides the per-hardware functionality of TV hardware. 4170 * 4171 * <p>TV hardware is physical hardware attached to the Android device; for example, HDMI ports, 4172 * Component/Composite ports, etc. Specifically, logical devices such as HDMI CEC logical 4173 * devices don't fall into this category. 4174 * 4175 * @hide 4176 */ 4177 @SystemApi 4178 public final static class Hardware { 4179 private final ITvInputHardware mInterface; 4180 4181 private Hardware(ITvInputHardware hardwareInterface) { 4182 mInterface = hardwareInterface; 4183 } 4184 4185 private ITvInputHardware getInterface() { 4186 return mInterface; 4187 } 4188 4189 public boolean setSurface(Surface surface, TvStreamConfig config) { 4190 try { 4191 return mInterface.setSurface(surface, config); 4192 } catch (RemoteException e) { 4193 throw new RuntimeException(e); 4194 } 4195 } 4196 4197 public void setStreamVolume(float volume) { 4198 try { 4199 mInterface.setStreamVolume(volume); 4200 } catch (RemoteException e) { 4201 throw new RuntimeException(e); 4202 } 4203 } 4204 4205 /** @removed */ 4206 @SystemApi 4207 public boolean dispatchKeyEventToHdmi(KeyEvent event) { 4208 return false; 4209 } 4210 4211 /** 4212 * Override default audio sink from audio policy. 4213 * 4214 * @param audioType device type of the audio sink to override with. 4215 * @param audioAddress device address of the audio sink to override with. 4216 * @param samplingRate desired sampling rate. Use default when it's 0. 4217 * @param channelMask desired channel mask. Use default when it's 4218 * AudioFormat.CHANNEL_OUT_DEFAULT. 4219 * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT. 4220 */ 4221 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate, 4222 int channelMask, int format) { 4223 try { 4224 mInterface.overrideAudioSink(audioType, audioAddress, samplingRate, channelMask, 4225 format); 4226 } catch (RemoteException e) { 4227 throw new RuntimeException(e); 4228 } 4229 } 4230 4231 /** 4232 * Override default audio sink from audio policy. 4233 * 4234 * @param device {@link android.media.AudioDeviceInfo} to use. 4235 * @param samplingRate desired sampling rate. Use default when it's 0. 4236 * @param channelMask desired channel mask. Use default when it's 4237 * AudioFormat.CHANNEL_OUT_DEFAULT. 4238 * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT. 4239 */ 4240 public void overrideAudioSink(@NonNull AudioDeviceInfo device, 4241 @IntRange(from = 0) int samplingRate, 4242 int channelMask, @Encoding int format) { 4243 Objects.requireNonNull(device); 4244 try { 4245 mInterface.overrideAudioSink( 4246 AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()), 4247 device.getAddress(), samplingRate, channelMask, format); 4248 } catch (RemoteException e) { 4249 throw new RuntimeException(e); 4250 } 4251 } 4252 } 4253 } 4254