1 /* 2 * Copyright 2021 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.interactive; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemService; 24 import android.content.Context; 25 import android.graphics.Rect; 26 import android.media.PlaybackParams; 27 import android.media.tv.AdBuffer; 28 import android.media.tv.AdRequest; 29 import android.media.tv.AdResponse; 30 import android.media.tv.BroadcastInfoRequest; 31 import android.media.tv.BroadcastInfoResponse; 32 import android.media.tv.TvContentRating; 33 import android.media.tv.TvInputManager; 34 import android.media.tv.TvRecordingInfo; 35 import android.media.tv.TvTrackInfo; 36 import android.net.Uri; 37 import android.net.http.SslCertificate; 38 import android.os.Bundle; 39 import android.os.Handler; 40 import android.os.IBinder; 41 import android.os.Looper; 42 import android.os.Message; 43 import android.os.RemoteException; 44 import android.util.Log; 45 import android.util.Pools; 46 import android.util.SparseArray; 47 import android.view.InputChannel; 48 import android.view.InputEvent; 49 import android.view.InputEventSender; 50 import android.view.Surface; 51 import android.view.View; 52 53 import com.android.internal.util.Preconditions; 54 55 import java.lang.annotation.Retention; 56 import java.lang.annotation.RetentionPolicy; 57 import java.util.ArrayList; 58 import java.util.Iterator; 59 import java.util.List; 60 import java.util.concurrent.Executor; 61 62 /** 63 * Central system API to the overall TV interactive application framework (TIAF) architecture, which 64 * arbitrates interaction between Android applications and TV interactive apps. 65 */ 66 @SystemService(Context.TV_INTERACTIVE_APP_SERVICE) 67 public final class TvInteractiveAppManager { 68 // TODO: cleanup and unhide public APIs 69 private static final String TAG = "TvInteractiveAppManager"; 70 71 /** @hide */ 72 @Retention(RetentionPolicy.SOURCE) 73 @IntDef(flag = false, prefix = "SERVICE_STATE_", value = { 74 SERVICE_STATE_UNREALIZED, 75 SERVICE_STATE_PREPARING, 76 SERVICE_STATE_READY, 77 SERVICE_STATE_ERROR}) 78 public @interface ServiceState {} 79 80 /** 81 * Unrealized state of interactive app service. 82 */ 83 public static final int SERVICE_STATE_UNREALIZED = 1; 84 /** 85 * Preparing state of interactive app service. 86 */ 87 public static final int SERVICE_STATE_PREPARING = 2; 88 /** 89 * Ready state of interactive app service. 90 * 91 * <p>In this state, the interactive app service is ready, and interactive apps can be started. 92 * 93 * @see TvInteractiveAppView#startInteractiveApp() 94 */ 95 public static final int SERVICE_STATE_READY = 3; 96 /** 97 * Error state of interactive app service. 98 */ 99 public static final int SERVICE_STATE_ERROR = 4; 100 101 102 /** @hide */ 103 @Retention(RetentionPolicy.SOURCE) 104 @IntDef(flag = false, prefix = "INTERACTIVE_APP_STATE_", value = { 105 INTERACTIVE_APP_STATE_STOPPED, 106 INTERACTIVE_APP_STATE_RUNNING, 107 INTERACTIVE_APP_STATE_ERROR}) 108 public @interface InteractiveAppState {} 109 110 /** 111 * Stopped (or not started) state of interactive application. 112 */ 113 public static final int INTERACTIVE_APP_STATE_STOPPED = 1; 114 /** 115 * Running state of interactive application. 116 */ 117 public static final int INTERACTIVE_APP_STATE_RUNNING = 2; 118 /** 119 * Error state of interactive application. 120 */ 121 public static final int INTERACTIVE_APP_STATE_ERROR = 3; 122 123 124 /** @hide */ 125 @Retention(RetentionPolicy.SOURCE) 126 @IntDef(flag = false, prefix = "ERROR_", value = { 127 ERROR_NONE, 128 ERROR_UNKNOWN, 129 ERROR_NOT_SUPPORTED, 130 ERROR_WEAK_SIGNAL, 131 ERROR_RESOURCE_UNAVAILABLE, 132 ERROR_BLOCKED, 133 ERROR_ENCRYPTED, 134 ERROR_UNKNOWN_CHANNEL, 135 }) 136 public @interface ErrorCode {} 137 138 /** 139 * No error. 140 */ 141 public static final int ERROR_NONE = 0; 142 /** 143 * Unknown error code. 144 */ 145 public static final int ERROR_UNKNOWN = 1; 146 /** 147 * Error code for an unsupported channel. 148 */ 149 public static final int ERROR_NOT_SUPPORTED = 2; 150 /** 151 * Error code for weak signal. 152 */ 153 public static final int ERROR_WEAK_SIGNAL = 3; 154 /** 155 * Error code when resource (e.g. tuner) is unavailable. 156 */ 157 public static final int ERROR_RESOURCE_UNAVAILABLE = 4; 158 /** 159 * Error code for blocked contents. 160 */ 161 public static final int ERROR_BLOCKED = 5; 162 /** 163 * Error code when the key or module is missing for the encrypted channel. 164 */ 165 public static final int ERROR_ENCRYPTED = 6; 166 /** 167 * Error code when the current channel is an unknown channel. 168 */ 169 public static final int ERROR_UNKNOWN_CHANNEL = 7; 170 171 /** @hide */ 172 @Retention(RetentionPolicy.SOURCE) 173 @IntDef(flag = false, prefix = "TELETEXT_APP_STATE_", value = { 174 TELETEXT_APP_STATE_SHOW, 175 TELETEXT_APP_STATE_HIDE, 176 TELETEXT_APP_STATE_ERROR}) 177 public @interface TeletextAppState {} 178 179 /** 180 * State of Teletext app: show 181 */ 182 public static final int TELETEXT_APP_STATE_SHOW = 1; 183 /** 184 * State of Teletext app: hide 185 */ 186 public static final int TELETEXT_APP_STATE_HIDE = 2; 187 /** 188 * State of Teletext app: error 189 */ 190 public static final int TELETEXT_APP_STATE_ERROR = 3; 191 192 /** 193 * Key for package name in app link. 194 * <p>Type: String 195 * 196 * @see #sendAppLinkCommand(String, Bundle) 197 */ 198 public static final String APP_LINK_KEY_PACKAGE_NAME = "package_name"; 199 200 /** 201 * Key for class name in app link. 202 * <p>Type: String 203 * 204 * @see #sendAppLinkCommand(String, Bundle) 205 */ 206 public static final String APP_LINK_KEY_CLASS_NAME = "class_name"; 207 208 /** 209 * Key for command type in app link command. 210 * <p>Type: String 211 * 212 * @see #sendAppLinkCommand(String, Bundle) 213 */ 214 public static final String APP_LINK_KEY_COMMAND_TYPE = "command_type"; 215 216 /** 217 * Key for service ID in app link command. 218 * <p>Type: String 219 * 220 * @see #sendAppLinkCommand(String, Bundle) 221 */ 222 public static final String APP_LINK_KEY_SERVICE_ID = "service_id"; 223 224 /** 225 * Key for back URI in app link command. 226 * <p>Type: String 227 * 228 * @see #sendAppLinkCommand(String, Bundle) 229 */ 230 public static final String APP_LINK_KEY_BACK_URI = "back_uri"; 231 232 /** 233 * Broadcast intent action to send app command to TV app. 234 * 235 * @see #sendAppLinkCommand(String, Bundle) 236 */ 237 public static final String ACTION_APP_LINK_COMMAND = 238 "android.media.tv.interactive.action.APP_LINK_COMMAND"; 239 240 /** 241 * Intent key for TV input ID. It's used to send app command to TV app. 242 * <p>Type: String 243 * 244 * @see #sendAppLinkCommand(String, Bundle) 245 * @see #ACTION_APP_LINK_COMMAND 246 */ 247 public static final String INTENT_KEY_TV_INPUT_ID = "tv_input_id"; 248 249 /** 250 * Intent key for TV interactive app ID. It's used to send app command to TV app. 251 * <p>Type: String 252 * 253 * @see #sendAppLinkCommand(String, Bundle) 254 * @see #ACTION_APP_LINK_COMMAND 255 * @see TvInteractiveAppServiceInfo#getId() 256 */ 257 public static final String INTENT_KEY_INTERACTIVE_APP_SERVICE_ID = "interactive_app_id"; 258 259 /** 260 * Intent key for TV channel URI. It's used to send app command to TV app. 261 * <p>Type: android.net.Uri 262 * 263 * @see #sendAppLinkCommand(String, Bundle) 264 * @see #ACTION_APP_LINK_COMMAND 265 */ 266 public static final String INTENT_KEY_CHANNEL_URI = "channel_uri"; 267 268 /** 269 * Intent key for broadcast-independent(BI) interactive app type. It's used to send app command 270 * to TV app. 271 * <p>Type: int 272 * 273 * @see #sendAppLinkCommand(String, Bundle) 274 * @see #ACTION_APP_LINK_COMMAND 275 * @see android.media.tv.interactive.TvInteractiveAppServiceInfo#getSupportedTypes() 276 * @see android.media.tv.interactive.TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) 277 */ 278 public static final String INTENT_KEY_BI_INTERACTIVE_APP_TYPE = "bi_interactive_app_type"; 279 280 /** 281 * Intent key for broadcast-independent(BI) interactive app URI. It's used to send app command 282 * to TV app. 283 * <p>Type: android.net.Uri 284 * 285 * @see #sendAppLinkCommand(String, Bundle) 286 * @see #ACTION_APP_LINK_COMMAND 287 * @see android.media.tv.interactive.TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) 288 */ 289 public static final String INTENT_KEY_BI_INTERACTIVE_APP_URI = "bi_interactive_app_uri"; 290 291 /** 292 * Intent key for command type. It's used to send app command to TV app. The value of this key 293 * could vary according to TV apps. 294 * <p>Type: String 295 * 296 * @see #sendAppLinkCommand(String, Bundle) 297 * @see #ACTION_APP_LINK_COMMAND 298 */ 299 public static final String INTENT_KEY_COMMAND_TYPE = "command_type"; 300 301 private final ITvInteractiveAppManager mService; 302 private final int mUserId; 303 304 // A mapping from the sequence number of a session to its SessionCallbackRecord. 305 private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap = 306 new SparseArray<>(); 307 308 // @GuardedBy("mLock") 309 private final List<TvInteractiveAppCallbackRecord> mCallbackRecords = new ArrayList<>(); 310 311 // A sequence number for the next session to be created. Should be protected by a lock 312 // {@code mSessionCallbackRecordMap}. 313 private int mNextSeq; 314 315 private final Object mLock = new Object(); 316 317 private final ITvInteractiveAppClient mClient; 318 319 /** @hide */ TvInteractiveAppManager(ITvInteractiveAppManager service, int userId)320 public TvInteractiveAppManager(ITvInteractiveAppManager service, int userId) { 321 mService = service; 322 mUserId = userId; 323 mClient = new ITvInteractiveAppClient.Stub() { 324 @Override 325 public void onSessionCreated(String iAppServiceId, IBinder token, InputChannel channel, 326 int seq) { 327 synchronized (mSessionCallbackRecordMap) { 328 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 329 if (record == null) { 330 Log.e(TAG, "Callback not found for " + token); 331 return; 332 } 333 Session session = null; 334 if (token != null) { 335 session = new Session(token, channel, mService, mUserId, seq, 336 mSessionCallbackRecordMap); 337 } else { 338 mSessionCallbackRecordMap.delete(seq); 339 } 340 record.postSessionCreated(session); 341 } 342 } 343 344 @Override 345 public void onSessionReleased(int seq) { 346 synchronized (mSessionCallbackRecordMap) { 347 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 348 mSessionCallbackRecordMap.delete(seq); 349 if (record == null) { 350 Log.e(TAG, "Callback not found for seq:" + seq); 351 return; 352 } 353 record.mSession.releaseInternal(); 354 record.postSessionReleased(); 355 } 356 } 357 358 @Override 359 public void onLayoutSurface(int left, int top, int right, int bottom, int seq) { 360 synchronized (mSessionCallbackRecordMap) { 361 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 362 if (record == null) { 363 Log.e(TAG, "Callback not found for seq " + seq); 364 return; 365 } 366 record.postLayoutSurface(left, top, right, bottom); 367 } 368 } 369 370 @Override 371 public void onBroadcastInfoRequest(BroadcastInfoRequest request, int seq) { 372 synchronized (mSessionCallbackRecordMap) { 373 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 374 if (record == null) { 375 Log.e(TAG, "Callback not found for seq " + seq); 376 return; 377 } 378 record.postBroadcastInfoRequest(request); 379 } 380 } 381 382 @Override 383 public void onRemoveBroadcastInfo(int requestId, int seq) { 384 synchronized (mSessionCallbackRecordMap) { 385 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 386 if (record == null) { 387 Log.e(TAG, "Callback not found for seq " + seq); 388 return; 389 } 390 record.postRemoveBroadcastInfo(requestId); 391 } 392 } 393 394 @Override 395 public void onCommandRequest( 396 @TvInteractiveAppService.PlaybackCommandType String cmdType, 397 Bundle parameters, 398 int seq) { 399 synchronized (mSessionCallbackRecordMap) { 400 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 401 if (record == null) { 402 Log.e(TAG, "Callback not found for seq " + seq); 403 return; 404 } 405 record.postCommandRequest(cmdType, parameters); 406 } 407 } 408 409 @Override 410 public void onTimeShiftCommandRequest( 411 @TvInteractiveAppService.TimeShiftCommandType String cmdType, 412 Bundle parameters, 413 int seq) { 414 synchronized (mSessionCallbackRecordMap) { 415 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 416 if (record == null) { 417 Log.e(TAG, "Callback not found for seq " + seq); 418 return; 419 } 420 record.postTimeShiftCommandRequest(cmdType, parameters); 421 } 422 } 423 424 @Override 425 public void onSetVideoBounds(Rect rect, int seq) { 426 synchronized (mSessionCallbackRecordMap) { 427 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 428 if (record == null) { 429 Log.e(TAG, "Callback not found for seq " + seq); 430 return; 431 } 432 record.postSetVideoBounds(rect); 433 } 434 } 435 436 @Override 437 public void onAdRequest(AdRequest request, int seq) { 438 synchronized (mSessionCallbackRecordMap) { 439 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 440 if (record == null) { 441 Log.e(TAG, "Callback not found for seq " + seq); 442 return; 443 } 444 record.postAdRequest(request); 445 } 446 } 447 448 @Override 449 public void onRequestCurrentVideoBounds(int seq) { 450 synchronized (mSessionCallbackRecordMap) { 451 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 452 if (record == null) { 453 Log.e(TAG, "Callback not found for seq " + seq); 454 return; 455 } 456 record.postRequestCurrentVideoBounds(); 457 } 458 } 459 460 @Override 461 public void onRequestCurrentChannelUri(int seq) { 462 synchronized (mSessionCallbackRecordMap) { 463 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 464 if (record == null) { 465 Log.e(TAG, "Callback not found for seq " + seq); 466 return; 467 } 468 record.postRequestCurrentChannelUri(); 469 } 470 } 471 472 @Override 473 public void onRequestCurrentChannelLcn(int seq) { 474 synchronized (mSessionCallbackRecordMap) { 475 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 476 if (record == null) { 477 Log.e(TAG, "Callback not found for seq " + seq); 478 return; 479 } 480 record.postRequestCurrentChannelLcn(); 481 } 482 } 483 484 @Override 485 public void onRequestStreamVolume(int seq) { 486 synchronized (mSessionCallbackRecordMap) { 487 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 488 if (record == null) { 489 Log.e(TAG, "Callback not found for seq " + seq); 490 return; 491 } 492 record.postRequestStreamVolume(); 493 } 494 } 495 496 @Override 497 public void onRequestTrackInfoList(int seq) { 498 synchronized (mSessionCallbackRecordMap) { 499 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 500 if (record == null) { 501 Log.e(TAG, "Callback not found for seq " + seq); 502 return; 503 } 504 record.postRequestTrackInfoList(); 505 } 506 } 507 508 @Override 509 public void onRequestSelectedTrackInfo(int seq) { 510 synchronized (mSessionCallbackRecordMap) { 511 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 512 if (record == null) { 513 Log.e(TAG, "Callback not found for seq " + seq); 514 return; 515 } 516 record.postRequestSelectedTrackInfo(); 517 } 518 } 519 520 @Override 521 public void onRequestCurrentTvInputId(int seq) { 522 synchronized (mSessionCallbackRecordMap) { 523 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 524 if (record == null) { 525 Log.e(TAG, "Callback not found for seq " + seq); 526 return; 527 } 528 record.postRequestCurrentTvInputId(); 529 } 530 } 531 532 @Override 533 public void onRequestTimeShiftMode(int seq) { 534 synchronized (mSessionCallbackRecordMap) { 535 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 536 if (record == null) { 537 Log.e(TAG, "Callback not found for seq " + seq); 538 return; 539 } 540 record.postRequestTimeShiftMode(); 541 } 542 } 543 544 @Override 545 public void onRequestAvailableSpeeds(int seq) { 546 synchronized (mSessionCallbackRecordMap) { 547 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 548 if (record == null) { 549 Log.e(TAG, "Callback not found for seq " + seq); 550 return; 551 } 552 record.postRequestAvailableSpeeds(); 553 } 554 } 555 556 @Override 557 public void onRequestStartRecording(String requestId, Uri programUri, int seq) { 558 synchronized (mSessionCallbackRecordMap) { 559 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 560 if (record == null) { 561 Log.e(TAG, "Callback not found for seq " + seq); 562 return; 563 } 564 record.postRequestStartRecording(requestId, programUri); 565 } 566 } 567 568 @Override 569 public void onRequestStopRecording(String recordingId, int seq) { 570 synchronized (mSessionCallbackRecordMap) { 571 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 572 if (record == null) { 573 Log.e(TAG, "Callback not found for seq " + seq); 574 return; 575 } 576 record.postRequestStopRecording(recordingId); 577 } 578 } 579 580 @Override 581 public void onRequestScheduleRecording(String requestId, String inputId, Uri channelUri, 582 Uri programUri, Bundle params, int seq) { 583 synchronized (mSessionCallbackRecordMap) { 584 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 585 if (record == null) { 586 Log.e(TAG, "Callback not found for seq " + seq); 587 return; 588 } 589 record.postRequestScheduleRecording( 590 requestId, inputId, channelUri, programUri, params); 591 } 592 } 593 594 @Override 595 public void onRequestScheduleRecording2(String requestId, String inputId, 596 Uri channelUri, long startTime, long duration, int repeatDays, Bundle params, 597 int seq) { 598 synchronized (mSessionCallbackRecordMap) { 599 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 600 if (record == null) { 601 Log.e(TAG, "Callback not found for seq " + seq); 602 return; 603 } 604 record.postRequestScheduleRecording(requestId, inputId, channelUri, startTime, 605 duration, repeatDays, params); 606 } 607 } 608 609 @Override 610 public void onSetTvRecordingInfo(String recordingId, TvRecordingInfo recordingInfo, 611 int seq) { 612 synchronized (mSessionCallbackRecordMap) { 613 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 614 if (record == null) { 615 Log.e(TAG, "Callback not found for seq " + seq); 616 return; 617 } 618 record.postSetTvRecordingInfo(recordingId, recordingInfo); 619 } 620 } 621 622 @Override 623 public void onRequestTvRecordingInfo(String recordingId, int seq) { 624 synchronized (mSessionCallbackRecordMap) { 625 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 626 if (record == null) { 627 Log.e(TAG, "Callback not found for seq " + seq); 628 return; 629 } 630 record.postRequestTvRecordingInfo(recordingId); 631 } 632 } 633 634 @Override 635 public void onRequestTvRecordingInfoList(int type, int seq) { 636 synchronized (mSessionCallbackRecordMap) { 637 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 638 if (record == null) { 639 Log.e(TAG, "Callback not found for seq " + seq); 640 return; 641 } 642 record.postRequestTvRecordingInfoList(type); 643 } 644 } 645 646 @Override 647 public void onRequestSigning( 648 String id, String algorithm, String alias, byte[] data, int seq) { 649 synchronized (mSessionCallbackRecordMap) { 650 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 651 if (record == null) { 652 Log.e(TAG, "Callback not found for seq " + seq); 653 return; 654 } 655 record.postRequestSigning(id, algorithm, alias, data); 656 } 657 } 658 659 @Override 660 public void onRequestSigning2( 661 String id, String algorithm, String host, int port, byte[] data, int seq) { 662 synchronized (mSessionCallbackRecordMap) { 663 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 664 if (record == null) { 665 Log.e(TAG, "Callback not found for seq " + seq); 666 return; 667 } 668 record.postRequestSigning(id, algorithm, host, port, data); 669 } 670 } 671 672 @Override 673 public void onRequestCertificate(String host, int port, int seq) { 674 synchronized (mSessionCallbackRecordMap) { 675 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 676 if (record == null) { 677 Log.e(TAG, "Callback not found for seq " + seq); 678 return; 679 } 680 record.postRequestCertificate(host, port); 681 } 682 } 683 684 @Override 685 public void onSessionStateChanged(int state, int err, int seq) { 686 synchronized (mSessionCallbackRecordMap) { 687 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 688 if (record == null) { 689 Log.e(TAG, "Callback not found for seq " + seq); 690 return; 691 } 692 record.postSessionStateChanged(state, err); 693 } 694 } 695 696 @Override 697 public void onBiInteractiveAppCreated(Uri biIAppUri, String biIAppId, int seq) { 698 synchronized (mSessionCallbackRecordMap) { 699 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 700 if (record == null) { 701 Log.e(TAG, "Callback not found for seq " + seq); 702 return; 703 } 704 record.postBiInteractiveAppCreated(biIAppUri, biIAppId); 705 } 706 } 707 708 @Override 709 public void onTeletextAppStateChanged(int state, int seq) { 710 synchronized (mSessionCallbackRecordMap) { 711 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 712 if (record == null) { 713 Log.e(TAG, "Callback not found for seq " + seq); 714 return; 715 } 716 record.postTeletextAppStateChanged(state); 717 } 718 } 719 720 @Override 721 public void onAdBufferReady(AdBuffer buffer, int seq) { 722 synchronized (mSessionCallbackRecordMap) { 723 SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); 724 if (record == null) { 725 Log.e(TAG, "Callback not found for seq " + seq); 726 return; 727 } 728 record.postAdBufferReady(buffer); 729 } 730 } 731 }; 732 ITvInteractiveAppManagerCallback managerCallback = 733 new ITvInteractiveAppManagerCallback.Stub() { 734 @Override 735 public void onInteractiveAppServiceAdded(String iAppServiceId) { 736 synchronized (mLock) { 737 for (TvInteractiveAppCallbackRecord record : mCallbackRecords) { 738 record.postInteractiveAppServiceAdded(iAppServiceId); 739 } 740 } 741 } 742 743 @Override 744 public void onInteractiveAppServiceRemoved(String iAppServiceId) { 745 synchronized (mLock) { 746 for (TvInteractiveAppCallbackRecord record : mCallbackRecords) { 747 record.postInteractiveAppServiceRemoved(iAppServiceId); 748 } 749 } 750 } 751 752 @Override 753 public void onInteractiveAppServiceUpdated(String iAppServiceId) { 754 synchronized (mLock) { 755 for (TvInteractiveAppCallbackRecord record : mCallbackRecords) { 756 record.postInteractiveAppServiceUpdated(iAppServiceId); 757 } 758 } 759 } 760 761 @Override 762 public void onTvInteractiveAppServiceInfoUpdated(TvInteractiveAppServiceInfo iAppInfo) { 763 // TODO: add public API updateInteractiveAppInfo() 764 synchronized (mLock) { 765 for (TvInteractiveAppCallbackRecord record : mCallbackRecords) { 766 record.postTvInteractiveAppServiceInfoUpdated(iAppInfo); 767 } 768 } 769 } 770 771 @Override 772 public void onStateChanged(String iAppServiceId, int type, int state, int err) { 773 synchronized (mLock) { 774 for (TvInteractiveAppCallbackRecord record : mCallbackRecords) { 775 record.postStateChanged(iAppServiceId, type, state, err); 776 } 777 } 778 } 779 }; 780 try { 781 if (mService != null) { 782 mService.registerCallback(managerCallback, mUserId); 783 } 784 } catch (RemoteException e) { 785 throw e.rethrowFromSystemServer(); 786 } 787 } 788 789 /** 790 * Callback used to monitor status of the TV Interactive App. 791 */ 792 public abstract static class TvInteractiveAppCallback { 793 /** 794 * This is called when a TV Interactive App service is added to the system. 795 * 796 * <p>Normally it happens when the user installs a new TV Interactive App service package 797 * that implements {@link TvInteractiveAppService} interface. 798 * 799 * @param iAppServiceId The ID of the TV Interactive App service. 800 */ onInteractiveAppServiceAdded(@onNull String iAppServiceId)801 public void onInteractiveAppServiceAdded(@NonNull String iAppServiceId) { 802 } 803 804 /** 805 * This is called when a TV Interactive App service is removed from the system. 806 * 807 * <p>Normally it happens when the user uninstalls the previously installed TV Interactive 808 * App service package. 809 * 810 * @param iAppServiceId The ID of the TV Interactive App service. 811 */ onInteractiveAppServiceRemoved(@onNull String iAppServiceId)812 public void onInteractiveAppServiceRemoved(@NonNull String iAppServiceId) { 813 } 814 815 /** 816 * This is called when a TV Interactive App service is updated on the system. 817 * 818 * <p>Normally it happens when a previously installed TV Interactive App service package is 819 * re-installed or a newer version of the package exists becomes available/unavailable. 820 * 821 * @param iAppServiceId The ID of the TV Interactive App service. 822 */ onInteractiveAppServiceUpdated(@onNull String iAppServiceId)823 public void onInteractiveAppServiceUpdated(@NonNull String iAppServiceId) { 824 } 825 826 /** 827 * This is called when the information about an existing TV Interactive App service has been 828 * updated. 829 * 830 * <p>Because the system automatically creates a <code>TvInteractiveAppServiceInfo</code> 831 * object for each TV Interactive App service based on the information collected from the 832 * <code>AndroidManifest.xml</code>, this method is only called back when such information 833 * has changed dynamically. 834 * 835 * @param iAppInfo The <code>TvInteractiveAppServiceInfo</code> object that contains new 836 * information. 837 * @hide 838 */ onTvInteractiveAppServiceInfoUpdated( @onNull TvInteractiveAppServiceInfo iAppInfo)839 public void onTvInteractiveAppServiceInfoUpdated( 840 @NonNull TvInteractiveAppServiceInfo iAppInfo) { 841 } 842 843 /** 844 * This is called when the state of the interactive app service is changed. 845 * 846 * @param iAppServiceId The ID of the TV Interactive App service. 847 * @param type the interactive app type 848 * @param state the current state of the service of the given type 849 * @param err the error code for error state. {@link #ERROR_NONE} is used when the state is 850 * not {@link #SERVICE_STATE_ERROR}. 851 */ onTvInteractiveAppServiceStateChanged( @onNull String iAppServiceId, @TvInteractiveAppServiceInfo.InteractiveAppType int type, @ServiceState int state, @ErrorCode int err)852 public void onTvInteractiveAppServiceStateChanged( 853 @NonNull String iAppServiceId, 854 @TvInteractiveAppServiceInfo.InteractiveAppType int type, 855 @ServiceState int state, 856 @ErrorCode int err) { 857 } 858 } 859 860 private static final class TvInteractiveAppCallbackRecord { 861 private final TvInteractiveAppCallback mCallback; 862 private final Executor mExecutor; 863 TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Executor executor)864 TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Executor executor) { 865 mCallback = callback; 866 mExecutor = executor; 867 } 868 getCallback()869 public TvInteractiveAppCallback getCallback() { 870 return mCallback; 871 } 872 postInteractiveAppServiceAdded(final String iAppServiceId)873 public void postInteractiveAppServiceAdded(final String iAppServiceId) { 874 mExecutor.execute(new Runnable() { 875 @Override 876 public void run() { 877 mCallback.onInteractiveAppServiceAdded(iAppServiceId); 878 } 879 }); 880 } 881 postInteractiveAppServiceRemoved(final String iAppServiceId)882 public void postInteractiveAppServiceRemoved(final String iAppServiceId) { 883 mExecutor.execute(new Runnable() { 884 @Override 885 public void run() { 886 mCallback.onInteractiveAppServiceRemoved(iAppServiceId); 887 } 888 }); 889 } 890 postInteractiveAppServiceUpdated(final String iAppServiceId)891 public void postInteractiveAppServiceUpdated(final String iAppServiceId) { 892 mExecutor.execute(new Runnable() { 893 @Override 894 public void run() { 895 mCallback.onInteractiveAppServiceUpdated(iAppServiceId); 896 } 897 }); 898 } 899 postTvInteractiveAppServiceInfoUpdated( final TvInteractiveAppServiceInfo iAppInfo)900 public void postTvInteractiveAppServiceInfoUpdated( 901 final TvInteractiveAppServiceInfo iAppInfo) { 902 mExecutor.execute(new Runnable() { 903 @Override 904 public void run() { 905 mCallback.onTvInteractiveAppServiceInfoUpdated(iAppInfo); 906 } 907 }); 908 } 909 postStateChanged(String iAppServiceId, int type, int state, int err)910 public void postStateChanged(String iAppServiceId, int type, int state, int err) { 911 mExecutor.execute(new Runnable() { 912 @Override 913 public void run() { 914 mCallback.onTvInteractiveAppServiceStateChanged( 915 iAppServiceId, type, state, err); 916 } 917 }); 918 } 919 } 920 921 /** 922 * Creates a {@link Session} for a given TV interactive application. 923 * 924 * <p>The number of sessions that can be created at the same time is limited by the capability 925 * of the given interactive application. 926 * 927 * @param iAppServiceId The ID of the interactive application. 928 * @param type the type of the interactive application. 929 * @param callback A callback used to receive the created session. 930 * @param handler A {@link Handler} that the session creation will be delivered to. 931 * @hide 932 */ createSession(@onNull String iAppServiceId, int type, @NonNull final SessionCallback callback, @NonNull Handler handler)933 public void createSession(@NonNull String iAppServiceId, int type, 934 @NonNull final SessionCallback callback, @NonNull Handler handler) { 935 createSessionInternal(iAppServiceId, type, callback, handler); 936 } 937 createSessionInternal(String iAppServiceId, int type, SessionCallback callback, Handler handler)938 private void createSessionInternal(String iAppServiceId, int type, SessionCallback callback, 939 Handler handler) { 940 Preconditions.checkNotNull(iAppServiceId); 941 Preconditions.checkNotNull(callback); 942 Preconditions.checkNotNull(handler); 943 SessionCallbackRecord record = new SessionCallbackRecord(callback, handler); 944 synchronized (mSessionCallbackRecordMap) { 945 int seq = mNextSeq++; 946 mSessionCallbackRecordMap.put(seq, record); 947 try { 948 mService.createSession(mClient, iAppServiceId, type, seq, mUserId); 949 } catch (RemoteException e) { 950 throw e.rethrowFromSystemServer(); 951 } 952 } 953 } 954 955 /** 956 * Returns the complete list of TV Interactive App service on the system. 957 * 958 * @return List of {@link TvInteractiveAppServiceInfo} for each TV Interactive App service that 959 * describes its meta information. 960 */ 961 @NonNull getTvInteractiveAppServiceList()962 public List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList() { 963 try { 964 return mService.getTvInteractiveAppServiceList(mUserId); 965 } catch (RemoteException e) { 966 throw e.rethrowFromSystemServer(); 967 } 968 } 969 970 /** 971 * Returns a list of available app link information. 972 * 973 * <P>A package must declare its app link info in its manifest using meta-data tag, so the info 974 * can be detected by the system. 975 * 976 * @return List of {@link AppLinkInfo} for each package that deslares its app link information. 977 */ 978 @NonNull getAppLinkInfoList()979 public List<AppLinkInfo> getAppLinkInfoList() { 980 try { 981 return mService.getAppLinkInfoList(mUserId); 982 } catch (RemoteException e) { 983 throw e.rethrowFromSystemServer(); 984 } 985 } 986 987 /** 988 * Registers an Android application link info record which can be used to launch the specific 989 * Android application by TV interactive App RTE. 990 * 991 * @param tvIAppServiceId The ID of TV interactive service which the command to be sent to. The 992 * ID can be found in {@link TvInteractiveAppServiceInfo#getId()}. 993 * @param appLinkInfo The Android application link info record to be registered. 994 */ registerAppLinkInfo( @onNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo)995 public void registerAppLinkInfo( 996 @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) { 997 try { 998 mService.registerAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId); 999 } catch (RemoteException e) { 1000 throw e.rethrowFromSystemServer(); 1001 } 1002 } 1003 1004 /** 1005 * Unregisters an Android application link info record which can be used to launch the specific 1006 * Android application by TV interactive App RTE. 1007 * 1008 * @param tvIAppServiceId The ID of TV interactive service which the command to be sent to. The 1009 * ID can be found in {@link TvInteractiveAppServiceInfo#getId()}. 1010 * @param appLinkInfo The Android application link info record to be unregistered. 1011 */ unregisterAppLinkInfo( @onNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo)1012 public void unregisterAppLinkInfo( 1013 @NonNull String tvIAppServiceId, @NonNull AppLinkInfo appLinkInfo) { 1014 try { 1015 mService.unregisterAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId); 1016 } catch (RemoteException e) { 1017 throw e.rethrowFromSystemServer(); 1018 } 1019 } 1020 1021 /** 1022 * Sends app link command. 1023 * 1024 * @param tvIAppServiceId The ID of TV interactive service which the command to be sent to. The 1025 * ID can be found in {@link TvInteractiveAppServiceInfo#getId()}. 1026 * @param command The command to be sent. 1027 */ sendAppLinkCommand(@onNull String tvIAppServiceId, @NonNull Bundle command)1028 public void sendAppLinkCommand(@NonNull String tvIAppServiceId, @NonNull Bundle command) { 1029 try { 1030 mService.sendAppLinkCommand(tvIAppServiceId, command, mUserId); 1031 } catch (RemoteException e) { 1032 throw e.rethrowFromSystemServer(); 1033 } 1034 } 1035 1036 /** 1037 * Registers a {@link TvInteractiveAppCallback}. 1038 * 1039 * @param callback A callback used to monitor status of the TV Interactive App services. 1040 * @param executor A {@link Executor} that the status change will be delivered to. 1041 */ registerCallback( @allbackExecutor @onNull Executor executor, @NonNull TvInteractiveAppCallback callback)1042 public void registerCallback( 1043 @CallbackExecutor @NonNull Executor executor, 1044 @NonNull TvInteractiveAppCallback callback) { 1045 Preconditions.checkNotNull(callback); 1046 Preconditions.checkNotNull(executor); 1047 synchronized (mLock) { 1048 mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, executor)); 1049 } 1050 } 1051 1052 /** 1053 * Unregisters the existing {@link TvInteractiveAppCallback}. 1054 * 1055 * @param callback The existing callback to remove. 1056 */ unregisterCallback(@onNull final TvInteractiveAppCallback callback)1057 public void unregisterCallback(@NonNull final TvInteractiveAppCallback callback) { 1058 Preconditions.checkNotNull(callback); 1059 synchronized (mLock) { 1060 for (Iterator<TvInteractiveAppCallbackRecord> it = mCallbackRecords.iterator(); 1061 it.hasNext(); ) { 1062 TvInteractiveAppCallbackRecord record = it.next(); 1063 if (record.getCallback() == callback) { 1064 it.remove(); 1065 break; 1066 } 1067 } 1068 } 1069 } 1070 1071 /** 1072 * The Session provides the per-session functionality of interactive app. 1073 * @hide 1074 */ 1075 public static final class Session { 1076 static final int DISPATCH_IN_PROGRESS = -1; 1077 static final int DISPATCH_NOT_HANDLED = 0; 1078 static final int DISPATCH_HANDLED = 1; 1079 1080 private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500; 1081 1082 private final ITvInteractiveAppManager mService; 1083 private final int mUserId; 1084 private final int mSeq; 1085 private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap; 1086 1087 // For scheduling input event handling on the main thread. This also serves as a lock to 1088 // protect pending input events and the input channel. 1089 private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper()); 1090 1091 private TvInputManager.Session mInputSession; 1092 private final Pools.Pool<PendingEvent> mPendingEventPool = new Pools.SimplePool<>(20); 1093 private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20); 1094 1095 private IBinder mToken; 1096 private TvInputEventSender mSender; 1097 private InputChannel mInputChannel; 1098 Session(IBinder token, InputChannel channel, ITvInteractiveAppManager service, int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap)1099 private Session(IBinder token, InputChannel channel, ITvInteractiveAppManager service, 1100 int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) { 1101 mToken = token; 1102 mInputChannel = channel; 1103 mService = service; 1104 mUserId = userId; 1105 mSeq = seq; 1106 mSessionCallbackRecordMap = sessionCallbackRecordMap; 1107 } 1108 getInputSession()1109 public TvInputManager.Session getInputSession() { 1110 return mInputSession; 1111 } 1112 setInputSession(TvInputManager.Session inputSession)1113 public void setInputSession(TvInputManager.Session inputSession) { 1114 mInputSession = inputSession; 1115 } 1116 startInteractiveApp()1117 void startInteractiveApp() { 1118 if (mToken == null) { 1119 Log.w(TAG, "The session has been already released"); 1120 return; 1121 } 1122 try { 1123 mService.startInteractiveApp(mToken, mUserId); 1124 } catch (RemoteException e) { 1125 throw e.rethrowFromSystemServer(); 1126 } 1127 } 1128 stopInteractiveApp()1129 void stopInteractiveApp() { 1130 if (mToken == null) { 1131 Log.w(TAG, "The session has been already released"); 1132 return; 1133 } 1134 try { 1135 mService.stopInteractiveApp(mToken, mUserId); 1136 } catch (RemoteException e) { 1137 throw e.rethrowFromSystemServer(); 1138 } 1139 } 1140 resetInteractiveApp()1141 void resetInteractiveApp() { 1142 if (mToken == null) { 1143 Log.w(TAG, "The session has been already released"); 1144 return; 1145 } 1146 try { 1147 mService.resetInteractiveApp(mToken, mUserId); 1148 } catch (RemoteException e) { 1149 throw e.rethrowFromSystemServer(); 1150 } 1151 } 1152 createBiInteractiveApp(Uri biIAppUri, Bundle params)1153 void createBiInteractiveApp(Uri biIAppUri, Bundle params) { 1154 if (mToken == null) { 1155 Log.w(TAG, "The session has been already released"); 1156 return; 1157 } 1158 try { 1159 mService.createBiInteractiveApp(mToken, biIAppUri, params, mUserId); 1160 } catch (RemoteException e) { 1161 throw e.rethrowFromSystemServer(); 1162 } 1163 } 1164 destroyBiInteractiveApp(String biIAppId)1165 void destroyBiInteractiveApp(String biIAppId) { 1166 if (mToken == null) { 1167 Log.w(TAG, "The session has been already released"); 1168 return; 1169 } 1170 try { 1171 mService.destroyBiInteractiveApp(mToken, biIAppId, mUserId); 1172 } catch (RemoteException e) { 1173 throw e.rethrowFromSystemServer(); 1174 } 1175 } 1176 setTeletextAppEnabled(boolean enable)1177 void setTeletextAppEnabled(boolean enable) { 1178 if (mToken == null) { 1179 Log.w(TAG, "The session has been already released"); 1180 return; 1181 } 1182 try { 1183 mService.setTeletextAppEnabled(mToken, enable, mUserId); 1184 } catch (RemoteException e) { 1185 throw e.rethrowFromSystemServer(); 1186 } 1187 } 1188 sendCurrentVideoBounds(@onNull Rect bounds)1189 void sendCurrentVideoBounds(@NonNull Rect bounds) { 1190 if (mToken == null) { 1191 Log.w(TAG, "The session has been already released"); 1192 return; 1193 } 1194 try { 1195 mService.sendCurrentVideoBounds(mToken, bounds, mUserId); 1196 } catch (RemoteException e) { 1197 throw e.rethrowFromSystemServer(); 1198 } 1199 } 1200 sendCurrentChannelUri(@ullable Uri channelUri)1201 void sendCurrentChannelUri(@Nullable Uri channelUri) { 1202 if (mToken == null) { 1203 Log.w(TAG, "The session has been already released"); 1204 return; 1205 } 1206 try { 1207 mService.sendCurrentChannelUri(mToken, channelUri, mUserId); 1208 } catch (RemoteException e) { 1209 throw e.rethrowFromSystemServer(); 1210 } 1211 } 1212 sendCurrentChannelLcn(int lcn)1213 void sendCurrentChannelLcn(int lcn) { 1214 if (mToken == null) { 1215 Log.w(TAG, "The session has been already released"); 1216 return; 1217 } 1218 try { 1219 mService.sendCurrentChannelLcn(mToken, lcn, mUserId); 1220 } catch (RemoteException e) { 1221 throw e.rethrowFromSystemServer(); 1222 } 1223 } 1224 sendStreamVolume(float volume)1225 void sendStreamVolume(float volume) { 1226 if (mToken == null) { 1227 Log.w(TAG, "The session has been already released"); 1228 return; 1229 } 1230 try { 1231 mService.sendStreamVolume(mToken, volume, mUserId); 1232 } catch (RemoteException e) { 1233 throw e.rethrowFromSystemServer(); 1234 } 1235 } 1236 sendTrackInfoList(@onNull List<TvTrackInfo> tracks)1237 void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) { 1238 if (mToken == null) { 1239 Log.w(TAG, "The session has been already released"); 1240 return; 1241 } 1242 try { 1243 mService.sendTrackInfoList(mToken, tracks, mUserId); 1244 } catch (RemoteException e) { 1245 throw e.rethrowFromSystemServer(); 1246 } 1247 } 1248 sendSelectedTrackInfo(@onNull List<TvTrackInfo> tracks)1249 void sendSelectedTrackInfo(@NonNull List<TvTrackInfo> tracks) { 1250 if (mToken == null) { 1251 Log.w(TAG, "The session has been already released"); 1252 return; 1253 } 1254 try { 1255 mService.sendSelectedTrackInfo(mToken, tracks, mUserId); 1256 } catch (RemoteException e) { 1257 throw e.rethrowFromSystemServer(); 1258 } 1259 } 1260 sendCurrentTvInputId(@ullable String inputId)1261 void sendCurrentTvInputId(@Nullable String inputId) { 1262 if (mToken == null) { 1263 Log.w(TAG, "The session has been already released"); 1264 return; 1265 } 1266 try { 1267 mService.sendCurrentTvInputId(mToken, inputId, mUserId); 1268 } catch (RemoteException e) { 1269 throw e.rethrowFromSystemServer(); 1270 } 1271 } 1272 sendTimeShiftMode(int mode)1273 void sendTimeShiftMode(int mode) { 1274 if (mToken == null) { 1275 Log.w(TAG, "The session has been already released"); 1276 return; 1277 } 1278 try { 1279 mService.sendTimeShiftMode(mToken, mode, mUserId); 1280 } catch (RemoteException e) { 1281 throw e.rethrowFromSystemServer(); 1282 } 1283 } 1284 sendAvailableSpeeds(float[] speeds)1285 void sendAvailableSpeeds(float[] speeds) { 1286 if (mToken == null) { 1287 Log.w(TAG, "The session has been already released"); 1288 return; 1289 } 1290 try { 1291 mService.sendAvailableSpeeds(mToken, speeds, mUserId); 1292 } catch (RemoteException e) { 1293 throw e.rethrowFromSystemServer(); 1294 } 1295 } 1296 sendTvRecordingInfo(@ullable TvRecordingInfo recordingInfo)1297 void sendTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) { 1298 if (mToken == null) { 1299 Log.w(TAG, "The session has been already released"); 1300 return; 1301 } 1302 try { 1303 mService.sendTvRecordingInfo(mToken, recordingInfo, mUserId); 1304 } catch (RemoteException e) { 1305 throw e.rethrowFromSystemServer(); 1306 } 1307 } 1308 sendTvRecordingInfoList(@ullable List<TvRecordingInfo> recordingInfoList)1309 void sendTvRecordingInfoList(@Nullable List<TvRecordingInfo> recordingInfoList) { 1310 if (mToken == null) { 1311 Log.w(TAG, "The session has been already released"); 1312 return; 1313 } 1314 try { 1315 mService.sendTvRecordingInfoList(mToken, recordingInfoList, mUserId); 1316 } catch (RemoteException e) { 1317 throw e.rethrowFromSystemServer(); 1318 } 1319 } 1320 notifyRecordingStarted(String recordingId, String requestId)1321 void notifyRecordingStarted(String recordingId, String requestId) { 1322 if (mToken == null) { 1323 Log.w(TAG, "The session has been already released"); 1324 return; 1325 } 1326 try { 1327 mService.notifyRecordingStarted(mToken, recordingId, requestId, mUserId); 1328 } catch (RemoteException e) { 1329 throw e.rethrowFromSystemServer(); 1330 } 1331 } 1332 notifyRecordingStopped(String recordingId)1333 void notifyRecordingStopped(String recordingId) { 1334 if (mToken == null) { 1335 Log.w(TAG, "The session has been already released"); 1336 return; 1337 } 1338 try { 1339 mService.notifyRecordingStopped(mToken, recordingId, mUserId); 1340 } catch (RemoteException e) { 1341 throw e.rethrowFromSystemServer(); 1342 } 1343 } 1344 sendSigningResult(@onNull String signingId, @NonNull byte[] result)1345 void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) { 1346 if (mToken == null) { 1347 Log.w(TAG, "The session has been already released"); 1348 return; 1349 } 1350 try { 1351 mService.sendSigningResult(mToken, signingId, result, mUserId); 1352 } catch (RemoteException e) { 1353 throw e.rethrowFromSystemServer(); 1354 } 1355 } 1356 sendCertificate(@onNull String host, int port, @NonNull SslCertificate cert)1357 void sendCertificate(@NonNull String host, int port, @NonNull SslCertificate cert) { 1358 if (mToken == null) { 1359 Log.w(TAG, "The session has been already released"); 1360 return; 1361 } 1362 try { 1363 mService.sendCertificate(mToken, host, port, SslCertificate.saveState(cert), 1364 mUserId); 1365 } catch (RemoteException e) { 1366 throw e.rethrowFromSystemServer(); 1367 } 1368 } 1369 notifyError(@onNull String errMsg, @NonNull Bundle params)1370 void notifyError(@NonNull String errMsg, @NonNull Bundle params) { 1371 if (mToken == null) { 1372 Log.w(TAG, "The session has been already released"); 1373 return; 1374 } 1375 try { 1376 mService.notifyError(mToken, errMsg, params, mUserId); 1377 } catch (RemoteException e) { 1378 throw e.rethrowFromSystemServer(); 1379 } 1380 } 1381 notifyTimeShiftPlaybackParams(@onNull PlaybackParams params)1382 void notifyTimeShiftPlaybackParams(@NonNull PlaybackParams params) { 1383 if (mToken == null) { 1384 Log.w(TAG, "The session has been already released"); 1385 return; 1386 } 1387 try { 1388 mService.notifyTimeShiftPlaybackParams(mToken, params, mUserId); 1389 } catch (RemoteException e) { 1390 throw e.rethrowFromSystemServer(); 1391 } 1392 } 1393 notifyTimeShiftStatusChanged( @onNull String inputId, @TvInputManager.TimeShiftStatus int status)1394 void notifyTimeShiftStatusChanged( 1395 @NonNull String inputId, @TvInputManager.TimeShiftStatus int status) { 1396 if (mToken == null) { 1397 Log.w(TAG, "The session has been already released"); 1398 return; 1399 } 1400 try { 1401 mService.notifyTimeShiftStatusChanged(mToken, inputId, status, mUserId); 1402 } catch (RemoteException e) { 1403 throw e.rethrowFromSystemServer(); 1404 } 1405 } 1406 notifyTimeShiftStartPositionChanged(@onNull String inputId, long timeMs)1407 void notifyTimeShiftStartPositionChanged(@NonNull String inputId, long timeMs) { 1408 if (mToken == null) { 1409 Log.w(TAG, "The session has been already released"); 1410 return; 1411 } 1412 try { 1413 mService.notifyTimeShiftStartPositionChanged(mToken, inputId, timeMs, mUserId); 1414 } catch (RemoteException e) { 1415 throw e.rethrowFromSystemServer(); 1416 } 1417 } 1418 notifyTimeShiftCurrentPositionChanged(@onNull String inputId, long timeMs)1419 void notifyTimeShiftCurrentPositionChanged(@NonNull String inputId, long timeMs) { 1420 if (mToken == null) { 1421 Log.w(TAG, "The session has been already released"); 1422 return; 1423 } 1424 try { 1425 mService.notifyTimeShiftCurrentPositionChanged(mToken, inputId, timeMs, mUserId); 1426 } catch (RemoteException e) { 1427 throw e.rethrowFromSystemServer(); 1428 } 1429 } 1430 notifyRecordingConnectionFailed(@onNull String recordingId, @NonNull String inputId)1431 void notifyRecordingConnectionFailed(@NonNull String recordingId, @NonNull String inputId) { 1432 if (mToken == null) { 1433 Log.w(TAG, "The session has been already released"); 1434 return; 1435 } 1436 try { 1437 mService.notifyRecordingConnectionFailed(mToken, recordingId, inputId, mUserId); 1438 } catch (RemoteException e) { 1439 throw e.rethrowFromSystemServer(); 1440 } 1441 } 1442 notifyRecordingDisconnected(@onNull String recordingId, @NonNull String inputId)1443 void notifyRecordingDisconnected(@NonNull String recordingId, @NonNull String inputId) { 1444 if (mToken == null) { 1445 Log.w(TAG, "The session has been already released"); 1446 return; 1447 } 1448 try { 1449 mService.notifyRecordingDisconnected(mToken, recordingId, inputId, mUserId); 1450 } catch (RemoteException e) { 1451 throw e.rethrowFromSystemServer(); 1452 } 1453 } 1454 notifyRecordingTuned(@onNull String recordingId, @NonNull Uri channelUri)1455 void notifyRecordingTuned(@NonNull String recordingId, @NonNull Uri channelUri) { 1456 if (mToken == null) { 1457 Log.w(TAG, "The session has been already released"); 1458 return; 1459 } 1460 try { 1461 mService.notifyRecordingTuned(mToken, recordingId, channelUri, mUserId); 1462 } catch (RemoteException e) { 1463 throw e.rethrowFromSystemServer(); 1464 } 1465 } 1466 notifyRecordingError(@onNull String recordingId, int err)1467 void notifyRecordingError(@NonNull String recordingId, int err) { 1468 if (mToken == null) { 1469 Log.w(TAG, "The session has been already released"); 1470 return; 1471 } 1472 try { 1473 mService.notifyRecordingError(mToken, recordingId, err, mUserId); 1474 } catch (RemoteException e) { 1475 throw e.rethrowFromSystemServer(); 1476 } 1477 } 1478 notifyRecordingScheduled(@onNull String recordingId, @Nullable String requestId)1479 void notifyRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) { 1480 if (mToken == null) { 1481 Log.w(TAG, "The session has been already released"); 1482 return; 1483 } 1484 try { 1485 mService.notifyRecordingScheduled(mToken, recordingId, requestId, mUserId); 1486 } catch (RemoteException e) { 1487 throw e.rethrowFromSystemServer(); 1488 } 1489 } 1490 1491 /** 1492 * Sets the {@link android.view.Surface} for this session. 1493 * 1494 * @param surface A {@link android.view.Surface} used to render video. 1495 */ setSurface(Surface surface)1496 public void setSurface(Surface surface) { 1497 if (mToken == null) { 1498 Log.w(TAG, "The session has been already released"); 1499 return; 1500 } 1501 // surface can be null. 1502 try { 1503 mService.setSurface(mToken, surface, mUserId); 1504 } catch (RemoteException e) { 1505 throw e.rethrowFromSystemServer(); 1506 } 1507 } 1508 1509 /** 1510 * Creates a media view. Once the media view is created, {@link #relayoutMediaView} 1511 * should be called whenever the layout of its containing view is changed. 1512 * {@link #removeMediaView()} should be called to remove the media view. 1513 * Since a session can have only one media view, this method should be called only once 1514 * or it can be called again after calling {@link #removeMediaView()}. 1515 * 1516 * @param view A view for interactive app. 1517 * @param frame A position of the media view. 1518 * @throws IllegalStateException if {@code view} is not attached to a window. 1519 */ createMediaView(@onNull View view, @NonNull Rect frame)1520 void createMediaView(@NonNull View view, @NonNull Rect frame) { 1521 Preconditions.checkNotNull(view); 1522 Preconditions.checkNotNull(frame); 1523 if (view.getWindowToken() == null) { 1524 throw new IllegalStateException("view must be attached to a window"); 1525 } 1526 if (mToken == null) { 1527 Log.w(TAG, "The session has been already released"); 1528 return; 1529 } 1530 try { 1531 mService.createMediaView(mToken, view.getWindowToken(), frame, mUserId); 1532 } catch (RemoteException e) { 1533 throw e.rethrowFromSystemServer(); 1534 } 1535 } 1536 1537 /** 1538 * Relayouts the current media view. 1539 * 1540 * @param frame A new position of the media view. 1541 */ relayoutMediaView(@onNull Rect frame)1542 void relayoutMediaView(@NonNull Rect frame) { 1543 Preconditions.checkNotNull(frame); 1544 if (mToken == null) { 1545 Log.w(TAG, "The session has been already released"); 1546 return; 1547 } 1548 try { 1549 mService.relayoutMediaView(mToken, frame, mUserId); 1550 } catch (RemoteException e) { 1551 throw e.rethrowFromSystemServer(); 1552 } 1553 } 1554 1555 /** 1556 * Removes the current media view. 1557 */ removeMediaView()1558 void removeMediaView() { 1559 if (mToken == null) { 1560 Log.w(TAG, "The session has been already released"); 1561 return; 1562 } 1563 try { 1564 mService.removeMediaView(mToken, mUserId); 1565 } catch (RemoteException e) { 1566 throw e.rethrowFromSystemServer(); 1567 } 1568 } 1569 1570 /** 1571 * Notifies of any structural changes (format or size) of the surface passed in 1572 * {@link #setSurface}. 1573 * 1574 * @param format The new PixelFormat of the surface. 1575 * @param width The new width of the surface. 1576 * @param height The new height of the surface. 1577 */ dispatchSurfaceChanged(int format, int width, int height)1578 public void dispatchSurfaceChanged(int format, int width, int height) { 1579 if (mToken == null) { 1580 Log.w(TAG, "The session has been already released"); 1581 return; 1582 } 1583 try { 1584 mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId); 1585 } catch (RemoteException e) { 1586 throw e.rethrowFromSystemServer(); 1587 } 1588 } 1589 1590 /** 1591 * Dispatches an input event to this session. 1592 * 1593 * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}. 1594 * @param token A token used to identify the input event later in the callback. 1595 * @param callback A callback used to receive the dispatch result. Cannot be {@code null}. 1596 * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be 1597 * {@code null}. 1598 * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns 1599 * {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns 1600 * {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will 1601 * be invoked later. 1602 * @hide 1603 */ dispatchInputEvent(@onNull InputEvent event, Object token, @NonNull FinishedInputEventCallback callback, @NonNull Handler handler)1604 public int dispatchInputEvent(@NonNull InputEvent event, Object token, 1605 @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) { 1606 Preconditions.checkNotNull(event); 1607 Preconditions.checkNotNull(callback); 1608 Preconditions.checkNotNull(handler); 1609 synchronized (mHandler) { 1610 if (mInputChannel == null) { 1611 return DISPATCH_NOT_HANDLED; 1612 } 1613 PendingEvent p = obtainPendingEventLocked(event, token, callback, handler); 1614 if (Looper.myLooper() == Looper.getMainLooper()) { 1615 // Already running on the main thread so we can send the event immediately. 1616 return sendInputEventOnMainLooperLocked(p); 1617 } 1618 1619 // Post the event to the main thread. 1620 Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p); 1621 msg.setAsynchronous(true); 1622 mHandler.sendMessage(msg); 1623 return DISPATCH_IN_PROGRESS; 1624 } 1625 } 1626 1627 /** 1628 * Notifies of any broadcast info response passed in from TIS. 1629 * 1630 * @param response response passed in from TIS. 1631 */ notifyBroadcastInfoResponse(BroadcastInfoResponse response)1632 public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) { 1633 if (mToken == null) { 1634 Log.w(TAG, "The session has been already released"); 1635 return; 1636 } 1637 try { 1638 mService.notifyBroadcastInfoResponse(mToken, response, mUserId); 1639 } catch (RemoteException e) { 1640 throw e.rethrowFromSystemServer(); 1641 } 1642 } 1643 1644 /** 1645 * Notifies of any advertisement response passed in from TIS. 1646 * 1647 * @param response response passed in from TIS. 1648 */ notifyAdResponse(AdResponse response)1649 public void notifyAdResponse(AdResponse response) { 1650 if (mToken == null) { 1651 Log.w(TAG, "The session has been already released"); 1652 return; 1653 } 1654 try { 1655 mService.notifyAdResponse(mToken, response, mUserId); 1656 } catch (RemoteException e) { 1657 throw e.rethrowFromSystemServer(); 1658 } 1659 } 1660 1661 /** 1662 * Notifies the advertisement buffer is consumed. 1663 */ notifyAdBufferConsumed(AdBuffer buffer)1664 public void notifyAdBufferConsumed(AdBuffer buffer) { 1665 if (mToken == null) { 1666 Log.w(TAG, "The session has been already released"); 1667 return; 1668 } 1669 try { 1670 mService.notifyAdBufferConsumed(mToken, buffer, mUserId); 1671 } catch (RemoteException e) { 1672 throw e.rethrowFromSystemServer(); 1673 } finally { 1674 if (buffer != null) { 1675 buffer.getSharedMemory().close(); 1676 } 1677 } 1678 } 1679 1680 /** 1681 * Releases this session. 1682 */ release()1683 public void release() { 1684 if (mToken == null) { 1685 Log.w(TAG, "The session has been already released"); 1686 return; 1687 } 1688 try { 1689 mService.releaseSession(mToken, mUserId); 1690 } catch (RemoteException e) { 1691 throw e.rethrowFromSystemServer(); 1692 } 1693 1694 releaseInternal(); 1695 } 1696 1697 /** 1698 * Notifies Interactive APP session when a channel is tuned. 1699 */ notifyTuned(Uri channelUri)1700 public void notifyTuned(Uri channelUri) { 1701 if (mToken == null) { 1702 Log.w(TAG, "The session has been already released"); 1703 return; 1704 } 1705 try { 1706 mService.notifyTuned(mToken, channelUri, mUserId); 1707 } catch (RemoteException e) { 1708 throw e.rethrowFromSystemServer(); 1709 } 1710 } 1711 1712 /** 1713 * Notifies Interactive APP session when a track is selected. 1714 */ notifyTrackSelected(int type, String trackId)1715 public void notifyTrackSelected(int type, String trackId) { 1716 if (mToken == null) { 1717 Log.w(TAG, "The session has been already released"); 1718 return; 1719 } 1720 try { 1721 mService.notifyTrackSelected(mToken, type, trackId, mUserId); 1722 } catch (RemoteException e) { 1723 throw e.rethrowFromSystemServer(); 1724 } 1725 } 1726 1727 /** 1728 * Notifies Interactive APP session when tracks are changed. 1729 */ notifyTracksChanged(List<TvTrackInfo> tracks)1730 public void notifyTracksChanged(List<TvTrackInfo> tracks) { 1731 if (mToken == null) { 1732 Log.w(TAG, "The session has been already released"); 1733 return; 1734 } 1735 try { 1736 mService.notifyTracksChanged(mToken, tracks, mUserId); 1737 } catch (RemoteException e) { 1738 throw e.rethrowFromSystemServer(); 1739 } 1740 } 1741 1742 /** 1743 * Notifies Interactive APP session when video is available. 1744 */ notifyVideoAvailable()1745 public void notifyVideoAvailable() { 1746 if (mToken == null) { 1747 Log.w(TAG, "The session has been already released"); 1748 return; 1749 } 1750 try { 1751 mService.notifyVideoAvailable(mToken, mUserId); 1752 } catch (RemoteException e) { 1753 throw e.rethrowFromSystemServer(); 1754 } 1755 } 1756 1757 /** 1758 * Notifies Interactive APP session when video is unavailable. 1759 */ notifyVideoUnavailable(int reason)1760 public void notifyVideoUnavailable(int reason) { 1761 if (mToken == null) { 1762 Log.w(TAG, "The session has been already released"); 1763 return; 1764 } 1765 try { 1766 mService.notifyVideoUnavailable(mToken, reason, mUserId); 1767 } catch (RemoteException e) { 1768 throw e.rethrowFromSystemServer(); 1769 } 1770 } 1771 1772 /** 1773 * Notifies Interactive app session when the video freeze state is updated 1774 * @param isFrozen Whether or not the video is frozen 1775 */ notifyVideoFreezeUpdated(boolean isFrozen)1776 public void notifyVideoFreezeUpdated(boolean isFrozen) { 1777 if (mToken == null) { 1778 Log.w(TAG, "The session has been already released"); 1779 return; 1780 } 1781 try { 1782 mService.notifyVideoFreezeUpdated(mToken, isFrozen, mUserId); 1783 } catch (RemoteException e) { 1784 throw e.rethrowFromSystemServer(); 1785 } 1786 } 1787 1788 /** 1789 * Notifies Interactive APP session when content is allowed. 1790 */ notifyContentAllowed()1791 public void notifyContentAllowed() { 1792 if (mToken == null) { 1793 Log.w(TAG, "The session has been already released"); 1794 return; 1795 } 1796 try { 1797 mService.notifyContentAllowed(mToken, mUserId); 1798 } catch (RemoteException e) { 1799 throw e.rethrowFromSystemServer(); 1800 } 1801 } 1802 1803 /** 1804 * Notifies Interactive APP session when content is blocked. 1805 */ notifyContentBlocked(TvContentRating rating)1806 public void notifyContentBlocked(TvContentRating rating) { 1807 if (mToken == null) { 1808 Log.w(TAG, "The session has been already released"); 1809 return; 1810 } 1811 try { 1812 mService.notifyContentBlocked(mToken, rating.flattenToString(), mUserId); 1813 } catch (RemoteException e) { 1814 throw e.rethrowFromSystemServer(); 1815 } 1816 } 1817 1818 /** 1819 * Notifies Interactive APP session when signal strength is changed. 1820 */ notifySignalStrength(int strength)1821 public void notifySignalStrength(int strength) { 1822 if (mToken == null) { 1823 Log.w(TAG, "The session has been already released"); 1824 return; 1825 } 1826 try { 1827 mService.notifySignalStrength(mToken, strength, mUserId); 1828 } catch (RemoteException e) { 1829 throw e.rethrowFromSystemServer(); 1830 } 1831 } 1832 1833 /** 1834 * Notifies Interactive APP session when a new TV message is received. 1835 */ notifyTvMessage(int type, Bundle data)1836 public void notifyTvMessage(int type, Bundle data) { 1837 if (mToken == null) { 1838 Log.w(TAG, "The session has been already released"); 1839 return; 1840 } 1841 try { 1842 mService.notifyTvMessage(mToken, type, data, mUserId); 1843 } catch (RemoteException e) { 1844 throw e.rethrowFromSystemServer(); 1845 } 1846 } 1847 flushPendingEventsLocked()1848 private void flushPendingEventsLocked() { 1849 mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT); 1850 1851 final int count = mPendingEvents.size(); 1852 for (int i = 0; i < count; i++) { 1853 int seq = mPendingEvents.keyAt(i); 1854 Message msg = mHandler.obtainMessage( 1855 InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0); 1856 msg.setAsynchronous(true); 1857 msg.sendToTarget(); 1858 } 1859 } 1860 releaseInternal()1861 private void releaseInternal() { 1862 mToken = null; 1863 synchronized (mHandler) { 1864 if (mInputChannel != null) { 1865 if (mSender != null) { 1866 flushPendingEventsLocked(); 1867 mSender.dispose(); 1868 mSender = null; 1869 } 1870 mInputChannel.dispose(); 1871 mInputChannel = null; 1872 } 1873 } 1874 synchronized (mSessionCallbackRecordMap) { 1875 mSessionCallbackRecordMap.delete(mSeq); 1876 } 1877 } 1878 obtainPendingEventLocked(InputEvent event, Object token, FinishedInputEventCallback callback, Handler handler)1879 private PendingEvent obtainPendingEventLocked(InputEvent event, Object token, 1880 FinishedInputEventCallback callback, Handler handler) { 1881 PendingEvent p = mPendingEventPool.acquire(); 1882 if (p == null) { 1883 p = new PendingEvent(); 1884 } 1885 p.mEvent = event; 1886 p.mEventToken = token; 1887 p.mCallback = callback; 1888 p.mEventHandler = handler; 1889 return p; 1890 } 1891 1892 // Assumes the event has already been removed from the queue. invokeFinishedInputEventCallback(PendingEvent p, boolean handled)1893 void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) { 1894 p.mHandled = handled; 1895 if (p.mEventHandler.getLooper().isCurrentThread()) { 1896 // Already running on the callback handler thread so we can send the callback 1897 // immediately. 1898 p.run(); 1899 } else { 1900 // Post the event to the callback handler thread. 1901 // In this case, the callback will be responsible for recycling the event. 1902 Message msg = Message.obtain(p.mEventHandler, p); 1903 msg.setAsynchronous(true); 1904 msg.sendToTarget(); 1905 } 1906 } 1907 1908 // Must be called on the main looper sendInputEventAndReportResultOnMainLooper(PendingEvent p)1909 private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) { 1910 synchronized (mHandler) { 1911 int result = sendInputEventOnMainLooperLocked(p); 1912 if (result == DISPATCH_IN_PROGRESS) { 1913 return; 1914 } 1915 } 1916 1917 invokeFinishedInputEventCallback(p, false); 1918 } 1919 sendInputEventOnMainLooperLocked(PendingEvent p)1920 private int sendInputEventOnMainLooperLocked(PendingEvent p) { 1921 if (mInputChannel != null) { 1922 if (mSender == null) { 1923 mSender = new TvInputEventSender(mInputChannel, mHandler.getLooper()); 1924 } 1925 1926 final InputEvent event = p.mEvent; 1927 final int seq = event.getSequenceNumber(); 1928 if (mSender.sendInputEvent(seq, event)) { 1929 mPendingEvents.put(seq, p); 1930 Message msg = mHandler.obtainMessage( 1931 InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p); 1932 msg.setAsynchronous(true); 1933 mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT); 1934 return DISPATCH_IN_PROGRESS; 1935 } 1936 1937 Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:" 1938 + event); 1939 } 1940 return DISPATCH_NOT_HANDLED; 1941 } 1942 finishedInputEvent(int seq, boolean handled, boolean timeout)1943 void finishedInputEvent(int seq, boolean handled, boolean timeout) { 1944 final PendingEvent p; 1945 synchronized (mHandler) { 1946 int index = mPendingEvents.indexOfKey(seq); 1947 if (index < 0) { 1948 return; // spurious, event already finished or timed out 1949 } 1950 1951 p = mPendingEvents.valueAt(index); 1952 mPendingEvents.removeAt(index); 1953 1954 if (timeout) { 1955 Log.w(TAG, "Timeout waiting for session to handle input event after " 1956 + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken); 1957 } else { 1958 mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p); 1959 } 1960 } 1961 1962 invokeFinishedInputEventCallback(p, handled); 1963 } 1964 recyclePendingEventLocked(PendingEvent p)1965 private void recyclePendingEventLocked(PendingEvent p) { 1966 p.recycle(); 1967 mPendingEventPool.release(p); 1968 } 1969 1970 /** 1971 * Callback that is invoked when an input event that was dispatched to this session has been 1972 * finished. 1973 * 1974 * @hide 1975 */ 1976 public interface FinishedInputEventCallback { 1977 /** 1978 * Called when the dispatched input event is finished. 1979 * 1980 * @param token A token passed to {@link #dispatchInputEvent}. 1981 * @param handled {@code true} if the dispatched input event was handled properly. 1982 * {@code false} otherwise. 1983 */ onFinishedInputEvent(Object token, boolean handled)1984 void onFinishedInputEvent(Object token, boolean handled); 1985 } 1986 1987 private final class InputEventHandler extends Handler { 1988 public static final int MSG_SEND_INPUT_EVENT = 1; 1989 public static final int MSG_TIMEOUT_INPUT_EVENT = 2; 1990 public static final int MSG_FLUSH_INPUT_EVENT = 3; 1991 InputEventHandler(Looper looper)1992 InputEventHandler(Looper looper) { 1993 super(looper, null, true); 1994 } 1995 1996 @Override handleMessage(Message msg)1997 public void handleMessage(Message msg) { 1998 switch (msg.what) { 1999 case MSG_SEND_INPUT_EVENT: { 2000 sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj); 2001 return; 2002 } 2003 case MSG_TIMEOUT_INPUT_EVENT: { 2004 finishedInputEvent(msg.arg1, false, true); 2005 return; 2006 } 2007 case MSG_FLUSH_INPUT_EVENT: { 2008 finishedInputEvent(msg.arg1, false, false); 2009 return; 2010 } 2011 } 2012 } 2013 } 2014 2015 private final class TvInputEventSender extends InputEventSender { TvInputEventSender(InputChannel inputChannel, Looper looper)2016 TvInputEventSender(InputChannel inputChannel, Looper looper) { 2017 super(inputChannel, looper); 2018 } 2019 2020 @Override onInputEventFinished(int seq, boolean handled)2021 public void onInputEventFinished(int seq, boolean handled) { 2022 finishedInputEvent(seq, handled, false); 2023 } 2024 } 2025 2026 private final class PendingEvent implements Runnable { 2027 public InputEvent mEvent; 2028 public Object mEventToken; 2029 public FinishedInputEventCallback mCallback; 2030 public Handler mEventHandler; 2031 public boolean mHandled; 2032 recycle()2033 public void recycle() { 2034 mEvent = null; 2035 mEventToken = null; 2036 mCallback = null; 2037 mEventHandler = null; 2038 mHandled = false; 2039 } 2040 2041 @Override run()2042 public void run() { 2043 mCallback.onFinishedInputEvent(mEventToken, mHandled); 2044 2045 synchronized (mEventHandler) { 2046 recyclePendingEventLocked(this); 2047 } 2048 } 2049 } 2050 } 2051 2052 private static final class SessionCallbackRecord { 2053 private final SessionCallback mSessionCallback; 2054 private final Handler mHandler; 2055 private Session mSession; 2056 SessionCallbackRecord(SessionCallback sessionCallback, Handler handler)2057 SessionCallbackRecord(SessionCallback sessionCallback, Handler handler) { 2058 mSessionCallback = sessionCallback; 2059 mHandler = handler; 2060 } 2061 postSessionCreated(final Session session)2062 void postSessionCreated(final Session session) { 2063 mSession = session; 2064 mHandler.post(new Runnable() { 2065 @Override 2066 public void run() { 2067 mSessionCallback.onSessionCreated(session); 2068 } 2069 }); 2070 } 2071 postSessionReleased()2072 void postSessionReleased() { 2073 mHandler.post(new Runnable() { 2074 @Override 2075 public void run() { 2076 mSessionCallback.onSessionReleased(mSession); 2077 } 2078 }); 2079 } 2080 postLayoutSurface(final int left, final int top, final int right, final int bottom)2081 void postLayoutSurface(final int left, final int top, final int right, 2082 final int bottom) { 2083 mHandler.post(new Runnable() { 2084 @Override 2085 public void run() { 2086 mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom); 2087 } 2088 }); 2089 } 2090 postBroadcastInfoRequest(final BroadcastInfoRequest request)2091 void postBroadcastInfoRequest(final BroadcastInfoRequest request) { 2092 mHandler.post(new Runnable() { 2093 @Override 2094 public void run() { 2095 if (mSession.getInputSession() != null) { 2096 mSession.getInputSession().requestBroadcastInfo(request); 2097 } 2098 } 2099 }); 2100 } 2101 postRemoveBroadcastInfo(final int requestId)2102 void postRemoveBroadcastInfo(final int requestId) { 2103 mHandler.post(new Runnable() { 2104 @Override 2105 public void run() { 2106 if (mSession.getInputSession() != null) { 2107 mSession.getInputSession().removeBroadcastInfo(requestId); 2108 } 2109 } 2110 }); 2111 } 2112 postCommandRequest( final @TvInteractiveAppService.PlaybackCommandType String cmdType, final Bundle parameters)2113 void postCommandRequest( 2114 final @TvInteractiveAppService.PlaybackCommandType String cmdType, 2115 final Bundle parameters) { 2116 mHandler.post(new Runnable() { 2117 @Override 2118 public void run() { 2119 mSessionCallback.onCommandRequest(mSession, cmdType, parameters); 2120 } 2121 }); 2122 } 2123 postTimeShiftCommandRequest( final @TvInteractiveAppService.TimeShiftCommandType String cmdType, final Bundle parameters)2124 void postTimeShiftCommandRequest( 2125 final @TvInteractiveAppService.TimeShiftCommandType String cmdType, 2126 final Bundle parameters) { 2127 mHandler.post(new Runnable() { 2128 @Override 2129 public void run() { 2130 mSessionCallback.onTimeShiftCommandRequest(mSession, cmdType, parameters); 2131 } 2132 }); 2133 } 2134 postSetVideoBounds(Rect rect)2135 void postSetVideoBounds(Rect rect) { 2136 mHandler.post(new Runnable() { 2137 @Override 2138 public void run() { 2139 mSessionCallback.onSetVideoBounds(mSession, rect); 2140 } 2141 }); 2142 } 2143 postRequestCurrentVideoBounds()2144 void postRequestCurrentVideoBounds() { 2145 mHandler.post(new Runnable() { 2146 @Override 2147 public void run() { 2148 mSessionCallback.onRequestCurrentVideoBounds(mSession); 2149 } 2150 }); 2151 } 2152 postRequestCurrentChannelUri()2153 void postRequestCurrentChannelUri() { 2154 mHandler.post(new Runnable() { 2155 @Override 2156 public void run() { 2157 mSessionCallback.onRequestCurrentChannelUri(mSession); 2158 } 2159 }); 2160 } 2161 postRequestCurrentChannelLcn()2162 void postRequestCurrentChannelLcn() { 2163 mHandler.post(new Runnable() { 2164 @Override 2165 public void run() { 2166 mSessionCallback.onRequestCurrentChannelLcn(mSession); 2167 } 2168 }); 2169 } 2170 postRequestStreamVolume()2171 void postRequestStreamVolume() { 2172 mHandler.post(new Runnable() { 2173 @Override 2174 public void run() { 2175 mSessionCallback.onRequestStreamVolume(mSession); 2176 } 2177 }); 2178 } 2179 postRequestTrackInfoList()2180 void postRequestTrackInfoList() { 2181 mHandler.post(new Runnable() { 2182 @Override 2183 public void run() { 2184 mSessionCallback.onRequestTrackInfoList(mSession); 2185 } 2186 }); 2187 } 2188 postRequestSelectedTrackInfo()2189 void postRequestSelectedTrackInfo() { 2190 mHandler.post(new Runnable() { 2191 @Override 2192 public void run() { 2193 mSessionCallback.onRequestSelectedTrackInfo(mSession); 2194 } 2195 }); 2196 } 2197 postRequestCurrentTvInputId()2198 void postRequestCurrentTvInputId() { 2199 mHandler.post(new Runnable() { 2200 @Override 2201 public void run() { 2202 mSessionCallback.onRequestCurrentTvInputId(mSession); 2203 } 2204 }); 2205 } 2206 postRequestTimeShiftMode()2207 void postRequestTimeShiftMode() { 2208 mHandler.post(new Runnable() { 2209 @Override 2210 public void run() { 2211 mSessionCallback.onRequestTimeShiftMode(mSession); 2212 } 2213 }); 2214 } 2215 postRequestAvailableSpeeds()2216 void postRequestAvailableSpeeds() { 2217 mHandler.post(new Runnable() { 2218 @Override 2219 public void run() { 2220 mSessionCallback.onRequestAvailableSpeeds(mSession); 2221 } 2222 }); 2223 } 2224 postRequestStartRecording(String requestId, Uri programUri)2225 void postRequestStartRecording(String requestId, Uri programUri) { 2226 mHandler.post(new Runnable() { 2227 @Override 2228 public void run() { 2229 mSessionCallback.onRequestStartRecording(mSession, requestId, programUri); 2230 } 2231 }); 2232 } 2233 postRequestStopRecording(String recordingId)2234 void postRequestStopRecording(String recordingId) { 2235 mHandler.post(new Runnable() { 2236 @Override 2237 public void run() { 2238 mSessionCallback.onRequestStopRecording(mSession, recordingId); 2239 } 2240 }); 2241 } 2242 postRequestScheduleRecording(String requestId, String inputId, Uri channelUri, Uri programUri, Bundle params)2243 void postRequestScheduleRecording(String requestId, String inputId, Uri channelUri, 2244 Uri programUri, Bundle params) { 2245 mHandler.post(new Runnable() { 2246 @Override 2247 public void run() { 2248 mSessionCallback.onRequestScheduleRecording( 2249 mSession, requestId, inputId, channelUri, programUri, params); 2250 } 2251 }); 2252 } 2253 postRequestScheduleRecording(String requestId, String inputId, Uri channelUri, long startTime, long duration, int repeatDays, Bundle params)2254 void postRequestScheduleRecording(String requestId, String inputId, Uri channelUri, 2255 long startTime, long duration, int repeatDays, Bundle params) { 2256 mHandler.post(new Runnable() { 2257 @Override 2258 public void run() { 2259 mSessionCallback.onRequestScheduleRecording(mSession, requestId, inputId, 2260 channelUri, startTime, duration, repeatDays, params); 2261 } 2262 }); 2263 } 2264 postRequestSigning(String id, String algorithm, String alias, byte[] data)2265 void postRequestSigning(String id, String algorithm, String alias, byte[] data) { 2266 mHandler.post(new Runnable() { 2267 @Override 2268 public void run() { 2269 mSessionCallback.onRequestSigning(mSession, id, algorithm, alias, data); 2270 } 2271 }); 2272 } 2273 postRequestSigning(String id, String algorithm, String host, int port, byte[] data)2274 void postRequestSigning(String id, String algorithm, String host, int port, 2275 byte[] data) { 2276 mHandler.post(new Runnable() { 2277 @Override 2278 public void run() { 2279 mSessionCallback.onRequestSigning(mSession, id, algorithm, host, 2280 port, data); 2281 } 2282 }); 2283 } 2284 postRequestCertificate(String host, int port)2285 void postRequestCertificate(String host, int port) { 2286 mHandler.post(new Runnable() { 2287 @Override 2288 public void run() { 2289 mSessionCallback.onRequestCertificate(mSession, host, port); 2290 } 2291 }); 2292 } 2293 postRequestTvRecordingInfo(String recordingId)2294 void postRequestTvRecordingInfo(String recordingId) { 2295 mHandler.post(new Runnable() { 2296 @Override 2297 public void run() { 2298 mSessionCallback.onRequestTvRecordingInfo(mSession, recordingId); 2299 } 2300 }); 2301 } 2302 postRequestTvRecordingInfoList(int type)2303 void postRequestTvRecordingInfoList(int type) { 2304 mHandler.post(new Runnable() { 2305 @Override 2306 public void run() { 2307 mSessionCallback.onRequestTvRecordingInfoList(mSession, type); 2308 } 2309 }); 2310 } 2311 postSetTvRecordingInfo(String recordingId, TvRecordingInfo recordingInfo)2312 void postSetTvRecordingInfo(String recordingId, TvRecordingInfo recordingInfo) { 2313 mHandler.post(new Runnable() { 2314 @Override 2315 public void run() { 2316 mSessionCallback.onSetTvRecordingInfo(mSession, recordingId, recordingInfo); 2317 } 2318 }); 2319 } 2320 postAdRequest(final AdRequest request)2321 void postAdRequest(final AdRequest request) { 2322 mHandler.post(new Runnable() { 2323 @Override 2324 public void run() { 2325 if (mSession.getInputSession() != null) { 2326 mSession.getInputSession().requestAd(request); 2327 } 2328 } 2329 }); 2330 } 2331 postSessionStateChanged(int state, int err)2332 void postSessionStateChanged(int state, int err) { 2333 mHandler.post(new Runnable() { 2334 @Override 2335 public void run() { 2336 mSessionCallback.onSessionStateChanged(mSession, state, err); 2337 } 2338 }); 2339 } 2340 postBiInteractiveAppCreated(Uri biIAppUri, String biIAppId)2341 void postBiInteractiveAppCreated(Uri biIAppUri, String biIAppId) { 2342 mHandler.post(new Runnable() { 2343 @Override 2344 public void run() { 2345 mSessionCallback.onBiInteractiveAppCreated(mSession, biIAppUri, biIAppId); 2346 } 2347 }); 2348 } 2349 postTeletextAppStateChanged(int state)2350 void postTeletextAppStateChanged(int state) { 2351 mHandler.post(new Runnable() { 2352 @Override 2353 public void run() { 2354 mSessionCallback.onTeletextAppStateChanged(mSession, state); 2355 } 2356 }); 2357 } 2358 postAdBufferReady(AdBuffer buffer)2359 void postAdBufferReady(AdBuffer buffer) { 2360 mHandler.post(new Runnable() { 2361 @Override 2362 public void run() { 2363 if (mSession.getInputSession() != null) { 2364 mSession.getInputSession().notifyAdBufferReady(buffer); 2365 } 2366 } 2367 }); 2368 } 2369 } 2370 2371 /** 2372 * Interface used to receive the created session. 2373 * @hide 2374 */ 2375 public abstract static class SessionCallback { 2376 /** 2377 * This is called after {@link TvInteractiveAppManager#createSession} has been processed. 2378 * 2379 * @param session A {@link TvInteractiveAppManager.Session} instance created. This can be 2380 * {@code null} if the creation request failed. 2381 */ onSessionCreated(@ullable Session session)2382 public void onSessionCreated(@Nullable Session session) { 2383 } 2384 2385 /** 2386 * This is called when {@link TvInteractiveAppManager.Session} is released. 2387 * This typically happens when the process hosting the session has crashed or been killed. 2388 * 2389 * @param session the {@link TvInteractiveAppManager.Session} instance released. 2390 */ onSessionReleased(@onNull Session session)2391 public void onSessionReleased(@NonNull Session session) { 2392 } 2393 2394 /** 2395 * This is called when {@link TvInteractiveAppService.Session#layoutSurface} is called to 2396 * change the layout of surface. 2397 * 2398 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2399 * @param left Left position. 2400 * @param top Top position. 2401 * @param right Right position. 2402 * @param bottom Bottom position. 2403 */ onLayoutSurface(Session session, int left, int top, int right, int bottom)2404 public void onLayoutSurface(Session session, int left, int top, int right, int bottom) { 2405 } 2406 2407 /** 2408 * This is called when {@link TvInteractiveAppService.Session#requestCommand} is called. 2409 * 2410 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2411 * @param cmdType type of the command. 2412 * @param parameters parameters of the command. 2413 */ onCommandRequest( Session session, @TvInteractiveAppService.PlaybackCommandType String cmdType, Bundle parameters)2414 public void onCommandRequest( 2415 Session session, 2416 @TvInteractiveAppService.PlaybackCommandType String cmdType, 2417 Bundle parameters) { 2418 } 2419 2420 /** 2421 * This is called when {@link TvInteractiveAppService.Session#requestTimeShiftCommand} is 2422 * called. 2423 * 2424 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2425 * @param cmdType type of the time shift command. 2426 * @param parameters parameters of the command. 2427 */ onTimeShiftCommandRequest( Session session, @TvInteractiveAppService.TimeShiftCommandType String cmdType, Bundle parameters)2428 public void onTimeShiftCommandRequest( 2429 Session session, 2430 @TvInteractiveAppService.TimeShiftCommandType String cmdType, 2431 Bundle parameters) { 2432 } 2433 2434 /** 2435 * This is called when {@link TvInteractiveAppService.Session#setVideoBounds} is called. 2436 * 2437 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2438 */ onSetVideoBounds(Session session, Rect rect)2439 public void onSetVideoBounds(Session session, Rect rect) { 2440 } 2441 2442 /** 2443 * This is called when {@link TvInteractiveAppService.Session#requestCurrentVideoBounds} is 2444 * called. 2445 * 2446 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2447 */ onRequestCurrentVideoBounds(Session session)2448 public void onRequestCurrentVideoBounds(Session session) { 2449 } 2450 2451 /** 2452 * This is called when {@link TvInteractiveAppService.Session#requestCurrentChannelUri} is 2453 * called. 2454 * 2455 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2456 */ onRequestCurrentChannelUri(Session session)2457 public void onRequestCurrentChannelUri(Session session) { 2458 } 2459 2460 /** 2461 * This is called when {@link TvInteractiveAppService.Session#requestCurrentChannelLcn} is 2462 * called. 2463 * 2464 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2465 */ onRequestCurrentChannelLcn(Session session)2466 public void onRequestCurrentChannelLcn(Session session) { 2467 } 2468 2469 /** 2470 * This is called when {@link TvInteractiveAppService.Session#requestStreamVolume} is 2471 * called. 2472 * 2473 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2474 */ onRequestStreamVolume(Session session)2475 public void onRequestStreamVolume(Session session) { 2476 } 2477 2478 /** 2479 * This is called when {@link TvInteractiveAppService.Session#requestTrackInfoList} is 2480 * called. 2481 * 2482 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2483 */ onRequestTrackInfoList(Session session)2484 public void onRequestTrackInfoList(Session session) { 2485 } 2486 2487 /** 2488 * This is called when {@link TvInteractiveAppService.Session#requestSelectedTrackInfo()} is 2489 * called. 2490 * 2491 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2492 */ onRequestSelectedTrackInfo(Session session)2493 public void onRequestSelectedTrackInfo(Session session) { 2494 } 2495 2496 /** 2497 * This is called when {@link TvInteractiveAppService.Session#requestCurrentTvInputId} is 2498 * called. 2499 * 2500 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2501 */ onRequestCurrentTvInputId(Session session)2502 public void onRequestCurrentTvInputId(Session session) { 2503 } 2504 2505 /** 2506 * This is called when {@link TvInteractiveAppService.Session#requestTimeShiftMode()} is 2507 * called. 2508 * 2509 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2510 */ onRequestTimeShiftMode(Session session)2511 public void onRequestTimeShiftMode(Session session) { 2512 } 2513 2514 /** 2515 * This is called when {@link TvInteractiveAppService.Session#requestAvailableSpeeds()} is 2516 * called. 2517 * 2518 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2519 */ onRequestAvailableSpeeds(Session session)2520 public void onRequestAvailableSpeeds(Session session) { 2521 } 2522 2523 /** 2524 * This is called when {@link TvInteractiveAppService.Session#requestStartRecording} is 2525 * called. 2526 * 2527 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2528 * @param programUri The Uri of the program to be recorded. 2529 */ onRequestStartRecording(Session session, String requestId, Uri programUri)2530 public void onRequestStartRecording(Session session, String requestId, Uri programUri) { 2531 } 2532 2533 /** 2534 * This is called when {@link TvInteractiveAppService.Session#requestStopRecording(String)} 2535 * is called. 2536 * 2537 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2538 * @param recordingId The recordingId of the recording to be stopped. 2539 */ onRequestStopRecording(Session session, String recordingId)2540 public void onRequestStopRecording(Session session, String recordingId) { 2541 } 2542 2543 /** 2544 * This is called when 2545 * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, String, Uri, Uri, Bundle)} 2546 * is called. 2547 * 2548 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2549 * @param inputId The ID of the TV input for the given channel. 2550 * @param channelUri The URI of a channel to be recorded. 2551 * @param programUri The URI of the TV program to be recorded. 2552 * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 2553 * name, i.e. prefixed with a package name you own, so that different developers 2554 * will not create conflicting keys. 2555 * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle) 2556 * @see android.media.tv.TvRecordingClient#startRecording(Uri) 2557 */ onRequestScheduleRecording(Session session, @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params)2558 public void onRequestScheduleRecording(Session session, @NonNull String requestId, 2559 @NonNull String inputId, @NonNull Uri channelUri, @NonNull Uri programUri, 2560 @NonNull Bundle params) { 2561 } 2562 2563 /** 2564 * This is called when 2565 * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, String, Uri, long, long, int, Bundle)} 2566 * is called. 2567 * 2568 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2569 * @param inputId The ID of the TV input for the given channel. 2570 * @param channelUri The URI of a channel to be recorded. 2571 * @param startTime The start time of the recording in milliseconds since epoch. 2572 * @param duration The duration of the recording in milliseconds. 2573 * @param repeatDays The repeated days. 0 if not repeated. 2574 * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 2575 * name, i.e. prefixed with a package name you own, so that different developers 2576 * will not create conflicting keys. 2577 * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle) 2578 * @see android.media.tv.TvRecordingClient#startRecording(Uri) 2579 */ onRequestScheduleRecording(Session session, @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration, int repeatDays, @NonNull Bundle params)2580 public void onRequestScheduleRecording(Session session, @NonNull String requestId, 2581 @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration, 2582 int repeatDays, @NonNull Bundle params) { 2583 } 2584 2585 /** 2586 * This is called when 2587 * {@link TvInteractiveAppService.Session#setTvRecordingInfo(String, TvRecordingInfo)} is 2588 * called. 2589 * 2590 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2591 * @param recordingId The recordingId of the recording which will have the info set. 2592 * @param recordingInfo The recording info to set to the recording. 2593 */ onSetTvRecordingInfo(Session session, String recordingId, TvRecordingInfo recordingInfo)2594 public void onSetTvRecordingInfo(Session session, String recordingId, 2595 TvRecordingInfo recordingInfo) { 2596 } 2597 2598 /** 2599 * This is called when {@link TvInteractiveAppService.Session#requestTvRecordingInfo} is 2600 * called. 2601 * 2602 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2603 * @param recordingId The recordingId of the recording to be stopped. 2604 */ onRequestTvRecordingInfo(Session session, String recordingId)2605 public void onRequestTvRecordingInfo(Session session, String recordingId) { 2606 } 2607 2608 /** 2609 * This is called when {@link TvInteractiveAppService.Session#requestTvRecordingInfoList} is 2610 * called. 2611 * 2612 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2613 * @param type The type of recordings to return 2614 */ onRequestTvRecordingInfoList(Session session, @TvRecordingInfo.TvRecordingListType int type)2615 public void onRequestTvRecordingInfoList(Session session, 2616 @TvRecordingInfo.TvRecordingListType int type) { 2617 } 2618 2619 /** 2620 * This is called when 2621 * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is 2622 * called. 2623 * 2624 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2625 * @param signingId the ID to identify the request. 2626 * @param algorithm the standard name of the signature algorithm requested, such as 2627 * MD5withRSA, SHA256withDSA, etc. 2628 * @param alias the alias of the corresponding {@link java.security.KeyStore}. 2629 * @param data the original bytes to be signed. 2630 */ onRequestSigning( Session session, String signingId, String algorithm, String alias, byte[] data)2631 public void onRequestSigning( 2632 Session session, String signingId, String algorithm, String alias, byte[] data) { 2633 } 2634 2635 /** 2636 * This is called when 2637 * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, int, byte[])} 2638 * is called. 2639 * 2640 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2641 * @param signingId the ID to identify the request. 2642 * @param algorithm the standard name of the signature algorithm requested, such as 2643 * MD5withRSA, SHA256withDSA, etc. 2644 * @param host The host of the SSL CLient Authentication Server 2645 * @param port The port of the SSL Client Authentication Server 2646 * @param data the original bytes to be signed. 2647 */ onRequestSigning( Session session, String signingId, String algorithm, String host, int port, byte[] data)2648 public void onRequestSigning( 2649 Session session, String signingId, String algorithm, String host, 2650 int port, byte[] data) { 2651 } 2652 2653 /** 2654 * This is called when the service requests a SSL certificate for client validation. 2655 * 2656 * @param session A {@link TvInteractiveAppService.Session} associated with this callback. 2657 * @param host the host name of the SSL authentication server. 2658 * @param port the port of the SSL authentication server. E.g., 443 2659 */ onRequestCertificate(Session session, String host, int port)2660 public void onRequestCertificate(Session session, String host, int port) { 2661 } 2662 2663 /** 2664 * This is called when {@link TvInteractiveAppService.Session#notifySessionStateChanged} is 2665 * called. 2666 * 2667 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2668 * @param state the current state. 2669 */ onSessionStateChanged( Session session, @InteractiveAppState int state, @ErrorCode int err)2670 public void onSessionStateChanged( 2671 Session session, 2672 @InteractiveAppState int state, 2673 @ErrorCode int err) { 2674 } 2675 2676 /** 2677 * This is called when {@link TvInteractiveAppService.Session#notifyBiInteractiveAppCreated} 2678 * is called. 2679 * 2680 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2681 * @param biIAppUri URI associated this BI interactive app. This is the same URI in 2682 * {@link Session#createBiInteractiveApp(Uri, Bundle)} 2683 * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive 2684 * app. 2685 */ onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId)2686 public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) { 2687 } 2688 2689 /** 2690 * This is called when {@link TvInteractiveAppService.Session#notifyTeletextAppStateChanged} 2691 * is called. 2692 * 2693 * @param session A {@link TvInteractiveAppManager.Session} associated with this callback. 2694 * @param state the current state. 2695 */ onTeletextAppStateChanged( Session session, @TvInteractiveAppManager.TeletextAppState int state)2696 public void onTeletextAppStateChanged( 2697 Session session, @TvInteractiveAppManager.TeletextAppState int state) { 2698 } 2699 } 2700 } 2701