TvInputService.java revision 783645e99f909ffc7a2d5d2fca9324cc0e9b7362
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 17package android.media.tv; 18 19import android.annotation.SuppressLint; 20import android.annotation.SystemApi; 21import android.app.Service; 22import android.content.Context; 23import android.content.Intent; 24import android.graphics.PixelFormat; 25import android.graphics.Rect; 26import android.hardware.hdmi.HdmiCecDeviceInfo; 27import android.net.Uri; 28import android.os.Bundle; 29import android.os.Handler; 30import android.os.IBinder; 31import android.os.Message; 32import android.os.RemoteCallbackList; 33import android.os.RemoteException; 34import android.util.Log; 35import android.view.Gravity; 36import android.view.InputChannel; 37import android.view.InputDevice; 38import android.view.InputEvent; 39import android.view.InputEventReceiver; 40import android.view.KeyEvent; 41import android.view.MotionEvent; 42import android.view.Surface; 43import android.view.View; 44import android.view.WindowManager; 45import android.view.accessibility.CaptioningManager; 46 47import com.android.internal.annotations.VisibleForTesting; 48import com.android.internal.os.SomeArgs; 49 50import java.util.List; 51 52/** 53 * The TvInputService class represents a TV input or source such as HDMI or built-in tuner which 54 * provides pass-through video or broadcast TV programs. 55 * <p> 56 * Applications will not normally use this service themselves, instead relying on the standard 57 * interaction provided by {@link TvView}. Those implementing TV input services should normally do 58 * so by deriving from this class and providing their own session implementation based on 59 * {@link TvInputService.Session}. All TV input services must require that clients hold the 60 * {@link android.Manifest.permission#BIND_TV_INPUT} in order to interact with the service; if this 61 * permission is not specified in the manifest, the system will refuse to bind to that TV input 62 * service. 63 * </p> 64 */ 65public abstract class TvInputService extends Service { 66 // STOPSHIP: Turn debugging off. 67 private static final boolean DEBUG = true; 68 private static final String TAG = "TvInputService"; 69 70 /** 71 * This is the interface name that a service implementing a TV input should say that it support 72 * -- that is, this is the action it uses for its intent filter. To be supported, the service 73 * must also require the {@link android.Manifest.permission#BIND_TV_INPUT} permission so that 74 * other applications cannot abuse it. 75 */ 76 public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService"; 77 78 /** 79 * Name under which a TvInputService component publishes information about itself. 80 * This meta-data must reference an XML resource containing an 81 * <code><{@link android.R.styleable#TvInputService tv-input}></code> 82 * tag. 83 */ 84 public static final String SERVICE_META_DATA = "android.media.tv.input"; 85 86 private final Handler mHandler = new ServiceHandler(); 87 private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks = 88 new RemoteCallbackList<ITvInputServiceCallback>(); 89 90 @Override 91 public final IBinder onBind(Intent intent) { 92 return new ITvInputService.Stub() { 93 @Override 94 public void registerCallback(ITvInputServiceCallback cb) { 95 if (cb != null) { 96 mCallbacks.register(cb); 97 } 98 } 99 100 @Override 101 public void unregisterCallback(ITvInputServiceCallback cb) { 102 if (cb != null) { 103 mCallbacks.unregister(cb); 104 } 105 } 106 107 @Override 108 public void createSession(InputChannel channel, ITvInputSessionCallback cb, 109 String inputId) { 110 if (channel == null) { 111 Log.w(TAG, "Creating session without input channel"); 112 } 113 if (cb == null) { 114 return; 115 } 116 SomeArgs args = SomeArgs.obtain(); 117 args.arg1 = channel; 118 args.arg2 = cb; 119 args.arg3 = inputId; 120 mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget(); 121 } 122 123 @Override 124 public void notifyHardwareAdded(TvInputHardwareInfo hardwareInfo) { 125 mHandler.obtainMessage(ServiceHandler.DO_ADD_HARDWARE_TV_INPUT, 126 hardwareInfo).sendToTarget(); 127 } 128 129 @Override 130 public void notifyHardwareRemoved(TvInputHardwareInfo hardwareInfo) { 131 mHandler.obtainMessage(ServiceHandler.DO_REMOVE_HARDWARE_TV_INPUT, 132 hardwareInfo).sendToTarget(); 133 } 134 135 @Override 136 public void notifyHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDeviceInfo) { 137 mHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_CEC_TV_INPUT, 138 cecDeviceInfo).sendToTarget(); 139 } 140 141 @Override 142 public void notifyHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDeviceInfo) { 143 mHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_CEC_TV_INPUT, 144 cecDeviceInfo).sendToTarget(); 145 } 146 }; 147 } 148 149 /** 150 * Get the number of callbacks that are registered. 151 * @hide 152 */ 153 @VisibleForTesting 154 public final int getRegisteredCallbackCount() { 155 return mCallbacks.getRegisteredCallbackCount(); 156 } 157 158 /** 159 * Returns a concrete implementation of {@link Session}. 160 * <p> 161 * May return {@code null} if this TV input service fails to create a session for some reason. 162 * </p> 163 * @param inputId The ID of the TV input associated with the session. 164 */ 165 public abstract Session onCreateSession(String inputId); 166 167 /** 168 * Returns a new {@link TvInputInfo} object if this service is responsible for 169 * {@code hardwareInfo}; otherwise, return {@code null}. Override to modify default behavior of 170 * ignoring all hardware input. 171 * 172 * @param hardwareInfo {@link TvInputHardwareInfo} object just added. 173 * @hide 174 */ 175 @SystemApi 176 public TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) { 177 return null; 178 } 179 180 /** 181 * Returns the input ID for {@code deviceId} if it is handled by this service; 182 * otherwise, return {@code null}. Override to modify default behavior of ignoring all hardware 183 * input. 184 * 185 * @param hardwareInfo {@link TvInputHardwareInfo} object just removed. 186 * @hide 187 */ 188 @SystemApi 189 public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) { 190 return null; 191 } 192 193 /** 194 * Returns a new {@link TvInputInfo} object if this service is responsible for 195 * {@code cecDeviceInfo}; otherwise, return {@code null}. Override to modify default behavior 196 * of ignoring all HDMI CEC logical input device. 197 * 198 * @param cecDeviceInfo {@link HdmiCecDeviceInfo} object just added. 199 * @hide 200 */ 201 @SystemApi 202 public TvInputInfo onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDeviceInfo) { 203 return null; 204 } 205 206 /** 207 * Returns the input ID for {@code logicalAddress} if it is handled by this service; 208 * otherwise, return {@code null}. Override to modify default behavior of ignoring all HDMI CEC 209 * logical input device. 210 * 211 * @param cecDeviceInfo {@link HdmiCecDeviceInfo} object just removed. 212 * @hide 213 */ 214 @SystemApi 215 public String onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDeviceInfo) { 216 return null; 217 } 218 219 /** 220 * Notify wrapped TV input ID of current input to TV input framework manager 221 * 222 * @param inputId The TV input ID of {@link TvInputPassthroughWrapperService} 223 * @param wrappedInputId The ID of the wrapped TV input such as external pass-though TV input 224 * @hide 225 */ 226 public final void notifyWrappedInputId(String inputId, String wrappedInputId) { 227 SomeArgs args = SomeArgs.obtain(); 228 args.arg1 = inputId; 229 args.arg2 = wrappedInputId; 230 mHandler.obtainMessage(TvInputService.ServiceHandler.DO_SET_WRAPPED_TV_INPUT_ID, 231 args).sendToTarget(); 232 } 233 234 /** 235 * Base class for derived classes to implement to provide a TV input session. 236 */ 237 public abstract class Session implements KeyEvent.Callback { 238 private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState(); 239 private final WindowManager mWindowManager; 240 private WindowManager.LayoutParams mWindowParams; 241 private Surface mSurface; 242 private View mOverlayView; 243 private boolean mOverlayViewEnabled; 244 private IBinder mWindowToken; 245 private Rect mOverlayFrame; 246 private ITvInputSessionCallback mSessionCallback; 247 248 public Session() { 249 mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 250 } 251 252 /** 253 * Enables or disables the overlay view. By default, the overlay view is disabled. Must be 254 * called explicitly after the session is created to enable the overlay view. 255 * 256 * @param enable {@code true} if you want to enable the overlay view. {@code false} 257 * otherwise. 258 */ 259 public void setOverlayViewEnabled(final boolean enable) { 260 mHandler.post(new Runnable() { 261 @Override 262 public void run() { 263 if (enable == mOverlayViewEnabled) { 264 return; 265 } 266 mOverlayViewEnabled = enable; 267 if (enable) { 268 if (mWindowToken != null) { 269 createOverlayView(mWindowToken, mOverlayFrame); 270 } 271 } else { 272 removeOverlayView(false); 273 } 274 } 275 }); 276 } 277 278 /** 279 * Dispatches an event to the application using this session. 280 * 281 * @param eventType The type of the event. 282 * @param eventArgs Optional arguments of the event. 283 * @hide 284 */ 285 public void notifySessionEvent(final String eventType, final Bundle eventArgs) { 286 if (eventType == null) { 287 throw new IllegalArgumentException("eventType should not be null."); 288 } 289 mHandler.post(new Runnable() { 290 @Override 291 public void run() { 292 try { 293 if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")"); 294 mSessionCallback.onSessionEvent(eventType, eventArgs); 295 } catch (RemoteException e) { 296 Log.w(TAG, "error in sending event (event=" + eventType + ")"); 297 } 298 } 299 }); 300 } 301 302 /** 303 * Notifies the channel of the session is retuned by TV input. 304 * 305 * @param channelUri The URI of a channel. 306 */ 307 public void notifyChannelRetuned(final Uri channelUri) { 308 mHandler.post(new Runnable() { 309 @Override 310 public void run() { 311 try { 312 if (DEBUG) Log.d(TAG, "notifyChannelRetuned"); 313 mSessionCallback.onChannelRetuned(channelUri); 314 } catch (RemoteException e) { 315 Log.w(TAG, "error in notifyChannelRetuned"); 316 } 317 } 318 }); 319 } 320 321 /** 322 * Sends the change on the track information. This is expected to be called whenever a 323 * track is added/removed and the metadata of a track is modified. 324 * 325 * @param tracks A list which includes track information. 326 */ 327 public void notifyTrackInfoChanged(final List<TvTrackInfo> tracks) { 328 if (!TvTrackInfo.checkSanity(tracks)) { 329 throw new IllegalArgumentException( 330 "Two or more selected tracks for a track type."); 331 } 332 mHandler.post(new Runnable() { 333 @Override 334 public void run() { 335 try { 336 if (DEBUG) Log.d(TAG, "notifyTrackInfoChanged"); 337 mSessionCallback.onTrackInfoChanged(tracks); 338 } catch (RemoteException e) { 339 Log.w(TAG, "error in notifyTrackInfoChanged"); 340 } 341 } 342 }); 343 } 344 345 /** 346 * Informs the application that video is available and the playback of the TV stream has 347 * been started. 348 */ 349 public void notifyVideoAvailable() { 350 mHandler.post(new Runnable() { 351 @Override 352 public void run() { 353 try { 354 if (DEBUG) Log.d(TAG, "notifyVideoAvailable"); 355 mSessionCallback.onVideoAvailable(); 356 } catch (RemoteException e) { 357 Log.w(TAG, "error in notifyVideoAvailable"); 358 } 359 } 360 }); 361 } 362 363 /** 364 * Informs the application that video is not available, so the TV input cannot continue 365 * playing the TV stream. 366 * 367 * @param reason The reason why the TV input stopped the playback: 368 * <ul> 369 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN} 370 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING} 371 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL} 372 * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING} 373 * </ul> 374 */ 375 public void notifyVideoUnavailable(final int reason) { 376 if (reason < TvInputManager.VIDEO_UNAVAILABLE_REASON_START 377 || reason > TvInputManager.VIDEO_UNAVAILABLE_REASON_END) { 378 throw new IllegalArgumentException("Unknown reason: " + reason); 379 } 380 mHandler.post(new Runnable() { 381 @Override 382 public void run() { 383 try { 384 if (DEBUG) Log.d(TAG, "notifyVideoUnavailable"); 385 mSessionCallback.onVideoUnavailable(reason); 386 } catch (RemoteException e) { 387 Log.w(TAG, "error in notifyVideoUnavailable"); 388 } 389 } 390 }); 391 } 392 393 /** 394 * Informs the application that the user is allowed to watch the current program content. 395 * <p> 396 * Each TV input service is required to query the system whether the user is allowed to 397 * watch the current program before showing it to the user if the parental controls is 398 * enabled (i.e. {@link TvInputManager#isParentalControlsEnabled 399 * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input 400 * service should block the content or not is determined by invoking 401 * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)} 402 * with the content rating for the current program. Then the {@link TvInputManager} makes a 403 * judgment based on the user blocked ratings stored in the secure settings and returns the 404 * result. If the rating in question turns out to be allowed by the user, the TV input 405 * service must call this method to notify the application that is permitted to show the 406 * content. 407 * </p><p> 408 * Each TV input service also needs to continuously listen to any changes made to the 409 * parental controls settings by registering a broadcast receiver to receive 410 * {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and 411 * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately 412 * reevaluate the current program with the new parental controls settings. 413 * </p> 414 * 415 * @see #notifyContentBlocked 416 * @see TvInputManager 417 */ 418 public void notifyContentAllowed() { 419 mHandler.post(new Runnable() { 420 @Override 421 public void run() { 422 try { 423 if (DEBUG) Log.d(TAG, "notifyContentAllowed"); 424 mSessionCallback.onContentAllowed(); 425 } catch (RemoteException e) { 426 Log.w(TAG, "error in notifyContentAllowed"); 427 } 428 } 429 }); 430 } 431 432 /** 433 * Informs the application that the current program content is blocked by parent controls. 434 * <p> 435 * Each TV input service is required to query the system whether the user is allowed to 436 * watch the current program before showing it to the user if the parental controls is 437 * enabled (i.e. {@link TvInputManager#isParentalControlsEnabled 438 * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input 439 * service should block the content or not is determined by invoking 440 * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)} 441 * with the content rating for the current program. Then the {@link TvInputManager} makes a 442 * judgment based on the user blocked ratings stored in the secure settings and returns the 443 * result. If the rating in question turns out to be blocked, the TV input service must 444 * immediately block the content and call this method with the content rating of the current 445 * program to prompt the PIN verification screen. 446 * </p><p> 447 * Each TV input service also needs to continuously listen to any changes made to the 448 * parental controls settings by registering a broadcast receiver to receive 449 * {@link TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED} and 450 * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately 451 * reevaluate the current program with the new parental controls settings. 452 * </p> 453 * 454 * @param rating The content rating for the current TV program. 455 * @see #notifyContentAllowed 456 * @see TvInputManager 457 */ 458 public void notifyContentBlocked(final TvContentRating rating) { 459 mHandler.post(new Runnable() { 460 @Override 461 public void run() { 462 try { 463 if (DEBUG) Log.d(TAG, "notifyContentBlocked"); 464 mSessionCallback.onContentBlocked(rating.flattenToString()); 465 } catch (RemoteException e) { 466 Log.w(TAG, "error in notifyContentBlocked"); 467 } 468 } 469 }); 470 } 471 472 /** 473 * Called when the session is released. 474 */ 475 public abstract void onRelease(); 476 477 /** 478 * Set the current session as the "main" session. See {@link TvView#setMainTvView} for the 479 * meaning of "main". 480 * <p> 481 * This is primarily for HDMI-CEC active source management. TV input service that manages 482 * HDMI-CEC logical device should make sure not only to select the corresponding HDMI 483 * logical device as source device on {@code onSetMainSession(true)}, but also to select 484 * internal device on {@code onSetMainSession(false)}. Also, if surface is set to non-main 485 * session, it needs to select internal device after temporarily selecting corresponding 486 * HDMI logical device for set up. 487 * </p><p> 488 * It is guaranteed that {@code onSetMainSession(true)} for new session is called first, 489 * and {@code onSetMainSession(false)} for old session is called afterwards. This allows 490 * {@code onSetMainSession(false)} to be no-op when TV input service knows that the next 491 * main session corresponds to another HDMI logical device. Practically, this implies that 492 * one TV input service should handle all HDMI port and HDMI-CEC logical devices for smooth 493 * active source transition. 494 * </p> 495 * 496 * @param isMainSession If true, session is main. 497 * @hide 498 */ 499 @SystemApi 500 public void onSetMainSession(boolean isMainSession) { 501 } 502 503 /** 504 * Sets the {@link Surface} for the current input session on which the TV input renders 505 * video. 506 * 507 * @param surface {@link Surface} an application passes to this TV input session. 508 * @return {@code true} if the surface was set, {@code false} otherwise. 509 */ 510 public abstract boolean onSetSurface(Surface surface); 511 512 /** 513 * Called after any structural changes (format or size) have been made to the 514 * {@link Surface} passed by {@link #onSetSurface}. This method is always called 515 * at least once, after {@link #onSetSurface} with non-null {@link Surface} is called. 516 * 517 * @param format The new PixelFormat of the {@link Surface}. 518 * @param width The new width of the {@link Surface}. 519 * @param height The new height of the {@link Surface}. 520 */ 521 public void onSurfaceChanged(int format, int width, int height) { 522 } 523 524 /** 525 * Sets the relative stream volume of the current TV input session to handle the change of 526 * audio focus by setting. 527 * 528 * @param volume Volume scale from 0.0 to 1.0. 529 */ 530 public abstract void onSetStreamVolume(float volume); 531 532 /** 533 * Tunes to a given channel. When the video is available, {@link #notifyVideoAvailable()} 534 * should be called. Also, {@link #notifyVideoUnavailable(int)} should be called when the 535 * TV input cannot continue playing the given channel. 536 * 537 * @param channelUri The URI of the channel. 538 * @return {@code true} the tuning was successful, {@code false} otherwise. 539 */ 540 public abstract boolean onTune(Uri channelUri); 541 542 /** 543 * Enables or disables the caption. 544 * <p> 545 * The locale for the user's preferred captioning language can be obtained by calling 546 * {@link CaptioningManager#getLocale CaptioningManager.getLocale()}. 547 * 548 * @param enabled {@code true} to enable, {@code false} to disable. 549 * @see CaptioningManager 550 */ 551 public abstract void onSetCaptionEnabled(boolean enabled); 552 553 /** 554 * Requests to unblock the content according to the given rating. 555 * <p> 556 * The implementation should unblock the content. 557 * TV input service has responsibility to decide when/how the unblock expires 558 * while it can keep previously unblocked ratings in order not to ask a user 559 * to unblock whenever a content rating is changed. 560 * Therefore an unblocked rating can be valid for a channel, a program, 561 * or certain amount of time depending on the implementation. 562 * </p> 563 * 564 * @param unblockedRating An unblocked content rating 565 */ 566 public void onUnblockContent(TvContentRating unblockedRating) { 567 } 568 569 /** 570 * Selects a given track. 571 * <p> 572 * If it is called multiple times on the same type of track (ie. Video, Audio, Text), the 573 * track selected previously should be unselected in the implementation of this method. 574 * Also, if the select operation was successful, the implementation should call 575 * {@link #notifyTrackInfoChanged(List)} to report the updated track information. 576 * </p> 577 * 578 * @param track The track to be selected. 579 * @return {@code true} if the select operation was successful, {@code false} otherwise. 580 * @see #notifyTrackInfoChanged 581 * @see TvTrackInfo#KEY_IS_SELECTED 582 */ 583 public boolean onSelectTrack(TvTrackInfo track) { 584 return false; 585 } 586 587 /** 588 * Unselects a given track. 589 * <p> 590 * If the unselect operation was successful, the implementation should call 591 * {@link #notifyTrackInfoChanged(List)} to report the updated track information. 592 * </p> 593 * 594 * @param track The track to be unselected. 595 * @return {@code true} if the unselect operation was successful, {@code false} otherwise. 596 * @see #notifyTrackInfoChanged 597 * @see TvTrackInfo#KEY_IS_SELECTED 598 */ 599 public boolean onUnselectTrack(TvTrackInfo track) { 600 return false; 601 } 602 603 /** 604 * Processes a private command sent from the application to the TV input. This can be used 605 * to provide domain-specific features that are only known between certain TV inputs and 606 * their clients. 607 * 608 * @param action Name of the command to be performed. This <em>must</em> be a scoped name, 609 * i.e. prefixed with a package name you own, so that different developers will 610 * not create conflicting commands. 611 * @param data Any data to include with the command. 612 * @hide 613 */ 614 @SystemApi 615 public void onAppPrivateCommand(String action, Bundle data) { 616 } 617 618 /** 619 * Called when an application requests to create an overlay view. Each session 620 * implementation can override this method and return its own view. 621 * 622 * @return a view attached to the overlay window 623 */ 624 public View onCreateOverlayView() { 625 return null; 626 } 627 628 /** 629 * Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent) 630 * KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event). 631 * <p> 632 * Override this to intercept key down events before they are processed by the application. 633 * If you return true, the application will not process the event itself. If you return 634 * false, the normal application processing will occur as if the TV input had not seen the 635 * event at all. 636 * 637 * @param keyCode The value in event.getKeyCode(). 638 * @param event Description of the key event. 639 * @return If you handled the event, return {@code true}. If you want to allow the event to 640 * be handled by the next receiver, return {@code false}. 641 */ 642 @Override 643 public boolean onKeyDown(int keyCode, KeyEvent event) { 644 return false; 645 } 646 647 /** 648 * Default implementation of 649 * {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent) 650 * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event). 651 * <p> 652 * Override this to intercept key long press events before they are processed by the 653 * application. If you return true, the application will not process the event itself. If 654 * you return false, the normal application processing will occur as if the TV input had not 655 * seen the event at all. 656 * 657 * @param keyCode The value in event.getKeyCode(). 658 * @param event Description of the key event. 659 * @return If you handled the event, return {@code true}. If you want to allow the event to 660 * be handled by the next receiver, return {@code false}. 661 */ 662 @Override 663 public boolean onKeyLongPress(int keyCode, KeyEvent event) { 664 return false; 665 } 666 667 /** 668 * Default implementation of 669 * {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent) 670 * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event). 671 * <p> 672 * Override this to intercept special key multiple events before they are processed by the 673 * application. If you return true, the application will not itself process the event. If 674 * you return false, the normal application processing will occur as if the TV input had not 675 * seen the event at all. 676 * 677 * @param keyCode The value in event.getKeyCode(). 678 * @param count The number of times the action was made. 679 * @param event Description of the key event. 680 * @return If you handled the event, return {@code true}. If you want to allow the event to 681 * be handled by the next receiver, return {@code false}. 682 */ 683 @Override 684 public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) { 685 return false; 686 } 687 688 /** 689 * Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent) 690 * KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event). 691 * <p> 692 * Override this to intercept key up events before they are processed by the application. If 693 * you return true, the application will not itself process the event. If you return false, 694 * the normal application processing will occur as if the TV input had not seen the event at 695 * all. 696 * 697 * @param keyCode The value in event.getKeyCode(). 698 * @param event Description of the key event. 699 * @return If you handled the event, return {@code true}. If you want to allow the event to 700 * be handled by the next receiver, return {@code false}. 701 */ 702 @Override 703 public boolean onKeyUp(int keyCode, KeyEvent event) { 704 return false; 705 } 706 707 /** 708 * Implement this method to handle touch screen motion events on the current input session. 709 * 710 * @param event The motion event being received. 711 * @return If you handled the event, return {@code true}. If you want to allow the event to 712 * be handled by the next receiver, return {@code false}. 713 * @see View#onTouchEvent 714 */ 715 public boolean onTouchEvent(MotionEvent event) { 716 return false; 717 } 718 719 /** 720 * Implement this method to handle trackball events on the current input session. 721 * 722 * @param event The motion event being received. 723 * @return If you handled the event, return {@code true}. If you want to allow the event to 724 * be handled by the next receiver, return {@code false}. 725 * @see View#onTrackballEvent 726 */ 727 public boolean onTrackballEvent(MotionEvent event) { 728 return false; 729 } 730 731 /** 732 * Implement this method to handle generic motion events on the current input session. 733 * 734 * @param event The motion event being received. 735 * @return If you handled the event, return {@code true}. If you want to allow the event to 736 * be handled by the next receiver, return {@code false}. 737 * @see View#onGenericMotionEvent 738 */ 739 public boolean onGenericMotionEvent(MotionEvent event) { 740 return false; 741 } 742 743 /** 744 * This method is called when the application would like to stop using the current input 745 * session. 746 */ 747 void release() { 748 removeOverlayView(true); 749 onRelease(); 750 if (mSurface != null) { 751 mSurface.release(); 752 mSurface = null; 753 } 754 } 755 756 /** 757 * Calls {@link #onSetMainSession}. 758 */ 759 void setMainSession(boolean isMainSession) { 760 onSetMainSession(isMainSession); 761 } 762 763 /** 764 * Calls {@link #onSetSurface}. 765 */ 766 void setSurface(Surface surface) { 767 onSetSurface(surface); 768 if (mSurface != null) { 769 mSurface.release(); 770 } 771 mSurface = surface; 772 // TODO: Handle failure. 773 } 774 775 /** 776 * Calls {@link #onSurfaceChanged}. 777 */ 778 void dispatchSurfaceChanged(int format, int width, int height) { 779 if (DEBUG) { 780 Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width 781 + ", height=" + height + ")"); 782 } 783 onSurfaceChanged(format, width, height); 784 } 785 786 /** 787 * Calls {@link #onSetStreamVolume}. 788 */ 789 void setStreamVolume(float volume) { 790 onSetStreamVolume(volume); 791 } 792 793 /** 794 * Calls {@link #onTune}. 795 */ 796 void tune(Uri channelUri) { 797 onTune(channelUri); 798 // TODO: Handle failure. 799 } 800 801 /** 802 * Calls {@link #onSetCaptionEnabled}. 803 */ 804 void setCaptionEnabled(boolean enabled) { 805 onSetCaptionEnabled(enabled); 806 } 807 808 /** 809 * Calls {@link #onSelectTrack}. 810 */ 811 void selectTrack(TvTrackInfo track) { 812 onSelectTrack(track); 813 // TODO: Handle failure. 814 } 815 816 /** 817 * Calls {@link #onUnselectTrack}. 818 */ 819 void unselectTrack(TvTrackInfo track) { 820 onUnselectTrack(track); 821 // TODO: Handle failure. 822 } 823 824 /** 825 * Calls {@link #onUnblockContent}. 826 */ 827 void unblockContent(String unblockedRating) { 828 onUnblockContent(TvContentRating.unflattenFromString(unblockedRating)); 829 // TODO: Handle failure. 830 } 831 832 /** 833 * Calls {@link #onAppPrivateCommand}. 834 */ 835 void appPrivateCommand(String action, Bundle data) { 836 onAppPrivateCommand(action, data); 837 } 838 839 /** 840 * Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach 841 * to the overlay window. 842 * 843 * @param windowToken A window token of an application. 844 * @param frame A position of the overlay view. 845 */ 846 void createOverlayView(IBinder windowToken, Rect frame) { 847 if (mOverlayView != null) { 848 mWindowManager.removeView(mOverlayView); 849 mOverlayView = null; 850 } 851 if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")"); 852 mWindowToken = windowToken; 853 mOverlayFrame = frame; 854 if (!mOverlayViewEnabled) { 855 return; 856 } 857 mOverlayView = onCreateOverlayView(); 858 if (mOverlayView == null) { 859 return; 860 } 861 // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create 862 // an overlay window above the media window but below the application window. 863 int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; 864 // We make the overlay view non-focusable and non-touchable so that 865 // the application that owns the window token can decide whether to consume or 866 // dispatch the input events. 867 int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 868 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 869 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 870 mWindowParams = new WindowManager.LayoutParams( 871 frame.right - frame.left, frame.bottom - frame.top, 872 frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT); 873 mWindowParams.privateFlags |= 874 WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 875 mWindowParams.gravity = Gravity.START | Gravity.TOP; 876 mWindowParams.token = windowToken; 877 mWindowManager.addView(mOverlayView, mWindowParams); 878 } 879 880 /** 881 * Relayouts the current overlay view. 882 * 883 * @param frame A new position of the overlay view. 884 */ 885 void relayoutOverlayView(Rect frame) { 886 if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")"); 887 mOverlayFrame = frame; 888 if (!mOverlayViewEnabled || mOverlayView == null) { 889 return; 890 } 891 mWindowParams.x = frame.left; 892 mWindowParams.y = frame.top; 893 mWindowParams.width = frame.right - frame.left; 894 mWindowParams.height = frame.bottom - frame.top; 895 mWindowManager.updateViewLayout(mOverlayView, mWindowParams); 896 } 897 898 /** 899 * Removes the current overlay view. 900 */ 901 void removeOverlayView(boolean clearWindowToken) { 902 if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayView + ")"); 903 if (clearWindowToken) { 904 mWindowToken = null; 905 mOverlayFrame = null; 906 } 907 if (mOverlayView != null) { 908 mWindowManager.removeView(mOverlayView); 909 mOverlayView = null; 910 mWindowParams = null; 911 } 912 } 913 914 /** 915 * Takes care of dispatching incoming input events and tells whether the event was handled. 916 */ 917 int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) { 918 if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")"); 919 boolean isNavigationKey = false; 920 if (event instanceof KeyEvent) { 921 KeyEvent keyEvent = (KeyEvent) event; 922 isNavigationKey = isNavigationKey(keyEvent.getKeyCode()); 923 if (keyEvent.dispatch(this, mDispatcherState, this)) { 924 return TvInputManager.Session.DISPATCH_HANDLED; 925 } 926 } else if (event instanceof MotionEvent) { 927 MotionEvent motionEvent = (MotionEvent) event; 928 final int source = motionEvent.getSource(); 929 if (motionEvent.isTouchEvent()) { 930 if (onTouchEvent(motionEvent)) { 931 return TvInputManager.Session.DISPATCH_HANDLED; 932 } 933 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { 934 if (onTrackballEvent(motionEvent)) { 935 return TvInputManager.Session.DISPATCH_HANDLED; 936 } 937 } else { 938 if (onGenericMotionEvent(motionEvent)) { 939 return TvInputManager.Session.DISPATCH_HANDLED; 940 } 941 } 942 } 943 if (mOverlayView == null || !mOverlayView.isAttachedToWindow()) { 944 return TvInputManager.Session.DISPATCH_NOT_HANDLED; 945 } 946 if (!mOverlayView.hasWindowFocus()) { 947 mOverlayView.getViewRootImpl().windowFocusChanged(true, true); 948 } 949 if (isNavigationKey && mOverlayView.hasFocusable()) { 950 // If mOverlayView has focusable views, navigation key events should be always 951 // handled. If not, it can make the application UI navigation messed up. 952 // For example, in the case that the left-most view is focused, a left key event 953 // will not be handled in ViewRootImpl. Then, the left key event will be handled in 954 // the application during the UI navigation of the TV input. 955 mOverlayView.getViewRootImpl().dispatchInputEvent(event); 956 return TvInputManager.Session.DISPATCH_HANDLED; 957 } else { 958 mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver); 959 return TvInputManager.Session.DISPATCH_IN_PROGRESS; 960 } 961 } 962 963 private void setSessionCallback(ITvInputSessionCallback callback) { 964 mSessionCallback = callback; 965 } 966 } 967 968 /** @hide */ 969 public static boolean isNavigationKey(int keyCode) { 970 switch (keyCode) { 971 case KeyEvent.KEYCODE_DPAD_LEFT: 972 case KeyEvent.KEYCODE_DPAD_RIGHT: 973 case KeyEvent.KEYCODE_DPAD_UP: 974 case KeyEvent.KEYCODE_DPAD_DOWN: 975 case KeyEvent.KEYCODE_DPAD_CENTER: 976 case KeyEvent.KEYCODE_PAGE_UP: 977 case KeyEvent.KEYCODE_PAGE_DOWN: 978 case KeyEvent.KEYCODE_MOVE_HOME: 979 case KeyEvent.KEYCODE_MOVE_END: 980 case KeyEvent.KEYCODE_TAB: 981 case KeyEvent.KEYCODE_SPACE: 982 case KeyEvent.KEYCODE_ENTER: 983 return true; 984 } 985 return false; 986 } 987 988 @SuppressLint("HandlerLeak") 989 private final class ServiceHandler extends Handler { 990 private static final int DO_CREATE_SESSION = 1; 991 private static final int DO_ADD_HARDWARE_TV_INPUT = 2; 992 private static final int DO_REMOVE_HARDWARE_TV_INPUT = 3; 993 private static final int DO_ADD_HDMI_CEC_TV_INPUT = 4; 994 private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 5; 995 private static final int DO_SET_WRAPPED_TV_INPUT_ID = 6; 996 997 private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) { 998 int n = mCallbacks.beginBroadcast(); 999 for (int i = 0; i < n; ++i) { 1000 try { 1001 mCallbacks.getBroadcastItem(i).addHardwareTvInput(deviceId, inputInfo); 1002 } catch (RemoteException e) { 1003 Log.e(TAG, "Error while broadcasting.", e); 1004 } 1005 } 1006 mCallbacks.finishBroadcast(); 1007 } 1008 1009 private void broadcastAddHdmiCecTvInput( 1010 int logicalAddress, TvInputInfo inputInfo) { 1011 int n = mCallbacks.beginBroadcast(); 1012 for (int i = 0; i < n; ++i) { 1013 try { 1014 mCallbacks.getBroadcastItem(i).addHdmiCecTvInput(logicalAddress, inputInfo); 1015 } catch (RemoteException e) { 1016 Log.e(TAG, "Error while broadcasting.", e); 1017 } 1018 } 1019 mCallbacks.finishBroadcast(); 1020 } 1021 1022 private void broadcastRemoveTvInput(String inputId) { 1023 int n = mCallbacks.beginBroadcast(); 1024 for (int i = 0; i < n; ++i) { 1025 try { 1026 mCallbacks.getBroadcastItem(i).removeTvInput(inputId); 1027 } catch (RemoteException e) { 1028 Log.e(TAG, "Error while broadcasting.", e); 1029 } 1030 } 1031 mCallbacks.finishBroadcast(); 1032 } 1033 1034 private void broadcastSetWrappedTvInputId(String inputId, String wrappedInputId) { 1035 int n = mCallbacks.beginBroadcast(); 1036 for (int i = 0; i < n; ++i) { 1037 try { 1038 mCallbacks.getBroadcastItem(i).setWrappedInputId(inputId, wrappedInputId); 1039 } catch (RemoteException e) { 1040 Log.e(TAG, "Error while broadcasting.", e); 1041 } 1042 } 1043 mCallbacks.finishBroadcast(); 1044 } 1045 1046 @Override 1047 public final void handleMessage(Message msg) { 1048 switch (msg.what) { 1049 case DO_CREATE_SESSION: { 1050 SomeArgs args = (SomeArgs) msg.obj; 1051 InputChannel channel = (InputChannel) args.arg1; 1052 ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2; 1053 String inputId = (String) args.arg3; 1054 try { 1055 Session sessionImpl = onCreateSession(inputId); 1056 if (sessionImpl == null) { 1057 // Failed to create a session. 1058 cb.onSessionCreated(null); 1059 } else { 1060 sessionImpl.setSessionCallback(cb); 1061 ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this, 1062 sessionImpl, channel); 1063 cb.onSessionCreated(stub); 1064 } 1065 } catch (RemoteException e) { 1066 Log.e(TAG, "error in onSessionCreated"); 1067 } 1068 args.recycle(); 1069 return; 1070 } 1071 case DO_ADD_HARDWARE_TV_INPUT: { 1072 TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj; 1073 TvInputInfo inputInfo = onHardwareAdded(hardwareInfo); 1074 if (inputInfo != null) { 1075 broadcastAddHardwareTvInput(hardwareInfo.getDeviceId(), inputInfo); 1076 } 1077 return; 1078 } 1079 case DO_REMOVE_HARDWARE_TV_INPUT: { 1080 TvInputHardwareInfo hardwareInfo = (TvInputHardwareInfo) msg.obj; 1081 String inputId = onHardwareRemoved(hardwareInfo); 1082 if (inputId != null) { 1083 broadcastRemoveTvInput(inputId); 1084 } 1085 return; 1086 } 1087 case DO_ADD_HDMI_CEC_TV_INPUT: { 1088 HdmiCecDeviceInfo cecDeviceInfo = (HdmiCecDeviceInfo) msg.obj; 1089 TvInputInfo inputInfo = onHdmiCecDeviceAdded(cecDeviceInfo); 1090 if (inputInfo != null) { 1091 broadcastAddHdmiCecTvInput(cecDeviceInfo.getLogicalAddress(), inputInfo); 1092 } 1093 return; 1094 } 1095 case DO_REMOVE_HDMI_CEC_TV_INPUT: { 1096 HdmiCecDeviceInfo cecDeviceInfo = (HdmiCecDeviceInfo) msg.obj; 1097 String inputId = onHdmiCecDeviceRemoved(cecDeviceInfo); 1098 if (inputId != null) { 1099 broadcastRemoveTvInput(inputId); 1100 } 1101 return; 1102 } 1103 case DO_SET_WRAPPED_TV_INPUT_ID: { 1104 SomeArgs args = (SomeArgs) msg.obj; 1105 String inputId = (String) args.arg1; 1106 String wrappedInputId = (String) args.arg2; 1107 broadcastSetWrappedTvInputId(inputId, wrappedInputId); 1108 return; 1109 } 1110 default: { 1111 Log.w(TAG, "Unhandled message code: " + msg.what); 1112 return; 1113 } 1114 } 1115 } 1116 } 1117} 1118