1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.tv.interactive; 18 19 import android.annotation.CallSuper; 20 import android.annotation.MainThread; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.Px; 24 import android.annotation.SdkConstant; 25 import android.annotation.StringDef; 26 import android.annotation.SuppressLint; 27 import android.app.ActivityManager; 28 import android.app.Service; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.graphics.PixelFormat; 32 import android.graphics.Rect; 33 import android.media.tv.AdRequest; 34 import android.media.tv.AdResponse; 35 import android.media.tv.BroadcastInfoRequest; 36 import android.media.tv.BroadcastInfoResponse; 37 import android.media.tv.TvContentRating; 38 import android.media.tv.TvInputInfo; 39 import android.media.tv.TvInputManager; 40 import android.media.tv.TvTrackInfo; 41 import android.media.tv.TvView; 42 import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback; 43 import android.net.Uri; 44 import android.os.AsyncTask; 45 import android.os.Bundle; 46 import android.os.Handler; 47 import android.os.IBinder; 48 import android.os.Looper; 49 import android.os.Message; 50 import android.os.Process; 51 import android.os.RemoteCallbackList; 52 import android.os.RemoteException; 53 import android.util.Log; 54 import android.view.Gravity; 55 import android.view.InputChannel; 56 import android.view.InputDevice; 57 import android.view.InputEvent; 58 import android.view.InputEventReceiver; 59 import android.view.KeyEvent; 60 import android.view.MotionEvent; 61 import android.view.Surface; 62 import android.view.View; 63 import android.view.WindowManager; 64 import android.widget.FrameLayout; 65 66 import com.android.internal.os.SomeArgs; 67 68 import java.lang.annotation.Retention; 69 import java.lang.annotation.RetentionPolicy; 70 import java.util.ArrayList; 71 import java.util.List; 72 73 /** 74 * A TV interactive application service is a service that provides runtime environment and runs TV 75 * interactive applications. 76 */ 77 public abstract class TvInteractiveAppService extends Service { 78 private static final boolean DEBUG = false; 79 private static final String TAG = "TvInteractiveAppService"; 80 81 private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000; 82 83 /** 84 * This is the interface name that a service implementing a TV Interactive App service should 85 * say that it supports -- that is, this is the action it uses for its intent filter. To be 86 * supported, the service must also require the 87 * {@link android.Manifest.permission#BIND_TV_INTERACTIVE_APP} permission so that other 88 * applications cannot abuse it. 89 */ 90 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 91 public static final String SERVICE_INTERFACE = 92 "android.media.tv.interactive.TvInteractiveAppService"; 93 94 /** 95 * Name under which a TvInteractiveAppService component publishes information about itself. This 96 * meta-data must reference an XML resource containing an 97 * <code><{@link android.R.styleable#TvInteractiveAppService tv-interactive-app}></code> 98 * tag. 99 */ 100 public static final String SERVICE_META_DATA = "android.media.tv.interactive.app"; 101 102 /** @hide */ 103 @Retention(RetentionPolicy.SOURCE) 104 @StringDef(prefix = "PLAYBACK_COMMAND_TYPE_", value = { 105 PLAYBACK_COMMAND_TYPE_TUNE, 106 PLAYBACK_COMMAND_TYPE_TUNE_NEXT, 107 PLAYBACK_COMMAND_TYPE_TUNE_PREV, 108 PLAYBACK_COMMAND_TYPE_STOP, 109 PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME, 110 PLAYBACK_COMMAND_TYPE_SELECT_TRACK 111 }) 112 public @interface PlaybackCommandType {} 113 114 /** 115 * Playback command type: tune to the given channel. 116 * @see #COMMAND_PARAMETER_KEY_CHANNEL_URI 117 */ 118 public static final String PLAYBACK_COMMAND_TYPE_TUNE = "tune"; 119 /** 120 * Playback command type: tune to the next channel. 121 */ 122 public static final String PLAYBACK_COMMAND_TYPE_TUNE_NEXT = "tune_next"; 123 /** 124 * Playback command type: tune to the previous channel. 125 */ 126 public static final String PLAYBACK_COMMAND_TYPE_TUNE_PREV = "tune_previous"; 127 /** 128 * Playback command type: stop the playback. 129 */ 130 public static final String PLAYBACK_COMMAND_TYPE_STOP = "stop"; 131 /** 132 * Playback command type: set the volume. 133 */ 134 public static final String PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME = 135 "set_stream_volume"; 136 /** 137 * Playback command type: select the given track. 138 */ 139 public static final String PLAYBACK_COMMAND_TYPE_SELECT_TRACK = "select_track"; 140 /** 141 * Playback command parameter: channel URI. 142 * <p>Type: android.net.Uri 143 * 144 * @see #PLAYBACK_COMMAND_TYPE_TUNE 145 */ 146 public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri"; 147 /** 148 * Playback command parameter: TV input ID. 149 * <p>Type: String 150 * 151 * @see TvInputInfo#getId() 152 */ 153 public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id"; 154 /** 155 * Playback command parameter: stream volume. 156 * <p>Type: float 157 * 158 * @see #PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME 159 */ 160 public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume"; 161 /** 162 * Playback command parameter: track type. 163 * <p>Type: int 164 * 165 * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK 166 * @see TvTrackInfo#getType() 167 */ 168 public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type"; 169 /** 170 * Playback command parameter: track ID. 171 * <p>Type: String 172 * 173 * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK 174 * @see TvTrackInfo#getId() 175 */ 176 public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id"; 177 /** 178 * Command to quiet channel change. No channel banner or channel info is shown. 179 * <p>Refer to HbbTV Spec 2.0.4 chapter A.2.4.3. 180 */ 181 public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY = 182 "command_change_channel_quietly"; 183 184 private final Handler mServiceHandler = new ServiceHandler(); 185 private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks = 186 new RemoteCallbackList<>(); 187 188 @Override 189 @Nullable onBind(@onNull Intent intent)190 public final IBinder onBind(@NonNull Intent intent) { 191 ITvInteractiveAppService.Stub tvIAppServiceBinder = new ITvInteractiveAppService.Stub() { 192 @Override 193 public void registerCallback(ITvInteractiveAppServiceCallback cb) { 194 if (cb != null) { 195 mCallbacks.register(cb); 196 } 197 } 198 199 @Override 200 public void unregisterCallback(ITvInteractiveAppServiceCallback cb) { 201 if (cb != null) { 202 mCallbacks.unregister(cb); 203 } 204 } 205 206 @Override 207 public void createSession(InputChannel channel, ITvInteractiveAppSessionCallback cb, 208 String iAppServiceId, int type) { 209 if (cb == null) { 210 return; 211 } 212 SomeArgs args = SomeArgs.obtain(); 213 args.arg1 = channel; 214 args.arg2 = cb; 215 args.arg3 = iAppServiceId; 216 args.arg4 = type; 217 mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args) 218 .sendToTarget(); 219 } 220 221 @Override 222 public void registerAppLinkInfo(AppLinkInfo appLinkInfo) { 223 onRegisterAppLinkInfo(appLinkInfo); 224 } 225 226 @Override 227 public void unregisterAppLinkInfo(AppLinkInfo appLinkInfo) { 228 onUnregisterAppLinkInfo(appLinkInfo); 229 } 230 231 @Override 232 public void sendAppLinkCommand(Bundle command) { 233 onAppLinkCommand(command); 234 } 235 }; 236 return tvIAppServiceBinder; 237 } 238 239 /** 240 * Called when a request to register an Android application link info record is received. 241 */ onRegisterAppLinkInfo(@onNull AppLinkInfo appLinkInfo)242 public void onRegisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) { 243 } 244 245 /** 246 * Called when a request to unregister an Android application link info record is received. 247 */ onUnregisterAppLinkInfo(@onNull AppLinkInfo appLinkInfo)248 public void onUnregisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) { 249 } 250 251 /** 252 * Called when app link command is received. 253 * 254 * @see android.media.tv.interactive.TvInteractiveAppManager#sendAppLinkCommand(String, Bundle) 255 */ onAppLinkCommand(@onNull Bundle command)256 public void onAppLinkCommand(@NonNull Bundle command) { 257 } 258 259 260 /** 261 * Returns a concrete implementation of {@link Session}. 262 * 263 * <p>May return {@code null} if this TV Interactive App service fails to create a session for 264 * some reason. 265 * 266 * @param iAppServiceId The ID of the TV Interactive App associated with the session. 267 * @param type The type of the TV Interactive App associated with the session. 268 */ 269 @Nullable onCreateSession( @onNull String iAppServiceId, @TvInteractiveAppServiceInfo.InteractiveAppType int type)270 public abstract Session onCreateSession( 271 @NonNull String iAppServiceId, 272 @TvInteractiveAppServiceInfo.InteractiveAppType int type); 273 274 /** 275 * Notifies the system when the state of the interactive app RTE has been changed. 276 * 277 * @param type the interactive app type 278 * @param state the current state of the service of the given type 279 * @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is 280 * used when the state is not 281 * {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}. 282 */ notifyStateChanged( @vInteractiveAppServiceInfo.InteractiveAppType int type, @TvInteractiveAppManager.ServiceState int state, @TvInteractiveAppManager.ErrorCode int error)283 public final void notifyStateChanged( 284 @TvInteractiveAppServiceInfo.InteractiveAppType int type, 285 @TvInteractiveAppManager.ServiceState int state, 286 @TvInteractiveAppManager.ErrorCode int error) { 287 SomeArgs args = SomeArgs.obtain(); 288 args.arg1 = type; 289 args.arg2 = state; 290 args.arg3 = error; 291 mServiceHandler 292 .obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, args).sendToTarget(); 293 } 294 295 /** 296 * Base class for derived classes to implement to provide a TV interactive app session. 297 * 298 * <p>A session is associated with a {@link TvInteractiveAppView} instance and handles 299 * corresponding communications. It also handles the communications with 300 * {@link android.media.tv.TvInputService.Session} if connected. 301 * 302 * @see TvInteractiveAppView#setTvView(TvView) 303 */ 304 public abstract static class Session implements KeyEvent.Callback { 305 private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 306 307 private final Object mLock = new Object(); 308 // @GuardedBy("mLock") 309 private ITvInteractiveAppSessionCallback mSessionCallback; 310 // @GuardedBy("mLock") 311 private final List<Runnable> mPendingActions = new ArrayList<>(); 312 313 private final Context mContext; 314 final Handler mHandler; 315 private final WindowManager mWindowManager; 316 private WindowManager.LayoutParams mWindowParams; 317 private Surface mSurface; 318 private FrameLayout mMediaViewContainer; 319 private View mMediaView; 320 private MediaViewCleanUpTask mMediaViewCleanUpTask; 321 private boolean mMediaViewEnabled; 322 private IBinder mWindowToken; 323 private Rect mMediaFrame; 324 325 /** 326 * Creates a new Session. 327 * 328 * @param context The context of the application 329 */ Session(@onNull Context context)330 public Session(@NonNull Context context) { 331 mContext = context; 332 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 333 mHandler = new Handler(context.getMainLooper()); 334 } 335 336 /** 337 * Enables or disables the media view. 338 * 339 * <p>By default, the media view is disabled. Must be called explicitly after the 340 * session is created to enable the media view. 341 * 342 * <p>The TV Interactive App service can disable its media view when needed. 343 * 344 * @param enable {@code true} if you want to enable the media view. {@code false} 345 * otherwise. 346 */ 347 @CallSuper setMediaViewEnabled(final boolean enable)348 public void setMediaViewEnabled(final boolean enable) { 349 mHandler.post(new Runnable() { 350 @Override 351 public void run() { 352 if (enable == mMediaViewEnabled) { 353 return; 354 } 355 mMediaViewEnabled = enable; 356 if (enable) { 357 if (mWindowToken != null) { 358 createMediaView(mWindowToken, mMediaFrame); 359 } 360 } else { 361 removeMediaView(false); 362 } 363 } 364 }); 365 } 366 367 /** 368 * Returns {@code true} if media view is enabled, {@code false} otherwise. 369 * 370 * @see #setMediaViewEnabled(boolean) 371 */ isMediaViewEnabled()372 public boolean isMediaViewEnabled() { 373 return mMediaViewEnabled; 374 } 375 376 /** 377 * Starts TvInteractiveAppService session. 378 */ onStartInteractiveApp()379 public void onStartInteractiveApp() { 380 } 381 382 /** 383 * Stops TvInteractiveAppService session. 384 */ onStopInteractiveApp()385 public void onStopInteractiveApp() { 386 } 387 388 /** 389 * Resets TvInteractiveAppService session. 390 */ onResetInteractiveApp()391 public void onResetInteractiveApp() { 392 } 393 394 /** 395 * Creates broadcast-independent(BI) interactive application. 396 * 397 * <p>The implementation should call {@link #notifyBiInteractiveAppCreated(Uri, String)}, 398 * no matter if it's created successfully or not. 399 * 400 * @see #notifyBiInteractiveAppCreated(Uri, String) 401 * @see #onDestroyBiInteractiveAppRequest(String) 402 */ onCreateBiInteractiveAppRequest( @onNull Uri biIAppUri, @Nullable Bundle params)403 public void onCreateBiInteractiveAppRequest( 404 @NonNull Uri biIAppUri, @Nullable Bundle params) { 405 } 406 407 408 /** 409 * Destroys broadcast-independent(BI) interactive application. 410 * 411 * @param biIAppId the BI interactive app ID from 412 * {@link #onCreateBiInteractiveAppRequest(Uri, Bundle)} 413 * 414 * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) 415 */ onDestroyBiInteractiveAppRequest(@onNull String biIAppId)416 public void onDestroyBiInteractiveAppRequest(@NonNull String biIAppId) { 417 } 418 419 /** 420 * To toggle Digital Teletext Application if there is one in AIT app list. 421 * @param enable {@code true} to enable teletext app; {@code false} otherwise. 422 */ onSetTeletextAppEnabled(boolean enable)423 public void onSetTeletextAppEnabled(boolean enable) { 424 } 425 426 /** 427 * Receives current channel URI. 428 */ onCurrentChannelUri(@ullable Uri channelUri)429 public void onCurrentChannelUri(@Nullable Uri channelUri) { 430 } 431 432 /** 433 * Receives logical channel number (LCN) of current channel. 434 */ onCurrentChannelLcn(int lcn)435 public void onCurrentChannelLcn(int lcn) { 436 } 437 438 /** 439 * Receives current stream volume. 440 * 441 * @param volume a volume value between {@code 0.0f} and {@code 1.0f}, inclusive. 442 */ onStreamVolume(float volume)443 public void onStreamVolume(float volume) { 444 } 445 446 /** 447 * Receives track list. 448 */ onTrackInfoList(@onNull List<TvTrackInfo> tracks)449 public void onTrackInfoList(@NonNull List<TvTrackInfo> tracks) { 450 } 451 452 /** 453 * Receives current TV input ID. 454 */ onCurrentTvInputId(@ullable String inputId)455 public void onCurrentTvInputId(@Nullable String inputId) { 456 } 457 458 /** 459 * Receives signing result. 460 * @param signingId the ID to identify the request. It's the same as the corresponding ID in 461 * {@link Session#requestSigning(String, String, String, byte[])} 462 * @param result the signed result. 463 * 464 * @see #requestSigning(String, String, String, byte[]) 465 */ onSigningResult(@onNull String signingId, @NonNull byte[] result)466 public void onSigningResult(@NonNull String signingId, @NonNull byte[] result) { 467 } 468 469 /** 470 * Called when the application sends information of an error. 471 * 472 * @param errMsg the message of the error. 473 * @param params additional parameters of the error. For example, the signingId of {@link 474 * TvInteractiveAppCallback#onRequestSigning(String, String, String, String, byte[])} 475 * can be included to identify the related signing request, and the method name 476 * "onRequestSigning" can also be added to the params. 477 * 478 * @see TvInteractiveAppView#ERROR_KEY_METHOD_NAME 479 */ onError(@onNull String errMsg, @NonNull Bundle params)480 public void onError(@NonNull String errMsg, @NonNull Bundle params) { 481 } 482 483 /** 484 * Called when the application sets the surface. 485 * 486 * <p>The TV Interactive App service should render interactive app UI onto the given 487 * surface. When called with {@code null}, the Interactive App service should immediately 488 * free any references to the currently set surface and stop using it. 489 * 490 * @param surface The surface to be used for interactive app UI rendering. Can be 491 * {@code null}. 492 * @return {@code true} if the surface was set successfully, {@code false} otherwise. 493 */ onSetSurface(@ullable Surface surface)494 public abstract boolean onSetSurface(@Nullable Surface surface); 495 496 /** 497 * Called after any structural changes (format or size) have been made to the surface passed 498 * in {@link #onSetSurface}. This method is always called at least once, after 499 * {@link #onSetSurface} is called with non-null surface. 500 * 501 * @param format The new {@link PixelFormat} of the surface. 502 * @param width The new width of the surface. 503 * @param height The new height of the surface. 504 */ onSurfaceChanged(@ixelFormat.Format int format, int width, int height)505 public void onSurfaceChanged(@PixelFormat.Format int format, int width, int height) { 506 } 507 508 /** 509 * Called when the size of the media view is changed by the application. 510 * 511 * <p>This is always called at least once when the session is created regardless of whether 512 * the media view is enabled or not. The media view container size is the same as the 513 * containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can 514 * be different if the surface was changed by calling {@link #layoutSurface}. 515 * 516 * @param width The width of the media view, in pixels. 517 * @param height The height of the media view, in pixels. 518 */ onMediaViewSizeChanged(@x int width, @Px int height)519 public void onMediaViewSizeChanged(@Px int width, @Px int height) { 520 } 521 522 /** 523 * Called when the application requests to create an media view. Each session 524 * implementation can override this method and return its own view. 525 * 526 * @return a view attached to the media window 527 */ 528 @Nullable onCreateMediaView()529 public View onCreateMediaView() { 530 return null; 531 } 532 533 /** 534 * Releases TvInteractiveAppService session. 535 */ onRelease()536 public abstract void onRelease(); 537 538 /** 539 * Called when the corresponding TV input tuned to a channel. 540 * 541 * @param channelUri The tuned channel URI. 542 */ onTuned(@onNull Uri channelUri)543 public void onTuned(@NonNull Uri channelUri) { 544 } 545 546 /** 547 * Called when the corresponding TV input selected to a track. 548 */ onTrackSelected(@vTrackInfo.Type int type, @NonNull String trackId)549 public void onTrackSelected(@TvTrackInfo.Type int type, @NonNull String trackId) { 550 } 551 552 /** 553 * Called when the tracks are changed. 554 */ onTracksChanged(@onNull List<TvTrackInfo> tracks)555 public void onTracksChanged(@NonNull List<TvTrackInfo> tracks) { 556 } 557 558 /** 559 * Called when video is available. 560 */ onVideoAvailable()561 public void onVideoAvailable() { 562 } 563 564 /** 565 * Called when video is unavailable. 566 */ onVideoUnavailable(@vInputManager.VideoUnavailableReason int reason)567 public void onVideoUnavailable(@TvInputManager.VideoUnavailableReason int reason) { 568 } 569 570 /** 571 * Called when content is allowed. 572 */ onContentAllowed()573 public void onContentAllowed() { 574 } 575 576 /** 577 * Called when content is blocked. 578 */ onContentBlocked(@onNull TvContentRating rating)579 public void onContentBlocked(@NonNull TvContentRating rating) { 580 } 581 582 /** 583 * Called when signal strength is changed. 584 */ onSignalStrength(@vInputManager.SignalStrength int strength)585 public void onSignalStrength(@TvInputManager.SignalStrength int strength) { 586 } 587 588 /** 589 * Called when a broadcast info response is received. 590 */ onBroadcastInfoResponse(@onNull BroadcastInfoResponse response)591 public void onBroadcastInfoResponse(@NonNull BroadcastInfoResponse response) { 592 } 593 594 /** 595 * Called when an advertisement response is received. 596 */ onAdResponse(@onNull AdResponse response)597 public void onAdResponse(@NonNull AdResponse response) { 598 } 599 600 @Override onKeyDown(int keyCode, @NonNull KeyEvent event)601 public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { 602 return false; 603 } 604 605 @Override onKeyLongPress(int keyCode, @NonNull KeyEvent event)606 public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) { 607 return false; 608 } 609 610 @Override onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event)611 public boolean onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event) { 612 return false; 613 } 614 615 @Override onKeyUp(int keyCode, @NonNull KeyEvent event)616 public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { 617 return false; 618 } 619 620 /** 621 * Implement this method to handle touch screen motion events on the current session. 622 * 623 * @param event The motion event being received. 624 * @return If you handled the event, return {@code true}. If you want to allow the event to 625 * be handled by the next receiver, return {@code false}. 626 * @see View#onTouchEvent 627 */ onTouchEvent(@onNull MotionEvent event)628 public boolean onTouchEvent(@NonNull MotionEvent event) { 629 return false; 630 } 631 632 /** 633 * Implement this method to handle trackball events on the current session. 634 * 635 * @param event The motion event being received. 636 * @return If you handled the event, return {@code true}. If you want to allow the event to 637 * be handled by the next receiver, return {@code false}. 638 * @see View#onTrackballEvent 639 */ onTrackballEvent(@onNull MotionEvent event)640 public boolean onTrackballEvent(@NonNull MotionEvent event) { 641 return false; 642 } 643 644 /** 645 * Implement this method to handle generic motion events on the current session. 646 * 647 * @param event The motion event being received. 648 * @return If you handled the event, return {@code true}. If you want to allow the event to 649 * be handled by the next receiver, return {@code false}. 650 * @see View#onGenericMotionEvent 651 */ onGenericMotionEvent(@onNull MotionEvent event)652 public boolean onGenericMotionEvent(@NonNull MotionEvent event) { 653 return false; 654 } 655 656 /** 657 * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position 658 * is relative to the overlay view that sits on top of this surface. 659 * 660 * @param left Left position in pixels, relative to the overlay view. 661 * @param top Top position in pixels, relative to the overlay view. 662 * @param right Right position in pixels, relative to the overlay view. 663 * @param bottom Bottom position in pixels, relative to the overlay view. 664 */ 665 @CallSuper layoutSurface(final int left, final int top, final int right, final int bottom)666 public void layoutSurface(final int left, final int top, final int right, 667 final int bottom) { 668 if (left > right || top > bottom) { 669 throw new IllegalArgumentException("Invalid parameter"); 670 } 671 executeOrPostRunnableOnMainThread(new Runnable() { 672 @MainThread 673 @Override 674 public void run() { 675 try { 676 if (DEBUG) { 677 Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top 678 + ", r=" + right + ", b=" + bottom + ",)"); 679 } 680 if (mSessionCallback != null) { 681 mSessionCallback.onLayoutSurface(left, top, right, bottom); 682 } 683 } catch (RemoteException e) { 684 Log.w(TAG, "error in layoutSurface", e); 685 } 686 } 687 }); 688 } 689 690 /** 691 * Requests broadcast related information from the related TV input. 692 * @param request the request for broadcast info 693 */ 694 @CallSuper requestBroadcastInfo(@onNull final BroadcastInfoRequest request)695 public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) { 696 executeOrPostRunnableOnMainThread(new Runnable() { 697 @MainThread 698 @Override 699 public void run() { 700 try { 701 if (DEBUG) { 702 Log.d(TAG, "requestBroadcastInfo (requestId=" 703 + request.getRequestId() + ")"); 704 } 705 if (mSessionCallback != null) { 706 mSessionCallback.onBroadcastInfoRequest(request); 707 } 708 } catch (RemoteException e) { 709 Log.w(TAG, "error in requestBroadcastInfo", e); 710 } 711 } 712 }); 713 } 714 715 /** 716 * Remove broadcast information request from the related TV input. 717 * @param requestId the ID of the request 718 */ 719 @CallSuper removeBroadcastInfo(final int requestId)720 public void removeBroadcastInfo(final int requestId) { 721 executeOrPostRunnableOnMainThread(new Runnable() { 722 @MainThread 723 @Override 724 public void run() { 725 try { 726 if (DEBUG) { 727 Log.d(TAG, "removeBroadcastInfo (requestId=" 728 + requestId + ")"); 729 } 730 if (mSessionCallback != null) { 731 mSessionCallback.onRemoveBroadcastInfo(requestId); 732 } 733 } catch (RemoteException e) { 734 Log.w(TAG, "error in removeBroadcastInfo", e); 735 } 736 } 737 }); 738 } 739 740 /** 741 * Sends a specific playback command to be processed by the related TV input. 742 * 743 * @param cmdType type of the specific command 744 * @param parameters parameters of the specific command 745 */ 746 @CallSuper sendPlaybackCommandRequest( @laybackCommandType @onNull String cmdType, @Nullable Bundle parameters)747 public void sendPlaybackCommandRequest( 748 @PlaybackCommandType @NonNull String cmdType, @Nullable Bundle parameters) { 749 executeOrPostRunnableOnMainThread(new Runnable() { 750 @MainThread 751 @Override 752 public void run() { 753 try { 754 if (DEBUG) { 755 Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters=" 756 + parameters.toString() + ")"); 757 } 758 if (mSessionCallback != null) { 759 mSessionCallback.onCommandRequest(cmdType, parameters); 760 } 761 } catch (RemoteException e) { 762 Log.w(TAG, "error in requestCommand", e); 763 } 764 } 765 }); 766 } 767 768 /** 769 * Sets broadcast video bounds. 770 */ 771 @CallSuper setVideoBounds(@onNull Rect rect)772 public void setVideoBounds(@NonNull Rect rect) { 773 executeOrPostRunnableOnMainThread(new Runnable() { 774 @MainThread 775 @Override 776 public void run() { 777 try { 778 if (DEBUG) { 779 Log.d(TAG, "setVideoBounds (rect=" + rect + ")"); 780 } 781 if (mSessionCallback != null) { 782 mSessionCallback.onSetVideoBounds(rect); 783 } 784 } catch (RemoteException e) { 785 Log.w(TAG, "error in setVideoBounds", e); 786 } 787 } 788 }); 789 } 790 791 /** 792 * Requests the URI of the current channel. 793 */ 794 @CallSuper requestCurrentChannelUri()795 public void requestCurrentChannelUri() { 796 executeOrPostRunnableOnMainThread(new Runnable() { 797 @MainThread 798 @Override 799 public void run() { 800 try { 801 if (DEBUG) { 802 Log.d(TAG, "requestCurrentChannelUri"); 803 } 804 if (mSessionCallback != null) { 805 mSessionCallback.onRequestCurrentChannelUri(); 806 } 807 } catch (RemoteException e) { 808 Log.w(TAG, "error in requestCurrentChannelUri", e); 809 } 810 } 811 }); 812 } 813 814 /** 815 * Requests the logic channel number (LCN) of the current channel. 816 */ 817 @CallSuper requestCurrentChannelLcn()818 public void requestCurrentChannelLcn() { 819 executeOrPostRunnableOnMainThread(new Runnable() { 820 @MainThread 821 @Override 822 public void run() { 823 try { 824 if (DEBUG) { 825 Log.d(TAG, "requestCurrentChannelLcn"); 826 } 827 if (mSessionCallback != null) { 828 mSessionCallback.onRequestCurrentChannelLcn(); 829 } 830 } catch (RemoteException e) { 831 Log.w(TAG, "error in requestCurrentChannelLcn", e); 832 } 833 } 834 }); 835 } 836 837 /** 838 * Requests stream volume. 839 */ 840 @CallSuper requestStreamVolume()841 public void requestStreamVolume() { 842 executeOrPostRunnableOnMainThread(new Runnable() { 843 @MainThread 844 @Override 845 public void run() { 846 try { 847 if (DEBUG) { 848 Log.d(TAG, "requestStreamVolume"); 849 } 850 if (mSessionCallback != null) { 851 mSessionCallback.onRequestStreamVolume(); 852 } 853 } catch (RemoteException e) { 854 Log.w(TAG, "error in requestStreamVolume", e); 855 } 856 } 857 }); 858 } 859 860 /** 861 * Requests the list of {@link TvTrackInfo}. 862 */ 863 @CallSuper requestTrackInfoList()864 public void requestTrackInfoList() { 865 executeOrPostRunnableOnMainThread(new Runnable() { 866 @MainThread 867 @Override 868 public void run() { 869 try { 870 if (DEBUG) { 871 Log.d(TAG, "requestTrackInfoList"); 872 } 873 if (mSessionCallback != null) { 874 mSessionCallback.onRequestTrackInfoList(); 875 } 876 } catch (RemoteException e) { 877 Log.w(TAG, "error in requestTrackInfoList", e); 878 } 879 } 880 }); 881 } 882 883 /** 884 * Requests current TV input ID. 885 * 886 * @see android.media.tv.TvInputInfo 887 */ 888 @CallSuper requestCurrentTvInputId()889 public void requestCurrentTvInputId() { 890 executeOrPostRunnableOnMainThread(new Runnable() { 891 @MainThread 892 @Override 893 public void run() { 894 try { 895 if (DEBUG) { 896 Log.d(TAG, "requestCurrentTvInputId"); 897 } 898 if (mSessionCallback != null) { 899 mSessionCallback.onRequestCurrentTvInputId(); 900 } 901 } catch (RemoteException e) { 902 Log.w(TAG, "error in requestCurrentTvInputId", e); 903 } 904 } 905 }); 906 } 907 908 /** 909 * Requests signing of the given data. 910 * 911 * <p>This is used when the corresponding server of the broadcast-independent interactive 912 * app requires signing during handshaking, and the interactive app service doesn't have 913 * the built-in private key. The private key is provided by the content providers and 914 * pre-built in the related app, such as TV app. 915 * 916 * @param signingId the ID to identify the request. When a result is received, this ID can 917 * be used to correlate the result with the request. 918 * @param algorithm the standard name of the signature algorithm requested, such as 919 * MD5withRSA, SHA256withDSA, etc. The name is from standards like 920 * FIPS PUB 186-4 and PKCS #1. 921 * @param alias the alias of the corresponding {@link java.security.KeyStore}. 922 * @param data the original bytes to be signed. 923 * 924 * @see #onSigningResult(String, byte[]) 925 * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) 926 * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS 927 */ 928 @CallSuper requestSigning(@onNull String signingId, @NonNull String algorithm, @NonNull String alias, @NonNull byte[] data)929 public void requestSigning(@NonNull String signingId, @NonNull String algorithm, 930 @NonNull String alias, @NonNull byte[] data) { 931 executeOrPostRunnableOnMainThread(new Runnable() { 932 @MainThread 933 @Override 934 public void run() { 935 try { 936 if (DEBUG) { 937 Log.d(TAG, "requestSigning"); 938 } 939 if (mSessionCallback != null) { 940 mSessionCallback.onRequestSigning(signingId, algorithm, alias, data); 941 } 942 } catch (RemoteException e) { 943 Log.w(TAG, "error in requestSigning", e); 944 } 945 } 946 }); 947 } 948 949 /** 950 * Sends an advertisement request to be processed by the related TV input. 951 * 952 * @param request The advertisement request 953 */ 954 @CallSuper requestAd(@onNull final AdRequest request)955 public void requestAd(@NonNull final AdRequest request) { 956 executeOrPostRunnableOnMainThread(new Runnable() { 957 @MainThread 958 @Override 959 public void run() { 960 try { 961 if (DEBUG) { 962 Log.d(TAG, "requestAd (id=" + request.getId() + ")"); 963 } 964 if (mSessionCallback != null) { 965 mSessionCallback.onAdRequest(request); 966 } 967 } catch (RemoteException e) { 968 Log.w(TAG, "error in requestAd", e); 969 } 970 } 971 }); 972 } 973 startInteractiveApp()974 void startInteractiveApp() { 975 onStartInteractiveApp(); 976 } 977 stopInteractiveApp()978 void stopInteractiveApp() { 979 onStopInteractiveApp(); 980 } 981 resetInteractiveApp()982 void resetInteractiveApp() { 983 onResetInteractiveApp(); 984 } 985 createBiInteractiveApp(@onNull Uri biIAppUri, @Nullable Bundle params)986 void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { 987 onCreateBiInteractiveAppRequest(biIAppUri, params); 988 } 989 destroyBiInteractiveApp(@onNull String biIAppId)990 void destroyBiInteractiveApp(@NonNull String biIAppId) { 991 onDestroyBiInteractiveAppRequest(biIAppId); 992 } 993 setTeletextAppEnabled(boolean enable)994 void setTeletextAppEnabled(boolean enable) { 995 onSetTeletextAppEnabled(enable); 996 } 997 sendCurrentChannelUri(@ullable Uri channelUri)998 void sendCurrentChannelUri(@Nullable Uri channelUri) { 999 onCurrentChannelUri(channelUri); 1000 } 1001 sendCurrentChannelLcn(int lcn)1002 void sendCurrentChannelLcn(int lcn) { 1003 onCurrentChannelLcn(lcn); 1004 } 1005 sendStreamVolume(float volume)1006 void sendStreamVolume(float volume) { 1007 onStreamVolume(volume); 1008 } 1009 sendTrackInfoList(@onNull List<TvTrackInfo> tracks)1010 void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) { 1011 onTrackInfoList(tracks); 1012 } 1013 sendCurrentTvInputId(@ullable String inputId)1014 void sendCurrentTvInputId(@Nullable String inputId) { 1015 onCurrentTvInputId(inputId); 1016 } 1017 sendSigningResult(String signingId, byte[] result)1018 void sendSigningResult(String signingId, byte[] result) { 1019 onSigningResult(signingId, result); 1020 } 1021 notifyError(String errMsg, Bundle params)1022 void notifyError(String errMsg, Bundle params) { 1023 onError(errMsg, params); 1024 } 1025 release()1026 void release() { 1027 onRelease(); 1028 if (mSurface != null) { 1029 mSurface.release(); 1030 mSurface = null; 1031 } 1032 synchronized (mLock) { 1033 mSessionCallback = null; 1034 mPendingActions.clear(); 1035 } 1036 // Removes the media view lastly so that any hanging on the main thread can be handled 1037 // in {@link #scheduleMediaViewCleanup}. 1038 removeMediaView(true); 1039 } 1040 notifyTuned(Uri channelUri)1041 void notifyTuned(Uri channelUri) { 1042 if (DEBUG) { 1043 Log.d(TAG, "notifyTuned (channelUri=" + channelUri + ")"); 1044 } 1045 onTuned(channelUri); 1046 } 1047 notifyTrackSelected(int type, String trackId)1048 void notifyTrackSelected(int type, String trackId) { 1049 if (DEBUG) { 1050 Log.d(TAG, "notifyTrackSelected (type=" + type + "trackId=" + trackId + ")"); 1051 } 1052 onTrackSelected(type, trackId); 1053 } 1054 notifyTracksChanged(List<TvTrackInfo> tracks)1055 void notifyTracksChanged(List<TvTrackInfo> tracks) { 1056 if (DEBUG) { 1057 Log.d(TAG, "notifyTracksChanged (tracks=" + tracks + ")"); 1058 } 1059 onTracksChanged(tracks); 1060 } 1061 notifyVideoAvailable()1062 void notifyVideoAvailable() { 1063 if (DEBUG) { 1064 Log.d(TAG, "notifyVideoAvailable"); 1065 } 1066 onVideoAvailable(); 1067 } 1068 notifyVideoUnavailable(int reason)1069 void notifyVideoUnavailable(int reason) { 1070 if (DEBUG) { 1071 Log.d(TAG, "notifyVideoAvailable (reason=" + reason + ")"); 1072 } 1073 onVideoUnavailable(reason); 1074 } 1075 notifyContentAllowed()1076 void notifyContentAllowed() { 1077 if (DEBUG) { 1078 Log.d(TAG, "notifyContentAllowed"); 1079 } 1080 onContentAllowed(); 1081 } 1082 notifyContentBlocked(TvContentRating rating)1083 void notifyContentBlocked(TvContentRating rating) { 1084 if (DEBUG) { 1085 Log.d(TAG, "notifyContentBlocked (rating=" + rating.flattenToString() + ")"); 1086 } 1087 onContentBlocked(rating); 1088 } 1089 notifySignalStrength(int strength)1090 void notifySignalStrength(int strength) { 1091 if (DEBUG) { 1092 Log.d(TAG, "notifySignalStrength (strength=" + strength + ")"); 1093 } 1094 onSignalStrength(strength); 1095 } 1096 1097 /** 1098 * Calls {@link #onBroadcastInfoResponse}. 1099 */ notifyBroadcastInfoResponse(BroadcastInfoResponse response)1100 void notifyBroadcastInfoResponse(BroadcastInfoResponse response) { 1101 if (DEBUG) { 1102 Log.d(TAG, "notifyBroadcastInfoResponse (requestId=" 1103 + response.getRequestId() + ")"); 1104 } 1105 onBroadcastInfoResponse(response); 1106 } 1107 1108 /** 1109 * Calls {@link #onAdResponse}. 1110 */ notifyAdResponse(AdResponse response)1111 void notifyAdResponse(AdResponse response) { 1112 if (DEBUG) { 1113 Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")"); 1114 } 1115 onAdResponse(response); 1116 } 1117 1118 /** 1119 * Notifies when the session state is changed. 1120 * 1121 * @param state the current session state. 1122 * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is 1123 * used when the state is not 1124 * {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}. 1125 */ 1126 @CallSuper notifySessionStateChanged( @vInteractiveAppManager.InteractiveAppState int state, @TvInteractiveAppManager.ErrorCode int err)1127 public void notifySessionStateChanged( 1128 @TvInteractiveAppManager.InteractiveAppState int state, 1129 @TvInteractiveAppManager.ErrorCode int err) { 1130 executeOrPostRunnableOnMainThread(new Runnable() { 1131 @MainThread 1132 @Override 1133 public void run() { 1134 try { 1135 if (DEBUG) { 1136 Log.d(TAG, "notifySessionStateChanged (state=" 1137 + state + "; err=" + err + ")"); 1138 } 1139 if (mSessionCallback != null) { 1140 mSessionCallback.onSessionStateChanged(state, err); 1141 } 1142 } catch (RemoteException e) { 1143 Log.w(TAG, "error in notifySessionStateChanged", e); 1144 } 1145 } 1146 }); 1147 } 1148 1149 /** 1150 * Notifies the broadcast-independent(BI) interactive application has been created. 1151 * 1152 * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive 1153 * app. {@code null} if it's not created successfully. 1154 * 1155 * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) 1156 */ 1157 @CallSuper notifyBiInteractiveAppCreated( @onNull Uri biIAppUri, @Nullable String biIAppId)1158 public final void notifyBiInteractiveAppCreated( 1159 @NonNull Uri biIAppUri, @Nullable String biIAppId) { 1160 executeOrPostRunnableOnMainThread(new Runnable() { 1161 @MainThread 1162 @Override 1163 public void run() { 1164 try { 1165 if (DEBUG) { 1166 Log.d(TAG, "notifyBiInteractiveAppCreated (biIAppId=" 1167 + biIAppId + ")"); 1168 } 1169 if (mSessionCallback != null) { 1170 mSessionCallback.onBiInteractiveAppCreated(biIAppUri, biIAppId); 1171 } 1172 } catch (RemoteException e) { 1173 Log.w(TAG, "error in notifyBiInteractiveAppCreated", e); 1174 } 1175 } 1176 }); 1177 } 1178 1179 /** 1180 * Notifies when the digital teletext app state is changed. 1181 * @param state the current state. 1182 */ 1183 @CallSuper notifyTeletextAppStateChanged( @vInteractiveAppManager.TeletextAppState int state)1184 public final void notifyTeletextAppStateChanged( 1185 @TvInteractiveAppManager.TeletextAppState int state) { 1186 executeOrPostRunnableOnMainThread(new Runnable() { 1187 @MainThread 1188 @Override 1189 public void run() { 1190 try { 1191 if (DEBUG) { 1192 Log.d(TAG, "notifyTeletextAppState (state=" 1193 + state + ")"); 1194 } 1195 if (mSessionCallback != null) { 1196 mSessionCallback.onTeletextAppStateChanged(state); 1197 } 1198 } catch (RemoteException e) { 1199 Log.w(TAG, "error in notifyTeletextAppState", e); 1200 } 1201 } 1202 }); 1203 } 1204 1205 /** 1206 * Takes care of dispatching incoming input events and tells whether the event was handled. 1207 */ dispatchInputEvent(InputEvent event, InputEventReceiver receiver)1208 int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { 1209 if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")"); 1210 if (event instanceof KeyEvent) { 1211 KeyEvent keyEvent = (KeyEvent) event; 1212 if (keyEvent.dispatch(this, mDispatcherState, this)) { 1213 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 1214 } 1215 1216 // TODO: special handlings of navigation keys and media keys 1217 } else if (event instanceof MotionEvent) { 1218 MotionEvent motionEvent = (MotionEvent) event; 1219 final int source = motionEvent.getSource(); 1220 if (motionEvent.isTouchEvent()) { 1221 if (onTouchEvent(motionEvent)) { 1222 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 1223 } 1224 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 1225 if (onTrackballEvent(motionEvent)) { 1226 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 1227 } 1228 } else { 1229 if (onGenericMotionEvent(motionEvent)) { 1230 return TvInteractiveAppManager.Session.DISPATCH_HANDLED; 1231 } 1232 } 1233 } 1234 // TODO: handle overlay view 1235 return TvInteractiveAppManager.Session.DISPATCH_NOT_HANDLED; 1236 } 1237 initialize(ITvInteractiveAppSessionCallback callback)1238 private void initialize(ITvInteractiveAppSessionCallback callback) { 1239 synchronized (mLock) { 1240 mSessionCallback = callback; 1241 for (Runnable runnable : mPendingActions) { 1242 runnable.run(); 1243 } 1244 mPendingActions.clear(); 1245 } 1246 } 1247 1248 /** 1249 * Calls {@link #onSetSurface}. 1250 */ setSurface(Surface surface)1251 void setSurface(Surface surface) { 1252 onSetSurface(surface); 1253 if (mSurface != null) { 1254 mSurface.release(); 1255 } 1256 mSurface = surface; 1257 // TODO: Handle failure. 1258 } 1259 1260 /** 1261 * Calls {@link #onSurfaceChanged}. 1262 */ dispatchSurfaceChanged(int format, int width, int height)1263 void dispatchSurfaceChanged(int format, int width, int height) { 1264 if (DEBUG) { 1265 Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width 1266 + ", height=" + height + ")"); 1267 } 1268 onSurfaceChanged(format, width, height); 1269 } 1270 executeOrPostRunnableOnMainThread(Runnable action)1271 private void executeOrPostRunnableOnMainThread(Runnable action) { 1272 synchronized (mLock) { 1273 if (mSessionCallback == null) { 1274 // The session is not initialized yet. 1275 mPendingActions.add(action); 1276 } else { 1277 if (mHandler.getLooper().isCurrentThread()) { 1278 action.run(); 1279 } else { 1280 // Posts the runnable if this is not called from the main thread 1281 mHandler.post(action); 1282 } 1283 } 1284 } 1285 } 1286 1287 /** 1288 * Creates an media view. This calls {@link #onCreateMediaView} to get a view to attach 1289 * to the media window. 1290 * 1291 * @param windowToken A window token of the application. 1292 * @param frame A position of the media view. 1293 */ createMediaView(IBinder windowToken, Rect frame)1294 void createMediaView(IBinder windowToken, Rect frame) { 1295 if (mMediaViewContainer != null) { 1296 removeMediaView(false); 1297 } 1298 if (DEBUG) Log.d(TAG, "create media view(" + frame + ")"); 1299 mWindowToken = windowToken; 1300 mMediaFrame = frame; 1301 onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); 1302 if (!mMediaViewEnabled) { 1303 return; 1304 } 1305 mMediaView = onCreateMediaView(); 1306 if (mMediaView == null) { 1307 return; 1308 } 1309 if (mMediaViewCleanUpTask != null) { 1310 mMediaViewCleanUpTask.cancel(true); 1311 mMediaViewCleanUpTask = null; 1312 } 1313 // Creates a container view to check hanging on the media view detaching. 1314 // Adding/removing the media view to/from the container make the view attach/detach 1315 // logic run on the main thread. 1316 mMediaViewContainer = new FrameLayout(mContext.getApplicationContext()); 1317 mMediaViewContainer.addView(mMediaView); 1318 1319 int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 1320 // We make the overlay view non-focusable and non-touchable so that 1321 // the application that owns the window token can decide whether to consume or 1322 // dispatch the input events. 1323 int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1324 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE 1325 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; 1326 if (ActivityManager.isHighEndGfx()) { 1327 flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 1328 } 1329 mWindowParams = new WindowManager.LayoutParams( 1330 frame.right - frame.left, frame.bottom - frame.top, 1331 frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT); 1332 mWindowParams.privateFlags |= 1333 WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 1334 mWindowParams.gravity = Gravity.START | Gravity.TOP; 1335 mWindowParams.token = windowToken; 1336 mWindowManager.addView(mMediaViewContainer, mWindowParams); 1337 } 1338 1339 /** 1340 * Relayouts the current media view. 1341 * 1342 * @param frame A new position of the media view. 1343 */ relayoutMediaView(Rect frame)1344 void relayoutMediaView(Rect frame) { 1345 if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")"); 1346 if (mMediaFrame == null || mMediaFrame.width() != frame.width() 1347 || mMediaFrame.height() != frame.height()) { 1348 // Note: relayoutMediaView is called whenever TvInteractiveAppView's layout is 1349 // changed regardless of setMediaViewEnabled. 1350 onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top); 1351 } 1352 mMediaFrame = frame; 1353 if (!mMediaViewEnabled || mMediaViewContainer == null) { 1354 return; 1355 } 1356 mWindowParams.x = frame.left; 1357 mWindowParams.y = frame.top; 1358 mWindowParams.width = frame.right - frame.left; 1359 mWindowParams.height = frame.bottom - frame.top; 1360 mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams); 1361 } 1362 1363 /** 1364 * Removes the current media view. 1365 */ removeMediaView(boolean clearWindowToken)1366 void removeMediaView(boolean clearWindowToken) { 1367 if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")"); 1368 if (clearWindowToken) { 1369 mWindowToken = null; 1370 mMediaFrame = null; 1371 } 1372 if (mMediaViewContainer != null) { 1373 // Removes the media view from the view hierarchy in advance so that it can be 1374 // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is 1375 // hanging. 1376 mMediaViewContainer.removeView(mMediaView); 1377 mMediaView = null; 1378 mWindowManager.removeView(mMediaViewContainer); 1379 mMediaViewContainer = null; 1380 mWindowParams = null; 1381 } 1382 } 1383 1384 /** 1385 * Schedules a task which checks whether the media view is detached and kills the process 1386 * if it is not. Note that this method is expected to be called in a non-main thread. 1387 */ scheduleMediaViewCleanup()1388 void scheduleMediaViewCleanup() { 1389 View mediaViewParent = mMediaViewContainer; 1390 if (mediaViewParent != null) { 1391 mMediaViewCleanUpTask = new MediaViewCleanUpTask(); 1392 mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 1393 mediaViewParent); 1394 } 1395 } 1396 } 1397 1398 private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> { 1399 @Override doInBackground(View... views)1400 protected Void doInBackground(View... views) { 1401 View mediaViewParent = views[0]; 1402 try { 1403 Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS); 1404 } catch (InterruptedException e) { 1405 return null; 1406 } 1407 if (isCancelled()) { 1408 return null; 1409 } 1410 if (mediaViewParent.isAttachedToWindow()) { 1411 Log.e(TAG, "Time out on releasing media view. Killing " 1412 + mediaViewParent.getContext().getPackageName()); 1413 android.os.Process.killProcess(Process.myPid()); 1414 } 1415 return null; 1416 } 1417 } 1418 1419 /** 1420 * Implements the internal ITvInteractiveAppSession interface. 1421 * @hide 1422 */ 1423 public static class ITvInteractiveAppSessionWrapper extends ITvInteractiveAppSession.Stub { 1424 // TODO: put ITvInteractiveAppSessionWrapper in a separate Java file 1425 private final Session mSessionImpl; 1426 private InputChannel mChannel; 1427 private TvInteractiveAppEventReceiver mReceiver; 1428 ITvInteractiveAppSessionWrapper( Context context, Session mSessionImpl, InputChannel channel)1429 public ITvInteractiveAppSessionWrapper( 1430 Context context, Session mSessionImpl, InputChannel channel) { 1431 this.mSessionImpl = mSessionImpl; 1432 mChannel = channel; 1433 if (channel != null) { 1434 mReceiver = new TvInteractiveAppEventReceiver(channel, context.getMainLooper()); 1435 } 1436 } 1437 1438 @Override startInteractiveApp()1439 public void startInteractiveApp() { 1440 mSessionImpl.startInteractiveApp(); 1441 } 1442 1443 @Override stopInteractiveApp()1444 public void stopInteractiveApp() { 1445 mSessionImpl.stopInteractiveApp(); 1446 } 1447 1448 @Override resetInteractiveApp()1449 public void resetInteractiveApp() { 1450 mSessionImpl.resetInteractiveApp(); 1451 } 1452 1453 @Override createBiInteractiveApp(@onNull Uri biIAppUri, @Nullable Bundle params)1454 public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { 1455 mSessionImpl.createBiInteractiveApp(biIAppUri, params); 1456 } 1457 1458 @Override setTeletextAppEnabled(boolean enable)1459 public void setTeletextAppEnabled(boolean enable) { 1460 mSessionImpl.setTeletextAppEnabled(enable); 1461 } 1462 1463 @Override destroyBiInteractiveApp(@onNull String biIAppId)1464 public void destroyBiInteractiveApp(@NonNull String biIAppId) { 1465 mSessionImpl.destroyBiInteractiveApp(biIAppId); 1466 } 1467 1468 @Override sendCurrentChannelUri(@ullable Uri channelUri)1469 public void sendCurrentChannelUri(@Nullable Uri channelUri) { 1470 mSessionImpl.sendCurrentChannelUri(channelUri); 1471 } 1472 1473 @Override sendCurrentChannelLcn(int lcn)1474 public void sendCurrentChannelLcn(int lcn) { 1475 mSessionImpl.sendCurrentChannelLcn(lcn); 1476 } 1477 1478 @Override sendStreamVolume(float volume)1479 public void sendStreamVolume(float volume) { 1480 mSessionImpl.sendStreamVolume(volume); 1481 } 1482 1483 @Override sendTrackInfoList(@onNull List<TvTrackInfo> tracks)1484 public void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) { 1485 mSessionImpl.sendTrackInfoList(tracks); 1486 } 1487 1488 @Override sendCurrentTvInputId(@ullable String inputId)1489 public void sendCurrentTvInputId(@Nullable String inputId) { 1490 mSessionImpl.sendCurrentTvInputId(inputId); 1491 } 1492 1493 @Override sendSigningResult(@onNull String signingId, @NonNull byte[] result)1494 public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) { 1495 mSessionImpl.sendSigningResult(signingId, result); 1496 } 1497 1498 @Override notifyError(@onNull String errMsg, @NonNull Bundle params)1499 public void notifyError(@NonNull String errMsg, @NonNull Bundle params) { 1500 mSessionImpl.notifyError(errMsg, params); 1501 } 1502 1503 @Override release()1504 public void release() { 1505 mSessionImpl.scheduleMediaViewCleanup(); 1506 mSessionImpl.release(); 1507 } 1508 1509 @Override notifyTuned(Uri channelUri)1510 public void notifyTuned(Uri channelUri) { 1511 mSessionImpl.notifyTuned(channelUri); 1512 } 1513 1514 @Override notifyTrackSelected(int type, final String trackId)1515 public void notifyTrackSelected(int type, final String trackId) { 1516 mSessionImpl.notifyTrackSelected(type, trackId); 1517 } 1518 1519 @Override notifyTracksChanged(List<TvTrackInfo> tracks)1520 public void notifyTracksChanged(List<TvTrackInfo> tracks) { 1521 mSessionImpl.notifyTracksChanged(tracks); 1522 } 1523 1524 @Override notifyVideoAvailable()1525 public void notifyVideoAvailable() { 1526 mSessionImpl.notifyVideoAvailable(); 1527 } 1528 1529 @Override notifyVideoUnavailable(int reason)1530 public void notifyVideoUnavailable(int reason) { 1531 mSessionImpl.notifyVideoUnavailable(reason); 1532 } 1533 1534 @Override notifyContentAllowed()1535 public void notifyContentAllowed() { 1536 mSessionImpl.notifyContentAllowed(); 1537 } 1538 1539 @Override notifyContentBlocked(String rating)1540 public void notifyContentBlocked(String rating) { 1541 mSessionImpl.notifyContentBlocked(TvContentRating.unflattenFromString(rating)); 1542 } 1543 1544 @Override notifySignalStrength(int strength)1545 public void notifySignalStrength(int strength) { 1546 mSessionImpl.notifySignalStrength(strength); 1547 } 1548 1549 @Override setSurface(Surface surface)1550 public void setSurface(Surface surface) { 1551 mSessionImpl.setSurface(surface); 1552 } 1553 1554 @Override dispatchSurfaceChanged(int format, int width, int height)1555 public void dispatchSurfaceChanged(int format, int width, int height) { 1556 mSessionImpl.dispatchSurfaceChanged(format, width, height); 1557 } 1558 1559 @Override notifyBroadcastInfoResponse(BroadcastInfoResponse response)1560 public void notifyBroadcastInfoResponse(BroadcastInfoResponse response) { 1561 mSessionImpl.notifyBroadcastInfoResponse(response); 1562 } 1563 1564 @Override notifyAdResponse(AdResponse response)1565 public void notifyAdResponse(AdResponse response) { 1566 mSessionImpl.notifyAdResponse(response); 1567 } 1568 1569 @Override createMediaView(IBinder windowToken, Rect frame)1570 public void createMediaView(IBinder windowToken, Rect frame) { 1571 mSessionImpl.createMediaView(windowToken, frame); 1572 } 1573 1574 @Override relayoutMediaView(Rect frame)1575 public void relayoutMediaView(Rect frame) { 1576 mSessionImpl.relayoutMediaView(frame); 1577 } 1578 1579 @Override removeMediaView()1580 public void removeMediaView() { 1581 mSessionImpl.removeMediaView(true); 1582 } 1583 1584 private final class TvInteractiveAppEventReceiver extends InputEventReceiver { TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper)1585 TvInteractiveAppEventReceiver(InputChannel inputChannel, Looper looper) { 1586 super(inputChannel, looper); 1587 } 1588 1589 @Override onInputEvent(InputEvent event)1590 public void onInputEvent(InputEvent event) { 1591 if (mSessionImpl == null) { 1592 // The session has been finished. 1593 finishInputEvent(event, false); 1594 return; 1595 } 1596 1597 int handled = mSessionImpl.dispatchInputEvent(event, this); 1598 if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) { 1599 finishInputEvent( 1600 event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED); 1601 } 1602 } 1603 } 1604 } 1605 1606 @SuppressLint("HandlerLeak") 1607 private final class ServiceHandler extends Handler { 1608 private static final int DO_CREATE_SESSION = 1; 1609 private static final int DO_NOTIFY_SESSION_CREATED = 2; 1610 private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3; 1611 broadcastRteStateChanged(int type, int state, int error)1612 private void broadcastRteStateChanged(int type, int state, int error) { 1613 int n = mCallbacks.beginBroadcast(); 1614 for (int i = 0; i < n; ++i) { 1615 try { 1616 mCallbacks.getBroadcastItem(i).onStateChanged(type, state, error); 1617 } catch (RemoteException e) { 1618 Log.e(TAG, "error in broadcastRteStateChanged", e); 1619 } 1620 } 1621 mCallbacks.finishBroadcast(); 1622 } 1623 1624 @Override handleMessage(Message msg)1625 public void handleMessage(Message msg) { 1626 switch (msg.what) { 1627 case DO_CREATE_SESSION: { 1628 SomeArgs args = (SomeArgs) msg.obj; 1629 InputChannel channel = (InputChannel) args.arg1; 1630 ITvInteractiveAppSessionCallback cb = 1631 (ITvInteractiveAppSessionCallback) args.arg2; 1632 String iAppServiceId = (String) args.arg3; 1633 int type = (int) args.arg4; 1634 args.recycle(); 1635 Session sessionImpl = onCreateSession(iAppServiceId, type); 1636 if (sessionImpl == null) { 1637 try { 1638 // Failed to create a session. 1639 cb.onSessionCreated(null); 1640 } catch (RemoteException e) { 1641 Log.e(TAG, "error in onSessionCreated", e); 1642 } 1643 return; 1644 } 1645 ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper( 1646 TvInteractiveAppService.this, sessionImpl, channel); 1647 1648 SomeArgs someArgs = SomeArgs.obtain(); 1649 someArgs.arg1 = sessionImpl; 1650 someArgs.arg2 = stub; 1651 someArgs.arg3 = cb; 1652 mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, 1653 someArgs).sendToTarget(); 1654 return; 1655 } 1656 case DO_NOTIFY_SESSION_CREATED: { 1657 SomeArgs args = (SomeArgs) msg.obj; 1658 Session sessionImpl = (Session) args.arg1; 1659 ITvInteractiveAppSession stub = (ITvInteractiveAppSession) args.arg2; 1660 ITvInteractiveAppSessionCallback cb = 1661 (ITvInteractiveAppSessionCallback) args.arg3; 1662 try { 1663 cb.onSessionCreated(stub); 1664 } catch (RemoteException e) { 1665 Log.e(TAG, "error in onSessionCreated", e); 1666 } 1667 if (sessionImpl != null) { 1668 sessionImpl.initialize(cb); 1669 } 1670 args.recycle(); 1671 return; 1672 } 1673 case DO_NOTIFY_RTE_STATE_CHANGED: { 1674 SomeArgs args = (SomeArgs) msg.obj; 1675 int type = (int) args.arg1; 1676 int state = (int) args.arg2; 1677 int error = (int) args.arg3; 1678 broadcastRteStateChanged(type, state, error); 1679 return; 1680 } 1681 default: { 1682 Log.w(TAG, "Unhandled message code: " + msg.what); 1683 return; 1684 } 1685 } 1686 } 1687 1688 } 1689 } 1690