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