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