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