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