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