1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.tv; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.FloatRange; 21 import android.annotation.IntDef; 22 import android.annotation.MainThread; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.SuppressLint; 26 import android.annotation.SystemApi; 27 import android.app.ActivityManager; 28 import android.app.Service; 29 import android.compat.annotation.UnsupportedAppUsage; 30 import android.content.AttributionSource; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.graphics.PixelFormat; 34 import android.graphics.Rect; 35 import android.hardware.hdmi.HdmiDeviceInfo; 36 import android.media.AudioPresentation; 37 import android.media.PlaybackParams; 38 import android.media.tv.ad.TvAdManager; 39 import android.media.tv.flags.Flags; 40 import android.media.tv.interactive.TvInteractiveAppService; 41 import android.net.Uri; 42 import android.os.AsyncTask; 43 import android.os.Build; 44 import android.os.Bundle; 45 import android.os.Handler; 46 import android.os.IBinder; 47 import android.os.Message; 48 import android.os.Process; 49 import android.os.RemoteCallbackList; 50 import android.os.RemoteException; 51 import android.text.TextUtils; 52 import android.util.Log; 53 import android.view.Gravity; 54 import android.view.InputChannel; 55 import android.view.InputDevice; 56 import android.view.InputEvent; 57 import android.view.InputEventReceiver; 58 import android.view.KeyEvent; 59 import android.view.MotionEvent; 60 import android.view.Surface; 61 import android.view.View; 62 import android.view.ViewRootImpl; 63 import android.view.WindowManager; 64 import android.view.accessibility.CaptioningManager; 65 import android.widget.FrameLayout; 66 67 import com.android.internal.os.SomeArgs; 68 import com.android.internal.util.Preconditions; 69 70 import java.io.IOException; 71 import java.lang.annotation.Retention; 72 import java.lang.annotation.RetentionPolicy; 73 import java.util.ArrayList; 74 import java.util.Arrays; 75 import java.util.List; 76 77 /** 78 * The TvInputService class represents a TV input or source such as HDMI or built-in tuner which 79 * provides pass-through video or broadcast TV programs. 80 * 81 * <p>Applications will not normally use this service themselves, instead relying on the standard 82 * interaction provided by {@link TvView}. Those implementing TV input services should normally do 83 * so by deriving from this class and providing their own session implementation based on 84 * {@link TvInputService.Session}. All TV input services must require that clients hold the 85 * {@link android.Manifest.permission#BIND_TV_INPUT} in order to interact with the service; if this 86 * permission is not specified in the manifest, the system will refuse to bind to that TV input 87 * service. 88 */ 89 public abstract class TvInputService extends Service { 90 private static final boolean DEBUG = false; 91 private static final String TAG = "TvInputService"; 92 93 private static final int DETACH_OVERLAY_VIEW_TIMEOUT_MS = 5000; 94 95 /** 96 * This is the interface name that a service implementing a TV input should say that it support 97 * -- that is, this is the action it uses for its intent filter. To be supported, the service 98 * must also require the {@link android.Manifest.permission#BIND_TV_INPUT} permission so that 99 * other applications cannot abuse it. 100 */ 101 public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService"; 102 103 /** 104 * Name under which a TvInputService component publishes information about itself. 105 * This meta-data must reference an XML resource containing an 106 * <code><{@link android.R.styleable#TvInputService tv-input}></code> 107 * tag. 108 */ 109 public static final String SERVICE_META_DATA = "android.media.tv.input"; 110 111 /** 112 * Prioirity hint from use case types. 113 * 114 * @hide 115 */ 116 @IntDef(prefix = "PRIORITY_HINT_USE_CASE_TYPE_", 117 value = {PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, PRIORITY_HINT_USE_CASE_TYPE_SCAN, 118 PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, PRIORITY_HINT_USE_CASE_TYPE_LIVE, 119 PRIORITY_HINT_USE_CASE_TYPE_RECORD}) 120 @Retention(RetentionPolicy.SOURCE) 121 public @interface PriorityHintUseCaseType {} 122 123 /** 124 * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, 125 * int)}: Background. TODO Link: Tuner#Tuner(Context, string, int). 126 */ 127 public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; 128 129 /** 130 * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, 131 * int)}: Scan. TODO Link: Tuner#Tuner(Context, string, int). 132 */ 133 public static final int PRIORITY_HINT_USE_CASE_TYPE_SCAN = 200; 134 135 /** 136 * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, 137 * int)}: Playback. TODO Link: Tuner#Tuner(Context, string, int). 138 */ 139 public static final int PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK = 300; 140 141 /** 142 * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, 143 * int)}: Live. TODO Link: Tuner#Tuner(Context, string, int). 144 */ 145 public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; 146 147 /** 148 * Use case of priority hint for {@link android.media.MediaCas#MediaCas(Context, int, String, 149 * int)}: Record. TODO Link: Tuner#Tuner(Context, string, int). 150 */ 151 public static final int PRIORITY_HINT_USE_CASE_TYPE_RECORD = 500; 152 153 /** 154 * Handler instance to handle request from TV Input Manager Service. Should be run in the main 155 * looper to be synchronously run with {@code Session.mHandler}. 156 */ 157 private final Handler mServiceHandler = new ServiceHandler(); 158 private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks = 159 new RemoteCallbackList<>(); 160 161 private TvInputManager mTvInputManager; 162 163 @Override onBind(Intent intent)164 public final IBinder onBind(Intent intent) { 165 ITvInputService.Stub tvInputServiceBinder = new ITvInputService.Stub() { 166 @Override 167 public void registerCallback(ITvInputServiceCallback cb) { 168 if (cb != null) { 169 mCallbacks.register(cb); 170 } 171 } 172 173 @Override 174 public void unregisterCallback(ITvInputServiceCallback cb) { 175 if (cb != null) { 176 mCallbacks.unregister(cb); 177 } 178 } 179 180 @Override 181 public void createSession(InputChannel channel, ITvInputSessionCallback cb, 182 String inputId, String sessionId, AttributionSource tvAppAttributionSource) { 183 if (channel == null) { 184 Log.w(TAG, "Creating session without input channel"); 185 } 186 if (cb == null) { 187 return; 188 } 189 SomeArgs args = SomeArgs.obtain(); 190 args.arg1 = channel; 191 args.arg2 = cb; 192 args.arg3 = inputId; 193 args.arg4 = sessionId; 194 args.arg5 = tvAppAttributionSource; 195 mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, 196 args).sendToTarget(); 197 } 198 199 @Override 200 public void createRecordingSession(ITvInputSessionCallback cb, String inputId, 201 String sessionId) { 202 if (cb == null) { 203 return; 204 } 205 SomeArgs args = SomeArgs.obtain(); 206 args.arg1 = cb; 207 args.arg2 = inputId; 208 args.arg3 = sessionId; 209 mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_RECORDING_SESSION, args) 210 .sendToTarget(); 211 } 212 213 @Override 214 public List<String> getAvailableExtensionInterfaceNames() { 215 return TvInputService.this.getAvailableExtensionInterfaceNames(); 216 } 217 218 @Override 219 public IBinder getExtensionInterface(String name) { 220 return TvInputService.this.getExtensionInterface(name); 221 } 222 223 @Override 224 public String getExtensionInterfacePermission(String name) { 225 return TvInputService.this.getExtensionInterfacePermission(name); 226 } 227 228 @Override 229 public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) { 230 mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_INPUT, 231 hardwareInfo).sendToTarget(); 232 } 233 234 @Override 235 public void notifyHardwareRemoved(TvInputHardwareInfo hardwareInfo) { 236 mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HARDWARE_INPUT, 237 hardwareInfo).sendToTarget(); 238 } 239 240 @Override 241 public void notifyHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { 242 mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_INPUT, 243 deviceInfo).sendToTarget(); 244 } 245 246 @Override 247 public void notifyHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { 248 mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_INPUT, 249 deviceInfo).sendToTarget(); 250 } 251 252 @Override 253 public void notifyHdmiDeviceUpdated(HdmiDeviceInfo deviceInfo) { 254 mServiceHandler.obtainMessage(ServiceHandler.DO_UPDATE_HDMI_INPUT, 255 deviceInfo).sendToTarget(); 256 } 257 }; 258 IBinder ext = createExtension(); 259 if (ext != null) { 260 tvInputServiceBinder.setExtension(ext); 261 } 262 return tvInputServiceBinder; 263 } 264 265 /** 266 * Returns a new {@link android.os.Binder} 267 * 268 * <p> if an extension is provided on top of existing {@link TvInputService}; otherwise, 269 * return {@code null}. Override to provide extended interface. 270 * 271 * @see android.os.Binder#setExtension(IBinder) 272 * @hide 273 */ 274 @Nullable 275 @SystemApi createExtension()276 public IBinder createExtension() { 277 return null; 278 } 279 280 /** 281 * Returns available extension interfaces. This can be used to provide domain-specific 282 * features that are only known between certain hardware TV inputs and their clients. 283 * 284 * <p>Note that this service-level extension interface mechanism is only for hardware 285 * TV inputs that are bound even when sessions are not created. 286 * 287 * @return a non-null list of available extension interface names. An empty list 288 * indicates the TV input doesn't support any extension interfaces. 289 * @see #getExtensionInterface 290 * @see #getExtensionInterfacePermission 291 * @hide 292 */ 293 @NonNull 294 @SystemApi getAvailableExtensionInterfaceNames()295 public List<String> getAvailableExtensionInterfaceNames() { 296 return new ArrayList<>(); 297 } 298 299 /** 300 * Returns an extension interface. This can be used to provide domain-specific features 301 * that are only known between certain hardware TV inputs and their clients. 302 * 303 * <p>Note that this service-level extension interface mechanism is only for hardware 304 * TV inputs that are bound even when sessions are not created. 305 * 306 * @param name The extension interface name. 307 * @return an {@link IBinder} for the given extension interface, {@code null} if the TV input 308 * doesn't support the given extension interface. 309 * @see #getAvailableExtensionInterfaceNames 310 * @see #getExtensionInterfacePermission 311 * @hide 312 */ 313 @Nullable 314 @SystemApi getExtensionInterface(@onNull String name)315 public IBinder getExtensionInterface(@NonNull String name) { 316 return null; 317 } 318 319 /** 320 * Returns a permission for the given extension interface. This can be used to provide 321 * domain-specific features that are only known between certain hardware TV inputs and their 322 * clients. 323 * 324 * <p>Note that this service-level extension interface mechanism is only for hardware 325 * TV inputs that are bound even when sessions are not created. 326 * 327 * @param name The extension interface name. 328 * @return a name of the permission being checked for the given extension interface, 329 * {@code null} if there is no required permission, or if the TV input doesn't 330 * support the given extension interface. 331 * @see #getAvailableExtensionInterfaceNames 332 * @see #getExtensionInterface 333 * @hide 334 */ 335 @Nullable 336 @SystemApi getExtensionInterfacePermission(@onNull String name)337 public String getExtensionInterfacePermission(@NonNull String name) { 338 return null; 339 } 340 341 /** 342 * Returns a concrete implementation of {@link Session}. 343 * 344 * <p>May return {@code null} if this TV input service fails to create a session for some 345 * reason. If TV input represents an external device connected to a hardware TV input, 346 * {@link HardwareSession} should be returned. 347 * 348 * @param inputId The ID of the TV input associated with the session. 349 */ 350 @Nullable onCreateSession(@onNull String inputId)351 public abstract Session onCreateSession(@NonNull String inputId); 352 353 /** 354 * Returns a concrete implementation of {@link RecordingSession}. 355 * 356 * <p>May return {@code null} if this TV input service fails to create a recording session for 357 * some reason. 358 * 359 * @param inputId The ID of the TV input associated with the recording session. 360 */ 361 @Nullable onCreateRecordingSession(@onNull String inputId)362 public RecordingSession onCreateRecordingSession(@NonNull String inputId) { 363 return null; 364 } 365 366 /** 367 * Returns a concrete implementation of {@link Session}. 368 * 369 * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager, 370 * it needs to override this method to get the sessionId passed. When no overriding, this method 371 * calls {@link #onCreateSession(String)} defaultly. 372 * 373 * @param inputId The ID of the TV input associated with the session. 374 * @param sessionId the unique sessionId created by TIF when session is created. 375 */ 376 @Nullable onCreateSession(@onNull String inputId, @NonNull String sessionId)377 public Session onCreateSession(@NonNull String inputId, @NonNull String sessionId) { 378 return onCreateSession(inputId); 379 } 380 381 /** 382 * Returns a concrete implementation of {@link Session}. 383 * 384 * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager and 385 * needs to specify custom AttributionSource to AudioTrack, it needs to override this method to 386 * get the sessionId and AttrubutionSource passed. When no overriding, this method calls {@link 387 * #onCreateSession(String, String)} defaultly. 388 * 389 * @param inputId The ID of the TV input associated with the session. 390 * @param sessionId the unique sessionId created by TIF when session is created. 391 * @param tvAppAttributionSource The Attribution Source of the TV App. 392 */ 393 @Nullable onCreateSession(@onNull String inputId, @NonNull String sessionId, @NonNull AttributionSource tvAppAttributionSource)394 public Session onCreateSession(@NonNull String inputId, @NonNull String sessionId, 395 @NonNull AttributionSource tvAppAttributionSource) { 396 return onCreateSession(inputId, sessionId); 397 } 398 399 /** 400 * Returns a concrete implementation of {@link RecordingSession}. 401 * 402 * <p>For any apps that needs sessionId to request tuner resources from TunerResourceManager, 403 * it needs to override this method to get the sessionId passed. When no overriding, this method 404 * calls {@link #onCreateRecordingSession(String)} defaultly. 405 * 406 * @param inputId The ID of the TV input associated with the recording session. 407 * @param sessionId the unique sessionId created by TIF when session is created. 408 */ 409 @Nullable onCreateRecordingSession( @onNull String inputId, @NonNull String sessionId)410 public RecordingSession onCreateRecordingSession( 411 @NonNull String inputId, @NonNull String sessionId) { 412 return onCreateRecordingSession(inputId); 413 } 414 415 /** 416 * Returns a new {@link TvInputInfo} object if this service is responsible for 417 * {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of 418 * ignoring all hardware input. 419 * 420 * @param hardwareInfo {@link TvInputHardwareInfo} object just added. 421 * @hide 422 */ 423 @Nullable 424 @SystemApi onHardwareAdded(TvInputHardwareInfo hardwareInfo)425 public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) { 426 return null; 427 } 428 429 /** 430 * Returns the input ID for {@code deviceId} if it is handled by this service; 431 * otherwise, return {@code null}. Override to modify default behavior of ignoring all hardware 432 * input. 433 * 434 * @param hardwareInfo {@link TvInputHardwareInfo} object just removed. 435 * @hide 436 */ 437 @Nullable 438 @SystemApi onHardwareRemoved(TvInputHardwareInfo hardwareInfo)439 public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) { 440 return null; 441 } 442 443 /** 444 * Returns a new {@link TvInputInfo} object if this service is responsible for 445 * {@code deviceInfo}; otherwise, return {@code null}. Override to modify default behavior of 446 * ignoring all HDMI logical input device. 447 * 448 * @param deviceInfo {@link HdmiDeviceInfo} object just added. 449 * @hide 450 */ 451 @Nullable 452 @SystemApi onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo)453 public TvInputInfo onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { 454 return null; 455 } 456 457 /** 458 * Returns the input ID for {@code deviceInfo} if it is handled by this service; otherwise, 459 * return {@code null}. Override to modify default behavior of ignoring all HDMI logical input 460 * device. 461 * 462 * @param deviceInfo {@link HdmiDeviceInfo} object just removed. 463 * @hide 464 */ 465 @Nullable 466 @SystemApi onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo)467 public String onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { 468 return null; 469 } 470 471 /** 472 * Called when {@code deviceInfo} is updated. 473 * 474 * <p>The changes are usually cuased by the corresponding HDMI-CEC logical device. 475 * 476 * <p>The default behavior ignores all changes. 477 * 478 * <p>The TV input service responsible for {@code deviceInfo} can update the {@link TvInputInfo} 479 * object based on the updated {@code deviceInfo} (e.g. update the label based on the preferred 480 * device OSD name). 481 * 482 * @param deviceInfo the updated {@link HdmiDeviceInfo} object. 483 * @hide 484 */ 485 @SystemApi onHdmiDeviceUpdated(@onNull HdmiDeviceInfo deviceInfo)486 public void onHdmiDeviceUpdated(@NonNull HdmiDeviceInfo deviceInfo) { 487 } 488 isPassthroughInput(String inputId)489 private boolean isPassthroughInput(String inputId) { 490 if (mTvInputManager == null) { 491 mTvInputManager = (TvInputManager) getSystemService(Context.TV_INPUT_SERVICE); 492 } 493 TvInputInfo info = mTvInputManager.getTvInputInfo(inputId); 494 return info != null && info.isPassthroughInput(); 495 } 496 497 /** 498 * Base class for derived classes to implement to provide a TV input session. 499 */ 500 public abstract static class Session implements KeyEvent.Callback { 501 private static final int POSITION_UPDATE_INTERVAL_MS = 1000; 502 503 private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 504 private final WindowManager mWindowManager; 505 final Handler mHandler; 506 private WindowManager.LayoutParams mWindowParams; 507 private Surface mSurface; 508 private final Context mContext; 509 private FrameLayout mOverlayViewContainer; 510 private View mOverlayView; 511 private OverlayViewCleanUpTask mOverlayViewCleanUpTask; 512 private boolean mOverlayViewEnabled; 513 private IBinder mWindowToken; 514 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 515 private Rect mOverlayFrame; 516 private long mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; 517 private long mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; 518 private final TimeShiftPositionTrackingRunnable 519 mTimeShiftPositionTrackingRunnable = new TimeShiftPositionTrackingRunnable(); 520 521 private final Object mLock = new Object(); 522 // @GuardedBy("mLock") 523 private ITvInputSessionCallback mSessionCallback; 524 // @GuardedBy("mLock") 525 private final List<Runnable> mPendingActions = new ArrayList<>(); 526 527 /** 528 * Creates a new Session. 529 * 530 * @param context The context of the application 531 */ Session(Context context)532 public Session(Context context) { 533 mContext = context; 534 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 535 mHandler = new Handler(context.getMainLooper()); 536 } 537 538 /** 539 * Enables or disables the overlay view. 540 * 541 * <p>By default, the overlay view is disabled. Must be called explicitly after the 542 * session is created to enable the overlay view. 543 * 544 * <p>The TV input service can disable its overlay view when the size of the overlay view is 545 * insufficient to display the whole information, such as when used in Picture-in-picture. 546 * Override {@link #onOverlayViewSizeChanged} to get the size of the overlay view, which 547 * then can be used to determine whether to enable/disable the overlay view. 548 * 549 * @param enable {@code true} if you want to enable the overlay view. {@code false} 550 * otherwise. 551 */ setOverlayViewEnabled(final boolean enable)552 public void setOverlayViewEnabled(final boolean enable) { 553 mHandler.post(new Runnable() { 554 @Override 555 public void run() { 556 if (enable == mOverlayViewEnabled) { 557 return; 558 } 559 mOverlayViewEnabled = enable; 560 if (enable) { 561 if (mWindowToken != null) { 562 createOverlayView(mWindowToken, mOverlayFrame); 563 } 564 } else { 565 removeOverlayView(false); 566 } 567 } 568 }); 569 } 570 571 /** 572 * Dispatches an event to the application using this session. 573 * 574 * @param eventType The type of the event. 575 * @param eventArgs Optional arguments of the event. 576 * @hide 577 */ 578 @SystemApi notifySessionEvent(@onNull final String eventType, final Bundle eventArgs)579 public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) { 580 Preconditions.checkNotNull(eventType); 581 executeOrPostRunnableOnMainThread(new Runnable() { 582 @Override 583 public void run() { 584 try { 585 if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")"); 586 if (mSessionCallback != null) { 587 mSessionCallback.onSessionEvent(eventType, eventArgs); 588 } 589 } catch (RemoteException e) { 590 Log.w(TAG, "error in sending event (event=" + eventType + ")", e); 591 } 592 } 593 }); 594 } 595 596 /** 597 * Informs the application that the current channel is re-tuned for some reason and the 598 * session now displays the content from a new channel. This is used to handle special cases 599 * such as when the current channel becomes unavailable, it is necessary to send the user to 600 * a certain channel or the user changes channel in some other way (e.g. by using a 601 * dedicated remote). 602 * 603 * @param channelUri The URI of the new channel. 604 */ notifyChannelRetuned(final Uri channelUri)605 public void notifyChannelRetuned(final Uri channelUri) { 606 executeOrPostRunnableOnMainThread(new Runnable() { 607 @MainThread 608 @Override 609 public void run() { 610 try { 611 if (DEBUG) Log.d(TAG, "notifyChannelRetuned"); 612 if (mSessionCallback != null) { 613 mSessionCallback.onChannelRetuned(channelUri); 614 } 615 } catch (RemoteException e) { 616 Log.w(TAG, "error in notifyChannelRetuned", e); 617 } 618 } 619 }); 620 } 621 622 /** 623 * Informs the application that this session has been tuned to the given channel. 624 * 625 * @param channelUri The URI of the tuned channel. 626 */ notifyTuned(@onNull Uri channelUri)627 public void notifyTuned(@NonNull Uri channelUri) { 628 executeOrPostRunnableOnMainThread(new Runnable() { 629 @MainThread 630 @Override 631 public void run() { 632 try { 633 if (DEBUG) Log.d(TAG, "notifyTuned"); 634 if (mSessionCallback != null) { 635 mSessionCallback.onTuned(channelUri); 636 } 637 } catch (RemoteException e) { 638 Log.w(TAG, "error in notifyTuned", e); 639 } 640 } 641 }); 642 } 643 644 /** 645 * Sends the list of all audio/video/subtitle tracks. The is used by the framework to 646 * maintain the track information for a given session, which in turn is used by 647 * {@link TvView#getTracks} for the application to retrieve metadata for a given track type. 648 * The TV input service must call this method as soon as the track information becomes 649 * available or is updated. Note that in a case where a part of the information for a 650 * certain track is updated, it is not necessary to create a new {@link TvTrackInfo} object 651 * with a different track ID. 652 * 653 * @param tracks A list which includes track information. 654 */ notifyTracksChanged(final List<TvTrackInfo> tracks)655 public void notifyTracksChanged(final List<TvTrackInfo> tracks) { 656 final List<TvTrackInfo> tracksCopy = new ArrayList<>(tracks); 657 executeOrPostRunnableOnMainThread(new Runnable() { 658 @MainThread 659 @Override 660 public void run() { 661 try { 662 if (DEBUG) Log.d(TAG, "notifyTracksChanged"); 663 if (mSessionCallback != null) { 664 mSessionCallback.onTracksChanged(tracksCopy); 665 } 666 } catch (RemoteException e) { 667 Log.w(TAG, "error in notifyTracksChanged", e); 668 } 669 } 670 }); 671 } 672 673 /** 674 * Sends the type and ID of a selected track. This is used to inform the application that a 675 * specific track is selected. The TV input service must call this method as soon as a track 676 * is selected either by default or in response to a call to {@link #onSelectTrack}. The 677 * selected track ID for a given type is maintained in the framework until the next call to 678 * this method even after the entire track list is updated (but is reset when the session is 679 * tuned to a new channel), so care must be taken not to result in an obsolete track ID. 680 * 681 * @param type The type of the selected track. The type can be 682 * {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or 683 * {@link TvTrackInfo#TYPE_SUBTITLE}. 684 * @param trackId The ID of the selected track. 685 * @see #onSelectTrack 686 */ notifyTrackSelected(final int type, final String trackId)687 public void notifyTrackSelected(final int type, final String trackId) { 688 executeOrPostRunnableOnMainThread(new Runnable() { 689 @MainThread 690 @Override 691 public void run() { 692 try { 693 if (DEBUG) Log.d(TAG, "notifyTrackSelected"); 694 if (mSessionCallback != null) { 695 mSessionCallback.onTrackSelected(type, trackId); 696 } 697 } catch (RemoteException e) { 698 Log.w(TAG, "error in notifyTrackSelected", e); 699 } 700 } 701 }); 702 } 703 704 /** 705 * Informs the application that the video is now available for watching. Video is blocked 706 * until this method is called. 707 * 708 * <p>The TV input service must call this method as soon as the content rendered onto its 709 * surface is ready for viewing. This method must be called each time {@link #onTune} 710 * is called. 711 * 712 * @see #notifyVideoUnavailable 713 */ notifyVideoAvailable()714 public void notifyVideoAvailable() { 715 executeOrPostRunnableOnMainThread(new Runnable() { 716 @MainThread 717 @Override 718 public void run() { 719 try { 720 if (DEBUG) Log.d(TAG, "notifyVideoAvailable"); 721 if (mSessionCallback != null) { 722 mSessionCallback.onVideoAvailable(); 723 } 724 } catch (RemoteException e) { 725 Log.w(TAG, "error in notifyVideoAvailable", e); 726 } 727 } 728 }); 729 } 730 731 /** 732 * Informs the application that the video became unavailable for some reason. This is 733 * primarily used to signal the application to block the screen not to show any intermittent 734 * video artifacts. 735 * 736 * @param reason The reason why the video became unavailable: 737 * <ul> 738 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} 739 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING} 740 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} 741 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} 742 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY} 743 * </ul> 744 * @see #notifyVideoAvailable 745 */ notifyVideoUnavailable( @vInputManager.VideoUnavailableReason final int reason)746 public void notifyVideoUnavailable( 747 @TvInputManager.VideoUnavailableReason final int reason) { 748 if (reason < TvInputManager.VIDEO_UNAVAILABLE_REASON_START 749 || reason > TvInputManager.VIDEO_UNAVAILABLE_REASON_END) { 750 Log.e(TAG, "notifyVideoUnavailable - unknown reason: " + reason); 751 } 752 executeOrPostRunnableOnMainThread(new Runnable() { 753 @MainThread 754 @Override 755 public void run() { 756 try { 757 if (DEBUG) Log.d(TAG, "notifyVideoUnavailable"); 758 if (mSessionCallback != null) { 759 mSessionCallback.onVideoUnavailable(reason); 760 } 761 } catch (RemoteException e) { 762 Log.w(TAG, "error in notifyVideoUnavailable", e); 763 } 764 } 765 }); 766 } 767 768 /** 769 * Informs the application that the video freeze state has been updated. 770 * 771 * <p>When {@code true}, the video is frozen on the last frame but audio playback remains 772 * active. 773 * 774 * @param isFrozen Whether or not the video is frozen 775 */ 776 @FlaggedApi(Flags.FLAG_TIAF_V_APIS) notifyVideoFreezeUpdated(boolean isFrozen)777 public void notifyVideoFreezeUpdated(boolean isFrozen) { 778 executeOrPostRunnableOnMainThread(new Runnable() { 779 @MainThread 780 @Override 781 public void run() { 782 try { 783 if (DEBUG) { 784 Log.d(TAG, "notifyVideoFreezeUpdated"); 785 } 786 if (mSessionCallback != null) { 787 mSessionCallback.onVideoFreezeUpdated(isFrozen); 788 } 789 } catch (RemoteException e) { 790 Log.e(TAG, "error in notifyVideoFreezeUpdated", e); 791 } 792 } 793 }); 794 } 795 796 /** 797 * Sends an updated list of all audio presentations available from a Next Generation Audio 798 * service. This is used by the framework to maintain the audio presentation information for 799 * a given track of {@link TvTrackInfo#TYPE_AUDIO}, which in turn is used by 800 * {@link TvView#getAudioPresentations} for the application to retrieve metadata for the 801 * current audio track. The TV input service must call this method as soon as the audio 802 * track presentation information becomes available or is updated. Note that in a case 803 * where a part of the information for the current track is updated, it is not necessary 804 * to create a new {@link TvTrackInfo} object with a different track ID. 805 * 806 * @param audioPresentations A list of audio presentation information pertaining to the 807 * selected track. 808 */ notifyAudioPresentationChanged(@onNull final List<AudioPresentation> audioPresentations)809 public void notifyAudioPresentationChanged(@NonNull final List<AudioPresentation> 810 audioPresentations) { 811 final List<AudioPresentation> ap = new ArrayList<>(audioPresentations); 812 executeOrPostRunnableOnMainThread(new Runnable() { 813 @MainThread 814 @Override 815 public void run() { 816 try { 817 if (DEBUG) { 818 Log.d(TAG, "notifyAudioPresentationsChanged"); 819 } 820 if (mSessionCallback != null) { 821 mSessionCallback.onAudioPresentationsChanged(ap); 822 } 823 } catch (RemoteException e) { 824 Log.e(TAG, "error in notifyAudioPresentationsChanged", e); 825 } 826 } 827 }); 828 } 829 830 /** 831 * Sends the presentation and program IDs of the selected audio presentation. This is used 832 * to inform the application that a specific audio presentation is selected. The TV input 833 * service must call this method as soon as an audio presentation is selected either by 834 * default or in response to a call to {@link #onSelectTrack}. The selected audio 835 * presentation ID for a currently selected audio track is maintained in the framework until 836 * the next call to this method even after the entire audio presentation list for the track 837 * is updated (but is reset when the session is tuned to a new channel), so care must be 838 * taken not to result in an obsolete track audio presentation ID. 839 * 840 * @param presentationId The ID of the selected audio presentation for the current track. 841 * @param programId The ID of the program providing the selected audio presentation. 842 * @see #onSelectAudioPresentation 843 */ notifyAudioPresentationSelected(final int presentationId, final int programId)844 public void notifyAudioPresentationSelected(final int presentationId, final int programId) { 845 executeOrPostRunnableOnMainThread(new Runnable() { 846 @MainThread 847 @Override 848 public void run() { 849 try { 850 if (DEBUG) { 851 Log.d(TAG, "notifyAudioPresentationSelected"); 852 } 853 if (mSessionCallback != null) { 854 mSessionCallback.onAudioPresentationSelected(presentationId, programId); 855 } 856 } catch (RemoteException e) { 857 Log.e(TAG, "error in notifyAudioPresentationSelected", e); 858 } 859 } 860 }); 861 } 862 863 864 /** 865 * Informs the application that the user is allowed to watch the current program content. 866 * 867 * <p>Each TV input service is required to query the system whether the user is allowed to 868 * watch the current program before showing it to the user if the parental controls is 869 * enabled (i.e. {@link TvInputManager#isParentalControlsEnabled 870 * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input 871 * service should block the content or not is determined by invoking 872 * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)} 873 * with the content rating for the current program. Then the {@link TvInputManager} makes a 874 * judgment based on the user blocked ratings stored in the secure settings and returns the 875 * result. If the rating in question turns out to be allowed by the user, the TV input 876 * service must call this method to notify the application that is permitted to show the 877 * content. 878 * 879 * <p>Each TV input service also needs to continuously listen to any changes made to the 880 * parental controls settings by registering a broadcast receiver to receive 881 * {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and 882 * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately 883 * reevaluate the current program with the new parental controls settings. 884 * 885 * @see #notifyContentBlocked 886 * @see TvInputManager 887 */ notifyContentAllowed()888 public void notifyContentAllowed() { 889 executeOrPostRunnableOnMainThread(new Runnable() { 890 @MainThread 891 @Override 892 public void run() { 893 try { 894 if (DEBUG) Log.d(TAG, "notifyContentAllowed"); 895 if (mSessionCallback != null) { 896 mSessionCallback.onContentAllowed(); 897 } 898 } catch (RemoteException e) { 899 Log.w(TAG, "error in notifyContentAllowed", e); 900 } 901 } 902 }); 903 } 904 905 /** 906 * Informs the application that the current program content is blocked by parent controls. 907 * 908 * <p>Each TV input service is required to query the system whether the user is allowed to 909 * watch the current program before showing it to the user if the parental controls is 910 * enabled (i.e. {@link TvInputManager#isParentalControlsEnabled 911 * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input 912 * service should block the content or not is determined by invoking 913 * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)} 914 * with the content rating for the current program or {@link TvContentRating#UNRATED} in 915 * case the rating information is missing. Then the {@link TvInputManager} makes a judgment 916 * based on the user blocked ratings stored in the secure settings and returns the result. 917 * If the rating in question turns out to be blocked, the TV input service must immediately 918 * block the content and call this method with the content rating of the current program to 919 * prompt the PIN verification screen. 920 * 921 * <p>Each TV input service also needs to continuously listen to any changes made to the 922 * parental controls settings by registering a broadcast receiver to receive 923 * {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and 924 * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately 925 * reevaluate the current program with the new parental controls settings. 926 * 927 * @param rating The content rating for the current TV program. Can be 928 * {@link TvContentRating#UNRATED}. 929 * @see #notifyContentAllowed 930 * @see TvInputManager 931 */ notifyContentBlocked(@onNull final TvContentRating rating)932 public void notifyContentBlocked(@NonNull final TvContentRating rating) { 933 Preconditions.checkNotNull(rating); 934 executeOrPostRunnableOnMainThread(new Runnable() { 935 @MainThread 936 @Override 937 public void run() { 938 try { 939 if (DEBUG) Log.d(TAG, "notifyContentBlocked"); 940 if (mSessionCallback != null) { 941 mSessionCallback.onContentBlocked(rating.flattenToString()); 942 } 943 } catch (RemoteException e) { 944 Log.w(TAG, "error in notifyContentBlocked", e); 945 } 946 } 947 }); 948 } 949 950 /** 951 * Informs the application that the time shift status is changed. 952 * 953 * <p>Prior to calling this method, the application assumes the status 954 * {@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}. Right after the session is created, it 955 * is important to invoke the method with the status 956 * {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} if the implementation does support 957 * time shifting, or {@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED} otherwise. Failure 958 * to notifying the current status change immediately might result in an undesirable 959 * behavior in the application such as hiding the play controls. 960 * 961 * <p>If the status {@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} is reported, the 962 * application assumes it can pause/resume playback, seek to a specified time position and 963 * set playback rate and audio mode. The implementation should override 964 * {@link #onTimeShiftPause}, {@link #onTimeShiftResume}, {@link #onTimeShiftSeekTo}, 965 * {@link #onTimeShiftGetStartPosition}, {@link #onTimeShiftGetCurrentPosition} and 966 * {@link #onTimeShiftSetPlaybackParams}. 967 * 968 * @param status The current time shift status. Should be one of the followings. 969 * <ul> 970 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED} 971 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE} 972 * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE} 973 * </ul> 974 */ notifyTimeShiftStatusChanged(@vInputManager.TimeShiftStatus final int status)975 public void notifyTimeShiftStatusChanged(@TvInputManager.TimeShiftStatus final int status) { 976 executeOrPostRunnableOnMainThread(new Runnable() { 977 @MainThread 978 @Override 979 public void run() { 980 timeShiftEnablePositionTracking( 981 status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE); 982 try { 983 if (DEBUG) Log.d(TAG, "notifyTimeShiftStatusChanged"); 984 if (mSessionCallback != null) { 985 mSessionCallback.onTimeShiftStatusChanged(status); 986 } 987 } catch (RemoteException e) { 988 Log.w(TAG, "error in notifyTimeShiftStatusChanged", e); 989 } 990 } 991 }); 992 } 993 994 /** 995 * Notifies response for broadcast info. 996 * 997 * @param response broadcast info response. 998 */ notifyBroadcastInfoResponse(@onNull final BroadcastInfoResponse response)999 public void notifyBroadcastInfoResponse(@NonNull final BroadcastInfoResponse response) { 1000 executeOrPostRunnableOnMainThread(new Runnable() { 1001 @MainThread 1002 @Override 1003 public void run() { 1004 try { 1005 if (DEBUG) Log.d(TAG, "notifyBroadcastInfoResponse"); 1006 if (mSessionCallback != null) { 1007 mSessionCallback.onBroadcastInfoResponse(response); 1008 } 1009 } catch (RemoteException e) { 1010 Log.w(TAG, "error in notifyBroadcastInfoResponse", e); 1011 } 1012 } 1013 }); 1014 } 1015 1016 /** 1017 * Notifies response for advertisement. 1018 * 1019 * @param response advertisement response. 1020 * @see android.media.tv.interactive.TvInteractiveAppService.Session#requestAd(AdRequest) 1021 */ notifyAdResponse(@onNull final AdResponse response)1022 public void notifyAdResponse(@NonNull final AdResponse response) { 1023 executeOrPostRunnableOnMainThread(new Runnable() { 1024 @MainThread 1025 @Override 1026 public void run() { 1027 try { 1028 if (DEBUG) Log.d(TAG, "notifyAdResponse"); 1029 if (mSessionCallback != null) { 1030 mSessionCallback.onAdResponse(response); 1031 } 1032 } catch (RemoteException e) { 1033 Log.w(TAG, "error in notifyAdResponse", e); 1034 } 1035 } 1036 }); 1037 } 1038 1039 /** 1040 * Notifies the advertisement buffer is consumed. 1041 * 1042 * @param buffer the {@link AdBuffer} that was consumed. 1043 */ notifyAdBufferConsumed(@onNull AdBuffer buffer)1044 public void notifyAdBufferConsumed(@NonNull AdBuffer buffer) { 1045 AdBuffer dupBuffer; 1046 try { 1047 dupBuffer = AdBuffer.dupAdBuffer(buffer); 1048 } catch (IOException e) { 1049 Log.w(TAG, "dup AdBuffer error in notifyAdBufferConsumed:", e); 1050 return; 1051 } 1052 executeOrPostRunnableOnMainThread(new Runnable() { 1053 @MainThread 1054 @Override 1055 public void run() { 1056 try { 1057 if (DEBUG) Log.d(TAG, "notifyAdBufferConsumed"); 1058 if (mSessionCallback != null) { 1059 mSessionCallback.onAdBufferConsumed(dupBuffer); 1060 } 1061 } catch (RemoteException e) { 1062 Log.w(TAG, "error in notifyAdBufferConsumed", e); 1063 } finally { 1064 if (dupBuffer != null) { 1065 dupBuffer.getSharedMemory().close(); 1066 } 1067 } 1068 } 1069 }); 1070 } 1071 1072 /** 1073 * Sends the raw data from the received TV message as well as the type of message received. 1074 * 1075 * @param type The of message that was sent, such as 1076 * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK} 1077 * @param data The raw data of the message. The bundle keys are: 1078 * {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID}, 1079 * {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID}, 1080 * {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE}, 1081 * {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}. 1082 * See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on 1083 * how to parse this data. 1084 */ notifyTvMessage(@vInputManager.TvMessageType int type, @NonNull Bundle data)1085 public void notifyTvMessage(@TvInputManager.TvMessageType int type, 1086 @NonNull Bundle data) { 1087 executeOrPostRunnableOnMainThread(new Runnable() { 1088 @MainThread 1089 @Override 1090 public void run() { 1091 try { 1092 if (DEBUG) Log.d(TAG, "notifyTvMessage"); 1093 if (mSessionCallback != null) { 1094 mSessionCallback.onTvMessage(type, data); 1095 } 1096 } catch (RemoteException e) { 1097 Log.w(TAG, "error in notifyTvMessage", e); 1098 } 1099 } 1100 }); 1101 } 1102 notifyTimeShiftStartPositionChanged(final long timeMs)1103 private void notifyTimeShiftStartPositionChanged(final long timeMs) { 1104 executeOrPostRunnableOnMainThread(new Runnable() { 1105 @MainThread 1106 @Override 1107 public void run() { 1108 try { 1109 if (DEBUG) Log.d(TAG, "notifyTimeShiftStartPositionChanged"); 1110 if (mSessionCallback != null) { 1111 mSessionCallback.onTimeShiftStartPositionChanged(timeMs); 1112 } 1113 } catch (RemoteException e) { 1114 Log.w(TAG, "error in notifyTimeShiftStartPositionChanged", e); 1115 } 1116 } 1117 }); 1118 } 1119 notifyTimeShiftCurrentPositionChanged(final long timeMs)1120 private void notifyTimeShiftCurrentPositionChanged(final long timeMs) { 1121 executeOrPostRunnableOnMainThread(new Runnable() { 1122 @MainThread 1123 @Override 1124 public void run() { 1125 try { 1126 if (DEBUG) Log.d(TAG, "notifyTimeShiftCurrentPositionChanged"); 1127 if (mSessionCallback != null) { 1128 mSessionCallback.onTimeShiftCurrentPositionChanged(timeMs); 1129 } 1130 } catch (RemoteException e) { 1131 Log.w(TAG, "error in notifyTimeShiftCurrentPositionChanged", e); 1132 } 1133 } 1134 }); 1135 } 1136 1137 /** 1138 * Informs the app that the AIT (Application Information Table) is updated. 1139 * 1140 * <p>This method should also be called when 1141 * {@link #onSetInteractiveAppNotificationEnabled(boolean)} is called to send the first AIT 1142 * info. 1143 * 1144 * @see #onSetInteractiveAppNotificationEnabled(boolean) 1145 */ notifyAitInfoUpdated(@onNull final AitInfo aitInfo)1146 public void notifyAitInfoUpdated(@NonNull final AitInfo aitInfo) { 1147 executeOrPostRunnableOnMainThread(new Runnable() { 1148 @MainThread 1149 @Override 1150 public void run() { 1151 try { 1152 if (DEBUG) Log.d(TAG, "notifyAitInfoUpdated"); 1153 if (mSessionCallback != null) { 1154 mSessionCallback.onAitInfoUpdated(aitInfo); 1155 } 1156 } catch (RemoteException e) { 1157 Log.w(TAG, "error in notifyAitInfoUpdated", e); 1158 } 1159 } 1160 }); 1161 } 1162 1163 /** 1164 * Informs the app that the time shift mode is set or updated. 1165 * 1166 * @param mode The current time shift mode. The value is one of the following: 1167 * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL}, 1168 * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK}, 1169 * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}. 1170 */ notifyTimeShiftMode(@ndroid.media.tv.TvInputManager.TimeShiftMode int mode)1171 public void notifyTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) { 1172 executeOrPostRunnableOnMainThread(new Runnable() { 1173 @MainThread 1174 @Override 1175 public void run() { 1176 try { 1177 if (DEBUG) Log.d(TAG, "notifyTimeShiftMode"); 1178 if (mSessionCallback != null) { 1179 mSessionCallback.onTimeShiftMode(mode); 1180 } 1181 } catch (RemoteException e) { 1182 Log.w(TAG, "error in notifyTimeShiftMode", e); 1183 } 1184 } 1185 }); 1186 } 1187 1188 /** 1189 * Informs the app available speeds for time-shifting. 1190 * <p>This should be called when time-shifting is enabled. 1191 * 1192 * @param speeds An ordered array of playback speeds, expressed as values relative to the 1193 * normal playback speed (1.0), at which the current content can be played as 1194 * a time-shifted broadcast. This is an empty array if the supported playback 1195 * speeds are unknown or the video/broadcast is not in time shift mode. If 1196 * currently in time shift mode, this array will normally include at least 1197 * the values 1.0 (normal speed) and 0.0 (paused). 1198 * @see PlaybackParams#getSpeed() 1199 */ notifyAvailableSpeeds(@onNull float[] speeds)1200 public void notifyAvailableSpeeds(@NonNull float[] speeds) { 1201 executeOrPostRunnableOnMainThread(new Runnable() { 1202 @MainThread 1203 @Override 1204 public void run() { 1205 try { 1206 if (DEBUG) Log.d(TAG, "notifyAvailableSpeeds"); 1207 if (mSessionCallback != null) { 1208 Arrays.sort(speeds); 1209 mSessionCallback.onAvailableSpeeds(speeds); 1210 } 1211 } catch (RemoteException e) { 1212 Log.w(TAG, "error in notifyAvailableSpeeds", e); 1213 } 1214 } 1215 }); 1216 } 1217 1218 /** 1219 * Notifies signal strength. 1220 */ notifySignalStrength(@vInputManager.SignalStrength final int strength)1221 public void notifySignalStrength(@TvInputManager.SignalStrength final int strength) { 1222 executeOrPostRunnableOnMainThread(new Runnable() { 1223 @MainThread 1224 @Override 1225 public void run() { 1226 try { 1227 if (DEBUG) Log.d(TAG, "notifySignalStrength"); 1228 if (mSessionCallback != null) { 1229 mSessionCallback.onSignalStrength(strength); 1230 } 1231 } catch (RemoteException e) { 1232 Log.w(TAG, "error in notifySignalStrength", e); 1233 } 1234 } 1235 }); 1236 } 1237 1238 /** 1239 * Informs the application that cueing message is available or unavailable. 1240 * 1241 * <p>The cueing message is used for digital program insertion, based on the standard 1242 * ANSI/SCTE 35 2019r1. 1243 * 1244 * @param available {@code true} if cueing message is available; {@code false} if it becomes 1245 * unavailable. 1246 */ notifyCueingMessageAvailability(boolean available)1247 public void notifyCueingMessageAvailability(boolean available) { 1248 executeOrPostRunnableOnMainThread(new Runnable() { 1249 @MainThread 1250 @Override 1251 public void run() { 1252 try { 1253 if (DEBUG) Log.d(TAG, "notifyCueingMessageAvailability"); 1254 if (mSessionCallback != null) { 1255 mSessionCallback.onCueingMessageAvailability(available); 1256 } 1257 } catch (RemoteException e) { 1258 Log.w(TAG, "error in notifyCueingMessageAvailability", e); 1259 } 1260 } 1261 }); 1262 } 1263 1264 /** 1265 * Sends data related to this session to corresponding linked 1266 * {@link android.media.tv.ad.TvAdService} object via TvAdView. 1267 * 1268 * <p>Methods like {@link #notifyBroadcastInfoResponse(BroadcastInfoResponse)} sends the 1269 * related data to linked {@link android.media.tv.interactive.TvInteractiveAppService}, but 1270 * don't work for {@link android.media.tv.ad.TvAdService}. The method is used specifically 1271 * for {@link android.media.tv.ad.TvAdService} use cases. 1272 * 1273 * @param type data type 1274 * @param data the related data values 1275 */ 1276 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) sendTvInputSessionData( @onNull @vInputManager.SessionDataType String type, @NonNull Bundle data)1277 public void sendTvInputSessionData( 1278 @NonNull @TvInputManager.SessionDataType String type, @NonNull Bundle data) { 1279 executeOrPostRunnableOnMainThread(new Runnable() { 1280 @MainThread 1281 @Override 1282 public void run() { 1283 try { 1284 if (DEBUG) Log.d(TAG, "sendTvInputSessionData"); 1285 if (mSessionCallback != null) { 1286 mSessionCallback.onTvInputSessionData(type, data); 1287 } 1288 } catch (RemoteException e) { 1289 Log.w(TAG, "error in sendTvInputSessionData", e); 1290 } 1291 } 1292 }); 1293 } 1294 1295 /** 1296 * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position 1297 * is relative to the overlay view that sits on top of this surface. 1298 * 1299 * @param left Left position in pixels, relative to the overlay view. 1300 * @param top Top position in pixels, relative to the overlay view. 1301 * @param right Right position in pixels, relative to the overlay view. 1302 * @param bottom Bottom position in pixels, relative to the overlay view. 1303 * @see #onOverlayViewSizeChanged 1304 */ layoutSurface(final int left, final int top, final int right, final int bottom)1305 public void layoutSurface(final int left, final int top, final int right, 1306 final int bottom) { 1307 if (left > right || top > bottom) { 1308 throw new IllegalArgumentException("Invalid parameter"); 1309 } 1310 executeOrPostRunnableOnMainThread(new Runnable() { 1311 @MainThread 1312 @Override 1313 public void run() { 1314 try { 1315 if (DEBUG) Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top + ", r=" 1316 + right + ", b=" + bottom + ",)"); 1317 if (mSessionCallback != null) { 1318 mSessionCallback.onLayoutSurface(left, top, right, bottom); 1319 } 1320 } catch (RemoteException e) { 1321 Log.w(TAG, "error in layoutSurface", e); 1322 } 1323 } 1324 }); 1325 } 1326 1327 /** 1328 * Called when the session is released. 1329 */ onRelease()1330 public abstract void onRelease(); 1331 1332 /** 1333 * Sets the current session as the main session. The main session is a session whose 1334 * corresponding TV input determines the HDMI-CEC active source device. 1335 * 1336 * <p>TV input service that manages HDMI-CEC logical device should implement {@link 1337 * #onSetMain} to (1) select the corresponding HDMI logical device as the source device 1338 * when {@code isMain} is {@code true}, and to (2) select the internal device (= TV itself) 1339 * as the source device when {@code isMain} is {@code false} and the session is still main. 1340 * Also, if a surface is passed to a non-main session and active source is changed to 1341 * initiate the surface, the active source should be returned to the main session. 1342 * 1343 * <p>{@link TvView} guarantees that, when tuning involves a session transition, {@code 1344 * onSetMain(true)} for new session is called first, {@code onSetMain(false)} for old 1345 * session is called afterwards. This allows {@code onSetMain(false)} to be no-op when TV 1346 * input service knows that the next main session corresponds to another HDMI logical 1347 * device. Practically, this implies that one TV input service should handle all HDMI port 1348 * and HDMI-CEC logical devices for smooth active source transition. 1349 * 1350 * @param isMain If true, session should become main. 1351 * @see TvView#setMain 1352 * @hide 1353 */ 1354 @SystemApi onSetMain(boolean isMain)1355 public void onSetMain(boolean isMain) { 1356 } 1357 1358 /** 1359 * Called when the application sets the surface. 1360 * 1361 * <p>The TV input service should render video onto the given surface. When called with 1362 * {@code null}, the input service should immediately free any references to the 1363 * currently set surface and stop using it. 1364 * 1365 * @param surface The surface to be used for video rendering. Can be {@code null}. 1366 * @return {@code true} if the surface was set successfully, {@code false} otherwise. 1367 */ onSetSurface(@ullable Surface surface)1368 public abstract boolean onSetSurface(@Nullable Surface surface); 1369 1370 /** 1371 * Called after any structural changes (format or size) have been made to the surface passed 1372 * in {@link #onSetSurface}. This method is always called at least once, after 1373 * {@link #onSetSurface} is called with non-null surface. 1374 * 1375 * @param format The new PixelFormat of the surface. 1376 * @param width The new width of the surface. 1377 * @param height The new height of the surface. 1378 */ onSurfaceChanged(int format, int width, int height)1379 public void onSurfaceChanged(int format, int width, int height) { 1380 } 1381 1382 /** 1383 * Called when the size of the overlay view is changed by the application. 1384 * 1385 * <p>This is always called at least once when the session is created regardless of whether 1386 * the overlay view is enabled or not. The overlay view size is the same as the containing 1387 * {@link TvView}. Note that the size of the underlying surface can be different if the 1388 * surface was changed by calling {@link #layoutSurface}. 1389 * 1390 * @param width The width of the overlay view. 1391 * @param height The height of the overlay view. 1392 */ onOverlayViewSizeChanged(int width, int height)1393 public void onOverlayViewSizeChanged(int width, int height) { 1394 } 1395 1396 /** 1397 * Sets the relative stream volume of the current TV input session. 1398 * 1399 * <p>The implementation should honor this request in order to handle audio focus changes or 1400 * mute the current session when multiple sessions, possibly from different inputs are 1401 * active. If the method has not yet been called, the implementation should assume the 1402 * default value of {@code 1.0f}. 1403 * 1404 * @param volume A volume value between {@code 0.0f} to {@code 1.0f}. 1405 */ onSetStreamVolume(@loatRangefrom = 0.0, to = 1.0) float volume)1406 public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume); 1407 1408 /** 1409 * Called when broadcast info is requested. 1410 * 1411 * @param request broadcast info request 1412 */ onRequestBroadcastInfo(@onNull BroadcastInfoRequest request)1413 public void onRequestBroadcastInfo(@NonNull BroadcastInfoRequest request) { 1414 } 1415 1416 /** 1417 * Called when broadcast info is removed. 1418 */ onRemoveBroadcastInfo(int requestId)1419 public void onRemoveBroadcastInfo(int requestId) { 1420 } 1421 1422 /** 1423 * Called when advertisement request is received. 1424 * 1425 * @param request advertisement request received 1426 */ onRequestAd(@onNull AdRequest request)1427 public void onRequestAd(@NonNull AdRequest request) { 1428 } 1429 1430 /** 1431 * Called when an advertisement buffer is ready for playback. 1432 * 1433 * @param buffer The {@link AdBuffer} that became ready for playback. 1434 */ onAdBufferReady(@onNull AdBuffer buffer)1435 public void onAdBufferReady(@NonNull AdBuffer buffer) { 1436 } 1437 1438 1439 /** 1440 * Called when data from the linked {@link android.media.tv.ad.TvAdService} is received. 1441 * 1442 * @param type the type of the data 1443 * @param data a bundle contains the data received 1444 * @see android.media.tv.ad.TvAdService.Session#sendTvAdSessionData(String, Bundle) 1445 * @see android.media.tv.ad.TvAdView#setTvView(TvView) 1446 */ 1447 @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW) onTvAdSessionData( @onNull @vAdManager.SessionDataType String type, @NonNull Bundle data)1448 public void onTvAdSessionData( 1449 @NonNull @TvAdManager.SessionDataType String type, @NonNull Bundle data) { 1450 } 1451 1452 /** 1453 * Tunes to a given channel. 1454 * 1455 * <p>No video will be displayed until {@link #notifyVideoAvailable()} is called. 1456 * Also, {@link #notifyVideoUnavailable(int)} should be called when the TV input cannot 1457 * continue playing the given channel. 1458 * 1459 * @param channelUri The URI of the channel. 1460 * @return {@code true} if the tuning was successful, {@code false} otherwise. 1461 */ onTune(Uri channelUri)1462 public abstract boolean onTune(Uri channelUri); 1463 1464 /** 1465 * Tunes to a given channel. Override this method in order to handle domain-specific 1466 * features that are only known between certain TV inputs and their clients. 1467 * 1468 * <p>The default implementation calls {@link #onTune(Uri)}. 1469 * 1470 * @param channelUri The URI of the channel. 1471 * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 1472 * name, i.e. prefixed with a package name you own, so that different developers 1473 * will not create conflicting keys. 1474 * @return {@code true} if the tuning was successful, {@code false} otherwise. 1475 */ onTune(Uri channelUri, Bundle params)1476 public boolean onTune(Uri channelUri, Bundle params) { 1477 return onTune(channelUri); 1478 } 1479 1480 /** 1481 * Enables or disables the caption. 1482 * 1483 * <p>The locale for the user's preferred captioning language can be obtained by calling 1484 * {@link CaptioningManager#getLocale CaptioningManager.getLocale()}. 1485 * 1486 * @param enabled {@code true} to enable, {@code false} to disable. 1487 * @see CaptioningManager 1488 */ onSetCaptionEnabled(boolean enabled)1489 public abstract void onSetCaptionEnabled(boolean enabled); 1490 1491 /** 1492 * Requests to unblock the content according to the given rating. 1493 * 1494 * <p>The implementation should unblock the content. 1495 * TV input service has responsibility to decide when/how the unblock expires 1496 * while it can keep previously unblocked ratings in order not to ask a user 1497 * to unblock whenever a content rating is changed. 1498 * Therefore an unblocked rating can be valid for a channel, a program, 1499 * or certain amount of time depending on the implementation. 1500 * 1501 * @param unblockedRating An unblocked content rating 1502 */ onUnblockContent(TvContentRating unblockedRating)1503 public void onUnblockContent(TvContentRating unblockedRating) { 1504 } 1505 1506 /** 1507 * Selects a given track. 1508 * 1509 * <p>If this is done successfully, the implementation should call 1510 * {@link #notifyTrackSelected} to help applications maintain the up-to-date list of the 1511 * selected tracks. 1512 * 1513 * @param trackId The ID of the track to select. {@code null} means to unselect the current 1514 * track for a given type. 1515 * @param type The type of the track to select. The type can be 1516 * {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or 1517 * {@link TvTrackInfo#TYPE_SUBTITLE}. 1518 * @return {@code true} if the track selection was successful, {@code false} otherwise. 1519 * @see #notifyTrackSelected 1520 */ onSelectTrack(int type, @Nullable String trackId)1521 public boolean onSelectTrack(int type, @Nullable String trackId) { 1522 return false; 1523 } 1524 1525 /** 1526 * Enables or disables interactive app notification. 1527 * 1528 * <p>This method enables or disables the event detection from the corresponding TV input. 1529 * When it's enabled, the TV input service detects events related to interactive app, such 1530 * as AIT (Application Information Table) and sends to TvView or the linked TV interactive 1531 * app service. 1532 * 1533 * @param enabled {@code true} to enable, {@code false} to disable. 1534 * 1535 * @see TvView#setInteractiveAppNotificationEnabled(boolean) 1536 * @see Session#notifyAitInfoUpdated(android.media.tv.AitInfo) 1537 */ onSetInteractiveAppNotificationEnabled(boolean enabled)1538 public void onSetInteractiveAppNotificationEnabled(boolean enabled) { 1539 } 1540 1541 /** 1542 * Selects an audio presentation. 1543 * 1544 * <p>On successfully selecting the audio presentation, 1545 * {@link #notifyAudioPresentationSelected} is invoked to provide updated information about 1546 * the selected audio presentation to applications. 1547 * 1548 * @param presentationId The ID of the audio presentation to select. 1549 * @param programId The ID of the program providing the selected audio presentation. 1550 * @return {@code true} if the audio presentation selection was successful, 1551 * {@code false} otherwise. 1552 * @see #notifyAudioPresentationSelected 1553 */ onSelectAudioPresentation(int presentationId, int programId)1554 public boolean onSelectAudioPresentation(int presentationId, int programId) { 1555 return false; 1556 } 1557 1558 /** 1559 * Processes a private command sent from the application to the TV input. This can be used 1560 * to provide domain-specific features that are only known between certain TV inputs and 1561 * their clients. 1562 * 1563 * @param action Name of the command to be performed. This <em>must</em> be a scoped name, 1564 * i.e. prefixed with a package name you own, so that different developers will 1565 * not create conflicting commands. 1566 * @param data Any data to include with the command. 1567 */ onAppPrivateCommand(@onNull String action, Bundle data)1568 public void onAppPrivateCommand(@NonNull String action, Bundle data) { 1569 } 1570 1571 /** 1572 * Called when the application requests to create an overlay view. Each session 1573 * implementation can override this method and return its own view. 1574 * 1575 * @return a view attached to the overlay window 1576 */ onCreateOverlayView()1577 public View onCreateOverlayView() { 1578 return null; 1579 } 1580 1581 /** 1582 * Called when the application enables or disables the detection of the specified message 1583 * type. 1584 * @param type The type of message received, such as 1585 * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK} 1586 * @param enabled {@code true} if TV message detection is enabled, 1587 * {@code false} otherwise. 1588 */ onSetTvMessageEnabled(@vInputManager.TvMessageType int type, boolean enabled)1589 public void onSetTvMessageEnabled(@TvInputManager.TvMessageType int type, 1590 boolean enabled) { 1591 } 1592 1593 /** 1594 * Called when a TV message is received 1595 * 1596 * @param type The type of message received, such as 1597 * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK} 1598 * @param data The raw data of the message. The bundle keys are: 1599 * {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID}, 1600 * {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID}, 1601 * {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE}, 1602 * {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}. 1603 * See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on 1604 * how to parse this data. 1605 */ onTvMessage(@vInputManager.TvMessageType int type, @NonNull Bundle data)1606 public void onTvMessage(@TvInputManager.TvMessageType int type, 1607 @NonNull Bundle data) { 1608 } 1609 1610 /** 1611 * Called when the application requests playback of the Audio, Video, and CC streams to be 1612 * stopped, but the metadata should continue to be filtered. 1613 * 1614 * <p>The metadata that will continue to be filtered includes the PSI 1615 * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1. 1616 * 1617 * <p> Note that this is different form {@link #timeShiftPause()} as should release the 1618 * stream, making it impossible to resume from this position again. 1619 * @param mode 1620 */ 1621 @FlaggedApi(Flags.FLAG_TIAF_V_APIS) onStopPlayback(@vInteractiveAppService.PlaybackCommandStopMode int mode)1622 public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) { 1623 } 1624 1625 /** 1626 * Resumes playback of the Audio, Video, and CC streams. 1627 * 1628 * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be 1629 * used after stopping playback. This is used to resume playback from the current position 1630 * in the live broadcast. 1631 */ 1632 @FlaggedApi(Flags.FLAG_TIAF_V_APIS) onResumePlayback()1633 public void onResumePlayback() { 1634 } 1635 1636 /** 1637 * Called when a request to freeze the video is received from the TV app. The audio should 1638 * continue playback while the video is frozen. 1639 * 1640 * <p> This should freeze the video to the last frame when the state is set to {@code true}. 1641 * @param isFrozen whether or not the video should be frozen. 1642 * @hide 1643 */ onSetVideoFrozen(boolean isFrozen)1644 public void onSetVideoFrozen(boolean isFrozen) { 1645 } 1646 1647 /** 1648 * Called when the application requests to play a given recorded TV program. 1649 * 1650 * @param recordedProgramUri The URI of a recorded TV program. 1651 * @see #onTimeShiftResume() 1652 * @see #onTimeShiftPause() 1653 * @see #onTimeShiftSeekTo(long) 1654 * @see #onTimeShiftSetPlaybackParams(PlaybackParams) 1655 * @see #onTimeShiftGetStartPosition() 1656 * @see #onTimeShiftGetCurrentPosition() 1657 */ onTimeShiftPlay(Uri recordedProgramUri)1658 public void onTimeShiftPlay(Uri recordedProgramUri) { 1659 } 1660 1661 /** 1662 * Called when the application requests to pause playback. 1663 * 1664 * @see #onTimeShiftPlay(Uri) 1665 * @see #onTimeShiftResume() 1666 * @see #onTimeShiftSeekTo(long) 1667 * @see #onTimeShiftSetPlaybackParams(PlaybackParams) 1668 * @see #onTimeShiftGetStartPosition() 1669 * @see #onTimeShiftGetCurrentPosition() 1670 */ onTimeShiftPause()1671 public void onTimeShiftPause() { 1672 } 1673 1674 /** 1675 * Called when the application requests to resume playback. 1676 * 1677 * @see #onTimeShiftPlay(Uri) 1678 * @see #onTimeShiftPause() 1679 * @see #onTimeShiftSeekTo(long) 1680 * @see #onTimeShiftSetPlaybackParams(PlaybackParams) 1681 * @see #onTimeShiftGetStartPosition() 1682 * @see #onTimeShiftGetCurrentPosition() 1683 */ onTimeShiftResume()1684 public void onTimeShiftResume() { 1685 } 1686 1687 /** 1688 * Called when the application requests to seek to a specified time position. Normally, the 1689 * position is given within range between the start and the current time, inclusively. The 1690 * implementation is expected to seek to the nearest time position if the given position is 1691 * not in the range. 1692 * 1693 * @param timeMs The time position to seek to, in milliseconds since the epoch. 1694 * @see #onTimeShiftPlay(Uri) 1695 * @see #onTimeShiftResume() 1696 * @see #onTimeShiftPause() 1697 * @see #onTimeShiftSetPlaybackParams(PlaybackParams) 1698 * @see #onTimeShiftGetStartPosition() 1699 * @see #onTimeShiftGetCurrentPosition() 1700 */ onTimeShiftSeekTo(long timeMs)1701 public void onTimeShiftSeekTo(long timeMs) { 1702 } 1703 1704 /** 1705 * Called when the application sets playback parameters containing the speed and audio mode. 1706 * 1707 * <p>Once the playback parameters are set, the implementation should honor the current 1708 * settings until the next tune request. Pause/resume/seek request does not reset the 1709 * parameters previously set. 1710 * 1711 * @param params The playback params. 1712 * @see #onTimeShiftPlay(Uri) 1713 * @see #onTimeShiftResume() 1714 * @see #onTimeShiftPause() 1715 * @see #onTimeShiftSeekTo(long) 1716 * @see #onTimeShiftGetStartPosition() 1717 * @see #onTimeShiftGetCurrentPosition() 1718 */ onTimeShiftSetPlaybackParams(PlaybackParams params)1719 public void onTimeShiftSetPlaybackParams(PlaybackParams params) { 1720 } 1721 1722 /** 1723 * Called when the application sets time shift mode. 1724 * 1725 * @param mode The time shift mode. The value is one of the following: 1726 * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL}, 1727 * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK}, 1728 * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}. 1729 */ onTimeShiftSetMode(@ndroid.media.tv.TvInputManager.TimeShiftMode int mode)1730 public void onTimeShiftSetMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) { 1731 } 1732 1733 /** 1734 * Returns the start position for time shifting, in milliseconds since the epoch. 1735 * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the 1736 * moment. 1737 * 1738 * <p>The start position for time shifting indicates the earliest possible time the user can 1739 * seek to. Initially this is equivalent to the time when the implementation starts 1740 * recording. Later it may be adjusted because there is insufficient space or the duration 1741 * of recording is limited by the implementation. The application does not allow the user to 1742 * seek to a position earlier than the start position. 1743 * 1744 * <p>For playback of a recorded program initiated by {@link #onTimeShiftPlay(Uri)}, the 1745 * start position should be 0 and does not change. 1746 * 1747 * @see #onTimeShiftPlay(Uri) 1748 * @see #onTimeShiftResume() 1749 * @see #onTimeShiftPause() 1750 * @see #onTimeShiftSeekTo(long) 1751 * @see #onTimeShiftSetPlaybackParams(PlaybackParams) 1752 * @see #onTimeShiftGetCurrentPosition() 1753 */ onTimeShiftGetStartPosition()1754 public long onTimeShiftGetStartPosition() { 1755 return TvInputManager.TIME_SHIFT_INVALID_TIME; 1756 } 1757 1758 /** 1759 * Returns the current position for time shifting, in milliseconds since the epoch. 1760 * Returns {@link TvInputManager#TIME_SHIFT_INVALID_TIME} if the position is unknown at the 1761 * moment. 1762 * 1763 * <p>The current position for time shifting is the same as the current position of 1764 * playback. It should be equal to or greater than the start position reported by 1765 * {@link #onTimeShiftGetStartPosition()}. When playback is completed, the current position 1766 * should stay where the playback ends, in other words, the returned value of this mehtod 1767 * should be equal to the start position plus the duration of the program. 1768 * 1769 * @see #onTimeShiftPlay(Uri) 1770 * @see #onTimeShiftResume() 1771 * @see #onTimeShiftPause() 1772 * @see #onTimeShiftSeekTo(long) 1773 * @see #onTimeShiftSetPlaybackParams(PlaybackParams) 1774 * @see #onTimeShiftGetStartPosition() 1775 */ onTimeShiftGetCurrentPosition()1776 public long onTimeShiftGetCurrentPosition() { 1777 return TvInputManager.TIME_SHIFT_INVALID_TIME; 1778 } 1779 1780 /** 1781 * Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent) 1782 * KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event). 1783 * 1784 * <p>Override this to intercept key down events before they are processed by the 1785 * application. If you return true, the application will not process the event itself. If 1786 * you return false, the normal application processing will occur as if the TV input had not 1787 * seen the event at all. 1788 * 1789 * @param keyCode The value in event.getKeyCode(). 1790 * @param event Description of the key event. 1791 * @return If you handled the event, return {@code true}. If you want to allow the event to 1792 * be handled by the next receiver, return {@code false}. 1793 */ 1794 @Override onKeyDown(int keyCode, KeyEvent event)1795 public boolean onKeyDown(int keyCode, KeyEvent event) { 1796 return false; 1797 } 1798 1799 /** 1800 * Default implementation of 1801 * {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent) 1802 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event). 1803 * 1804 * <p>Override this to intercept key long press events before they are processed by the 1805 * application. If you return true, the application will not process the event itself. If 1806 * you return false, the normal application processing will occur as if the TV input had not 1807 * seen the event at all. 1808 * 1809 * @param keyCode The value in event.getKeyCode(). 1810 * @param event Description of the key event. 1811 * @return If you handled the event, return {@code true}. If you want to allow the event to 1812 * be handled by the next receiver, return {@code false}. 1813 */ 1814 @Override onKeyLongPress(int keyCode, KeyEvent event)1815 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 1816 return false; 1817 } 1818 1819 /** 1820 * Default implementation of 1821 * {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) 1822 * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event). 1823 * 1824 * <p>Override this to intercept special key multiple events before they are processed by 1825 * the application. If you return true, the application will not itself process the event. 1826 * If you return false, the normal application processing will occur as if the TV input had 1827 * not seen the event at all. 1828 * 1829 * @param keyCode The value in event.getKeyCode(). 1830 * @param count The number of times the action was made. 1831 * @param event Description of the key event. 1832 * @return If you handled the event, return {@code true}. If you want to allow the event to 1833 * be handled by the next receiver, return {@code false}. 1834 */ 1835 @Override onKeyMultiple(int keyCode, int count, KeyEvent event)1836 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 1837 return false; 1838 } 1839 1840 /** 1841 * Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent) 1842 * KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event). 1843 * 1844 * <p>Override this to intercept key up events before they are processed by the application. 1845 * If you return true, the application will not itself process the event. If you return false, 1846 * the normal application processing will occur as if the TV input had not seen the event at 1847 * all. 1848 * 1849 * @param keyCode The value in event.getKeyCode(). 1850 * @param event Description of the key event. 1851 * @return If you handled the event, return {@code true}. If you want to allow the event to 1852 * be handled by the next receiver, return {@code false}. 1853 */ 1854 @Override onKeyUp(int keyCode, KeyEvent event)1855 public boolean onKeyUp(int keyCode, KeyEvent event) { 1856 return false; 1857 } 1858 1859 /** 1860 * Implement this method to handle touch screen motion events on the current input session. 1861 * 1862 * @param event The motion event being received. 1863 * @return If you handled the event, return {@code true}. If you want to allow the event to 1864 * be handled by the next receiver, return {@code false}. 1865 * @see View#onTouchEvent 1866 */ onTouchEvent(MotionEvent event)1867 public boolean onTouchEvent(MotionEvent event) { 1868 return false; 1869 } 1870 1871 /** 1872 * Implement this method to handle trackball events on the current input session. 1873 * 1874 * @param event The motion event being received. 1875 * @return If you handled the event, return {@code true}. If you want to allow the event to 1876 * be handled by the next receiver, return {@code false}. 1877 * @see View#onTrackballEvent 1878 */ onTrackballEvent(MotionEvent event)1879 public boolean onTrackballEvent(MotionEvent event) { 1880 return false; 1881 } 1882 1883 /** 1884 * Implement this method to handle generic motion events on the current input session. 1885 * 1886 * @param event The motion event being received. 1887 * @return If you handled the event, return {@code true}. If you want to allow the event to 1888 * be handled by the next receiver, return {@code false}. 1889 * @see View#onGenericMotionEvent 1890 */ onGenericMotionEvent(MotionEvent event)1891 public boolean onGenericMotionEvent(MotionEvent event) { 1892 return false; 1893 } 1894 1895 /** 1896 * This method is called when the application would like to stop using the current input 1897 * session. 1898 */ release()1899 void release() { 1900 onRelease(); 1901 if (mSurface != null) { 1902 mSurface.release(); 1903 mSurface = null; 1904 } 1905 synchronized(mLock) { 1906 mSessionCallback = null; 1907 mPendingActions.clear(); 1908 } 1909 // Removes the overlay view lastly so that any hanging on the main thread can be handled 1910 // in {@link #scheduleOverlayViewCleanup}. 1911 removeOverlayView(true); 1912 mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable); 1913 } 1914 1915 /** 1916 * Calls {@link #onSetMain}. 1917 */ setMain(boolean isMain)1918 void setMain(boolean isMain) { 1919 onSetMain(isMain); 1920 } 1921 1922 /** 1923 * Calls {@link #onSetSurface}. 1924 */ setSurface(Surface surface)1925 void setSurface(Surface surface) { 1926 onSetSurface(surface); 1927 if (mSurface != null) { 1928 mSurface.release(); 1929 } 1930 mSurface = surface; 1931 // TODO: Handle failure. 1932 } 1933 1934 /** 1935 * Calls {@link #onSurfaceChanged}. 1936 */ dispatchSurfaceChanged(int format, int width, int height)1937 void dispatchSurfaceChanged(int format, int width, int height) { 1938 if (DEBUG) { 1939 Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width 1940 + ", height=" + height + ")"); 1941 } 1942 onSurfaceChanged(format, width, height); 1943 } 1944 1945 /** 1946 * Calls {@link #onSetStreamVolume}. 1947 */ setStreamVolume(float volume)1948 void setStreamVolume(float volume) { 1949 onSetStreamVolume(volume); 1950 } 1951 1952 /** 1953 * Calls {@link #onTune(Uri, Bundle)}. 1954 */ tune(Uri channelUri, Bundle params)1955 void tune(Uri channelUri, Bundle params) { 1956 mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; 1957 onTune(channelUri, params); 1958 // TODO: Handle failure. 1959 } 1960 1961 /** 1962 * Calls {@link #onSetCaptionEnabled}. 1963 */ setCaptionEnabled(boolean enabled)1964 void setCaptionEnabled(boolean enabled) { 1965 onSetCaptionEnabled(enabled); 1966 } 1967 1968 /** 1969 * Calls {@link #onSelectAudioPresentation}. 1970 */ selectAudioPresentation(int presentationId, int programId)1971 void selectAudioPresentation(int presentationId, int programId) { 1972 onSelectAudioPresentation(presentationId, programId); 1973 } 1974 1975 /** 1976 * Calls {@link #onSelectTrack}. 1977 */ selectTrack(int type, String trackId)1978 void selectTrack(int type, String trackId) { 1979 onSelectTrack(type, trackId); 1980 } 1981 1982 /** 1983 * Calls {@link #onUnblockContent}. 1984 */ unblockContent(String unblockedRating)1985 void unblockContent(String unblockedRating) { 1986 onUnblockContent(TvContentRating.unflattenFromString(unblockedRating)); 1987 // TODO: Handle failure. 1988 } 1989 1990 /** 1991 * Calls {@link #onSetInteractiveAppNotificationEnabled}. 1992 */ setInteractiveAppNotificationEnabled(boolean enabled)1993 void setInteractiveAppNotificationEnabled(boolean enabled) { 1994 onSetInteractiveAppNotificationEnabled(enabled); 1995 } 1996 1997 /** 1998 * Calls {@link #onSetTvMessageEnabled(int, boolean)}. 1999 */ setTvMessageEnabled(int type, boolean enabled)2000 void setTvMessageEnabled(int type, boolean enabled) { 2001 onSetTvMessageEnabled(type, enabled); 2002 } 2003 2004 /** 2005 * Calls {@link #onAppPrivateCommand}. 2006 */ appPrivateCommand(String action, Bundle data)2007 void appPrivateCommand(String action, Bundle data) { 2008 onAppPrivateCommand(action, data); 2009 } 2010 2011 /** 2012 * Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach 2013 * to the overlay window. 2014 * 2015 * @param windowToken A window token of the application. 2016 * @param frame A position of the overlay view. 2017 */ createOverlayView(IBinder windowToken, Rect frame)2018 void createOverlayView(IBinder windowToken, Rect frame) { 2019 if (mOverlayViewContainer != null) { 2020 removeOverlayView(false); 2021 } 2022 if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")"); 2023 mWindowToken = windowToken; 2024 mOverlayFrame = frame; 2025 onOverlayViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); 2026 if (!mOverlayViewEnabled) { 2027 return; 2028 } 2029 mOverlayView = onCreateOverlayView(); 2030 if (mOverlayView == null) { 2031 return; 2032 } 2033 if (mOverlayViewCleanUpTask != null) { 2034 mOverlayViewCleanUpTask.cancel(true); 2035 mOverlayViewCleanUpTask = null; 2036 } 2037 // Creates a container view to check hanging on the overlay view detaching. 2038 // Adding/removing the overlay view to/from the container make the view attach/detach 2039 // logic run on the main thread. 2040 mOverlayViewContainer = new FrameLayout(mContext.getApplicationContext()); 2041 mOverlayViewContainer.addView(mOverlayView); 2042 // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create 2043 // an overlay window above the media window but below the application window. 2044 int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; 2045 // We make the overlay view non-focusable and non-touchable so that 2046 // the application that owns the window token can decide whether to consume or 2047 // dispatch the input events. 2048 int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 2049 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 2050 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; 2051 if (ActivityManager.isHighEndGfx()) { 2052 flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 2053 } 2054 mWindowParams = new WindowManager.LayoutParams( 2055 frame.right - frame.left, frame.bottom - frame.top, 2056 frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT); 2057 mWindowParams.privateFlags |= 2058 WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 2059 mWindowParams.gravity = Gravity.START | Gravity.TOP; 2060 mWindowParams.token = windowToken; 2061 mWindowManager.addView(mOverlayViewContainer, mWindowParams); 2062 } 2063 2064 /** 2065 * Relayouts the current overlay view. 2066 * 2067 * @param frame A new position of the overlay view. 2068 */ relayoutOverlayView(Rect frame)2069 void relayoutOverlayView(Rect frame) { 2070 if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")"); 2071 if (mOverlayFrame == null || mOverlayFrame.width() != frame.width() 2072 || mOverlayFrame.height() != frame.height()) { 2073 // Note: relayoutOverlayView is called whenever TvView's layout is changed 2074 // regardless of setOverlayViewEnabled. 2075 onOverlayViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); 2076 } 2077 mOverlayFrame = frame; 2078 if (!mOverlayViewEnabled || mOverlayViewContainer == null) { 2079 return; 2080 } 2081 mWindowParams.x = frame.left; 2082 mWindowParams.y = frame.top; 2083 mWindowParams.width = frame.right - frame.left; 2084 mWindowParams.height = frame.bottom - frame.top; 2085 mWindowManager.updateViewLayout(mOverlayViewContainer, mWindowParams); 2086 } 2087 2088 /** 2089 * Removes the current overlay view. 2090 */ removeOverlayView(boolean clearWindowToken)2091 void removeOverlayView(boolean clearWindowToken) { 2092 if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayViewContainer + ")"); 2093 if (clearWindowToken) { 2094 mWindowToken = null; 2095 mOverlayFrame = null; 2096 } 2097 if (mOverlayViewContainer != null) { 2098 // Removes the overlay view from the view hierarchy in advance so that it can be 2099 // cleaned up in the {@link OverlayViewCleanUpTask} if the remove process is 2100 // hanging. 2101 mOverlayViewContainer.removeView(mOverlayView); 2102 mOverlayView = null; 2103 mWindowManager.removeView(mOverlayViewContainer); 2104 mOverlayViewContainer = null; 2105 mWindowParams = null; 2106 } 2107 } 2108 2109 /** 2110 * Calls {@link #onStopPlayback(int)}. 2111 */ stopPlayback(int mode)2112 void stopPlayback(int mode) { 2113 onStopPlayback(mode); 2114 } 2115 2116 /** 2117 * Calls {@link #onResumePlayback()}. 2118 */ resumePlayback()2119 void resumePlayback() { 2120 onResumePlayback(); 2121 } 2122 2123 /** 2124 * Calls {@link #onSetVideoFrozen(boolean)}. 2125 */ setVideoFrozen(boolean isFrozen)2126 void setVideoFrozen(boolean isFrozen) { 2127 onSetVideoFrozen(isFrozen); 2128 } 2129 2130 /** 2131 * Calls {@link #onTimeShiftPlay(Uri)}. 2132 */ timeShiftPlay(Uri recordedProgramUri)2133 void timeShiftPlay(Uri recordedProgramUri) { 2134 mCurrentPositionMs = 0; 2135 onTimeShiftPlay(recordedProgramUri); 2136 } 2137 2138 /** 2139 * Calls {@link #onTimeShiftPause}. 2140 */ timeShiftPause()2141 void timeShiftPause() { 2142 onTimeShiftPause(); 2143 } 2144 2145 /** 2146 * Calls {@link #onTimeShiftResume}. 2147 */ timeShiftResume()2148 void timeShiftResume() { 2149 onTimeShiftResume(); 2150 } 2151 2152 /** 2153 * Calls {@link #onTimeShiftSeekTo}. 2154 */ timeShiftSeekTo(long timeMs)2155 void timeShiftSeekTo(long timeMs) { 2156 onTimeShiftSeekTo(timeMs); 2157 } 2158 2159 /** 2160 * Calls {@link #onTimeShiftSetPlaybackParams}. 2161 */ timeShiftSetPlaybackParams(PlaybackParams params)2162 void timeShiftSetPlaybackParams(PlaybackParams params) { 2163 onTimeShiftSetPlaybackParams(params); 2164 } 2165 2166 /** 2167 * Calls {@link #onTimeShiftSetMode}. 2168 */ timeShiftSetMode(int mode)2169 void timeShiftSetMode(int mode) { 2170 onTimeShiftSetMode(mode); 2171 } 2172 2173 /** 2174 * Enable/disable position tracking. 2175 * 2176 * @param enable {@code true} to enable tracking, {@code false} otherwise. 2177 */ timeShiftEnablePositionTracking(boolean enable)2178 void timeShiftEnablePositionTracking(boolean enable) { 2179 if (enable) { 2180 mHandler.post(mTimeShiftPositionTrackingRunnable); 2181 } else { 2182 mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable); 2183 mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; 2184 mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME; 2185 } 2186 } 2187 2188 /** 2189 * Schedules a task which checks whether the overlay view is detached and kills the process 2190 * if it is not. Note that this method is expected to be called in a non-main thread. 2191 */ scheduleOverlayViewCleanup()2192 void scheduleOverlayViewCleanup() { 2193 View overlayViewParent = mOverlayViewContainer; 2194 if (overlayViewParent != null) { 2195 mOverlayViewCleanUpTask = new OverlayViewCleanUpTask(); 2196 mOverlayViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 2197 overlayViewParent); 2198 } 2199 } 2200 requestBroadcastInfo(BroadcastInfoRequest request)2201 void requestBroadcastInfo(BroadcastInfoRequest request) { 2202 onRequestBroadcastInfo(request); 2203 } 2204 removeBroadcastInfo(int requestId)2205 void removeBroadcastInfo(int requestId) { 2206 onRemoveBroadcastInfo(requestId); 2207 } 2208 requestAd(AdRequest request)2209 void requestAd(AdRequest request) { 2210 onRequestAd(request); 2211 } 2212 notifyAdBufferReady(AdBuffer buffer)2213 void notifyAdBufferReady(AdBuffer buffer) { 2214 onAdBufferReady(buffer); 2215 } 2216 notifyTvAdSessionData(String type, Bundle data)2217 void notifyTvAdSessionData(String type, Bundle data) { 2218 onTvAdSessionData(type, data); 2219 } 2220 onTvMessageReceived(int type, Bundle data)2221 void onTvMessageReceived(int type, Bundle data) { 2222 onTvMessage(type, data); 2223 } 2224 2225 /** 2226 * Takes care of dispatching incoming input events and tells whether the event was handled. 2227 */ dispatchInputEvent(InputEvent event, InputEventReceiver receiver)2228 int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { 2229 if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")"); 2230 boolean isNavigationKey = false; 2231 boolean skipDispatchToOverlayView = false; 2232 if (event instanceof KeyEvent) { 2233 KeyEvent keyEvent = (KeyEvent) event; 2234 if (keyEvent.dispatch(this, mDispatcherState, this)) { 2235 return TvInputManager.Session.DISPATCH_HANDLED; 2236 } 2237 isNavigationKey = isNavigationKey(keyEvent.getKeyCode()); 2238 // When media keys and KEYCODE_MEDIA_AUDIO_TRACK are dispatched to ViewRootImpl, 2239 // ViewRootImpl always consumes the keys. In this case, the application loses 2240 // a chance to handle media keys. Therefore, media keys are not dispatched to 2241 // ViewRootImpl. 2242 skipDispatchToOverlayView = KeyEvent.isMediaSessionKey(keyEvent.getKeyCode()) 2243 || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK; 2244 } else if (event instanceof MotionEvent) { 2245 MotionEvent motionEvent = (MotionEvent) event; 2246 final int source = motionEvent.getSource(); 2247 if (motionEvent.isTouchEvent()) { 2248 if (onTouchEvent(motionEvent)) { 2249 return TvInputManager.Session.DISPATCH_HANDLED; 2250 } 2251 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 2252 if (onTrackballEvent(motionEvent)) { 2253 return TvInputManager.Session.DISPATCH_HANDLED; 2254 } 2255 } else { 2256 if (onGenericMotionEvent(motionEvent)) { 2257 return TvInputManager.Session.DISPATCH_HANDLED; 2258 } 2259 } 2260 } 2261 if (mOverlayViewContainer == null || !mOverlayViewContainer.isAttachedToWindow() 2262 || skipDispatchToOverlayView) { 2263 return TvInputManager.Session.DISPATCH_NOT_HANDLED; 2264 } 2265 if (!mOverlayViewContainer.hasWindowFocus()) { 2266 ViewRootImpl viewRoot = mOverlayViewContainer.getViewRootImpl(); 2267 viewRoot.windowFocusChanged(true); 2268 } 2269 if (isNavigationKey && mOverlayViewContainer.hasFocusable()) { 2270 // If mOverlayView has focusable views, navigation key events should be always 2271 // handled. If not, it can make the application UI navigation messed up. 2272 // For example, in the case that the left-most view is focused, a left key event 2273 // will not be handled in ViewRootImpl. Then, the left key event will be handled in 2274 // the application during the UI navigation of the TV input. 2275 mOverlayViewContainer.getViewRootImpl().dispatchInputEvent(event); 2276 return TvInputManager.Session.DISPATCH_HANDLED; 2277 } else { 2278 mOverlayViewContainer.getViewRootImpl().dispatchInputEvent(event, receiver); 2279 return TvInputManager.Session.DISPATCH_IN_PROGRESS; 2280 } 2281 } 2282 initialize(ITvInputSessionCallback callback)2283 private void initialize(ITvInputSessionCallback callback) { 2284 synchronized(mLock) { 2285 mSessionCallback = callback; 2286 for (Runnable runnable : mPendingActions) { 2287 runnable.run(); 2288 } 2289 mPendingActions.clear(); 2290 } 2291 } 2292 executeOrPostRunnableOnMainThread(Runnable action)2293 private void executeOrPostRunnableOnMainThread(Runnable action) { 2294 synchronized(mLock) { 2295 if (mSessionCallback == null) { 2296 // The session is not initialized yet. 2297 mPendingActions.add(action); 2298 } else { 2299 if (mHandler.getLooper().isCurrentThread()) { 2300 action.run(); 2301 } else { 2302 // Posts the runnable if this is not called from the main thread 2303 mHandler.post(action); 2304 } 2305 } 2306 } 2307 } 2308 2309 private final class TimeShiftPositionTrackingRunnable implements Runnable { 2310 @Override run()2311 public void run() { 2312 long startPositionMs = onTimeShiftGetStartPosition(); 2313 if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME 2314 || mStartPositionMs != startPositionMs) { 2315 mStartPositionMs = startPositionMs; 2316 notifyTimeShiftStartPositionChanged(startPositionMs); 2317 } 2318 long currentPositionMs = onTimeShiftGetCurrentPosition(); 2319 if (currentPositionMs < mStartPositionMs) { 2320 Log.w(TAG, "Current position (" + currentPositionMs + ") cannot be earlier than" 2321 + " start position (" + mStartPositionMs + "). Reset to the start " 2322 + "position."); 2323 currentPositionMs = mStartPositionMs; 2324 } 2325 if (mCurrentPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME 2326 || mCurrentPositionMs != currentPositionMs) { 2327 mCurrentPositionMs = currentPositionMs; 2328 notifyTimeShiftCurrentPositionChanged(currentPositionMs); 2329 } 2330 mHandler.removeCallbacks(mTimeShiftPositionTrackingRunnable); 2331 mHandler.postDelayed(mTimeShiftPositionTrackingRunnable, 2332 POSITION_UPDATE_INTERVAL_MS); 2333 } 2334 } 2335 } 2336 2337 private static final class OverlayViewCleanUpTask extends AsyncTask<View, Void, Void> { 2338 @Override doInBackground(View... views)2339 protected Void doInBackground(View... views) { 2340 View overlayViewParent = views[0]; 2341 try { 2342 Thread.sleep(DETACH_OVERLAY_VIEW_TIMEOUT_MS); 2343 } catch (InterruptedException e) { 2344 return null; 2345 } 2346 if (isCancelled()) { 2347 return null; 2348 } 2349 if (overlayViewParent.isAttachedToWindow()) { 2350 Log.e(TAG, "Time out on releasing overlay view. Killing " 2351 + overlayViewParent.getContext().getPackageName()); 2352 Process.killProcess(Process.myPid()); 2353 } 2354 return null; 2355 } 2356 } 2357 2358 /** 2359 * Base class for derived classes to implement to provide a TV input recording session. 2360 */ 2361 public abstract static class RecordingSession { 2362 final Handler mHandler; 2363 2364 private final Object mLock = new Object(); 2365 // @GuardedBy("mLock") 2366 private ITvInputSessionCallback mSessionCallback; 2367 // @GuardedBy("mLock") 2368 private final List<Runnable> mPendingActions = new ArrayList<>(); 2369 2370 /** 2371 * Creates a new RecordingSession. 2372 * 2373 * @param context The context of the application 2374 */ RecordingSession(Context context)2375 public RecordingSession(Context context) { 2376 mHandler = new Handler(context.getMainLooper()); 2377 } 2378 2379 /** 2380 * Informs the application that this recording session has been tuned to the given channel 2381 * and is ready to start recording. 2382 * 2383 * <p>Upon receiving a call to {@link #onTune(Uri)}, the session is expected to tune to the 2384 * passed channel and call this method to indicate that it is now available for immediate 2385 * recording. When {@link #onStartRecording(Uri)} is called, recording must start with 2386 * minimal delay. 2387 * 2388 * @param channelUri The URI of a channel. 2389 */ notifyTuned(Uri channelUri)2390 public void notifyTuned(Uri channelUri) { 2391 executeOrPostRunnableOnMainThread(new Runnable() { 2392 @MainThread 2393 @Override 2394 public void run() { 2395 try { 2396 if (DEBUG) Log.d(TAG, "notifyTuned"); 2397 if (mSessionCallback != null) { 2398 mSessionCallback.onTuned(channelUri); 2399 } 2400 } catch (RemoteException e) { 2401 Log.w(TAG, "error in notifyTuned", e); 2402 } 2403 } 2404 }); 2405 } 2406 2407 /** 2408 * Informs the application that this recording session has stopped recording and created a 2409 * new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly 2410 * recorded program. 2411 * 2412 * <p>The recording session must call this method in response to {@link #onStopRecording()}. 2413 * The session may call it even before receiving a call to {@link #onStopRecording()} if a 2414 * partially recorded program is available when there is an error. 2415 * 2416 * @param recordedProgramUri The URI of the newly recorded program. 2417 */ notifyRecordingStopped(final Uri recordedProgramUri)2418 public void notifyRecordingStopped(final Uri recordedProgramUri) { 2419 executeOrPostRunnableOnMainThread(new Runnable() { 2420 @MainThread 2421 @Override 2422 public void run() { 2423 try { 2424 if (DEBUG) Log.d(TAG, "notifyRecordingStopped"); 2425 if (mSessionCallback != null) { 2426 mSessionCallback.onRecordingStopped(recordedProgramUri); 2427 } 2428 } catch (RemoteException e) { 2429 Log.w(TAG, "error in notifyRecordingStopped", e); 2430 } 2431 } 2432 }); 2433 } 2434 2435 /** 2436 * Informs the application that there is an error and this recording session is no longer 2437 * able to start or continue recording. It may be called at any time after the recording 2438 * session is created until {@link #onRelease()} is called. 2439 * 2440 * <p>The application may release the current session upon receiving the error code through 2441 * {@link TvRecordingClient.RecordingCallback#onError(int)}. The session may call 2442 * {@link #notifyRecordingStopped(Uri)} if a partially recorded but still playable program 2443 * is available, before calling this method. 2444 * 2445 * @param error The error code. Should be one of the followings. 2446 * <ul> 2447 * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN} 2448 * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE} 2449 * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY} 2450 * </ul> 2451 */ notifyError(@vInputManager.RecordingError int error)2452 public void notifyError(@TvInputManager.RecordingError int error) { 2453 if (error < TvInputManager.RECORDING_ERROR_START 2454 || error > TvInputManager.RECORDING_ERROR_END) { 2455 Log.w(TAG, "notifyError - invalid error code (" + error 2456 + ") is changed to RECORDING_ERROR_UNKNOWN."); 2457 error = TvInputManager.RECORDING_ERROR_UNKNOWN; 2458 } 2459 final int validError = error; 2460 executeOrPostRunnableOnMainThread(new Runnable() { 2461 @MainThread 2462 @Override 2463 public void run() { 2464 try { 2465 if (DEBUG) Log.d(TAG, "notifyError"); 2466 if (mSessionCallback != null) { 2467 mSessionCallback.onError(validError); 2468 } 2469 } catch (RemoteException e) { 2470 Log.w(TAG, "error in notifyError", e); 2471 } 2472 } 2473 }); 2474 } 2475 2476 /** 2477 * Dispatches an event to the application using this recording session. 2478 * 2479 * @param eventType The type of the event. 2480 * @param eventArgs Optional arguments of the event. 2481 * @hide 2482 */ 2483 @SystemApi notifySessionEvent(@onNull final String eventType, final Bundle eventArgs)2484 public void notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) { 2485 Preconditions.checkNotNull(eventType); 2486 executeOrPostRunnableOnMainThread(new Runnable() { 2487 @MainThread 2488 @Override 2489 public void run() { 2490 try { 2491 if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")"); 2492 if (mSessionCallback != null) { 2493 mSessionCallback.onSessionEvent(eventType, eventArgs); 2494 } 2495 } catch (RemoteException e) { 2496 Log.w(TAG, "error in sending event (event=" + eventType + ")", e); 2497 } 2498 } 2499 }); 2500 } 2501 2502 /** 2503 * Called when the application requests to tune to a given channel for TV program recording. 2504 * 2505 * <p>The application may call this method before starting or after stopping recording, but 2506 * not during recording. 2507 * 2508 * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or 2509 * {@link #notifyError(int)} otherwise. 2510 * 2511 * @param channelUri The URI of a channel. 2512 */ onTune(Uri channelUri)2513 public abstract void onTune(Uri channelUri); 2514 2515 /** 2516 * Called when the application requests to tune to a given channel for TV program recording. 2517 * Override this method in order to handle domain-specific features that are only known 2518 * between certain TV inputs and their clients. 2519 * 2520 * <p>The application may call this method before starting or after stopping recording, but 2521 * not during recording. The default implementation calls {@link #onTune(Uri)}. 2522 * 2523 * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or 2524 * {@link #notifyError(int)} otherwise. 2525 * 2526 * @param channelUri The URI of a channel. 2527 * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 2528 * name, i.e. prefixed with a package name you own, so that different developers 2529 * will not create conflicting keys. 2530 */ onTune(Uri channelUri, Bundle params)2531 public void onTune(Uri channelUri, Bundle params) { 2532 onTune(channelUri); 2533 } 2534 2535 /** 2536 * Called when the application requests to start TV program recording. Recording must start 2537 * immediately when this method is called. 2538 * 2539 * <p>The application may supply the URI for a TV program for filling in program specific 2540 * data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table. 2541 * A non-null {@code programUri} implies the started recording should be of that specific 2542 * program, whereas null {@code programUri} does not impose such a requirement and the 2543 * recording can span across multiple TV programs. In either case, the application must call 2544 * {@link TvRecordingClient#stopRecording()} to stop the recording. 2545 * 2546 * <p>The session must call {@link #notifyError(int)} if the start request cannot be 2547 * fulfilled. 2548 * 2549 * @param programUri The URI for the TV program to record, built by 2550 * {@link TvContract#buildProgramUri(long)}. Can be {@code null}. 2551 */ onStartRecording(@ullable Uri programUri)2552 public abstract void onStartRecording(@Nullable Uri programUri); 2553 2554 /** 2555 * Called when the application requests to start TV program recording. Recording must start 2556 * immediately when this method is called. 2557 * 2558 * <p>The application may supply the URI for a TV program for filling in program specific 2559 * data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table. 2560 * A non-null {@code programUri} implies the started recording should be of that specific 2561 * program, whereas null {@code programUri} does not impose such a requirement and the 2562 * recording can span across multiple TV programs. In either case, the application must call 2563 * {@link TvRecordingClient#stopRecording()} to stop the recording. 2564 * 2565 * <p>The session must call {@link #notifyError(int)} if the start request cannot be 2566 * fulfilled. 2567 * 2568 * @param programUri The URI for the TV program to record, built by 2569 * {@link TvContract#buildProgramUri(long)}. Can be {@code null}. 2570 * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped 2571 * name, i.e. prefixed with a package name you own, so that different developers 2572 * will not create conflicting keys. 2573 */ onStartRecording(@ullable Uri programUri, @NonNull Bundle params)2574 public void onStartRecording(@Nullable Uri programUri, @NonNull Bundle params) { 2575 onStartRecording(programUri); 2576 } 2577 2578 /** 2579 * Called when the application requests to stop TV program recording. Recording must stop 2580 * immediately when this method is called. 2581 * 2582 * <p>The session must create a new data entry in the 2583 * {@link android.media.tv.TvContract.RecordedPrograms} table that describes the newly 2584 * recorded program and call {@link #notifyRecordingStopped(Uri)} with the URI to that 2585 * entry. 2586 * If the stop request cannot be fulfilled, the session must call {@link #notifyError(int)}. 2587 * 2588 */ onStopRecording()2589 public abstract void onStopRecording(); 2590 2591 2592 /** 2593 * Called when the application requests to pause TV program recording. Recording must pause 2594 * immediately when this method is called. 2595 * 2596 * If the pause request cannot be fulfilled, the session must call 2597 * {@link #notifyError(int)}. 2598 * 2599 * @param params Domain-specific data for recording request. 2600 */ onPauseRecording(@onNull Bundle params)2601 public void onPauseRecording(@NonNull Bundle params) { } 2602 2603 /** 2604 * Called when the application requests to resume TV program recording. Recording must 2605 * resume immediately when this method is called. 2606 * 2607 * If the resume request cannot be fulfilled, the session must call 2608 * {@link #notifyError(int)}. 2609 * 2610 * @param params Domain-specific data for recording request. 2611 */ onResumeRecording(@onNull Bundle params)2612 public void onResumeRecording(@NonNull Bundle params) { } 2613 2614 /** 2615 * Called when the application requests to release all the resources held by this recording 2616 * session. 2617 */ onRelease()2618 public abstract void onRelease(); 2619 2620 /** 2621 * Processes a private command sent from the application to the TV input. This can be used 2622 * to provide domain-specific features that are only known between certain TV inputs and 2623 * their clients. 2624 * 2625 * @param action Name of the command to be performed. This <em>must</em> be a scoped name, 2626 * i.e. prefixed with a package name you own, so that different developers will 2627 * not create conflicting commands. 2628 * @param data Any data to include with the command. 2629 */ onAppPrivateCommand(@onNull String action, Bundle data)2630 public void onAppPrivateCommand(@NonNull String action, Bundle data) { 2631 } 2632 2633 /** 2634 * Calls {@link #onTune(Uri, Bundle)}. 2635 * 2636 */ tune(Uri channelUri, Bundle params)2637 void tune(Uri channelUri, Bundle params) { 2638 onTune(channelUri, params); 2639 } 2640 2641 /** 2642 * Calls {@link #onRelease()}. 2643 * 2644 */ release()2645 void release() { 2646 onRelease(); 2647 } 2648 2649 /** 2650 * Calls {@link #onStartRecording(Uri, Bundle)}. 2651 * 2652 */ startRecording(@ullable Uri programUri, @NonNull Bundle params)2653 void startRecording(@Nullable Uri programUri, @NonNull Bundle params) { 2654 onStartRecording(programUri, params); 2655 } 2656 2657 /** 2658 * Calls {@link #onStopRecording()}. 2659 * 2660 */ stopRecording()2661 void stopRecording() { 2662 onStopRecording(); 2663 } 2664 2665 /** 2666 * Calls {@link #onPauseRecording(Bundle)}. 2667 * 2668 */ pauseRecording(@onNull Bundle params)2669 void pauseRecording(@NonNull Bundle params) { 2670 onPauseRecording(params); 2671 } 2672 2673 /** 2674 * Calls {@link #onResumeRecording(Bundle)}. 2675 * 2676 */ resumeRecording(@onNull Bundle params)2677 void resumeRecording(@NonNull Bundle params) { 2678 onResumeRecording(params); 2679 } 2680 2681 /** 2682 * Calls {@link #onAppPrivateCommand(String, Bundle)}. 2683 */ appPrivateCommand(String action, Bundle data)2684 void appPrivateCommand(String action, Bundle data) { 2685 onAppPrivateCommand(action, data); 2686 } 2687 initialize(ITvInputSessionCallback callback)2688 private void initialize(ITvInputSessionCallback callback) { 2689 synchronized(mLock) { 2690 mSessionCallback = callback; 2691 for (Runnable runnable : mPendingActions) { 2692 runnable.run(); 2693 } 2694 mPendingActions.clear(); 2695 } 2696 } 2697 executeOrPostRunnableOnMainThread(Runnable action)2698 private void executeOrPostRunnableOnMainThread(Runnable action) { 2699 synchronized(mLock) { 2700 if (mSessionCallback == null) { 2701 // The session is not initialized yet. 2702 mPendingActions.add(action); 2703 } else { 2704 if (mHandler.getLooper().isCurrentThread()) { 2705 action.run(); 2706 } else { 2707 // Posts the runnable if this is not called from the main thread 2708 mHandler.post(action); 2709 } 2710 } 2711 } 2712 } 2713 } 2714 2715 /** 2716 * Base class for a TV input session which represents an external device connected to a 2717 * hardware TV input. 2718 * 2719 * <p>This class is for an input which provides channels for the external set-top box to the 2720 * application. Once a TV input returns an implementation of this class on 2721 * {@link #onCreateSession(String)}, the framework will create a separate session for 2722 * a hardware TV Input (e.g. HDMI 1) and forward the application's surface to the session so 2723 * that the user can see the screen of the hardware TV Input when she tunes to a channel from 2724 * this TV input. The implementation of this class is expected to change the channel of the 2725 * external set-top box via a proprietary protocol when {@link HardwareSession#onTune} is 2726 * requested by the application. 2727 * 2728 * <p>Note that this class is not for inputs for internal hardware like built-in tuner and HDMI 2729 * 1. 2730 * 2731 * @see #onCreateSession(String) 2732 */ 2733 public abstract static class HardwareSession extends Session { 2734 2735 /** 2736 * Creates a new HardwareSession. 2737 * 2738 * @param context The context of the application 2739 */ HardwareSession(Context context)2740 public HardwareSession(Context context) { 2741 super(context); 2742 } 2743 2744 private TvInputManager.Session mHardwareSession; 2745 private ITvInputSession mProxySession; 2746 private ITvInputSessionCallback mProxySessionCallback; 2747 private Handler mServiceHandler; 2748 2749 /** 2750 * Returns the hardware TV input ID the external device is connected to. 2751 * 2752 * <p>TV input is expected to provide {@link android.R.attr#setupActivity} so that 2753 * the application can launch it before using this TV input. The setup activity may let 2754 * the user select the hardware TV input to which the external device is connected. The ID 2755 * of the selected one should be stored in the TV input so that it can be returned here. 2756 */ getHardwareInputId()2757 public abstract String getHardwareInputId(); 2758 2759 private final TvInputManager.SessionCallback mHardwareSessionCallback = 2760 new TvInputManager.SessionCallback() { 2761 @Override 2762 public void onSessionCreated(TvInputManager.Session session) { 2763 mHardwareSession = session; 2764 SomeArgs args = SomeArgs.obtain(); 2765 if (session != null) { 2766 args.arg1 = HardwareSession.this; 2767 args.arg2 = mProxySession; 2768 args.arg3 = mProxySessionCallback; 2769 args.arg4 = session.getToken(); 2770 session.tune(TvContract.buildChannelUriForPassthroughInput( 2771 getHardwareInputId())); 2772 } else { 2773 args.arg1 = null; 2774 args.arg2 = null; 2775 args.arg3 = mProxySessionCallback; 2776 args.arg4 = null; 2777 onRelease(); 2778 } 2779 mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, 2780 args).sendToTarget(); 2781 } 2782 2783 @Override 2784 public void onVideoAvailable(final TvInputManager.Session session) { 2785 if (mHardwareSession == session) { 2786 onHardwareVideoAvailable(); 2787 } 2788 } 2789 2790 @Override 2791 public void onVideoUnavailable(final TvInputManager.Session session, 2792 final int reason) { 2793 if (mHardwareSession == session) { 2794 onHardwareVideoUnavailable(reason); 2795 } 2796 } 2797 }; 2798 2799 /** 2800 * This method will not be called in {@link HardwareSession}. Framework will 2801 * forward the application's surface to the hardware TV input. 2802 */ 2803 @Override onSetSurface(Surface surface)2804 public final boolean onSetSurface(Surface surface) { 2805 Log.e(TAG, "onSetSurface() should not be called in HardwareProxySession."); 2806 return false; 2807 } 2808 2809 /** 2810 * Called when the underlying hardware TV input session calls 2811 * {@link TvInputService.Session#notifyVideoAvailable()}. 2812 */ onHardwareVideoAvailable()2813 public void onHardwareVideoAvailable() { } 2814 2815 /** 2816 * Called when the underlying hardware TV input session calls 2817 * {@link TvInputService.Session#notifyVideoUnavailable(int)}. 2818 * 2819 * @param reason The reason that the hardware TV input stopped the playback: 2820 * <ul> 2821 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} 2822 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING} 2823 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} 2824 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} 2825 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY} 2826 * </ul> 2827 */ onHardwareVideoUnavailable(int reason)2828 public void onHardwareVideoUnavailable(int reason) { } 2829 2830 @Override release()2831 void release() { 2832 if (mHardwareSession != null) { 2833 mHardwareSession.release(); 2834 mHardwareSession = null; 2835 } 2836 super.release(); 2837 } 2838 } 2839 2840 /** @hide */ isNavigationKey(int keyCode)2841 public static boolean isNavigationKey(int keyCode) { 2842 switch (keyCode) { 2843 case KeyEvent.KEYCODE_DPAD_LEFT: 2844 case KeyEvent.KEYCODE_DPAD_RIGHT: 2845 case KeyEvent.KEYCODE_DPAD_UP: 2846 case KeyEvent.KEYCODE_DPAD_DOWN: 2847 case KeyEvent.KEYCODE_DPAD_CENTER: 2848 case KeyEvent.KEYCODE_PAGE_UP: 2849 case KeyEvent.KEYCODE_PAGE_DOWN: 2850 case KeyEvent.KEYCODE_MOVE_HOME: 2851 case KeyEvent.KEYCODE_MOVE_END: 2852 case KeyEvent.KEYCODE_TAB: 2853 case KeyEvent.KEYCODE_SPACE: 2854 case KeyEvent.KEYCODE_ENTER: 2855 return true; 2856 } 2857 return false; 2858 } 2859 2860 @SuppressLint("HandlerLeak") 2861 private final class ServiceHandler extends Handler { 2862 private static final int DO_CREATE_SESSION = 1; 2863 private static final int DO_NOTIFY_SESSION_CREATED = 2; 2864 private static final int DO_CREATE_RECORDING_SESSION = 3; 2865 private static final int DO_ADD_HARDWARE_INPUT = 4; 2866 private static final int DO_REMOVE_HARDWARE_INPUT = 5; 2867 private static final int DO_ADD_HDMI_INPUT = 6; 2868 private static final int DO_REMOVE_HDMI_INPUT = 7; 2869 private static final int DO_UPDATE_HDMI_INPUT = 8; 2870 broadcastAddHardwareInput(int deviceId, TvInputInfo inputInfo)2871 private void broadcastAddHardwareInput(int deviceId, TvInputInfo inputInfo) { 2872 int n = mCallbacks.beginBroadcast(); 2873 for (int i = 0; i < n; ++i) { 2874 try { 2875 mCallbacks.getBroadcastItem(i).addHardwareInput(deviceId, inputInfo); 2876 } catch (RemoteException e) { 2877 Log.e(TAG, "error in broadcastAddHardwareInput", e); 2878 } 2879 } 2880 mCallbacks.finishBroadcast(); 2881 } 2882 broadcastAddHdmiInput(int id, TvInputInfo inputInfo)2883 private void broadcastAddHdmiInput(int id, TvInputInfo inputInfo) { 2884 int n = mCallbacks.beginBroadcast(); 2885 for (int i = 0; i < n; ++i) { 2886 try { 2887 mCallbacks.getBroadcastItem(i).addHdmiInput(id, inputInfo); 2888 } catch (RemoteException e) { 2889 Log.e(TAG, "error in broadcastAddHdmiInput", e); 2890 } 2891 } 2892 mCallbacks.finishBroadcast(); 2893 } 2894 broadcastRemoveHardwareInput(String inputId)2895 private void broadcastRemoveHardwareInput(String inputId) { 2896 int n = mCallbacks.beginBroadcast(); 2897 for (int i = 0; i < n; ++i) { 2898 try { 2899 mCallbacks.getBroadcastItem(i).removeHardwareInput(inputId); 2900 } catch (RemoteException e) { 2901 Log.e(TAG, "error in broadcastRemoveHardwareInput", e); 2902 } 2903 } 2904 mCallbacks.finishBroadcast(); 2905 } 2906 2907 @Override handleMessage(Message msg)2908 public final void handleMessage(Message msg) { 2909 switch (msg.what) { 2910 case DO_CREATE_SESSION: { 2911 SomeArgs args = (SomeArgs) msg.obj; 2912 InputChannel channel = (InputChannel) args.arg1; 2913 ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2; 2914 String inputId = (String) args.arg3; 2915 String sessionId = (String) args.arg4; 2916 AttributionSource tvAppAttributionSource = (AttributionSource) args.arg5; 2917 args.recycle(); 2918 Session sessionImpl = 2919 onCreateSession(inputId, sessionId, tvAppAttributionSource); 2920 if (sessionImpl == null) { 2921 try { 2922 // Failed to create a session. 2923 cb.onSessionCreated(null, null); 2924 } catch (RemoteException e) { 2925 Log.e(TAG, "error in onSessionCreated", e); 2926 } 2927 return; 2928 } 2929 ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this, 2930 sessionImpl, channel); 2931 if (sessionImpl instanceof HardwareSession) { 2932 HardwareSession proxySession = 2933 ((HardwareSession) sessionImpl); 2934 String hardwareInputId = proxySession.getHardwareInputId(); 2935 if (TextUtils.isEmpty(hardwareInputId) || 2936 !isPassthroughInput(hardwareInputId)) { 2937 if (TextUtils.isEmpty(hardwareInputId)) { 2938 Log.w(TAG, "Hardware input id is not setup yet."); 2939 } else { 2940 Log.w(TAG, "Invalid hardware input id : " + hardwareInputId); 2941 } 2942 sessionImpl.onRelease(); 2943 try { 2944 cb.onSessionCreated(null, null); 2945 } catch (RemoteException e) { 2946 Log.e(TAG, "error in onSessionCreated", e); 2947 } 2948 return; 2949 } 2950 proxySession.mProxySession = stub; 2951 proxySession.mProxySessionCallback = cb; 2952 proxySession.mServiceHandler = mServiceHandler; 2953 TvInputManager manager = (TvInputManager) getSystemService( 2954 Context.TV_INPUT_SERVICE); 2955 manager.createSession(hardwareInputId, tvAppAttributionSource, 2956 proxySession.mHardwareSessionCallback, mServiceHandler); 2957 } else { 2958 SomeArgs someArgs = SomeArgs.obtain(); 2959 someArgs.arg1 = sessionImpl; 2960 someArgs.arg2 = stub; 2961 someArgs.arg3 = cb; 2962 someArgs.arg4 = null; 2963 mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, 2964 someArgs).sendToTarget(); 2965 } 2966 return; 2967 } 2968 case DO_NOTIFY_SESSION_CREATED: { 2969 SomeArgs args = (SomeArgs) msg.obj; 2970 Session sessionImpl = (Session) args.arg1; 2971 ITvInputSession stub = (ITvInputSession) args.arg2; 2972 ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg3; 2973 IBinder hardwareSessionToken = (IBinder) args.arg4; 2974 try { 2975 cb.onSessionCreated(stub, hardwareSessionToken); 2976 } catch (RemoteException e) { 2977 Log.e(TAG, "error in onSessionCreated", e); 2978 } 2979 if (sessionImpl != null) { 2980 sessionImpl.initialize(cb); 2981 } 2982 args.recycle(); 2983 return; 2984 } 2985 case DO_CREATE_RECORDING_SESSION: { 2986 SomeArgs args = (SomeArgs) msg.obj; 2987 ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg1; 2988 String inputId = (String) args.arg2; 2989 String sessionId = (String) args.arg3; 2990 args.recycle(); 2991 RecordingSession recordingSessionImpl = 2992 onCreateRecordingSession(inputId, sessionId); 2993 if (recordingSessionImpl == null) { 2994 try { 2995 // Failed to create a recording session. 2996 cb.onSessionCreated(null, null); 2997 } catch (RemoteException e) { 2998 Log.e(TAG, "error in onSessionCreated", e); 2999 } 3000 return; 3001 } 3002 ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this, 3003 recordingSessionImpl); 3004 try { 3005 cb.onSessionCreated(stub, null); 3006 } catch (RemoteException e) { 3007 Log.e(TAG, "error in onSessionCreated", e); 3008 } 3009 recordingSessionImpl.initialize(cb); 3010 return; 3011 } 3012 case DO_ADD_HARDWARE_INPUT: { 3013 TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj; 3014 TvInputInfo inputInfo = onHardwareAdded(hardwareInfo); 3015 if (inputInfo != null) { 3016 broadcastAddHardwareInput(hardwareInfo.getDeviceId(), inputInfo); 3017 } 3018 return; 3019 } 3020 case DO_REMOVE_HARDWARE_INPUT: { 3021 TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj; 3022 String inputId = onHardwareRemoved(hardwareInfo); 3023 if (inputId != null) { 3024 broadcastRemoveHardwareInput(inputId); 3025 } 3026 return; 3027 } 3028 case DO_ADD_HDMI_INPUT: { 3029 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj; 3030 TvInputInfo inputInfo = onHdmiDeviceAdded(deviceInfo); 3031 if (inputInfo != null) { 3032 broadcastAddHdmiInput(deviceInfo.getId(), inputInfo); 3033 } 3034 return; 3035 } 3036 case DO_REMOVE_HDMI_INPUT: { 3037 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj; 3038 String inputId = onHdmiDeviceRemoved(deviceInfo); 3039 if (inputId != null) { 3040 broadcastRemoveHardwareInput(inputId); 3041 } 3042 return; 3043 } 3044 case DO_UPDATE_HDMI_INPUT: { 3045 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj; 3046 onHdmiDeviceUpdated(deviceInfo); 3047 return; 3048 } 3049 default: { 3050 Log.w(TAG, "Unhandled message code: " + msg.what); 3051 return; 3052 } 3053 } 3054 } 3055 } 3056 } 3057