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