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.CallSuper; 20 import android.annotation.IntDef; 21 import android.annotation.MainThread; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.Px; 25 import android.annotation.SdkConstant; 26 import android.annotation.StringDef; 27 import android.annotation.SuppressLint; 28 import android.app.ActivityManager; 29 import android.app.Service; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.graphics.PixelFormat; 33 import android.graphics.Rect; 34 import android.media.PlaybackParams; 35 import android.media.tv.AdBuffer; 36 import android.media.tv.AdRequest; 37 import android.media.tv.AdResponse; 38 import android.media.tv.BroadcastInfoRequest; 39 import android.media.tv.BroadcastInfoResponse; 40 import android.media.tv.TvContentRating; 41 import android.media.tv.TvContract; 42 import android.media.tv.TvInputInfo; 43 import android.media.tv.TvInputManager; 44 import android.media.tv.TvRecordingInfo; 45 import android.media.tv.TvTrackInfo; 46 import android.media.tv.TvView; 47 import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback; 48 import android.net.Uri; 49 import android.os.AsyncTask; 50 import android.os.Bundle; 51 import android.os.Handler; 52 import android.os.IBinder; 53 import android.os.Message; 54 import android.os.Process; 55 import android.os.RemoteCallbackList; 56 import android.os.RemoteException; 57 import android.util.Log; 58 import android.view.Gravity; 59 import android.view.InputChannel; 60 import android.view.InputDevice; 61 import android.view.InputEvent; 62 import android.view.InputEventReceiver; 63 import android.view.KeyEvent; 64 import android.view.MotionEvent; 65 import android.view.Surface; 66 import android.view.View; 67 import android.view.WindowManager; 68 import android.widget.FrameLayout; 69 70 import com.android.internal.os.SomeArgs; 71 72 import java.io.IOException; 73 import java.lang.annotation.Retention; 74 import java.lang.annotation.RetentionPolicy; 75 import java.util.ArrayList; 76 import java.util.List; 77 78 /** 79 * A TV interactive application service is a service that provides runtime environment and runs TV 80 * interactive applications. 81 */ 82 public abstract class TvInteractiveAppService extends Service { 83 private static final boolean DEBUG = false; 84 private static final String TAG = "TvInteractiveAppService"; 85 86 private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000; 87 88 /** 89 * This is the interface name that a service implementing a TV Interactive App service should 90 * say that it supports -- that is, this is the action it uses for its intent filter. To be 91 * supported, the service must also require the 92 * {@link android.Manifest.permission#BIND_TV_INTERACTIVE_APP} permission so that other 93 * applications cannot abuse it. 94 */ 95 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 96 public static final String SERVICE_INTERFACE = 97 "android.media.tv.interactive.TvInteractiveAppService"; 98 99 /** 100 * Name under which a TvInteractiveAppService component publishes information about itself. This 101 * meta-data must reference an XML resource containing an 102 * <code><{@link android.R.styleable#TvInteractiveAppService tv-interactive-app}></code> 103 * tag. 104 */ 105 public static final String SERVICE_META_DATA = "android.media.tv.interactive.app"; 106 107 /** @hide */ 108 @Retention(RetentionPolicy.SOURCE) 109 @StringDef(prefix = "PLAYBACK_COMMAND_TYPE_", value = { 110 PLAYBACK_COMMAND_TYPE_TUNE, 111 PLAYBACK_COMMAND_TYPE_TUNE_NEXT, 112 PLAYBACK_COMMAND_TYPE_TUNE_PREV, 113 PLAYBACK_COMMAND_TYPE_STOP, 114 PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME, 115 PLAYBACK_COMMAND_TYPE_SELECT_TRACK 116 }) 117 public @interface PlaybackCommandType {} 118 119 /** 120 * Playback command type: tune to the given channel. 121 * @see #COMMAND_PARAMETER_KEY_CHANNEL_URI 122 */ 123 public static final String PLAYBACK_COMMAND_TYPE_TUNE = "tune"; 124 /** 125 * Playback command type: tune to the next channel. 126 */ 127 public static final String PLAYBACK_COMMAND_TYPE_TUNE_NEXT = "tune_next"; 128 /** 129 * Playback command type: tune to the previous channel. 130 */ 131 public static final String PLAYBACK_COMMAND_TYPE_TUNE_PREV = "tune_previous"; 132 /** 133 * Playback command type: stop the playback. 134 */ 135 public static final String PLAYBACK_COMMAND_TYPE_STOP = "stop"; 136 /** 137 * Playback command type: set the volume. 138 */ 139 public static final String PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME = 140 "set_stream_volume"; 141 /** 142 * Playback command type: select the given track. 143 */ 144 public static final String PLAYBACK_COMMAND_TYPE_SELECT_TRACK = "select_track"; 145 146 147 148 /** @hide */ 149 @Retention(RetentionPolicy.SOURCE) 150 @IntDef(prefix = "COMMAND_PARAMETER_VALUE_STOP_MODE_", value = { 151 COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK, 152 COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE 153 }) 154 public @interface PlaybackCommandStopMode {} 155 156 /** 157 * Playback command stop mode: show a blank screen. 158 * @see #PLAYBACK_COMMAND_TYPE_STOP 159 */ 160 public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK = 1; 161 162 /** 163 * Playback command stop mode: freeze the video. 164 * @see #PLAYBACK_COMMAND_TYPE_STOP 165 */ 166 public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE = 2; 167 168 /** 169 * Playback command parameter: stop mode. 170 * <p>Type: int 171 * 172 * @see #PLAYBACK_COMMAND_TYPE_STOP 173 */ 174 public static final String COMMAND_PARAMETER_KEY_STOP_MODE = "command_stop_mode"; 175 176 /** 177 * Playback command parameter: channel URI. 178 * <p>Type: android.net.Uri 179 * 180 * @see #PLAYBACK_COMMAND_TYPE_TUNE 181 */ 182 public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri"; 183 /** 184 * Playback command parameter: TV input ID. 185 * <p>Type: String 186 * 187 * @see TvInputInfo#getId() 188 */ 189 public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id"; 190 /** 191 * Playback command parameter: stream volume. 192 * <p>Type: float 193 * 194 * @see #PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME 195 */ 196 public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume"; 197 /** 198 * Playback command parameter: track type. 199 * <p>Type: int 200 * 201 * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK 202 * @see TvTrackInfo#getType() 203 */ 204 public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type"; 205 /** 206 * Playback command parameter: track ID. 207 * <p>Type: String 208 * 209 * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK 210 * @see TvTrackInfo#getId() 211 */ 212 public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id"; 213 /** 214 * Command to quiet channel change. No channel banner or channel info is shown. 215 * <p>Refer to HbbTV Spec 2.0.4 chapter A.2.4.3. 216 */ 217 public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY = 218 "command_change_channel_quietly"; 219 220 /** @hide */ 221 @Retention(RetentionPolicy.SOURCE) 222 @StringDef(prefix = "TIME_SHIFT_COMMAND_TYPE_", value = { 223 TIME_SHIFT_COMMAND_TYPE_PLAY, 224 TIME_SHIFT_COMMAND_TYPE_PAUSE, 225 TIME_SHIFT_COMMAND_TYPE_RESUME, 226 TIME_SHIFT_COMMAND_TYPE_SEEK_TO, 227 TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS, 228 TIME_SHIFT_COMMAND_TYPE_SET_MODE, 229 }) 230 public @interface TimeShiftCommandType {} 231 232 /** 233 * Time shift command type: play. 234 * 235 * @see TvView#timeShiftPlay(String, Uri) 236 */ 237 public static final String TIME_SHIFT_COMMAND_TYPE_PLAY = "play"; 238 /** 239 * Time shift command type: pause. 240 * 241 * @see TvView#timeShiftPause() 242 */ 243 public static final String TIME_SHIFT_COMMAND_TYPE_PAUSE = "pause"; 244 /** 245 * Time shift command type: resume. 246 * 247 * @see TvView#timeShiftResume() 248 */ 249 public static final String TIME_SHIFT_COMMAND_TYPE_RESUME = "resume"; 250 /** 251 * Time shift command type: seek to. 252 * 253 * @see TvView#timeShiftSeekTo(long) 254 */ 255 public static final String TIME_SHIFT_COMMAND_TYPE_SEEK_TO = "seek_to"; 256 /** 257 * Time shift command type: set playback params. 258 * 259 * @see TvView#timeShiftSetPlaybackParams(PlaybackParams) 260 */ 261 public static final String TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS = "set_playback_params"; 262 /** 263 * Time shift command type: set time shift mode. 264 */ 265 public static final String TIME_SHIFT_COMMAND_TYPE_SET_MODE = "set_mode"; 266 267 /** 268 * Time shift command parameter: program URI. 269 * <p>Type: android.net.Uri 270 * 271 * @see #TIME_SHIFT_COMMAND_TYPE_PLAY 272 */ 273 public static final String COMMAND_PARAMETER_KEY_PROGRAM_URI = "command_program_uri"; 274 /** 275 * Time shift command parameter: time position for time shifting, in milliseconds. 276 * <p>Type: long 277 * 278 * @see #TIME_SHIFT_COMMAND_TYPE_SEEK_TO 279 */ 280 public static final String COMMAND_PARAMETER_KEY_TIME_POSITION = "command_time_position"; 281 /** 282 * Time shift command parameter: playback params. 283 * <p>Type: android.media.PlaybackParams 284 * 285 * @see #TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS 286 */ 287 public static final String COMMAND_PARAMETER_KEY_PLAYBACK_PARAMS = "command_playback_params"; 288 /** 289 * Time shift command parameter: playback params. 290 * <p>Type: Integer. One of {@link TvInputManager#TIME_SHIFT_MODE_OFF}, 291 * {@link TvInputManager#TIME_SHIFT_MODE_LOCAL}, 292 * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK}, 293 * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}. 294 * 295 * @see #TIME_SHIFT_COMMAND_TYPE_SET_MODE 296 */ 297 public static final String COMMAND_PARAMETER_KEY_TIME_SHIFT_MODE = "command_time_shift_mode"; 298 299 private final Handler mServiceHandler = new ServiceHandler(); 300 private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks = 301 new RemoteCallbackList<>(); 302 303 @Override 304 @Nullable onBind(@onNull Intent intent)305 public final IBinder onBind(@NonNull Intent intent) { 306 ITvInteractiveAppService.Stub tvIAppServiceBinder = new ITvInteractiveAppService.Stub() { 307 @Override 308 public void registerCallback(ITvInteractiveAppServiceCallback cb) { 309 if (cb != null) { 310 mCallbacks.register(cb); 311 } 312 } 313 314 @Override 315 public void unregisterCallback(ITvInteractiveAppServiceCallback cb) { 316 if (cb != null) { 317 mCallbacks.unregister(cb); 318 } 319 } 320 321 @Override 322 public void createSession(InputChannel channel, ITvInteractiveAppSessionCallback cb, 323 String iAppServiceId, int type) { 324 if (cb == null) { 325 return; 326 } 327 SomeArgs args = SomeArgs.obtain(); 328 args.arg1 = channel; 329 args.arg2 = cb; 330 args.arg3 = iAppServiceId; 331 args.arg4 = type; 332 mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args) 333 .sendToTarget(); 334 } 335 336 @Override 337 public void registerAppLinkInfo(AppLinkInfo appLinkInfo) { 338 onRegisterAppLinkInfo(appLinkInfo); 339 } 340 341 @Override 342 public void unregisterAppLinkInfo(AppLinkInfo appLinkInfo) { 343 onUnregisterAppLinkInfo(appLinkInfo); 344 } 345 346 @Override 347 public void sendAppLinkCommand(Bundle command) { 348 onAppLinkCommand(command); 349 } 350 }; 351 return tvIAppServiceBinder; 352 } 353 354 /** 355 * Called when a request to register an Android application link info record is received. 356 */ onRegisterAppLinkInfo(@onNull AppLinkInfo appLinkInfo)357 public void onRegisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) { 358 } 359 360 /** 361 * Called when a request to unregister an Android application link info record is received. 362 */ onUnregisterAppLinkInfo(@onNull AppLinkInfo appLinkInfo)363 public void onUnregisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) { 364 } 365 366 /** 367 * Called when app link command is received. 368 * 369 * @see android.media.tv.interactive.TvInteractiveAppManager#sendAppLinkCommand(String, Bundle) 370 */ onAppLinkCommand(@onNull Bundle command)371 public void onAppLinkCommand(@NonNull Bundle command) { 372 } 373 374 375 /** 376 * Returns a concrete implementation of {@link Session}. 377 * 378 * <p>May return {@code null} if this TV Interactive App service fails to create a session for 379 * some reason. 380 * 381 * @param iAppServiceId The ID of the TV Interactive App associated with the session. 382 * @param type The type of the TV Interactive App associated with the session. 383 */ 384 @Nullable onCreateSession( @onNull String iAppServiceId, @TvInteractiveAppServiceInfo.InteractiveAppType int type)385 public abstract Session onCreateSession( 386 @NonNull String iAppServiceId, 387 @TvInteractiveAppServiceInfo.InteractiveAppType int type); 388 389 /** 390 * Notifies the system when the state of the interactive app RTE has been changed. 391 * 392 * @param type the interactive app type 393 * @param state the current state of the service of the given type 394 * @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is 395 * used when the state is not 396 * {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}. 397 */ notifyStateChanged( @vInteractiveAppServiceInfo.InteractiveAppType int type, @TvInteractiveAppManager.ServiceState int state, @TvInteractiveAppManager.ErrorCode int error)398 public final void notifyStateChanged( 399 @TvInteractiveAppServiceInfo.InteractiveAppType int type, 400 @TvInteractiveAppManager.ServiceState int state, 401 @TvInteractiveAppManager.ErrorCode int error) { 402 SomeArgs args = SomeArgs.obtain(); 403 args.arg1 = type; 404 args.arg2 = state; 405 args.arg3 = error; 406 mServiceHandler 407 .obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, args).sendToTarget(); 408 } 409 410 /** 411 * Base class for derived classes to implement to provide a TV interactive app session. 412 * 413 * <p>A session is associated with a {@link TvInteractiveAppView} instance and handles 414 * corresponding communications. It also handles the communications with 415 * {@link android.media.tv.TvInputService.Session} if connected. 416 * 417 * @see TvInteractiveAppView#setTvView(TvView) 418 */ 419 public abstract static class Session implements KeyEvent.Callback { 420 private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 421 422 private final Object mLock = new Object(); 423 // @GuardedBy("mLock") 424 private ITvInteractiveAppSessionCallback mSessionCallback; 425 // @GuardedBy("mLock") 426 private final List<Runnable> mPendingActions = new ArrayList<>(); 427 428 private final Context mContext; 429 final Handler mHandler; 430 private final WindowManager mWindowManager; 431 private WindowManager.LayoutParams mWindowParams; 432 private Surface mSurface; 433 private FrameLayout mMediaViewContainer; 434 private View mMediaView; 435 private MediaViewCleanUpTask mMediaViewCleanUpTask; 436 private boolean mMediaViewEnabled; 437 private IBinder mWindowToken; 438 private Rect mMediaFrame; 439 440 /** 441 * Creates a new Session. 442 * 443 * @param context The context of the application 444 */ Session(@onNull Context context)445 public Session(@NonNull Context context) { 446 mContext = context; 447 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 448 mHandler = new Handler(context.getMainLooper()); 449 } 450 451 /** 452 * Enables or disables the media view. 453 * 454 * <p>By default, the media view is disabled. Must be called explicitly after the 455 * session is created to enable the media view. 456 * 457 * <p>The TV Interactive App service can disable its media view when needed. 458 * 459 * @param enable {@code true} if you want to enable the media view. {@code false} 460 * otherwise. 461 */ 462 @CallSuper setMediaViewEnabled(final boolean enable)463 public void setMediaViewEnabled(final boolean enable) { 464 mHandler.post(new Runnable() { 465 @Override 466 public void run() { 467 if (enable == mMediaViewEnabled) { 468 return; 469 } 470 mMediaViewEnabled = enable; 471 if (enable) { 472 if (mWindowToken != null) { 473 createMediaView(mWindowToken, mMediaFrame); 474 } 475 } else { 476 removeMediaView(false); 477 } 478 } 479 }); 480 } 481 482 /** 483 * Returns {@code true} if media view is enabled, {@code false} otherwise. 484 * 485 * @see #setMediaViewEnabled(boolean) 486 */ isMediaViewEnabled()487 public boolean isMediaViewEnabled() { 488 return mMediaViewEnabled; 489 } 490 491 /** 492 * Starts TvInteractiveAppService session. 493 */ onStartInteractiveApp()494 public void onStartInteractiveApp() { 495 } 496 497 /** 498 * Stops TvInteractiveAppService session. 499 */ onStopInteractiveApp()500 public void onStopInteractiveApp() { 501 } 502 503 /** 504 * Resets TvInteractiveAppService session. 505 */ onResetInteractiveApp()506 public void onResetInteractiveApp() { 507 } 508 509 /** 510 * Creates broadcast-independent(BI) interactive application. 511 * 512 * <p>The implementation should call {@link #notifyBiInteractiveAppCreated(Uri, String)}, 513 * no matter if it's created successfully or not. 514 * 515 * @see #notifyBiInteractiveAppCreated(Uri, String) 516 * @see #onDestroyBiInteractiveAppRequest(String) 517 */ onCreateBiInteractiveAppRequest( @onNull Uri biIAppUri, @Nullable Bundle params)518 public void onCreateBiInteractiveAppRequest( 519 @NonNull Uri biIAppUri, @Nullable Bundle params) { 520 } 521 522 523 /** 524 * Destroys broadcast-independent(BI) interactive application. 525 * 526 * @param biIAppId the BI interactive app ID from 527 * {@link #onCreateBiInteractiveAppRequest(Uri, Bundle)} 528 * 529 * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) 530 */ onDestroyBiInteractiveAppRequest(@onNull String biIAppId)531 public void onDestroyBiInteractiveAppRequest(@NonNull String biIAppId) { 532 } 533 534 /** 535 * To toggle Digital Teletext Application if there is one in AIT app list. 536 * @param enable {@code true} to enable teletext app; {@code false} otherwise. 537 */ onSetTeletextAppEnabled(boolean enable)538 public void onSetTeletextAppEnabled(boolean enable) { 539 } 540 541 /** 542 * Receives current video bounds. 543 * 544 * @param bounds the rectangle area for rendering the current video. 545 */ onCurrentVideoBounds(@onNull Rect bounds)546 public void onCurrentVideoBounds(@NonNull Rect bounds) { 547 } 548 549 /** 550 * Receives current channel URI. 551 */ onCurrentChannelUri(@ullable Uri channelUri)552 public void onCurrentChannelUri(@Nullable Uri channelUri) { 553 } 554 555 /** 556 * Receives logical channel number (LCN) of current channel. 557 */ onCurrentChannelLcn(int lcn)558 public void onCurrentChannelLcn(int lcn) { 559 } 560 561 /** 562 * Receives current stream volume. 563 * 564 * @param volume a volume value between {@code 0.0f} and {@code 1.0f}, inclusive. 565 */ onStreamVolume(float volume)566 public void onStreamVolume(float volume) { 567 } 568 569 /** 570 * Receives track list. 571 */ onTrackInfoList(@onNull List<TvTrackInfo> tracks)572 public void onTrackInfoList(@NonNull List<TvTrackInfo> tracks) { 573 } 574 575 /** 576 * Receives current TV input ID. 577 */ onCurrentTvInputId(@ullable String inputId)578 public void onCurrentTvInputId(@Nullable String inputId) { 579 } 580 581 /** 582 * Receives current time shift mode. 583 * 584 * @param mode The current time shift mode. The value is one of the following: 585 * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL}, 586 * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK}, 587 * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}. 588 */ onTimeShiftMode(@ndroid.media.tv.TvInputManager.TimeShiftMode int mode)589 public void onTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) { 590 } 591 592 /** 593 * Receives available playback speeds. 594 * 595 * @param speeds An ordered array of playback speeds, expressed as values relative to the 596 * normal playback speed (1.0), at which the current content can be played as 597 * a time-shifted broadcast. This is an empty array if the supported playback 598 * speeds are unknown or the video/broadcast is not in time shift mode. If 599 * currently in time shift mode, this array will normally include at least 600 * the values 1.0 (normal speed) and 0.0 (paused). 601 */ onAvailableSpeeds(@onNull float[] speeds)602 public void onAvailableSpeeds(@NonNull float[] speeds) { 603 } 604 605 /** 606 * Receives the requested {@link android.media.tv.TvRecordingInfo}. 607 * 608 * @see #requestTvRecordingInfo(String) 609 * @param recordingInfo The requested recording info. {@code null} if no recording found. 610 */ onTvRecordingInfo(@ullable TvRecordingInfo recordingInfo)611 public void onTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) { 612 } 613 614 /** 615 * Receives requested recording info list. 616 * 617 * @see #requestTvRecordingInfoList(int) 618 * @param recordingInfoList The list of recording info requested. Returns an empty list if 619 * no matching recording info found. 620 */ onTvRecordingInfoList(@onNull List<TvRecordingInfo> recordingInfoList)621 public void onTvRecordingInfoList(@NonNull List<TvRecordingInfo> recordingInfoList) {} 622 623 /** 624 * This is called when a recording has been started. 625 * 626 * <p>When a scheduled recording is started, this is also called, and the request ID in this 627 * case is {@code null}. 628 * 629 * @param recordingId The ID of the recording started. The TV app should provide and 630 * maintain this ID to identify the recording in the future. 631 * @param requestId The ID of the request when 632 * {@link #requestStartRecording(String, Uri)} is called. 633 * {@code null} if the recording is not triggered by a 634 * {@link #requestStartRecording(String, Uri)} request. 635 * This ID should be created by the {@link TvInteractiveAppService} and 636 * can be any string. 637 * @see #onRecordingStopped(String) 638 */ onRecordingStarted(@onNull String recordingId, @Nullable String requestId)639 public void onRecordingStarted(@NonNull String recordingId, @Nullable String requestId) { 640 } 641 642 /** 643 * This is called when the recording has been stopped. 644 * 645 * @param recordingId The ID of the recording stopped. This ID is created and maintained by 646 * the TV app when the recording was started. 647 * @see #onRecordingStarted(String, String) 648 */ onRecordingStopped(@onNull String recordingId)649 public void onRecordingStopped(@NonNull String recordingId) { 650 } 651 652 /** 653 * This is called when an error occurred while establishing a connection to the recording 654 * session for the corresponding TV input. 655 * 656 * @param recordingId The ID of the related recording which is sent via 657 * {@link TvInteractiveAppView#notifyRecordingStarted(String, String)} 658 * @param inputId The ID of the TV input bound to the current TvRecordingClient. 659 * @see android.media.tv.TvRecordingClient.RecordingCallback#onConnectionFailed(String) 660 */ onRecordingConnectionFailed( @onNull String recordingId, @NonNull String inputId)661 public void onRecordingConnectionFailed( 662 @NonNull String recordingId, @NonNull String inputId) { 663 } 664 665 /** 666 * This is called when the connection to the current recording session is lost. 667 * 668 * @param recordingId The ID of the related recording which is sent via 669 * {@link TvInteractiveAppView#notifyRecordingStarted(String, String)} 670 * @param inputId The ID of the TV input bound to the current TvRecordingClient. 671 * @see android.media.tv.TvRecordingClient.RecordingCallback#onDisconnected(String) 672 */ onRecordingDisconnected(@onNull String recordingId, @NonNull String inputId)673 public void onRecordingDisconnected(@NonNull String recordingId, @NonNull String inputId) { 674 } 675 676 /** 677 * This is called when the recording session has been tuned to the given channel and is 678 * ready to start recording. 679 * 680 * @param recordingId The ID of the related recording which is sent via 681 * {@link TvInteractiveAppView#notifyRecordingStarted(String, String)} 682 * @param channelUri The URI of the tuned channel. 683 * @see android.media.tv.TvRecordingClient.RecordingCallback#onTuned(Uri) 684 */ onRecordingTuned(@onNull String recordingId, @NonNull Uri channelUri)685 public void onRecordingTuned(@NonNull String recordingId, @NonNull Uri channelUri) { 686 } 687 688 /** 689 * This is called when an issue has occurred. It may be called at any time after the current 690 * recording session is created until it is released. 691 * 692 * @param recordingId The ID of the related recording which is sent via 693 * {@link TvInteractiveAppView#notifyRecordingStarted(String, String)} 694 * @param err The error code. Should be one of the following. 695 * <ul> 696 * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN} 697 * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE} 698 * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY} 699 * </ul> 700 * @see android.media.tv.TvRecordingClient.RecordingCallback#onError(int) 701 */ onRecordingError( @onNull String recordingId, @TvInputManager.RecordingError int err)702 public void onRecordingError( 703 @NonNull String recordingId, @TvInputManager.RecordingError int err) { 704 } 705 706 /** 707 * This is called when the recording has been scheduled. 708 * 709 * @param recordingId The ID assigned to this recording by the app. It can be used to send 710 * recording related requests such as 711 * {@link #requestStopRecording(String)}. 712 * @param requestId The ID of the request when 713 * {@link #requestScheduleRecording} is called. 714 * {@code null} if the recording is not triggered by a request. 715 * This ID should be created by the {@link TvInteractiveAppService} and 716 * can be any string. 717 */ onRecordingScheduled(@onNull String recordingId, @Nullable String requestId)718 public void onRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) { 719 } 720 721 /** 722 * Receives signing result. 723 * @param signingId the ID to identify the request. It's the same as the corresponding ID in 724 * {@link Session#requestSigning(String, String, String, byte[])} 725 * @param result the signed result. 726 * 727 * @see #requestSigning(String, String, String, byte[]) 728 */ onSigningResult(@onNull String signingId, @NonNull byte[] result)729 public void onSigningResult(@NonNull String signingId, @NonNull byte[] result) { 730 } 731 732 /** 733 * Called when the application sends information of an error. 734 * 735 * @param errMsg the message of the error. 736 * @param params additional parameters of the error. For example, the signingId of {@link 737 * TvInteractiveAppCallback#onRequestSigning(String, String, String, String, byte[])} 738 * can be included to identify the related signing request, and the method name 739 * "onRequestSigning" can also be added to the params. 740 * 741 * @see TvInteractiveAppView#ERROR_KEY_METHOD_NAME 742 */ onError(@onNull String errMsg, @NonNull Bundle params)743 public void onError(@NonNull String errMsg, @NonNull Bundle params) { 744 } 745 746 /** 747 * Called when the time shift {@link android.media.PlaybackParams} is set or changed. 748 * 749 * @param params The new {@link PlaybackParams} that was set or changed. 750 * @see TvView#timeShiftSetPlaybackParams(PlaybackParams) 751 */ onTimeShiftPlaybackParams(@onNull PlaybackParams params)752 public void onTimeShiftPlaybackParams(@NonNull PlaybackParams params) { 753 } 754 755 /** 756 * Called when time shift status is changed. 757 * 758 * @see TvView.TvInputCallback#onTimeShiftStatusChanged(String, int) 759 * @see android.media.tv.TvInputService.Session#notifyTimeShiftStatusChanged(int) 760 * @param inputId The ID of the input for which the time shift status has changed. 761 * @param status The status of which the input has changed to. Should be one of the 762 * following. 763 * <ul> 764 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN} 765 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED} 766 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE} 767 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} 768 * </ul> 769 */ onTimeShiftStatusChanged( @onNull String inputId, @TvInputManager.TimeShiftStatus int status)770 public void onTimeShiftStatusChanged( 771 @NonNull String inputId, @TvInputManager.TimeShiftStatus int status) {} 772 773 /** 774 * Called when time shift start position is changed. 775 * 776 * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged(String, long) 777 * @param inputId The ID of the input for which the time shift start position has changed. 778 * @param timeMs The start position for time shifting, in milliseconds since the epoch. 779 */ onTimeShiftStartPositionChanged(@onNull String inputId, long timeMs)780 public void onTimeShiftStartPositionChanged(@NonNull String inputId, long timeMs) { 781 } 782 783 /** 784 * Called when time shift current position is changed. 785 * 786 * @see TvView.TimeShiftPositionCallback#onTimeShiftCurrentPositionChanged(String, long) 787 * @param inputId The ID of the input for which the time shift current position has changed. 788 * @param timeMs The current position for time shifting, in milliseconds since the epoch. 789 */ onTimeShiftCurrentPositionChanged(@onNull String inputId, long timeMs)790 public void onTimeShiftCurrentPositionChanged(@NonNull String inputId, long timeMs) { 791 } 792 793 /** 794 * Called when the application sets the surface. 795 * 796 * <p>The TV Interactive App service should render interactive app UI onto the given 797 * surface. When called with {@code null}, the Interactive App service should immediately 798 * free any references to the currently set surface and stop using it. 799 * 800 * @param surface The surface to be used for interactive app UI rendering. Can be 801 * {@code null}. 802 * @return {@code true} if the surface was set successfully, {@code false} otherwise. 803 */ onSetSurface(@ullable Surface surface)804 public abstract boolean onSetSurface(@Nullable Surface surface); 805 806 /** 807 * Called after any structural changes (format or size) have been made to the surface passed 808 * in {@link #onSetSurface}. This method is always called at least once, after 809 * {@link #onSetSurface} is called with non-null surface. 810 * 811 * @param format The new {@link PixelFormat} of the surface. 812 * @param width The new width of the surface. 813 * @param height The new height of the surface. 814 */ onSurfaceChanged(@ixelFormat.Format int format, int width, int height)815 public void onSurfaceChanged(@PixelFormat.Format int format, int width, int height) { 816 } 817 818 /** 819 * Called when the size of the media view is changed by the application. 820 * 821 * <p>This is always called at least once when the session is created regardless of whether 822 * the media view is enabled or not. The media view container size is the same as the 823 * containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can 824 * be different if the surface was changed by calling {@link #layoutSurface}. 825 * 826 * @param width The width of the media view, in pixels. 827 * @param height The height of the media view, in pixels. 828 */ onMediaViewSizeChanged(@x int width, @Px int height)829 public void onMediaViewSizeChanged(@Px int width, @Px int height) { 830 } 831 832 /** 833 * Called when the application requests to create an media view. Each session 834 * implementation can override this method and return its own view. 835 * 836 * @return a view attached to the media window 837 */ 838 @Nullable onCreateMediaView()839 public View onCreateMediaView() { 840 return null; 841 } 842 843 /** 844 * Releases TvInteractiveAppService session. 845 */ onRelease()846 public abstract void onRelease(); 847 848 /** 849 * Called when the corresponding TV input tuned to a channel. 850 * 851 * @param channelUri The tuned channel URI. 852 */ onTuned(@onNull Uri channelUri)853 public void onTuned(@NonNull Uri channelUri) { 854 } 855 856 /** 857 * Called when the corresponding TV input selected to a track. 858 */ onTrackSelected(@vTrackInfo.Type int type, @NonNull String trackId)859 public void onTrackSelected(@TvTrackInfo.Type int type, @NonNull String trackId) { 860 } 861 862 /** 863 * Called when the tracks are changed. 864 */ onTracksChanged(@onNull List<TvTrackInfo> tracks)865 public void onTracksChanged(@NonNull List<TvTrackInfo> tracks) { 866 } 867 868 /** 869 * Called when video is available. 870 */ onVideoAvailable()871 public void onVideoAvailable() { 872 } 873 874 /** 875 * Called when video is unavailable. 876 */ onVideoUnavailable(@vInputManager.VideoUnavailableReason int reason)877 public void onVideoUnavailable(@TvInputManager.VideoUnavailableReason int reason) { 878 } 879 880 /** 881 * Called when content is allowed. 882 */ onContentAllowed()883 public void onContentAllowed() { 884 } 885 886 /** 887 * Called when content is blocked. 888 */ onContentBlocked(@onNull TvContentRating rating)889 public void onContentBlocked(@NonNull TvContentRating rating) { 890 } 891 892 /** 893 * Called when signal strength is changed. 894 */ onSignalStrength(@vInputManager.SignalStrength int strength)895 public void onSignalStrength(@TvInputManager.SignalStrength int strength) { 896 } 897 898 /** 899 * Called when a broadcast info response is received. 900 */ onBroadcastInfoResponse(@onNull BroadcastInfoResponse response)901 public void onBroadcastInfoResponse(@NonNull BroadcastInfoResponse response) { 902 } 903 904 /** 905 * Called when an advertisement response is received. 906 */ onAdResponse(@onNull AdResponse response)907 public void onAdResponse(@NonNull AdResponse response) { 908 } 909 910 /** 911 * Called when an advertisement buffer is consumed. 912 * 913 * @param buffer The {@link AdBuffer} that was consumed. 914 */ onAdBufferConsumed(@onNull AdBuffer buffer)915 public void onAdBufferConsumed(@NonNull AdBuffer buffer) { 916 } 917 918 /** 919 * Called when a TV message is received 920 * 921 * @param type The type of message received, such as 922 * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK} 923 * @param data The raw data of the message. The bundle keys are: 924 * {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID}, 925 * {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID}, 926 * {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE}, 927 * {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}. 928 * See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on 929 * how to parse this data. 930 */ onTvMessage(@vInputManager.TvMessageType int type, @NonNull Bundle data)931 public void onTvMessage(@TvInputManager.TvMessageType int type, 932 @NonNull Bundle data) { 933 } 934 935 @Override onKeyDown(int keyCode, @NonNull KeyEvent event)936 public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { 937 return false; 938 } 939 940 @Override onKeyLongPress(int keyCode, @NonNull KeyEvent event)941 public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) { 942 return false; 943 } 944 945 @Override onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event)946 public boolean onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event) { 947 return false; 948 } 949 950 @Override onKeyUp(int keyCode, @NonNull KeyEvent event)951 public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { 952 return false; 953 } 954 955 /** 956 * Implement this method to handle touch screen motion events on the current session. 957 * 958 * @param event The motion event being received. 959 * @return If you handled the event, return {@code true}. If you want to allow the event to 960 * be handled by the next receiver, return {@code false}. 961 * @see View#onTouchEvent 962 */ onTouchEvent(@onNull MotionEvent event)963 public boolean onTouchEvent(@NonNull MotionEvent event) { 964 return false; 965 } 966 967 /** 968 * Implement this method to handle trackball events on the current session. 969 * 970 * @param event The motion event being received. 971 * @return If you handled the event, return {@code true}. If you want to allow the event to 972 * be handled by the next receiver, return {@code false}. 973 * @see View#onTrackballEvent 974 */ onTrackballEvent(@onNull MotionEvent event)975 public boolean onTrackballEvent(@NonNull MotionEvent event) { 976 return false; 977 } 978 979 /** 980 * Implement this method to handle generic motion events on the current session. 981 * 982 * @param event The motion event being received. 983 * @return If you handled the event, return {@code true}. If you want to allow the event to 984 * be handled by the next receiver, return {@code false}. 985 * @see View#onGenericMotionEvent 986 */ onGenericMotionEvent(@onNull MotionEvent event)987 public boolean onGenericMotionEvent(@NonNull MotionEvent event) { 988 return false; 989 } 990 991 /** 992 * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position 993 * is relative to the overlay view that sits on top of this surface. 994 * 995 * @param left Left position in pixels, relative to the overlay view. 996 * @param top Top position in pixels, relative to the overlay view. 997 * @param right Right position in pixels, relative to the overlay view. 998 * @param bottom Bottom position in pixels, relative to the overlay view. 999 */ 1000 @CallSuper layoutSurface(final int left, final int top, final int right, final int bottom)1001 public void layoutSurface(final int left, final int top, final int right, 1002 final int bottom) { 1003 if (left > right || top > bottom) { 1004 throw new IllegalArgumentException("Invalid parameter"); 1005 } 1006 executeOrPostRunnableOnMainThread(new Runnable() { 1007 @MainThread 1008 @Override 1009 public void run() { 1010 try { 1011 if (DEBUG) { 1012 Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top 1013 + ", r=" + right + ", b=" + bottom + ",)"); 1014 } 1015 if (mSessionCallback != null) { 1016 mSessionCallback.onLayoutSurface(left, top, right, bottom); 1017 } 1018 } catch (RemoteException e) { 1019 Log.w(TAG, "error in layoutSurface", e); 1020 } 1021 } 1022 }); 1023 } 1024 1025 /** 1026 * Requests broadcast related information from the related TV input. 1027 * @param request the request for broadcast info 1028 */ 1029 @CallSuper requestBroadcastInfo(@onNull final BroadcastInfoRequest request)1030 public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) { 1031 executeOrPostRunnableOnMainThread(new Runnable() { 1032 @MainThread 1033 @Override 1034 public void run() { 1035 try { 1036 if (DEBUG) { 1037 Log.d(TAG, "requestBroadcastInfo (requestId=" 1038 + request.getRequestId() + ")"); 1039 } 1040 if (mSessionCallback != null) { 1041 mSessionCallback.onBroadcastInfoRequest(request); 1042 } 1043 } catch (RemoteException e) { 1044 Log.w(TAG, "error in requestBroadcastInfo", e); 1045 } 1046 } 1047 }); 1048 } 1049 1050 /** 1051 * Remove broadcast information request from the related TV input. 1052 * @param requestId the ID of the request 1053 */ 1054 @CallSuper removeBroadcastInfo(final int requestId)1055 public void removeBroadcastInfo(final int requestId) { 1056 executeOrPostRunnableOnMainThread(new Runnable() { 1057 @MainThread 1058 @Override 1059 public void run() { 1060 try { 1061 if (DEBUG) { 1062 Log.d(TAG, "removeBroadcastInfo (requestId=" 1063 + requestId + ")"); 1064 } 1065 if (mSessionCallback != null) { 1066 mSessionCallback.onRemoveBroadcastInfo(requestId); 1067 } 1068 } catch (RemoteException e) { 1069 Log.w(TAG, "error in removeBroadcastInfo", e); 1070 } 1071 } 1072 }); 1073 } 1074 1075 /** 1076 * Sends a specific playback command to be processed by the related TV input. 1077 * 1078 * @param cmdType type of the specific command 1079 * @param parameters parameters of the specific command 1080 */ 1081 @CallSuper sendPlaybackCommandRequest( @laybackCommandType @onNull String cmdType, @Nullable Bundle parameters)1082 public void sendPlaybackCommandRequest( 1083 @PlaybackCommandType @NonNull String cmdType, @Nullable Bundle parameters) { 1084 executeOrPostRunnableOnMainThread(new Runnable() { 1085 @MainThread 1086 @Override 1087 public void run() { 1088 try { 1089 if (DEBUG) { 1090 Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters=" 1091 + parameters.toString() + ")"); 1092 } 1093 if (mSessionCallback != null) { 1094 mSessionCallback.onCommandRequest(cmdType, parameters); 1095 } 1096 } catch (RemoteException e) { 1097 Log.w(TAG, "error in requestCommand", e); 1098 } 1099 } 1100 }); 1101 } 1102 1103 /** 1104 * Sends a specific time shift command to be processed by the related TV input. 1105 * 1106 * @param cmdType type of the specific command 1107 * @param parameters parameters of the specific command 1108 */ 1109 @CallSuper sendTimeShiftCommandRequest( @imeShiftCommandType @onNull String cmdType, @Nullable Bundle parameters)1110 public void sendTimeShiftCommandRequest( 1111 @TimeShiftCommandType @NonNull String cmdType, @Nullable Bundle parameters) { 1112 executeOrPostRunnableOnMainThread(new Runnable() { 1113 @MainThread 1114 @Override 1115 public void run() { 1116 try { 1117 if (DEBUG) { 1118 Log.d(TAG, "requestTimeShiftCommand (cmdType=" + cmdType 1119 + ", parameters=" + parameters.toString() + ")"); 1120 } 1121 if (mSessionCallback != null) { 1122 mSessionCallback.onTimeShiftCommandRequest(cmdType, parameters); 1123 } 1124 } catch (RemoteException e) { 1125 Log.w(TAG, "error in requestTimeShiftCommand", e); 1126 } 1127 } 1128 }); 1129 } 1130 1131 /** 1132 * Sets broadcast video bounds. 1133 */ 1134 @CallSuper setVideoBounds(@onNull Rect rect)1135 public void setVideoBounds(@NonNull Rect rect) { 1136 executeOrPostRunnableOnMainThread(new Runnable() { 1137 @MainThread 1138 @Override 1139 public void run() { 1140 try { 1141 if (DEBUG) { 1142 Log.d(TAG, "setVideoBounds (rect=" + rect + ")"); 1143 } 1144 if (mSessionCallback != null) { 1145 mSessionCallback.onSetVideoBounds(rect); 1146 } 1147 } catch (RemoteException e) { 1148 Log.w(TAG, "error in setVideoBounds", e); 1149 } 1150 } 1151 }); 1152 } 1153 1154 /** 1155 * Requests the bounds of the current video. 1156 */ 1157 @CallSuper requestCurrentVideoBounds()1158 public void requestCurrentVideoBounds() { 1159 executeOrPostRunnableOnMainThread(new Runnable() { 1160 @MainThread 1161 @Override 1162 public void run() { 1163 try { 1164 if (DEBUG) { 1165 Log.d(TAG, "requestCurrentVideoBounds"); 1166 } 1167 if (mSessionCallback != null) { 1168 mSessionCallback.onRequestCurrentVideoBounds(); 1169 } 1170 } catch (RemoteException e) { 1171 Log.w(TAG, "error in requestCurrentVideoBounds", e); 1172 } 1173 } 1174 }); 1175 } 1176 1177 /** 1178 * Requests the URI of the current channel. 1179 */ 1180 @CallSuper requestCurrentChannelUri()1181 public void requestCurrentChannelUri() { 1182 executeOrPostRunnableOnMainThread(new Runnable() { 1183 @MainThread 1184 @Override 1185 public void run() { 1186 try { 1187 if (DEBUG) { 1188 Log.d(TAG, "requestCurrentChannelUri"); 1189 } 1190 if (mSessionCallback != null) { 1191 mSessionCallback.onRequestCurrentChannelUri(); 1192 } 1193 } catch (RemoteException e) { 1194 Log.w(TAG, "error in requestCurrentChannelUri", e); 1195 } 1196 } 1197 }); 1198 } 1199 1200 /** 1201 * Requests the logic channel number (LCN) of the current channel. 1202 */ 1203 @CallSuper requestCurrentChannelLcn()1204 public void requestCurrentChannelLcn() { 1205 executeOrPostRunnableOnMainThread(new Runnable() { 1206 @MainThread 1207 @Override 1208 public void run() { 1209 try { 1210 if (DEBUG) { 1211 Log.d(TAG, "requestCurrentChannelLcn"); 1212 } 1213 if (mSessionCallback != null) { 1214 mSessionCallback.onRequestCurrentChannelLcn(); 1215 } 1216 } catch (RemoteException e) { 1217 Log.w(TAG, "error in requestCurrentChannelLcn", e); 1218 } 1219 } 1220 }); 1221 } 1222 1223 /** 1224 * Requests stream volume. 1225 */ 1226 @CallSuper requestStreamVolume()1227 public void requestStreamVolume() { 1228 executeOrPostRunnableOnMainThread(new Runnable() { 1229 @MainThread 1230 @Override 1231 public void run() { 1232 try { 1233 if (DEBUG) { 1234 Log.d(TAG, "requestStreamVolume"); 1235 } 1236 if (mSessionCallback != null) { 1237 mSessionCallback.onRequestStreamVolume(); 1238 } 1239 } catch (RemoteException e) { 1240 Log.w(TAG, "error in requestStreamVolume", e); 1241 } 1242 } 1243 }); 1244 } 1245 1246 /** 1247 * Requests the list of {@link TvTrackInfo}. 1248 */ 1249 @CallSuper requestTrackInfoList()1250 public void requestTrackInfoList() { 1251 executeOrPostRunnableOnMainThread(new Runnable() { 1252 @MainThread 1253 @Override 1254 public void run() { 1255 try { 1256 if (DEBUG) { 1257 Log.d(TAG, "requestTrackInfoList"); 1258 } 1259 if (mSessionCallback != null) { 1260 mSessionCallback.onRequestTrackInfoList(); 1261 } 1262 } catch (RemoteException e) { 1263 Log.w(TAG, "error in requestTrackInfoList", e); 1264 } 1265 } 1266 }); 1267 } 1268 1269 /** 1270 * Requests current TV input ID. 1271 * 1272 * @see android.media.tv.TvInputInfo 1273 */ 1274 @CallSuper requestCurrentTvInputId()1275 public void requestCurrentTvInputId() { 1276 executeOrPostRunnableOnMainThread(new Runnable() { 1277 @MainThread 1278 @Override 1279 public void run() { 1280 try { 1281 if (DEBUG) { 1282 Log.d(TAG, "requestCurrentTvInputId"); 1283 } 1284 if (mSessionCallback != null) { 1285 mSessionCallback.onRequestCurrentTvInputId(); 1286 } 1287 } catch (RemoteException e) { 1288 Log.w(TAG, "error in requestCurrentTvInputId", e); 1289 } 1290 } 1291 }); 1292 } 1293 1294 /** 1295 * Requests time shift mode. 1296 */ 1297 @CallSuper requestTimeShiftMode()1298 public void requestTimeShiftMode() { 1299 executeOrPostRunnableOnMainThread(new Runnable() { 1300 @MainThread 1301 @Override 1302 public void run() { 1303 try { 1304 if (DEBUG) { 1305 Log.d(TAG, "requestTimeShiftMode"); 1306 } 1307 if (mSessionCallback != null) { 1308 mSessionCallback.onRequestTimeShiftMode(); 1309 } 1310 } catch (RemoteException e) { 1311 Log.w(TAG, "error in requestTimeShiftMode", e); 1312 } 1313 } 1314 }); 1315 } 1316 1317 /** 1318 * Requests available speeds for time shift. 1319 */ 1320 @CallSuper requestAvailableSpeeds()1321 public void requestAvailableSpeeds() { 1322 executeOrPostRunnableOnMainThread(new Runnable() { 1323 @MainThread 1324 @Override 1325 public void run() { 1326 try { 1327 if (DEBUG) { 1328 Log.d(TAG, "requestAvailableSpeeds"); 1329 } 1330 if (mSessionCallback != null) { 1331 mSessionCallback.onRequestAvailableSpeeds(); 1332 } 1333 } catch (RemoteException e) { 1334 Log.w(TAG, "error in requestAvailableSpeeds", e); 1335 } 1336 } 1337 }); 1338 } 1339 1340 /** 1341 * Requests starting of recording 1342 * 1343 * <p> This is used to request the active {@link android.media.tv.TvRecordingClient} to 1344 * call {@link android.media.tv.TvRecordingClient#startRecording(Uri)} with the provided 1345 * {@code programUri}. 1346 * A non-null {@code programUri} implies the started recording should be of that specific 1347 * program, whereas null {@code programUri} does not impose such a requirement and the 1348 * recording can span across multiple TV programs. 1349 * 1350 * @param requestId The ID of this request which is used to match the corresponding 1351 * response. The request ID in 1352 * {@link #onRecordingStarted(String, String)} for this request is the 1353 * same as the ID sent here. This should be defined by the 1354 * {@link TvInteractiveAppService} and can be any string. 1355 * Should this API be called with the same requestId twice, both 1356 * requests should be handled regardless by the TV application. 1357 * @param programUri The URI for the TV program to record, built by 1358 * {@link TvContract#buildProgramUri(long)}. Can be {@code null}. 1359 * @see android.media.tv.TvRecordingClient#startRecording(Uri) 1360 */ 1361 @CallSuper requestStartRecording(@onNull String requestId, @Nullable Uri programUri)1362 public void requestStartRecording(@NonNull String requestId, @Nullable Uri programUri) { 1363 executeOrPostRunnableOnMainThread(() -> { 1364 try { 1365 if (DEBUG) { 1366 Log.d(TAG, "requestStartRecording"); 1367 } 1368 if (mSessionCallback != null) { 1369 mSessionCallback.onRequestStartRecording(requestId, programUri); 1370 } 1371 } catch (RemoteException e) { 1372 Log.w(TAG, "error in requestStartRecording", e); 1373 } 1374 }); 1375 } 1376 1377 /** 1378 * Requests the recording associated with the recordingId to stop. 1379 * 1380 * <p> This is used to request the associated {@link android.media.tv.TvRecordingClient} to 1381 * call {@link android.media.tv.TvRecordingClient#stopRecording()}. 1382 * 1383 * @param recordingId The ID of the recording to stop. This is provided by the TV app in 1384 * {@link TvInteractiveAppView#notifyRecordingStarted(String, String)} 1385 * @see android.media.tv.TvRecordingClient#stopRecording() 1386 */ 1387 @CallSuper requestStopRecording(@onNull String recordingId)1388 public void requestStopRecording(@NonNull String recordingId) { 1389 executeOrPostRunnableOnMainThread(() -> { 1390 try { 1391 if (DEBUG) { 1392 Log.d(TAG, "requestStopRecording"); 1393 } 1394 if (mSessionCallback != null) { 1395 mSessionCallback.onRequestStopRecording(recordingId); 1396 } 1397 } catch (RemoteException e) { 1398 Log.w(TAG, "error in requestStopRecording", e); 1399 } 1400 }); 1401 } 1402 1403 /** 1404 * Requests scheduling of a recording. 1405 * 1406 * @param requestId The ID of this request which is used to match the corresponding 1407 * response. The request ID in 1408 * {@link #onRecordingScheduled(String, String)} for this request is the 1409 * same as the ID sent here. This should be defined by the 1410 * {@link TvInteractiveAppService} and can be any string. 1411 * Should this API be called with the same requestId twice, both requests 1412 * should be handled regardless by the TV application. 1413 * @param inputId The ID of the TV input for the given channel. 1414 * @param channelUri The URI of a channel to be recorded. 1415 * @param programUri The URI of the TV program to be recorded. 1416 * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 1417 * name, i.e. prefixed with a package name you own, so that different developers 1418 * will not create conflicting keys. 1419 * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle) 1420 * @see android.media.tv.TvRecordingClient#startRecording(Uri) 1421 */ 1422 @CallSuper requestScheduleRecording(@onNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params)1423 public void requestScheduleRecording(@NonNull String requestId, @NonNull String inputId, 1424 @NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params) { 1425 executeOrPostRunnableOnMainThread(() -> { 1426 try { 1427 if (DEBUG) { 1428 Log.d(TAG, "requestScheduleRecording"); 1429 } 1430 if (mSessionCallback != null) { 1431 mSessionCallback.onRequestScheduleRecording( 1432 requestId, inputId, channelUri, programUri, params); 1433 } 1434 } catch (RemoteException e) { 1435 Log.w(TAG, "error in requestScheduleRecording", e); 1436 } 1437 }); 1438 } 1439 1440 /** 1441 * Requests scheduling of a recording. 1442 * 1443 * @param requestId The ID of this request which is used to match the corresponding 1444 * response. The request ID in 1445 * {@link #onRecordingScheduled(String, String)} for this request is the 1446 * same as the ID sent here. This should be defined by the 1447 * {@link TvInteractiveAppService} and can be any string. Should this API 1448 * be called with the same requestId twice, both requests should be handled 1449 * regardless by the TV application. 1450 * @param inputId The ID of the TV input for the given channel. 1451 * @param channelUri The URI of a channel to be recorded. 1452 * @param startTime The start time of the recording in milliseconds since epoch. 1453 * @param duration The duration of the recording in milliseconds. 1454 * @param repeatDays The repeated days. 0 if not repeated. 1455 * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 1456 * name, i.e. prefixed with a package name you own, so that different developers 1457 * will not create conflicting keys. 1458 * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle) 1459 * @see android.media.tv.TvRecordingClient#startRecording(Uri) 1460 */ 1461 @CallSuper requestScheduleRecording(@onNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration, int repeatDays, @NonNull Bundle params)1462 public void requestScheduleRecording(@NonNull String requestId, @NonNull String inputId, 1463 @NonNull Uri channelUri, long startTime, long duration, int repeatDays, 1464 @NonNull Bundle params) { 1465 executeOrPostRunnableOnMainThread(() -> { 1466 try { 1467 if (DEBUG) { 1468 Log.d(TAG, "requestScheduleRecording"); 1469 } 1470 if (mSessionCallback != null) { 1471 mSessionCallback.onRequestScheduleRecording2(requestId, inputId, channelUri, 1472 startTime, duration, repeatDays, params); 1473 } 1474 } catch (RemoteException e) { 1475 Log.w(TAG, "error in requestScheduleRecording", e); 1476 } 1477 }); 1478 } 1479 1480 /** 1481 * Sets the recording info for the specified recording 1482 * 1483 * @param recordingId The ID of the recording to set the info for. This is provided by the 1484 * TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String, String)} 1485 * @param recordingInfo The {@link TvRecordingInfo} to set to the recording. 1486 */ 1487 @CallSuper setTvRecordingInfo( @onNull String recordingId, @NonNull TvRecordingInfo recordingInfo)1488 public void setTvRecordingInfo( 1489 @NonNull String recordingId, @NonNull TvRecordingInfo recordingInfo) { 1490 executeOrPostRunnableOnMainThread(() -> { 1491 try { 1492 if (DEBUG) { 1493 Log.d(TAG, "setTvRecordingInfo"); 1494 } 1495 if (mSessionCallback != null) { 1496 mSessionCallback.onSetTvRecordingInfo(recordingId, recordingInfo); 1497 } 1498 } catch (RemoteException e) { 1499 Log.w(TAG, "error in setTvRecordingInfo", e); 1500 } 1501 }); 1502 } 1503 1504 /** 1505 * Gets the recording info for the specified recording 1506 * @param recordingId The ID of the recording to set the info for. This is provided by the 1507 * TV app in 1508 * {@link TvInteractiveAppView#notifyRecordingStarted(String, String)} 1509 */ 1510 @CallSuper requestTvRecordingInfo(@onNull String recordingId)1511 public void requestTvRecordingInfo(@NonNull String recordingId) { 1512 executeOrPostRunnableOnMainThread(() -> { 1513 try { 1514 if (DEBUG) { 1515 Log.d(TAG, "requestTvRecordingInfo"); 1516 } 1517 if (mSessionCallback != null) { 1518 mSessionCallback.onRequestTvRecordingInfo(recordingId); 1519 } 1520 } catch (RemoteException e) { 1521 Log.w(TAG, "error in requestTvRecordingInfo", e); 1522 } 1523 }); 1524 } 1525 1526 /** 1527 * Gets a list of {@link TvRecordingInfo} for the specified recording type. 1528 * 1529 * @param type The type of recording to retrieve. 1530 */ 1531 @CallSuper requestTvRecordingInfoList(@vRecordingInfo.TvRecordingListType int type)1532 public void requestTvRecordingInfoList(@TvRecordingInfo.TvRecordingListType int type) { 1533 executeOrPostRunnableOnMainThread(() -> { 1534 try { 1535 if (DEBUG) { 1536 Log.d(TAG, "requestTvRecordingInfoList"); 1537 } 1538 if (mSessionCallback != null) { 1539 mSessionCallback.onRequestTvRecordingInfoList(type); 1540 } 1541 } catch (RemoteException e) { 1542 Log.w(TAG, "error in requestTvRecordingInfoList", e); 1543 } 1544 }); 1545 } 1546 1547 /** 1548 * Requests signing of the given data. 1549 * 1550 * <p>This is used when the corresponding server of the broadcast-independent interactive 1551 * app requires signing during handshaking, and the interactive app service doesn't have 1552 * the built-in private key. The private key is provided by the content providers and 1553 * pre-built in the related app, such as TV app. 1554 * 1555 * @param signingId the ID to identify the request. When a result is received, this ID can 1556 * be used to correlate the result with the request. 1557 * @param algorithm the standard name of the signature algorithm requested, such as 1558 * MD5withRSA, SHA256withDSA, etc. The name is from standards like 1559 * FIPS PUB 186-4 and PKCS #1. 1560 * @param alias the alias of the corresponding {@link java.security.KeyStore}. 1561 * @param data the original bytes to be signed. 1562 * 1563 * @see #onSigningResult(String, byte[]) 1564 * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) 1565 * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS 1566 */ 1567 @CallSuper requestSigning(@onNull String signingId, @NonNull String algorithm, @NonNull String alias, @NonNull byte[] data)1568 public void requestSigning(@NonNull String signingId, @NonNull String algorithm, 1569 @NonNull String alias, @NonNull byte[] data) { 1570 executeOrPostRunnableOnMainThread(new Runnable() { 1571 @MainThread 1572 @Override 1573 public void run() { 1574 try { 1575 if (DEBUG) { 1576 Log.d(TAG, "requestSigning"); 1577 } 1578 if (mSessionCallback != null) { 1579 mSessionCallback.onRequestSigning(signingId, algorithm, alias, data); 1580 } 1581 } catch (RemoteException e) { 1582 Log.w(TAG, "error in requestSigning", e); 1583 } 1584 } 1585 }); 1586 } 1587 1588 /** 1589 * Sends an advertisement request to be processed by the related TV input. 1590 * 1591 * @param request The advertisement request 1592 */ 1593 @CallSuper requestAd(@onNull final AdRequest request)1594 public void requestAd(@NonNull final AdRequest request) { 1595 executeOrPostRunnableOnMainThread(new Runnable() { 1596 @MainThread 1597 @Override 1598 public void run() { 1599 try { 1600 if (DEBUG) { 1601 Log.d(TAG, "requestAd (id=" + request.getId() + ")"); 1602 } 1603 if (mSessionCallback != null) { 1604 mSessionCallback.onAdRequest(request); 1605 } 1606 } catch (RemoteException e) { 1607 Log.w(TAG, "error in requestAd", e); 1608 } 1609 } 1610 }); 1611 } 1612 startInteractiveApp()1613 void startInteractiveApp() { 1614 onStartInteractiveApp(); 1615 } 1616 stopInteractiveApp()1617 void stopInteractiveApp() { 1618 onStopInteractiveApp(); 1619 } 1620 resetInteractiveApp()1621 void resetInteractiveApp() { 1622 onResetInteractiveApp(); 1623 } 1624 createBiInteractiveApp(@onNull Uri biIAppUri, @Nullable Bundle params)1625 void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { 1626 onCreateBiInteractiveAppRequest(biIAppUri, params); 1627 } 1628 destroyBiInteractiveApp(@onNull String biIAppId)1629 void destroyBiInteractiveApp(@NonNull String biIAppId) { 1630 onDestroyBiInteractiveAppRequest(biIAppId); 1631 } 1632 setTeletextAppEnabled(boolean enable)1633 void setTeletextAppEnabled(boolean enable) { 1634 onSetTeletextAppEnabled(enable); 1635 } 1636 sendCurrentVideoBounds(@onNull Rect bounds)1637 void sendCurrentVideoBounds(@NonNull Rect bounds) { 1638 onCurrentVideoBounds(bounds); 1639 } 1640 sendCurrentChannelUri(@ullable Uri channelUri)1641 void sendCurrentChannelUri(@Nullable Uri channelUri) { 1642 onCurrentChannelUri(channelUri); 1643 } 1644 sendCurrentChannelLcn(int lcn)1645 void sendCurrentChannelLcn(int lcn) { 1646 onCurrentChannelLcn(lcn); 1647 } 1648 sendStreamVolume(float volume)1649 void sendStreamVolume(float volume) { 1650 onStreamVolume(volume); 1651 } 1652 sendTrackInfoList(@onNull List<TvTrackInfo> tracks)1653 void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) { 1654 onTrackInfoList(tracks); 1655 } 1656 sendCurrentTvInputId(@ullable String inputId)1657 void sendCurrentTvInputId(@Nullable String inputId) { 1658 onCurrentTvInputId(inputId); 1659 } 1660 sendTimeShiftMode(int mode)1661 void sendTimeShiftMode(int mode) { 1662 onTimeShiftMode(mode); 1663 } 1664 sendAvailableSpeeds(@onNull float[] speeds)1665 void sendAvailableSpeeds(@NonNull float[] speeds) { 1666 onAvailableSpeeds(speeds); 1667 } 1668 sendTvRecordingInfo(@ullable TvRecordingInfo recordingInfo)1669 void sendTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) { 1670 onTvRecordingInfo(recordingInfo); 1671 } 1672 sendTvRecordingInfoList(@ullable List<TvRecordingInfo> recordingInfoList)1673 void sendTvRecordingInfoList(@Nullable List<TvRecordingInfo> recordingInfoList) { 1674 onTvRecordingInfoList(recordingInfoList); 1675 } 1676 sendSigningResult(String signingId, byte[] result)1677 void sendSigningResult(String signingId, byte[] result) { 1678 onSigningResult(signingId, result); 1679 } 1680 notifyError(String errMsg, Bundle params)1681 void notifyError(String errMsg, Bundle params) { 1682 onError(errMsg, params); 1683 } 1684 release()1685 void release() { 1686 onRelease(); 1687 if (mSurface != null) { 1688 mSurface.release(); 1689 mSurface = null; 1690 } 1691 synchronized (mLock) { 1692 mSessionCallback = null; 1693 mPendingActions.clear(); 1694 } 1695 // Removes the media view lastly so that any hanging on the main thread can be handled 1696 // in {@link #scheduleMediaViewCleanup}. 1697 removeMediaView(true); 1698 } 1699 notifyTuned(Uri channelUri)1700 void notifyTuned(Uri channelUri) { 1701 if (DEBUG) { 1702 Log.d(TAG, "notifyTuned (channelUri=" + channelUri + ")"); 1703 } 1704 onTuned(channelUri); 1705 } 1706 notifyTrackSelected(int type, String trackId)1707 void notifyTrackSelected(int type, String trackId) { 1708 if (DEBUG) { 1709 Log.d(TAG, "notifyTrackSelected (type=" + type + "trackId=" + trackId + ")"); 1710 } 1711 onTrackSelected(type, trackId); 1712 } 1713 notifyTracksChanged(List<TvTrackInfo> tracks)1714 void notifyTracksChanged(List<TvTrackInfo> tracks) { 1715 if (DEBUG) { 1716 Log.d(TAG, "notifyTracksChanged (tracks=" + tracks + ")"); 1717 } 1718 onTracksChanged(tracks); 1719 } 1720 notifyVideoAvailable()1721 void notifyVideoAvailable() { 1722 if (DEBUG) { 1723 Log.d(TAG, "notifyVideoAvailable"); 1724 } 1725 onVideoAvailable(); 1726 } 1727 notifyVideoUnavailable(int reason)1728 void notifyVideoUnavailable(int reason) { 1729 if (DEBUG) { 1730 Log.d(TAG, "notifyVideoAvailable (reason=" + reason + ")"); 1731 } 1732 onVideoUnavailable(reason); 1733 } 1734 notifyContentAllowed()1735 void notifyContentAllowed() { 1736 if (DEBUG) { 1737 Log.d(TAG, "notifyContentAllowed"); 1738 } 1739 onContentAllowed(); 1740 } 1741 notifyContentBlocked(TvContentRating rating)1742 void notifyContentBlocked(TvContentRating rating) { 1743 if (DEBUG) { 1744 Log.d(TAG, "notifyContentBlocked (rating=" + rating.flattenToString() + ")"); 1745 } 1746 onContentBlocked(rating); 1747 } 1748 notifySignalStrength(int strength)1749 void notifySignalStrength(int strength) { 1750 if (DEBUG) { 1751 Log.d(TAG, "notifySignalStrength (strength=" + strength + ")"); 1752 } 1753 onSignalStrength(strength); 1754 } 1755 1756 /** 1757 * Calls {@link #onBroadcastInfoResponse}. 1758 */ notifyBroadcastInfoResponse(BroadcastInfoResponse response)1759 void notifyBroadcastInfoResponse(BroadcastInfoResponse response) { 1760 if (DEBUG) { 1761 Log.d(TAG, "notifyBroadcastInfoResponse (requestId=" 1762 + response.getRequestId() + ")"); 1763 } 1764 onBroadcastInfoResponse(response); 1765 } 1766 1767 /** 1768 * Calls {@link #onAdResponse}. 1769 */ notifyAdResponse(AdResponse response)1770 void notifyAdResponse(AdResponse response) { 1771 if (DEBUG) { 1772 Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")"); 1773 } 1774 onAdResponse(response); 1775 } 1776 notifyTvMessage(int type, Bundle data)1777 void notifyTvMessage(int type, Bundle data) { 1778 if (DEBUG) { 1779 Log.d(TAG, "notifyTvMessage (type=" + type + ", data= " + data + ")"); 1780 } 1781 onTvMessage(type, data); 1782 } 1783 1784 /** 1785 * Calls {@link #onAdBufferConsumed}. 1786 */ notifyAdBufferConsumed(AdBuffer buffer)1787 void notifyAdBufferConsumed(AdBuffer buffer) { 1788 if (DEBUG) { 1789 Log.d(TAG, 1790 "notifyAdBufferConsumed (buffer=" + buffer + ")"); 1791 } 1792 onAdBufferConsumed(buffer); 1793 } 1794 1795 /** 1796 * Calls {@link #onRecordingStarted(String, String)}. 1797 */ notifyRecordingStarted(String recordingId, String requestId)1798 void notifyRecordingStarted(String recordingId, String requestId) { 1799 onRecordingStarted(recordingId, requestId); 1800 } 1801 1802 /** 1803 * Calls {@link #onRecordingStopped(String)}. 1804 */ notifyRecordingStopped(String recordingId)1805 void notifyRecordingStopped(String recordingId) { 1806 onRecordingStopped(recordingId); 1807 } 1808 1809 /** 1810 * Calls {@link #onRecordingConnectionFailed(String, String)}. 1811 */ notifyRecordingConnectionFailed(String recordingId, String inputId)1812 void notifyRecordingConnectionFailed(String recordingId, String inputId) { 1813 onRecordingConnectionFailed(recordingId, inputId); 1814 } 1815 1816 /** 1817 * Calls {@link #onRecordingDisconnected(String, String)}. 1818 */ notifyRecordingDisconnected(String recordingId, String inputId)1819 void notifyRecordingDisconnected(String recordingId, String inputId) { 1820 onRecordingDisconnected(recordingId, inputId); 1821 } 1822 1823 /** 1824 * Calls {@link #onRecordingTuned(String, Uri)}. 1825 */ notifyRecordingTuned(String recordingId, Uri channelUri)1826 void notifyRecordingTuned(String recordingId, Uri channelUri) { 1827 onRecordingTuned(recordingId, channelUri); 1828 } 1829 1830 /** 1831 * Calls {@link #onRecordingError(String, int)}. 1832 */ notifyRecordingError(String recordingId, int err)1833 void notifyRecordingError(String recordingId, int err) { 1834 onRecordingError(recordingId, err); 1835 } 1836 1837 /** 1838 * Calls {@link #onRecordingScheduled(String, String)}. 1839 */ notifyRecordingScheduled(String recordingId, String requestId)1840 void notifyRecordingScheduled(String recordingId, String requestId) { 1841 onRecordingScheduled(recordingId, requestId); 1842 } 1843 1844 /** 1845 * Calls {@link #onTimeShiftPlaybackParams(PlaybackParams)}. 1846 */ notifyTimeShiftPlaybackParams(PlaybackParams params)1847 void notifyTimeShiftPlaybackParams(PlaybackParams params) { 1848 onTimeShiftPlaybackParams(params); 1849 } 1850 1851 /** 1852 * Calls {@link #onTimeShiftStatusChanged(String, int)}. 1853 */ notifyTimeShiftStatusChanged(String inputId, int status)1854 void notifyTimeShiftStatusChanged(String inputId, int status) { 1855 onTimeShiftStatusChanged(inputId, status); 1856 } 1857 1858 /** 1859 * Calls {@link #onTimeShiftStartPositionChanged(String, long)}. 1860 */ notifyTimeShiftStartPositionChanged(String inputId, long timeMs)1861 void notifyTimeShiftStartPositionChanged(String inputId, long timeMs) { 1862 onTimeShiftStartPositionChanged(inputId, timeMs); 1863 } 1864 1865 /** 1866 * Calls {@link #onTimeShiftCurrentPositionChanged(String, long)}. 1867 */ notifyTimeShiftCurrentPositionChanged(String inputId, long timeMs)1868 void notifyTimeShiftCurrentPositionChanged(String inputId, long timeMs) { 1869 onTimeShiftCurrentPositionChanged(inputId, timeMs); 1870 } 1871 1872 /** 1873 * Notifies when the session state is changed. 1874 * 1875 * @param state the current session state. 1876 * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is 1877 * used when the state is not 1878 * {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}. 1879 */ 1880 @CallSuper notifySessionStateChanged( @vInteractiveAppManager.InteractiveAppState int state, @TvInteractiveAppManager.ErrorCode int err)1881 public void notifySessionStateChanged( 1882 @TvInteractiveAppManager.InteractiveAppState int state, 1883 @TvInteractiveAppManager.ErrorCode int err) { 1884 executeOrPostRunnableOnMainThread(new Runnable() { 1885 @MainThread 1886 @Override 1887 public void run() { 1888 try { 1889 if (DEBUG) { 1890 Log.d(TAG, "notifySessionStateChanged (state=" 1891 + state + "; err=" + err + ")"); 1892 } 1893 if (mSessionCallback != null) { 1894 mSessionCallback.onSessionStateChanged(state, err); 1895 } 1896 } catch (RemoteException e) { 1897 Log.w(TAG, "error in notifySessionStateChanged", e); 1898 } 1899 } 1900 }); 1901 } 1902 1903 /** 1904 * Notifies the broadcast-independent(BI) interactive application has been created. 1905 * 1906 * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive 1907 * app. {@code null} if it's not created successfully. 1908 * 1909 * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) 1910 */ 1911 @CallSuper notifyBiInteractiveAppCreated( @onNull Uri biIAppUri, @Nullable String biIAppId)1912 public final void notifyBiInteractiveAppCreated( 1913 @NonNull Uri biIAppUri, @Nullable String biIAppId) { 1914 executeOrPostRunnableOnMainThread(new Runnable() { 1915 @MainThread 1916 @Override 1917 public void run() { 1918 try { 1919 if (DEBUG) { 1920 Log.d(TAG, "notifyBiInteractiveAppCreated (biIAppId=" 1921 + biIAppId + ")"); 1922 } 1923 if (mSessionCallback != null) { 1924 mSessionCallback.onBiInteractiveAppCreated(biIAppUri, biIAppId); 1925 } 1926 } catch (RemoteException e) { 1927 Log.w(TAG, "error in notifyBiInteractiveAppCreated", e); 1928 } 1929 } 1930 }); 1931 } 1932 1933 /** 1934 * Notifies when the digital teletext app state is changed. 1935 * @param state the current state. 1936 */ 1937 @CallSuper notifyTeletextAppStateChanged( @vInteractiveAppManager.TeletextAppState int state)1938 public final void notifyTeletextAppStateChanged( 1939 @TvInteractiveAppManager.TeletextAppState int state) { 1940 executeOrPostRunnableOnMainThread(new Runnable() { 1941 @MainThread 1942 @Override 1943 public void run() { 1944 try { 1945 if (DEBUG) { 1946 Log.d(TAG, "notifyTeletextAppState (state=" 1947 + state + ")"); 1948 } 1949 if (mSessionCallback != null) { 1950 mSessionCallback.onTeletextAppStateChanged(state); 1951 } 1952 } catch (RemoteException e) { 1953 Log.w(TAG, "error in notifyTeletextAppState", e); 1954 } 1955 } 1956 }); 1957 } 1958 1959 1960 /** 1961 * Notifies when the advertisement buffer is filled and ready to be read. 1962 * 1963 * @param buffer The {@link AdBuffer} to be received 1964 */ 1965 @CallSuper notifyAdBufferReady(@onNull AdBuffer buffer)1966 public void notifyAdBufferReady(@NonNull AdBuffer buffer) { 1967 AdBuffer dupBuffer; 1968 try { 1969 dupBuffer = AdBuffer.dupAdBuffer(buffer); 1970 } catch (IOException e) { 1971 Log.w(TAG, "dup AdBuffer error in notifyAdBufferReady:", e); 1972 return; 1973 } 1974 executeOrPostRunnableOnMainThread(new Runnable() { 1975 @MainThread 1976 @Override 1977 public void run() { 1978 try { 1979 if (DEBUG) { 1980 Log.d(TAG, 1981 "notifyAdBufferReady(buffer=" + buffer + ")"); 1982 } 1983 if (mSessionCallback != null) { 1984 mSessionCallback.onAdBufferReady(dupBuffer); 1985 } 1986 } catch (RemoteException e) { 1987 Log.w(TAG, "error in notifyAdBuffer", e); 1988 } finally { 1989 if (dupBuffer != null) { 1990 dupBuffer.getSharedMemory().close(); 1991 } 1992 } 1993 } 1994 }); 1995 } 1996 1997 1998 /** 1999 * Takes care of dispatching incoming input events and tells whether the event was handled. 2000 */ dispatchInputEvent(InputEvent event, InputEventReceiver receiver)2001 int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { 2002 if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")"); 2003 if (event instanceof KeyEvent) { 2004 KeyEvent keyEvent = (KeyEvent) event; 2005 if (keyEvent.dispatch(this, mDispatcherState, this)) { 2006 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 2007 } 2008 2009 // TODO: special handlings of navigation keys and media keys 2010 } else if (event instanceof MotionEvent) { 2011 MotionEvent motionEvent = (MotionEvent) event; 2012 final int source = motionEvent.getSource(); 2013 if (motionEvent.isTouchEvent()) { 2014 if (onTouchEvent(motionEvent)) { 2015 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 2016 } 2017 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 2018 if (onTrackballEvent(motionEvent)) { 2019 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 2020 } 2021 } else { 2022 if (onGenericMotionEvent(motionEvent)) { 2023 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 2024 } 2025 } 2026 } 2027 // TODO: handle overlay view 2028 return TvInteractiveAppManager.Session.DISPATCH_NOT_HANDLED; 2029 } 2030 initialize(ITvInteractiveAppSessionCallback callback)2031 private void initialize(ITvInteractiveAppSessionCallback callback) { 2032 synchronized (mLock) { 2033 mSessionCallback = callback; 2034 for (Runnable runnable : mPendingActions) { 2035 runnable.run(); 2036 } 2037 mPendingActions.clear(); 2038 } 2039 } 2040 2041 /** 2042 * Calls {@link #onSetSurface}. 2043 */ setSurface(Surface surface)2044 void setSurface(Surface surface) { 2045 onSetSurface(surface); 2046 if (mSurface != null) { 2047 mSurface.release(); 2048 } 2049 mSurface = surface; 2050 // TODO: Handle failure. 2051 } 2052 2053 /** 2054 * Calls {@link #onSurfaceChanged}. 2055 */ dispatchSurfaceChanged(int format, int width, int height)2056 void dispatchSurfaceChanged(int format, int width, int height) { 2057 if (DEBUG) { 2058 Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width 2059 + ", height=" + height + ")"); 2060 } 2061 onSurfaceChanged(format, width, height); 2062 } 2063 executeOrPostRunnableOnMainThread(Runnable action)2064 private void executeOrPostRunnableOnMainThread(Runnable action) { 2065 synchronized (mLock) { 2066 if (mSessionCallback == null) { 2067 // The session is not initialized yet. 2068 mPendingActions.add(action); 2069 } else { 2070 if (mHandler.getLooper().isCurrentThread()) { 2071 action.run(); 2072 } else { 2073 // Posts the runnable if this is not called from the main thread 2074 mHandler.post(action); 2075 } 2076 } 2077 } 2078 } 2079 2080 /** 2081 * Creates an media view. This calls {@link #onCreateMediaView} to get a view to attach 2082 * to the media window. 2083 * 2084 * @param windowToken A window token of the application. 2085 * @param frame A position of the media view. 2086 */ createMediaView(IBinder windowToken, Rect frame)2087 void createMediaView(IBinder windowToken, Rect frame) { 2088 if (mMediaViewContainer != null) { 2089 removeMediaView(false); 2090 } 2091 if (DEBUG) Log.d(TAG, "create media view(" + frame + ")"); 2092 mWindowToken = windowToken; 2093 mMediaFrame = frame; 2094 onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); 2095 if (!mMediaViewEnabled) { 2096 return; 2097 } 2098 mMediaView = onCreateMediaView(); 2099 if (mMediaView == null) { 2100 return; 2101 } 2102 if (mMediaViewCleanUpTask != null) { 2103 mMediaViewCleanUpTask.cancel(true); 2104 mMediaViewCleanUpTask = null; 2105 } 2106 // Creates a container view to check hanging on the media view detaching. 2107 // Adding/removing the media view to/from the container make the view attach/detach 2108 // logic run on the main thread. 2109 mMediaViewContainer = new FrameLayout(mContext.getApplicationContext()); 2110 mMediaViewContainer.addView(mMediaView); 2111 2112 int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 2113 // We make the overlay view non-focusable and non-touchable so that 2114 // the application that owns the window token can decide whether to consume or 2115 // dispatch the input events. 2116 int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 2117 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 2118 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; 2119 if (ActivityManager.isHighEndGfx()) { 2120 flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 2121 } 2122 mWindowParams = new WindowManager.LayoutParams( 2123 frame.right - frame.left, frame.bottom - frame.top, 2124 frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT); 2125 mWindowParams.privateFlags |= 2126 WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 2127 mWindowParams.gravity = Gravity.START | Gravity.TOP; 2128 mWindowParams.token = windowToken; 2129 mWindowManager.addView(mMediaViewContainer, mWindowParams); 2130 } 2131 2132 /** 2133 * Relayouts the current media view. 2134 * 2135 * @param frame A new position of the media view. 2136 */ relayoutMediaView(Rect frame)2137 void relayoutMediaView(Rect frame) { 2138 if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")"); 2139 if (mMediaFrame == null || mMediaFrame.width() != frame.width() 2140 || mMediaFrame.height() != frame.height()) { 2141 // Note: relayoutMediaView is called whenever TvInteractiveAppView's layout is 2142 // changed regardless of setMediaViewEnabled. 2143 onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); 2144 } 2145 mMediaFrame = frame; 2146 if (!mMediaViewEnabled || mMediaViewContainer == null) { 2147 return; 2148 } 2149 mWindowParams.x = frame.left; 2150 mWindowParams.y = frame.top; 2151 mWindowParams.width = frame.right - frame.left; 2152 mWindowParams.height = frame.bottom - frame.top; 2153 mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams); 2154 } 2155 2156 /** 2157 * Removes the current media view. 2158 */ removeMediaView(boolean clearWindowToken)2159 void removeMediaView(boolean clearWindowToken) { 2160 if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")"); 2161 if (clearWindowToken) { 2162 mWindowToken = null; 2163 mMediaFrame = null; 2164 } 2165 if (mMediaViewContainer != null) { 2166 // Removes the media view from the view hierarchy in advance so that it can be 2167 // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is 2168 // hanging. 2169 mMediaViewContainer.removeView(mMediaView); 2170 mMediaView = null; 2171 mWindowManager.removeView(mMediaViewContainer); 2172 mMediaViewContainer = null; 2173 mWindowParams = null; 2174 } 2175 } 2176 2177 /** 2178 * Schedules a task which checks whether the media view is detached and kills the process 2179 * if it is not. Note that this method is expected to be called in a non-main thread. 2180 */ scheduleMediaViewCleanup()2181 void scheduleMediaViewCleanup() { 2182 View mediaViewParent = mMediaViewContainer; 2183 if (mediaViewParent != null) { 2184 mMediaViewCleanUpTask = new MediaViewCleanUpTask(); 2185 mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 2186 mediaViewParent); 2187 } 2188 } 2189 } 2190 2191 private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> { 2192 @Override doInBackground(View... views)2193 protected Void doInBackground(View... views) { 2194 View mediaViewParent = views[0]; 2195 try { 2196 Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS); 2197 } catch (InterruptedException e) { 2198 return null; 2199 } 2200 if (isCancelled()) { 2201 return null; 2202 } 2203 if (mediaViewParent.isAttachedToWindow()) { 2204 Log.e(TAG, "Time out on releasing media view. Killing " 2205 + mediaViewParent.getContext().getPackageName()); 2206 android.os.Process.killProcess(Process.myPid()); 2207 } 2208 return null; 2209 } 2210 } 2211 2212 @SuppressLint("HandlerLeak") 2213 private final class ServiceHandler extends Handler { 2214 private static final int DO_CREATE_SESSION = 1; 2215 private static final int DO_NOTIFY_SESSION_CREATED = 2; 2216 private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3; 2217 broadcastRteStateChanged(int type, int state, int error)2218 private void broadcastRteStateChanged(int type, int state, int error) { 2219 int n = mCallbacks.beginBroadcast(); 2220 for (int i = 0; i < n; ++i) { 2221 try { 2222 mCallbacks.getBroadcastItem(i).onStateChanged(type, state, error); 2223 } catch (RemoteException e) { 2224 Log.e(TAG, "error in broadcastRteStateChanged", e); 2225 } 2226 } 2227 mCallbacks.finishBroadcast(); 2228 } 2229 2230 @Override handleMessage(Message msg)2231 public void handleMessage(Message msg) { 2232 switch (msg.what) { 2233 case DO_CREATE_SESSION: { 2234 SomeArgs args = (SomeArgs) msg.obj; 2235 InputChannel channel = (InputChannel) args.arg1; 2236 ITvInteractiveAppSessionCallback cb = 2237 (ITvInteractiveAppSessionCallback) args.arg2; 2238 String iAppServiceId = (String) args.arg3; 2239 int type = (int) args.arg4; 2240 args.recycle(); 2241 Session sessionImpl = onCreateSession(iAppServiceId, type); 2242 if (sessionImpl == null) { 2243 try { 2244 // Failed to create a session. 2245 cb.onSessionCreated(null); 2246 } catch (RemoteException e) { 2247 Log.e(TAG, "error in onSessionCreated", e); 2248 } 2249 return; 2250 } 2251 ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper( 2252 TvInteractiveAppService.this, sessionImpl, channel); 2253 2254 SomeArgs someArgs = SomeArgs.obtain(); 2255 someArgs.arg1 = sessionImpl; 2256 someArgs.arg2 = stub; 2257 someArgs.arg3 = cb; 2258 mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, 2259 someArgs).sendToTarget(); 2260 return; 2261 } 2262 case DO_NOTIFY_SESSION_CREATED: { 2263 SomeArgs args = (SomeArgs) msg.obj; 2264 Session sessionImpl = (Session) args.arg1; 2265 ITvInteractiveAppSession stub = (ITvInteractiveAppSession) args.arg2; 2266 ITvInteractiveAppSessionCallback cb = 2267 (ITvInteractiveAppSessionCallback) args.arg3; 2268 try { 2269 cb.onSessionCreated(stub); 2270 } catch (RemoteException e) { 2271 Log.e(TAG, "error in onSessionCreated", e); 2272 } 2273 if (sessionImpl != null) { 2274 sessionImpl.initialize(cb); 2275 } 2276 args.recycle(); 2277 return; 2278 } 2279 case DO_NOTIFY_RTE_STATE_CHANGED: { 2280 SomeArgs args = (SomeArgs) msg.obj; 2281 int type = (int) args.arg1; 2282 int state = (int) args.arg2; 2283 int error = (int) args.arg3; 2284 broadcastRteStateChanged(type, state, error); 2285 return; 2286 } 2287 default: { 2288 Log.w(TAG, "Unhandled message code: " + msg.what); 2289 return; 2290 } 2291 } 2292 } 2293 2294 } 2295 } 2296