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