1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.tv.interactive;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.FlaggedApi;
21 import android.annotation.IntDef;
22 import android.annotation.MainThread;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.Px;
26 import android.annotation.SdkConstant;
27 import android.annotation.StringDef;
28 import android.annotation.SuppressLint;
29 import android.app.ActivityManager;
30 import android.app.Service;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.graphics.PixelFormat;
34 import android.graphics.Rect;
35 import android.media.PlaybackParams;
36 import android.media.tv.AdBuffer;
37 import android.media.tv.AdRequest;
38 import android.media.tv.AdResponse;
39 import android.media.tv.BroadcastInfoRequest;
40 import android.media.tv.BroadcastInfoResponse;
41 import android.media.tv.TvContentRating;
42 import android.media.tv.TvContract;
43 import android.media.tv.TvInputInfo;
44 import android.media.tv.TvInputManager;
45 import android.media.tv.TvRecordingInfo;
46 import android.media.tv.TvTrackInfo;
47 import android.media.tv.TvView;
48 import android.media.tv.flags.Flags;
49 import android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback;
50 import android.net.Uri;
51 import android.net.http.SslCertificate;
52 import android.os.AsyncTask;
53 import android.os.Bundle;
54 import android.os.Handler;
55 import android.os.IBinder;
56 import android.os.Message;
57 import android.os.Process;
58 import android.os.RemoteCallbackList;
59 import android.os.RemoteException;
60 import android.util.Log;
61 import android.view.Gravity;
62 import android.view.InputChannel;
63 import android.view.InputDevice;
64 import android.view.InputEvent;
65 import android.view.InputEventReceiver;
66 import android.view.KeyEvent;
67 import android.view.MotionEvent;
68 import android.view.Surface;
69 import android.view.View;
70 import android.view.WindowManager;
71 import android.widget.FrameLayout;
72 
73 import com.android.internal.os.SomeArgs;
74 
75 import java.io.IOException;
76 import java.lang.annotation.Retention;
77 import java.lang.annotation.RetentionPolicy;
78 import java.util.ArrayList;
79 import java.util.List;
80 
81 /**
82  * A TV interactive application service is a service that provides runtime environment and runs TV
83  * interactive applications.
84  */
85 public abstract class TvInteractiveAppService extends Service {
86     private static final boolean DEBUG = false;
87     private static final String TAG = "TvInteractiveAppService";
88 
89     private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000;
90 
91     /**
92      * This is the interface name that a service implementing a TV Interactive App service should
93      * say that it supports -- that is, this is the action it uses for its intent filter. To be
94      * supported, the service must also require the
95      * {@link android.Manifest.permission#BIND_TV_INTERACTIVE_APP} permission so that other
96      * applications cannot abuse it.
97      */
98     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
99     public static final String SERVICE_INTERFACE =
100             "android.media.tv.interactive.TvInteractiveAppService";
101 
102     /**
103      * Name under which a TvInteractiveAppService component publishes information about itself. This
104      * meta-data must reference an XML resource containing an
105      * <code>&lt;{@link android.R.styleable#TvInteractiveAppService tv-interactive-app}&gt;</code>
106      * tag.
107      */
108     public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
109 
110     /** @hide */
111     @Retention(RetentionPolicy.SOURCE)
112     @StringDef(prefix = "PLAYBACK_COMMAND_TYPE_", value = {
113             PLAYBACK_COMMAND_TYPE_TUNE,
114             PLAYBACK_COMMAND_TYPE_TUNE_NEXT,
115             PLAYBACK_COMMAND_TYPE_TUNE_PREV,
116             PLAYBACK_COMMAND_TYPE_STOP,
117             PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME,
118             PLAYBACK_COMMAND_TYPE_SELECT_TRACK,
119             PLAYBACK_COMMAND_TYPE_FREEZE
120     })
121     public @interface PlaybackCommandType {}
122 
123     /**
124      * Playback command type: tune to the given channel.
125      * @see #COMMAND_PARAMETER_KEY_CHANNEL_URI
126      */
127     public static final String PLAYBACK_COMMAND_TYPE_TUNE = "tune";
128     /**
129      * Playback command type: tune to the next channel.
130      */
131     public static final String PLAYBACK_COMMAND_TYPE_TUNE_NEXT = "tune_next";
132     /**
133      * Playback command type: tune to the previous channel.
134      */
135     public static final String PLAYBACK_COMMAND_TYPE_TUNE_PREV = "tune_previous";
136     /**
137      * Playback command type: stop the playback.
138      */
139     public static final String PLAYBACK_COMMAND_TYPE_STOP = "stop";
140     /**
141      * Playback command type: set the volume.
142      */
143     public static final String PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME =
144             "set_stream_volume";
145     /**
146      * Playback command type: select the given track.
147      */
148     public static final String PLAYBACK_COMMAND_TYPE_SELECT_TRACK = "select_track";
149     /**
150      * Playback command type: freeze the video playback on the current frame.
151      * @hide
152      */
153     public static final String PLAYBACK_COMMAND_TYPE_FREEZE = "freeze";
154 
155     /** @hide */
156     @Retention(RetentionPolicy.SOURCE)
157     @IntDef(prefix = "COMMAND_PARAMETER_VALUE_STOP_MODE_", value = {
158             COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK,
159             COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE
160     })
161     public @interface PlaybackCommandStopMode {}
162 
163     /**
164      * Playback command stop mode: show a blank screen.
165      * @see #PLAYBACK_COMMAND_TYPE_STOP
166      */
167     public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK = 1;
168 
169     /**
170      * Playback command stop mode: freeze the video.
171      * @see #PLAYBACK_COMMAND_TYPE_STOP
172      */
173     public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE = 2;
174 
175     /**
176      * Playback command parameter: stop mode.
177      * <p>Type: int
178      *
179      * @see #PLAYBACK_COMMAND_TYPE_STOP
180      */
181     public static final String COMMAND_PARAMETER_KEY_STOP_MODE = "command_stop_mode";
182 
183     /**
184      * Playback command parameter: channel URI.
185      * <p>Type: android.net.Uri
186      *
187      * @see #PLAYBACK_COMMAND_TYPE_TUNE
188      */
189     public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri";
190     /**
191      * Playback command parameter: TV input ID.
192      * <p>Type: String
193      *
194      * @see TvInputInfo#getId()
195      */
196     public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id";
197     /**
198      * Playback command parameter: stream volume.
199      * <p>Type: float
200      *
201      * @see #PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME
202      */
203     public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume";
204     /**
205      * Playback command parameter: track type.
206      * <p>Type: int
207      *
208      * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK
209      * @see TvTrackInfo#getType()
210      */
211     public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type";
212     /**
213      * Playback command parameter: track ID.
214      * <p>Type: String
215      *
216      * @see #PLAYBACK_COMMAND_TYPE_SELECT_TRACK
217      * @see TvTrackInfo#getId()
218      */
219     public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id";
220     /**
221      * Command to quiet channel change. No channel banner or channel info is shown.
222      * <p>Refer to HbbTV Spec 2.0.4 chapter A.2.4.3.
223      */
224     public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY =
225             "command_change_channel_quietly";
226 
227     /** @hide */
228     @Retention(RetentionPolicy.SOURCE)
229     @StringDef(prefix = "TIME_SHIFT_COMMAND_TYPE_", value = {
230             TIME_SHIFT_COMMAND_TYPE_PLAY,
231             TIME_SHIFT_COMMAND_TYPE_PAUSE,
232             TIME_SHIFT_COMMAND_TYPE_RESUME,
233             TIME_SHIFT_COMMAND_TYPE_SEEK_TO,
234             TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS,
235             TIME_SHIFT_COMMAND_TYPE_SET_MODE,
236     })
237     public @interface TimeShiftCommandType {}
238 
239     /**
240      * Time shift command type: play.
241      *
242      * @see TvView#timeShiftPlay(String, Uri)
243      */
244     public static final String TIME_SHIFT_COMMAND_TYPE_PLAY = "play";
245     /**
246      * Time shift command type: pause.
247      *
248      * @see TvView#timeShiftPause()
249      */
250     public static final String TIME_SHIFT_COMMAND_TYPE_PAUSE = "pause";
251     /**
252      * Time shift command type: resume.
253      *
254      * @see TvView#timeShiftResume()
255      */
256     public static final String TIME_SHIFT_COMMAND_TYPE_RESUME = "resume";
257     /**
258      * Time shift command type: seek to.
259      *
260      * @see TvView#timeShiftSeekTo(long)
261      */
262     public static final String TIME_SHIFT_COMMAND_TYPE_SEEK_TO = "seek_to";
263     /**
264      * Time shift command type: set playback params.
265      *
266      * @see TvView#timeShiftSetPlaybackParams(PlaybackParams)
267      */
268     public static final String TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS = "set_playback_params";
269     /**
270      * Time shift command type: set time shift mode.
271      */
272     public static final String TIME_SHIFT_COMMAND_TYPE_SET_MODE = "set_mode";
273 
274     /**
275      * Time shift command parameter: program URI.
276      * <p>Type: android.net.Uri
277      *
278      * @see #TIME_SHIFT_COMMAND_TYPE_PLAY
279      */
280     public static final String COMMAND_PARAMETER_KEY_PROGRAM_URI = "command_program_uri";
281     /**
282      * Time shift command parameter: time position for time shifting, in milliseconds.
283      * <p>Type: long
284      *
285      * @see #TIME_SHIFT_COMMAND_TYPE_SEEK_TO
286      */
287     public static final String COMMAND_PARAMETER_KEY_TIME_POSITION = "command_time_position";
288     /**
289      * Time shift command parameter: playback params.
290      * <p>Type: android.media.PlaybackParams
291      *
292      * @see #TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS
293      */
294     public static final String COMMAND_PARAMETER_KEY_PLAYBACK_PARAMS = "command_playback_params";
295     /**
296      * Time shift command parameter: playback params.
297      * <p>Type: Integer. One of {@link TvInputManager#TIME_SHIFT_MODE_OFF},
298      * {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
299      * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
300      * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
301      *
302      * @see #TIME_SHIFT_COMMAND_TYPE_SET_MODE
303      */
304     public static final String COMMAND_PARAMETER_KEY_TIME_SHIFT_MODE = "command_time_shift_mode";
305 
306     private final Handler mServiceHandler = new ServiceHandler();
307     private final RemoteCallbackList<ITvInteractiveAppServiceCallback> mCallbacks =
308             new RemoteCallbackList<>();
309 
310     @Override
311     @Nullable
onBind(@onNull Intent intent)312     public final IBinder onBind(@NonNull Intent intent) {
313         ITvInteractiveAppService.Stub tvIAppServiceBinder = new ITvInteractiveAppService.Stub() {
314             @Override
315             public void registerCallback(ITvInteractiveAppServiceCallback cb) {
316                 if (cb != null) {
317                     mCallbacks.register(cb);
318                 }
319             }
320 
321             @Override
322             public void unregisterCallback(ITvInteractiveAppServiceCallback cb) {
323                 if (cb != null) {
324                     mCallbacks.unregister(cb);
325                 }
326             }
327 
328             @Override
329             public void createSession(InputChannel channel, ITvInteractiveAppSessionCallback cb,
330                     String iAppServiceId, int type) {
331                 if (cb == null) {
332                     return;
333                 }
334                 SomeArgs args = SomeArgs.obtain();
335                 args.arg1 = channel;
336                 args.arg2 = cb;
337                 args.arg3 = iAppServiceId;
338                 args.arg4 = type;
339                 mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args)
340                         .sendToTarget();
341             }
342 
343             @Override
344             public void registerAppLinkInfo(AppLinkInfo appLinkInfo) {
345                 onRegisterAppLinkInfo(appLinkInfo);
346             }
347 
348             @Override
349             public void unregisterAppLinkInfo(AppLinkInfo appLinkInfo) {
350                 onUnregisterAppLinkInfo(appLinkInfo);
351             }
352 
353             @Override
354             public void sendAppLinkCommand(Bundle command) {
355                 onAppLinkCommand(command);
356             }
357         };
358         return tvIAppServiceBinder;
359     }
360 
361     /**
362      * Called when a request to register an Android application link info record is received.
363      */
onRegisterAppLinkInfo(@onNull AppLinkInfo appLinkInfo)364     public void onRegisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) {
365     }
366 
367     /**
368      * Called when a request to unregister an Android application link info record is received.
369      */
onUnregisterAppLinkInfo(@onNull AppLinkInfo appLinkInfo)370     public void onUnregisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) {
371     }
372 
373     /**
374      * Called when app link command is received.
375      *
376      * @see android.media.tv.interactive.TvInteractiveAppManager#sendAppLinkCommand(String, Bundle)
377      */
onAppLinkCommand(@onNull Bundle command)378     public void onAppLinkCommand(@NonNull Bundle command) {
379     }
380 
381 
382     /**
383      * Returns a concrete implementation of {@link Session}.
384      *
385      * <p>May return {@code null} if this TV Interactive App service fails to create a session for
386      * some reason.
387      *
388      * @param iAppServiceId The ID of the TV Interactive App associated with the session.
389      * @param type The type of the TV Interactive App associated with the session.
390      */
391     @Nullable
onCreateSession( @onNull String iAppServiceId, @TvInteractiveAppServiceInfo.InteractiveAppType int type)392     public abstract Session onCreateSession(
393             @NonNull String iAppServiceId,
394             @TvInteractiveAppServiceInfo.InteractiveAppType int type);
395 
396     /**
397      * Notifies the system when the state of the interactive app RTE has been changed.
398      *
399      * @param type the interactive app type
400      * @param state the current state of the service of the given type
401      * @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
402      *              used when the state is not
403      *              {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}.
404      */
notifyStateChanged( @vInteractiveAppServiceInfo.InteractiveAppType int type, @TvInteractiveAppManager.ServiceState int state, @TvInteractiveAppManager.ErrorCode int error)405     public final void notifyStateChanged(
406             @TvInteractiveAppServiceInfo.InteractiveAppType int type,
407             @TvInteractiveAppManager.ServiceState int state,
408             @TvInteractiveAppManager.ErrorCode int error) {
409         SomeArgs args = SomeArgs.obtain();
410         args.arg1 = type;
411         args.arg2 = state;
412         args.arg3 = error;
413         mServiceHandler
414                 .obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, args).sendToTarget();
415     }
416 
417     /**
418      * Base class for derived classes to implement to provide a TV interactive app session.
419      *
420      * <p>A session is associated with a {@link TvInteractiveAppView} instance and handles
421      * corresponding communications. It also handles the communications with
422      * {@link android.media.tv.TvInputService.Session} if connected.
423      *
424      * @see TvInteractiveAppView#setTvView(TvView)
425      */
426     public abstract static class Session implements KeyEvent.Callback {
427         private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
428 
429         private final Object mLock = new Object();
430         // @GuardedBy("mLock")
431         private ITvInteractiveAppSessionCallback mSessionCallback;
432         // @GuardedBy("mLock")
433         private final List<Runnable> mPendingActions = new ArrayList<>();
434 
435         private final Context mContext;
436         final Handler mHandler;
437         private final WindowManager mWindowManager;
438         private WindowManager.LayoutParams mWindowParams;
439         private Surface mSurface;
440         private FrameLayout mMediaViewContainer;
441         private View mMediaView;
442         private MediaViewCleanUpTask mMediaViewCleanUpTask;
443         private boolean mMediaViewEnabled;
444         private IBinder mWindowToken;
445         private Rect mMediaFrame;
446 
447         /**
448          * Creates a new Session.
449          *
450          * @param context The context of the application
451          */
Session(@onNull Context context)452         public Session(@NonNull Context context) {
453             mContext = context;
454             mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
455             mHandler = new Handler(context.getMainLooper());
456         }
457 
458         /**
459          * Enables or disables the media view.
460          *
461          * <p>By default, the media view is disabled. Must be called explicitly after the
462          * session is created to enable the media view.
463          *
464          * <p>The TV Interactive App service can disable its media view when needed.
465          *
466          * @param enable {@code true} if you want to enable the media view. {@code false}
467          *            otherwise.
468          */
469         @CallSuper
setMediaViewEnabled(final boolean enable)470         public void setMediaViewEnabled(final boolean enable) {
471             mHandler.post(new Runnable() {
472                 @Override
473                 public void run() {
474                     if (enable == mMediaViewEnabled) {
475                         return;
476                     }
477                     mMediaViewEnabled = enable;
478                     if (enable) {
479                         if (mWindowToken != null) {
480                             createMediaView(mWindowToken, mMediaFrame);
481                         }
482                     } else {
483                         removeMediaView(false);
484                     }
485                 }
486             });
487         }
488 
489         /**
490          * Returns {@code true} if media view is enabled, {@code false} otherwise.
491          *
492          * @see #setMediaViewEnabled(boolean)
493          */
isMediaViewEnabled()494         public boolean isMediaViewEnabled() {
495             return mMediaViewEnabled;
496         }
497 
498         /**
499          * Starts TvInteractiveAppService session.
500          */
onStartInteractiveApp()501         public void onStartInteractiveApp() {
502         }
503 
504         /**
505          * Stops TvInteractiveAppService session.
506          */
onStopInteractiveApp()507         public void onStopInteractiveApp() {
508         }
509 
510         /**
511          * Resets TvInteractiveAppService session.
512          */
onResetInteractiveApp()513         public void onResetInteractiveApp() {
514         }
515 
516         /**
517          * Creates broadcast-independent(BI) interactive application.
518          *
519          * <p>The implementation should call {@link #notifyBiInteractiveAppCreated(Uri, String)},
520          * no matter if it's created successfully or not.
521          *
522          * @see #notifyBiInteractiveAppCreated(Uri, String)
523          * @see #onDestroyBiInteractiveAppRequest(String)
524          */
onCreateBiInteractiveAppRequest( @onNull Uri biIAppUri, @Nullable Bundle params)525         public void onCreateBiInteractiveAppRequest(
526                 @NonNull Uri biIAppUri, @Nullable Bundle params) {
527         }
528 
529 
530         /**
531          * Destroys broadcast-independent(BI) interactive application.
532          *
533          * @param biIAppId the BI interactive app ID from
534          *                 {@link #onCreateBiInteractiveAppRequest(Uri, Bundle)}
535          *
536          * @see #onCreateBiInteractiveAppRequest(Uri, Bundle)
537          */
onDestroyBiInteractiveAppRequest(@onNull String biIAppId)538         public void onDestroyBiInteractiveAppRequest(@NonNull String biIAppId) {
539         }
540 
541         /**
542          * To toggle Digital Teletext Application if there is one in AIT app list.
543          * @param enable {@code true} to enable teletext app; {@code false} otherwise.
544          */
onSetTeletextAppEnabled(boolean enable)545         public void onSetTeletextAppEnabled(boolean enable) {
546         }
547 
548         /**
549          * Receives current video bounds.
550          *
551          * @param bounds the rectangle area for rendering the current video.
552          */
onCurrentVideoBounds(@onNull Rect bounds)553         public void onCurrentVideoBounds(@NonNull Rect bounds) {
554         }
555 
556         /**
557          * Receives current channel URI.
558          */
onCurrentChannelUri(@ullable Uri channelUri)559         public void onCurrentChannelUri(@Nullable Uri channelUri) {
560         }
561 
562         /**
563          * Receives logical channel number (LCN) of current channel.
564          */
onCurrentChannelLcn(int lcn)565         public void onCurrentChannelLcn(int lcn) {
566         }
567 
568         /**
569          * Receives current stream volume.
570          *
571          * @param volume a volume value between {@code 0.0f} and {@code 1.0f}, inclusive.
572          */
onStreamVolume(float volume)573         public void onStreamVolume(float volume) {
574         }
575 
576         /**
577          * Receives track list.
578          */
onTrackInfoList(@onNull List<TvTrackInfo> tracks)579         public void onTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
580         }
581 
582         /**
583          * Receives current TV input ID.
584          */
onCurrentTvInputId(@ullable String inputId)585         public void onCurrentTvInputId(@Nullable String inputId) {
586         }
587 
588         /**
589          * Receives current time shift mode.
590          *
591          * @param mode The current time shift mode. The value is one of the following:
592          * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
593          * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
594          * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
595          */
onTimeShiftMode(@ndroid.media.tv.TvInputManager.TimeShiftMode int mode)596         public void onTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
597         }
598 
599         /**
600          * Receives available playback speeds.
601          *
602          * @param speeds An ordered array of playback speeds, expressed as values relative to the
603          *               normal playback speed (1.0), at which the current content can be played as
604          *               a time-shifted broadcast. This is an empty array if the supported playback
605          *               speeds are unknown or the video/broadcast is not in time shift mode. If
606          *               currently in time shift mode, this array will normally include at least
607          *               the values 1.0 (normal speed) and 0.0 (paused).
608          */
onAvailableSpeeds(@onNull float[] speeds)609         public void onAvailableSpeeds(@NonNull float[] speeds) {
610         }
611 
612         /**
613          * Receives the requested {@link android.media.tv.TvRecordingInfo}.
614          *
615          * @see #requestTvRecordingInfo(String)
616          * @param recordingInfo The requested recording info. {@code null} if no recording found.
617          */
onTvRecordingInfo(@ullable TvRecordingInfo recordingInfo)618         public void onTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) {
619         }
620 
621         /**
622          * Receives requested recording info list.
623          *
624          * @see #requestTvRecordingInfoList(int)
625          * @param recordingInfoList The list of recording info requested. Returns an empty list if
626          *                          no matching recording info found.
627          */
onTvRecordingInfoList(@onNull List<TvRecordingInfo> recordingInfoList)628         public void onTvRecordingInfoList(@NonNull List<TvRecordingInfo> recordingInfoList) {}
629 
630         /**
631          * This is called when a recording has been started.
632          *
633          * <p>When a scheduled recording is started, this is also called, and the request ID in this
634          * case is {@code null}.
635          *
636          * @param recordingId The ID of the recording started. The TV app should provide and
637          *                    maintain this ID to identify the recording in the future.
638          * @param requestId The ID of the request when
639          *                  {@link #requestStartRecording(String, Uri)} is called.
640          *                  {@code null} if the recording is not triggered by a
641          *                  {@link #requestStartRecording(String, Uri)} request.
642          *                  This ID should be created by the {@link TvInteractiveAppService} and
643          *                  can be any string.
644          * @see #onRecordingStopped(String)
645          */
onRecordingStarted(@onNull String recordingId, @Nullable String requestId)646         public void onRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
647         }
648 
649         /**
650          * This is called when the recording has been stopped.
651          *
652          * @param recordingId The ID of the recording stopped. This ID is created and maintained by
653          *                    the TV app when the recording was started.
654          * @see #onRecordingStarted(String, String)
655          */
onRecordingStopped(@onNull String recordingId)656         public void onRecordingStopped(@NonNull String recordingId) {
657         }
658 
659         /**
660          * This is called when an error occurred while establishing a connection to the recording
661          * session for the corresponding TV input.
662          *
663          * @param recordingId The ID of the related recording which is sent via
664          *                    {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
665          * @param inputId The ID of the TV input bound to the current TvRecordingClient.
666          * @see android.media.tv.TvRecordingClient.RecordingCallback#onConnectionFailed(String)
667          */
onRecordingConnectionFailed( @onNull String recordingId, @NonNull String inputId)668         public void onRecordingConnectionFailed(
669                 @NonNull String recordingId, @NonNull String inputId) {
670         }
671 
672         /**
673          * This is called when the connection to the current recording session is lost.
674          *
675          * @param recordingId The ID of the related recording which is sent via
676          *                    {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
677          * @param inputId The ID of the TV input bound to the current TvRecordingClient.
678          * @see android.media.tv.TvRecordingClient.RecordingCallback#onDisconnected(String)
679          */
onRecordingDisconnected(@onNull String recordingId, @NonNull String inputId)680         public void onRecordingDisconnected(@NonNull String recordingId, @NonNull String inputId) {
681         }
682 
683         /**
684          * This is called when the recording session has been tuned to the given channel and is
685          * ready to start recording.
686          *
687          * @param recordingId The ID of the related recording which is sent via
688          *                    {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
689          * @param channelUri The URI of the tuned channel.
690          * @see android.media.tv.TvRecordingClient.RecordingCallback#onTuned(Uri)
691          */
onRecordingTuned(@onNull String recordingId, @NonNull Uri channelUri)692         public void onRecordingTuned(@NonNull String recordingId, @NonNull Uri channelUri) {
693         }
694 
695         /**
696          * This is called when an issue has occurred. It may be called at any time after the current
697          * recording session is created until it is released.
698          *
699          * @param recordingId The ID of the related recording which is sent via
700          *                    {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
701          * @param err The error code. Should be one of the following.
702          * <ul>
703          * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
704          * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
705          * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
706          * </ul>
707          * @see android.media.tv.TvRecordingClient.RecordingCallback#onError(int)
708          */
onRecordingError( @onNull String recordingId, @TvInputManager.RecordingError int err)709         public void onRecordingError(
710                 @NonNull String recordingId, @TvInputManager.RecordingError int err) {
711         }
712 
713         /**
714          * This is called when the recording has been scheduled.
715          *
716          * @param recordingId The ID assigned to this recording by the app. It can be used to send
717          *                    recording related requests such as
718          *                    {@link #requestStopRecording(String)}.
719          * @param requestId The ID of the request when
720          *                  {@link #requestScheduleRecording}  is called.
721          *                  {@code null} if the recording is not triggered by a request.
722          *                  This ID should be created by the {@link TvInteractiveAppService} and
723          *                  can be any string.
724          */
onRecordingScheduled(@onNull String recordingId, @Nullable String requestId)725         public void onRecordingScheduled(@NonNull String recordingId, @Nullable String requestId) {
726         }
727 
728         /**
729          * Receives signing result.
730          * @param signingId the ID to identify the request. It's the same as the corresponding ID in
731          *        {@link Session#requestSigning(String, String, String, byte[])}
732          * @param result the signed result.
733          *
734          * @see #requestSigning(String, String, String, byte[])
735          */
onSigningResult(@onNull String signingId, @NonNull byte[] result)736         public void onSigningResult(@NonNull String signingId, @NonNull byte[] result) {
737         }
738 
739         /**
740          * Receives the requested Certificate
741          *
742          * @param host the host name of the SSL authentication server.
743          * @param port the port of the SSL authentication server. E.g., 443
744          * @param cert the SSL certificate received.
745          */
746         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
onCertificate(@onNull String host, int port, @NonNull SslCertificate cert)747         public void onCertificate(@NonNull String host, int port, @NonNull SslCertificate cert) {
748         }
749 
750         /**
751          * Called when the application sends information of an error.
752          *
753          * @param errMsg the message of the error.
754          * @param params additional parameters of the error. For example, the signingId of {@link
755          *     TvInteractiveAppCallback#onRequestSigning(String, String, String, String, byte[])}
756          *     can be included to identify the related signing request, and the method name
757          *     "onRequestSigning" can also be added to the params.
758          *
759          * @see TvInteractiveAppView#ERROR_KEY_METHOD_NAME
760          */
onError(@onNull String errMsg, @NonNull Bundle params)761         public void onError(@NonNull String errMsg, @NonNull Bundle params) {
762         }
763 
764         /**
765          * Called when the time shift {@link android.media.PlaybackParams} is set or changed.
766          *
767          * @param params The new {@link PlaybackParams} that was set or changed.
768          * @see TvView#timeShiftSetPlaybackParams(PlaybackParams)
769          */
onTimeShiftPlaybackParams(@onNull PlaybackParams params)770         public void onTimeShiftPlaybackParams(@NonNull PlaybackParams params) {
771         }
772 
773         /**
774          * Called when time shift status is changed.
775          *
776          * @see TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)
777          * @see android.media.tv.TvInputService.Session#notifyTimeShiftStatusChanged(int)
778          * @param inputId The ID of the input for which the time shift status has changed.
779          * @param status The status of which the input has changed to. Should be one of the
780          *               following.
781          *               <ul>
782          *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}
783          *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
784          *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
785          *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
786          *               </ul>
787          */
onTimeShiftStatusChanged( @onNull String inputId, @TvInputManager.TimeShiftStatus int status)788         public void onTimeShiftStatusChanged(
789                 @NonNull String inputId, @TvInputManager.TimeShiftStatus int status) {}
790 
791         /**
792          * Called when time shift start position is changed.
793          *
794          * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged(String, long)
795          * @param inputId The ID of the input for which the time shift start position has changed.
796          * @param timeMs The start position for time shifting, in milliseconds since the epoch.
797          */
onTimeShiftStartPositionChanged(@onNull String inputId, long timeMs)798         public void onTimeShiftStartPositionChanged(@NonNull String inputId, long timeMs) {
799         }
800 
801         /**
802          * Called when time shift current position is changed.
803          *
804          * @see TvView.TimeShiftPositionCallback#onTimeShiftCurrentPositionChanged(String, long)
805          * @param inputId The ID of the input for which the time shift current position has changed.
806          * @param timeMs The current position for time shifting, in milliseconds since the epoch.
807          */
onTimeShiftCurrentPositionChanged(@onNull String inputId, long timeMs)808         public void onTimeShiftCurrentPositionChanged(@NonNull String inputId, long timeMs) {
809         }
810 
811         /**
812          * Called when the application sets the surface.
813          *
814          * <p>The TV Interactive App service should render interactive app UI onto the given
815          * surface. When called with {@code null}, the Interactive App service should immediately
816          * free any references to the currently set surface and stop using it.
817          *
818          * @param surface The surface to be used for interactive app UI rendering. Can be
819          *                {@code null}.
820          * @return {@code true} if the surface was set successfully, {@code false} otherwise.
821          */
onSetSurface(@ullable Surface surface)822         public abstract boolean onSetSurface(@Nullable Surface surface);
823 
824         /**
825          * Called after any structural changes (format or size) have been made to the surface passed
826          * in {@link #onSetSurface}. This method is always called at least once, after
827          * {@link #onSetSurface} is called with non-null surface.
828          *
829          * @param format The new {@link PixelFormat} of the surface.
830          * @param width The new width of the surface.
831          * @param height The new height of the surface.
832          */
onSurfaceChanged(@ixelFormat.Format int format, int width, int height)833         public void onSurfaceChanged(@PixelFormat.Format int format, int width, int height) {
834         }
835 
836         /**
837          * Called when the size of the media view is changed by the application.
838          *
839          * <p>This is always called at least once when the session is created regardless of whether
840          * the media view is enabled or not. The media view container size is the same as the
841          * containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can
842          * be different if the surface was changed by calling {@link #layoutSurface}.
843          *
844          * @param width The width of the media view, in pixels.
845          * @param height The height of the media view, in pixels.
846          */
onMediaViewSizeChanged(@x int width, @Px int height)847         public void onMediaViewSizeChanged(@Px int width, @Px int height) {
848         }
849 
850         /**
851          * Called when the application requests to create an media view. Each session
852          * implementation can override this method and return its own view.
853          *
854          * @return a view attached to the media window
855          */
856         @Nullable
onCreateMediaView()857         public View onCreateMediaView() {
858             return null;
859         }
860 
861         /**
862          * Releases TvInteractiveAppService session.
863          */
onRelease()864         public abstract void onRelease();
865 
866         /**
867          * Called when the corresponding TV input tuned to a channel.
868          *
869          * @param channelUri The tuned channel URI.
870          */
onTuned(@onNull Uri channelUri)871         public void onTuned(@NonNull Uri channelUri) {
872         }
873 
874         /**
875          * Called when the corresponding TV input selected to a track.
876          *
877          * If the track is deselected and no track is currently selected,
878          * trackId is an empty string.
879          */
onTrackSelected(@vTrackInfo.Type int type, @NonNull String trackId)880         public void onTrackSelected(@TvTrackInfo.Type int type, @NonNull String trackId) {
881         }
882 
883         /**
884          * Called when the tracks are changed.
885          */
onTracksChanged(@onNull List<TvTrackInfo> tracks)886         public void onTracksChanged(@NonNull List<TvTrackInfo> tracks) {
887         }
888 
889         /**
890          * Called when video is available.
891          */
onVideoAvailable()892         public void onVideoAvailable() {
893         }
894 
895         /**
896          * Called when video is unavailable.
897          */
onVideoUnavailable(@vInputManager.VideoUnavailableReason int reason)898         public void onVideoUnavailable(@TvInputManager.VideoUnavailableReason int reason) {
899         }
900 
901         /**
902          * Called when video becomes frozen or unfrozen. Audio playback will continue while video
903          * will be frozen to the last frame if {@code true}.
904          *
905          * @param isFrozen Whether or not the video is frozen.
906          */
907         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
onVideoFreezeUpdated(boolean isFrozen)908         public void onVideoFreezeUpdated(boolean isFrozen) {}
909 
910         /**
911          * Called when content is allowed.
912          */
onContentAllowed()913         public void onContentAllowed() {
914         }
915 
916         /**
917          * Called when content is blocked.
918          */
onContentBlocked(@onNull TvContentRating rating)919         public void onContentBlocked(@NonNull TvContentRating rating) {
920         }
921 
922         /**
923          * Called when signal strength is changed.
924          */
onSignalStrength(@vInputManager.SignalStrength int strength)925         public void onSignalStrength(@TvInputManager.SignalStrength int strength) {
926         }
927 
928         /**
929          * Called when a broadcast info response is received.
930          */
onBroadcastInfoResponse(@onNull BroadcastInfoResponse response)931         public void onBroadcastInfoResponse(@NonNull BroadcastInfoResponse response) {
932         }
933 
934         /**
935          * Called when an advertisement response is received.
936          */
onAdResponse(@onNull AdResponse response)937         public void onAdResponse(@NonNull AdResponse response) {
938         }
939 
940         /**
941          * Called when an advertisement buffer is consumed.
942          *
943          * @param buffer The {@link AdBuffer} that was consumed.
944          */
onAdBufferConsumed(@onNull AdBuffer buffer)945         public void onAdBufferConsumed(@NonNull AdBuffer buffer) {
946         }
947 
948         /**
949          * Called when a TV message is received
950          *
951          * @param type The type of message received, such as
952          * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
953          * @param data The raw data of the message. The bundle keys are:
954          *             {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID},
955          *             {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID},
956          *             {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE},
957          *             {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}.
958          *             See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on
959          *             how to parse this data.
960          */
onTvMessage(@vInputManager.TvMessageType int type, @NonNull Bundle data)961         public void onTvMessage(@TvInputManager.TvMessageType int type,
962                 @NonNull Bundle data) {
963         }
964 
965         /**
966          * Called when the TV App sends the selected track info as a response to
967          * {@link #requestSelectedTrackInfo()}.
968          *
969          * <p> When a selected track changes as a result of a new selection,
970          * {@link #onTrackSelected(int, String)} should be used instead to communicate the specific
971          * track selection.
972          *
973          * @param tracks A list of {@link TvTrackInfo} that are currently selected
974          */
975         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
onSelectedTrackInfo(@onNull List<TvTrackInfo> tracks)976         public void onSelectedTrackInfo(@NonNull List<TvTrackInfo> tracks) {
977         }
978 
979         @Override
onKeyDown(int keyCode, @NonNull KeyEvent event)980         public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
981             return false;
982         }
983 
984         @Override
onKeyLongPress(int keyCode, @NonNull KeyEvent event)985         public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
986             return false;
987         }
988 
989         @Override
onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event)990         public boolean onKeyMultiple(int keyCode, int count, @NonNull KeyEvent event) {
991             return false;
992         }
993 
994         @Override
onKeyUp(int keyCode, @NonNull KeyEvent event)995         public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
996             return false;
997         }
998 
999         /**
1000          * Implement this method to handle touch screen motion events on the current session.
1001          *
1002          * @param event The motion event being received.
1003          * @return If you handled the event, return {@code true}. If you want to allow the event to
1004          *         be handled by the next receiver, return {@code false}.
1005          * @see View#onTouchEvent
1006          */
onTouchEvent(@onNull MotionEvent event)1007         public boolean onTouchEvent(@NonNull MotionEvent event) {
1008             return false;
1009         }
1010 
1011         /**
1012          * Implement this method to handle trackball events on the current session.
1013          *
1014          * @param event The motion event being received.
1015          * @return If you handled the event, return {@code true}. If you want to allow the event to
1016          *         be handled by the next receiver, return {@code false}.
1017          * @see View#onTrackballEvent
1018          */
onTrackballEvent(@onNull MotionEvent event)1019         public boolean onTrackballEvent(@NonNull MotionEvent event) {
1020             return false;
1021         }
1022 
1023         /**
1024          * Implement this method to handle generic motion events on the current session.
1025          *
1026          * @param event The motion event being received.
1027          * @return If you handled the event, return {@code true}. If you want to allow the event to
1028          *         be handled by the next receiver, return {@code false}.
1029          * @see View#onGenericMotionEvent
1030          */
onGenericMotionEvent(@onNull MotionEvent event)1031         public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
1032             return false;
1033         }
1034 
1035         /**
1036          * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
1037          * is relative to the overlay view that sits on top of this surface.
1038          *
1039          * @param left Left position in pixels, relative to the overlay view.
1040          * @param top Top position in pixels, relative to the overlay view.
1041          * @param right Right position in pixels, relative to the overlay view.
1042          * @param bottom Bottom position in pixels, relative to the overlay view.
1043          */
1044         @CallSuper
layoutSurface(final int left, final int top, final int right, final int bottom)1045         public void layoutSurface(final int left, final int top, final int right,
1046                 final int bottom) {
1047             if (left > right || top > bottom) {
1048                 throw new IllegalArgumentException("Invalid parameter");
1049             }
1050             executeOrPostRunnableOnMainThread(new Runnable() {
1051                 @MainThread
1052                 @Override
1053                 public void run() {
1054                     try {
1055                         if (DEBUG) {
1056                             Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top
1057                                     + ", r=" + right + ", b=" + bottom + ",)");
1058                         }
1059                         if (mSessionCallback != null) {
1060                             mSessionCallback.onLayoutSurface(left, top, right, bottom);
1061                         }
1062                     } catch (RemoteException e) {
1063                         Log.w(TAG, "error in layoutSurface", e);
1064                     }
1065                 }
1066             });
1067         }
1068 
1069         /**
1070          * Requests broadcast related information from the related TV input.
1071          * @param request the request for broadcast info
1072          */
1073         @CallSuper
requestBroadcastInfo(@onNull final BroadcastInfoRequest request)1074         public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) {
1075             executeOrPostRunnableOnMainThread(new Runnable() {
1076                 @MainThread
1077                 @Override
1078                 public void run() {
1079                     try {
1080                         if (DEBUG) {
1081                             Log.d(TAG, "requestBroadcastInfo (requestId="
1082                                     + request.getRequestId() + ")");
1083                         }
1084                         if (mSessionCallback != null) {
1085                             mSessionCallback.onBroadcastInfoRequest(request);
1086                         }
1087                     } catch (RemoteException e) {
1088                         Log.w(TAG, "error in requestBroadcastInfo", e);
1089                     }
1090                 }
1091             });
1092         }
1093 
1094         /**
1095          * Remove broadcast information request from the related TV input.
1096          * @param requestId the ID of the request
1097          */
1098         @CallSuper
removeBroadcastInfo(final int requestId)1099         public void removeBroadcastInfo(final int requestId) {
1100             executeOrPostRunnableOnMainThread(new Runnable() {
1101                 @MainThread
1102                 @Override
1103                 public void run() {
1104                     try {
1105                         if (DEBUG) {
1106                             Log.d(TAG, "removeBroadcastInfo (requestId="
1107                                     + requestId + ")");
1108                         }
1109                         if (mSessionCallback != null) {
1110                             mSessionCallback.onRemoveBroadcastInfo(requestId);
1111                         }
1112                     } catch (RemoteException e) {
1113                         Log.w(TAG, "error in removeBroadcastInfo", e);
1114                     }
1115                 }
1116             });
1117         }
1118 
1119         /**
1120          * Sends a specific playback command to be processed by the related TV input.
1121          *
1122          * @param cmdType type of the specific command
1123          * @param parameters parameters of the specific command
1124          */
1125         @CallSuper
sendPlaybackCommandRequest( @laybackCommandType @onNull String cmdType, @Nullable Bundle parameters)1126         public void sendPlaybackCommandRequest(
1127                 @PlaybackCommandType @NonNull String cmdType, @Nullable Bundle parameters) {
1128             executeOrPostRunnableOnMainThread(new Runnable() {
1129                 @MainThread
1130                 @Override
1131                 public void run() {
1132                     try {
1133                         if (DEBUG) {
1134                             Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters="
1135                                     + parameters.toString() + ")");
1136                         }
1137                         if (mSessionCallback != null) {
1138                             mSessionCallback.onCommandRequest(cmdType, parameters);
1139                         }
1140                     } catch (RemoteException e) {
1141                         Log.w(TAG, "error in requestCommand", e);
1142                     }
1143                 }
1144             });
1145         }
1146 
1147         /**
1148          * Sends a specific time shift command to be processed by the related TV input.
1149          *
1150          * @param cmdType type of the specific command
1151          * @param parameters parameters of the specific command
1152          */
1153         @CallSuper
sendTimeShiftCommandRequest( @imeShiftCommandType @onNull String cmdType, @Nullable Bundle parameters)1154         public void sendTimeShiftCommandRequest(
1155                 @TimeShiftCommandType @NonNull String cmdType, @Nullable Bundle parameters) {
1156             executeOrPostRunnableOnMainThread(new Runnable() {
1157                 @MainThread
1158                 @Override
1159                 public void run() {
1160                     try {
1161                         if (DEBUG) {
1162                             Log.d(TAG, "requestTimeShiftCommand (cmdType=" + cmdType
1163                                     + ", parameters=" + parameters.toString() + ")");
1164                         }
1165                         if (mSessionCallback != null) {
1166                             mSessionCallback.onTimeShiftCommandRequest(cmdType, parameters);
1167                         }
1168                     } catch (RemoteException e) {
1169                         Log.w(TAG, "error in requestTimeShiftCommand", e);
1170                     }
1171                 }
1172             });
1173         }
1174 
1175         /**
1176          * Sets broadcast video bounds.
1177          */
1178         @CallSuper
setVideoBounds(@onNull Rect rect)1179         public void setVideoBounds(@NonNull Rect rect) {
1180             executeOrPostRunnableOnMainThread(new Runnable() {
1181                 @MainThread
1182                 @Override
1183                 public void run() {
1184                     try {
1185                         if (DEBUG) {
1186                             Log.d(TAG, "setVideoBounds (rect=" + rect + ")");
1187                         }
1188                         if (mSessionCallback != null) {
1189                             mSessionCallback.onSetVideoBounds(rect);
1190                         }
1191                     } catch (RemoteException e) {
1192                         Log.w(TAG, "error in setVideoBounds", e);
1193                     }
1194                 }
1195             });
1196         }
1197 
1198         /**
1199          * Requests the bounds of the current video.
1200          */
1201         @CallSuper
requestCurrentVideoBounds()1202         public void requestCurrentVideoBounds() {
1203             executeOrPostRunnableOnMainThread(new Runnable() {
1204                 @MainThread
1205                 @Override
1206                 public void run() {
1207                     try {
1208                         if (DEBUG) {
1209                             Log.d(TAG, "requestCurrentVideoBounds");
1210                         }
1211                         if (mSessionCallback != null) {
1212                             mSessionCallback.onRequestCurrentVideoBounds();
1213                         }
1214                     } catch (RemoteException e) {
1215                         Log.w(TAG, "error in requestCurrentVideoBounds", e);
1216                     }
1217                 }
1218             });
1219         }
1220 
1221         /**
1222          * Requests the URI of the current channel.
1223          */
1224         @CallSuper
requestCurrentChannelUri()1225         public void requestCurrentChannelUri() {
1226             executeOrPostRunnableOnMainThread(new Runnable() {
1227                 @MainThread
1228                 @Override
1229                 public void run() {
1230                     try {
1231                         if (DEBUG) {
1232                             Log.d(TAG, "requestCurrentChannelUri");
1233                         }
1234                         if (mSessionCallback != null) {
1235                             mSessionCallback.onRequestCurrentChannelUri();
1236                         }
1237                     } catch (RemoteException e) {
1238                         Log.w(TAG, "error in requestCurrentChannelUri", e);
1239                     }
1240                 }
1241             });
1242         }
1243 
1244         /**
1245          * Requests the logic channel number (LCN) of the current channel.
1246          */
1247         @CallSuper
requestCurrentChannelLcn()1248         public void requestCurrentChannelLcn() {
1249             executeOrPostRunnableOnMainThread(new Runnable() {
1250                 @MainThread
1251                 @Override
1252                 public void run() {
1253                     try {
1254                         if (DEBUG) {
1255                             Log.d(TAG, "requestCurrentChannelLcn");
1256                         }
1257                         if (mSessionCallback != null) {
1258                             mSessionCallback.onRequestCurrentChannelLcn();
1259                         }
1260                     } catch (RemoteException e) {
1261                         Log.w(TAG, "error in requestCurrentChannelLcn", e);
1262                     }
1263                 }
1264             });
1265         }
1266 
1267         /**
1268          * Requests stream volume.
1269          */
1270         @CallSuper
requestStreamVolume()1271         public void requestStreamVolume() {
1272             executeOrPostRunnableOnMainThread(new Runnable() {
1273                 @MainThread
1274                 @Override
1275                 public void run() {
1276                     try {
1277                         if (DEBUG) {
1278                             Log.d(TAG, "requestStreamVolume");
1279                         }
1280                         if (mSessionCallback != null) {
1281                             mSessionCallback.onRequestStreamVolume();
1282                         }
1283                     } catch (RemoteException e) {
1284                         Log.w(TAG, "error in requestStreamVolume", e);
1285                     }
1286                 }
1287             });
1288         }
1289 
1290         /**
1291          * Requests the list of {@link TvTrackInfo}.
1292          */
1293         @CallSuper
requestTrackInfoList()1294         public void requestTrackInfoList() {
1295             executeOrPostRunnableOnMainThread(new Runnable() {
1296                 @MainThread
1297                 @Override
1298                 public void run() {
1299                     try {
1300                         if (DEBUG) {
1301                             Log.d(TAG, "requestTrackInfoList");
1302                         }
1303                         if (mSessionCallback != null) {
1304                             mSessionCallback.onRequestTrackInfoList();
1305                         }
1306                     } catch (RemoteException e) {
1307                         Log.w(TAG, "error in requestTrackInfoList", e);
1308                     }
1309                 }
1310             });
1311         }
1312 
1313         /**
1314          * Requests current TV input ID.
1315          *
1316          * @see android.media.tv.TvInputInfo
1317          */
1318         @CallSuper
requestCurrentTvInputId()1319         public void requestCurrentTvInputId() {
1320             executeOrPostRunnableOnMainThread(new Runnable() {
1321                 @MainThread
1322                 @Override
1323                 public void run() {
1324                     try {
1325                         if (DEBUG) {
1326                             Log.d(TAG, "requestCurrentTvInputId");
1327                         }
1328                         if (mSessionCallback != null) {
1329                             mSessionCallback.onRequestCurrentTvInputId();
1330                         }
1331                     } catch (RemoteException e) {
1332                         Log.w(TAG, "error in requestCurrentTvInputId", e);
1333                     }
1334                 }
1335             });
1336         }
1337 
1338         /**
1339          * Requests time shift mode.
1340          */
1341         @CallSuper
requestTimeShiftMode()1342         public void requestTimeShiftMode() {
1343             executeOrPostRunnableOnMainThread(new Runnable() {
1344                 @MainThread
1345                 @Override
1346                 public void run() {
1347                     try {
1348                         if (DEBUG) {
1349                             Log.d(TAG, "requestTimeShiftMode");
1350                         }
1351                         if (mSessionCallback != null) {
1352                             mSessionCallback.onRequestTimeShiftMode();
1353                         }
1354                     } catch (RemoteException e) {
1355                         Log.w(TAG, "error in requestTimeShiftMode", e);
1356                     }
1357                 }
1358             });
1359         }
1360 
1361         /**
1362          * Requests available speeds for time shift.
1363          */
1364         @CallSuper
requestAvailableSpeeds()1365         public void requestAvailableSpeeds() {
1366             executeOrPostRunnableOnMainThread(new Runnable() {
1367                 @MainThread
1368                 @Override
1369                 public void run() {
1370                     try {
1371                         if (DEBUG) {
1372                             Log.d(TAG, "requestAvailableSpeeds");
1373                         }
1374                         if (mSessionCallback != null) {
1375                             mSessionCallback.onRequestAvailableSpeeds();
1376                         }
1377                     } catch (RemoteException e) {
1378                         Log.w(TAG, "error in requestAvailableSpeeds", e);
1379                     }
1380                 }
1381             });
1382         }
1383 
1384         /**
1385          * Requests a list of the currently selected {@link TvTrackInfo} from the TV App.
1386          *
1387          * <p> Normally, track info cannot be synchronized until the channel has
1388          * been changed. This is used when the session of the {@link TvInteractiveAppService}
1389          * is newly created and the normal synchronization has not happened yet.
1390          *
1391          * <p> The track info will be returned in {@link #onSelectedTrackInfo(List)}
1392          */
1393         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
1394         @CallSuper
requestSelectedTrackInfo()1395         public void requestSelectedTrackInfo() {
1396             executeOrPostRunnableOnMainThread(() -> {
1397                 try {
1398                     if (DEBUG) {
1399                         Log.d(TAG, "requestSelectedTrackInfo");
1400                     }
1401                     if (mSessionCallback != null) {
1402                         mSessionCallback.onRequestSelectedTrackInfo();
1403                     }
1404                 } catch (RemoteException e) {
1405                     Log.w(TAG, "error in requestSelectedTrackInfo", e);
1406                 }
1407             });
1408         }
1409 
1410         /**
1411          * Requests starting of recording
1412          *
1413          * <p> This is used to request the active {@link android.media.tv.TvRecordingClient} to
1414          * call {@link android.media.tv.TvRecordingClient#startRecording(Uri)} with the provided
1415          * {@code programUri}.
1416          * A non-null {@code programUri} implies the started recording should be of that specific
1417          * program, whereas null {@code programUri} does not impose such a requirement and the
1418          * recording can span across multiple TV programs.
1419          *
1420          * @param requestId The ID of this request which is used to match the corresponding
1421          *                  response. The request ID in
1422          *                  {@link #onRecordingStarted(String, String)} for this request is the
1423          *                  same as the ID sent here. This should be defined by the
1424          *                  {@link TvInteractiveAppService} and can be any string.
1425          *                  Should this API be called with the same requestId twice, both
1426          *                  requests should be handled regardless by the TV application.
1427          * @param programUri The URI for the TV program to record, built by
1428          *            {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
1429          * @see android.media.tv.TvRecordingClient#startRecording(Uri)
1430          */
1431         @CallSuper
requestStartRecording(@onNull String requestId, @Nullable Uri programUri)1432         public void requestStartRecording(@NonNull String requestId, @Nullable Uri programUri) {
1433             executeOrPostRunnableOnMainThread(() -> {
1434                 try {
1435                     if (DEBUG) {
1436                         Log.d(TAG, "requestStartRecording");
1437                     }
1438                     if (mSessionCallback != null) {
1439                         mSessionCallback.onRequestStartRecording(requestId, programUri);
1440                     }
1441                 } catch (RemoteException e) {
1442                     Log.w(TAG, "error in requestStartRecording", e);
1443                 }
1444             });
1445         }
1446 
1447         /**
1448          * Requests the recording associated with the recordingId to stop.
1449          *
1450          * <p> This is used to request the associated {@link android.media.tv.TvRecordingClient} to
1451          * call {@link android.media.tv.TvRecordingClient#stopRecording()}.
1452          *
1453          * @param recordingId The ID of the recording to stop. This is provided by the TV app in
1454          *                    {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
1455          * @see android.media.tv.TvRecordingClient#stopRecording()
1456          */
1457         @CallSuper
requestStopRecording(@onNull String recordingId)1458         public void requestStopRecording(@NonNull String recordingId) {
1459             executeOrPostRunnableOnMainThread(() -> {
1460                 try {
1461                     if (DEBUG) {
1462                         Log.d(TAG, "requestStopRecording");
1463                     }
1464                     if (mSessionCallback != null) {
1465                         mSessionCallback.onRequestStopRecording(recordingId);
1466                     }
1467                 } catch (RemoteException e) {
1468                     Log.w(TAG, "error in requestStopRecording", e);
1469                 }
1470             });
1471         }
1472 
1473         /**
1474          * Requests scheduling of a recording.
1475          *
1476          * @param requestId The ID of this request which is used to match the corresponding
1477          *                  response. The request ID in
1478          *                  {@link #onRecordingScheduled(String, String)} for this request is the
1479          *                  same as the ID sent here. This should be defined by the
1480          *                  {@link TvInteractiveAppService} and can be any string.
1481          *                  Should this API be called with the same requestId twice, both requests
1482          *                  should be handled regardless by the TV application.
1483          * @param inputId The ID of the TV input for the given channel.
1484          * @param channelUri The URI of a channel to be recorded.
1485          * @param programUri The URI of the TV program to be recorded.
1486          * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
1487          *            name, i.e. prefixed with a package name you own, so that different developers
1488          *            will not create conflicting keys.
1489          * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle)
1490          * @see android.media.tv.TvRecordingClient#startRecording(Uri)
1491          */
1492         @CallSuper
requestScheduleRecording(@onNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params)1493         public void requestScheduleRecording(@NonNull String requestId, @NonNull String inputId,
1494                 @NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params) {
1495             executeOrPostRunnableOnMainThread(() -> {
1496                 try {
1497                     if (DEBUG) {
1498                         Log.d(TAG, "requestScheduleRecording");
1499                     }
1500                     if (mSessionCallback != null) {
1501                         mSessionCallback.onRequestScheduleRecording(
1502                                 requestId, inputId, channelUri, programUri, params);
1503                     }
1504                 } catch (RemoteException e) {
1505                     Log.w(TAG, "error in requestScheduleRecording", e);
1506                 }
1507             });
1508         }
1509 
1510         /**
1511          * Requests scheduling of a recording.
1512          *
1513          * @param requestId The ID of this request which is used to match the corresponding
1514          *                  response. The request ID in
1515          *                  {@link #onRecordingScheduled(String, String)} for this request is the
1516          *                  same as the ID sent here. This should be defined by the
1517          *                  {@link TvInteractiveAppService} and can be any string. Should this API
1518          *                  be called with the same requestId twice, both requests should be handled
1519          *                  regardless by the TV application.
1520          * @param inputId The ID of the TV input for the given channel.
1521          * @param channelUri The URI of a channel to be recorded.
1522          * @param startTime The start time of the recording in milliseconds since epoch.
1523          * @param duration The duration of the recording in milliseconds.
1524          * @param repeatDays The repeated days. 0 if not repeated.
1525          * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
1526          *            name, i.e. prefixed with a package name you own, so that different developers
1527          *            will not create conflicting keys.
1528          * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle)
1529          * @see android.media.tv.TvRecordingClient#startRecording(Uri)
1530          */
1531         @CallSuper
requestScheduleRecording(@onNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration, int repeatDays, @NonNull Bundle params)1532         public void requestScheduleRecording(@NonNull String requestId, @NonNull String inputId,
1533                 @NonNull Uri channelUri, long startTime, long duration, int repeatDays,
1534                 @NonNull Bundle params) {
1535             executeOrPostRunnableOnMainThread(() -> {
1536                 try {
1537                     if (DEBUG) {
1538                         Log.d(TAG, "requestScheduleRecording");
1539                     }
1540                     if (mSessionCallback != null) {
1541                         mSessionCallback.onRequestScheduleRecording2(requestId, inputId, channelUri,
1542                                 startTime, duration, repeatDays, params);
1543                     }
1544                 } catch (RemoteException e) {
1545                     Log.w(TAG, "error in requestScheduleRecording", e);
1546                 }
1547             });
1548         }
1549 
1550         /**
1551          * Sets the recording info for the specified recording
1552          *
1553          * @param recordingId The ID of the recording to set the info for. This is provided by the
1554          *     TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
1555          * @param recordingInfo The {@link TvRecordingInfo} to set to the recording.
1556          */
1557         @CallSuper
setTvRecordingInfo( @onNull String recordingId, @NonNull TvRecordingInfo recordingInfo)1558         public void setTvRecordingInfo(
1559                 @NonNull String recordingId, @NonNull TvRecordingInfo recordingInfo) {
1560             executeOrPostRunnableOnMainThread(() -> {
1561                 try {
1562                     if (DEBUG) {
1563                         Log.d(TAG, "setTvRecordingInfo");
1564                     }
1565                     if (mSessionCallback != null) {
1566                         mSessionCallback.onSetTvRecordingInfo(recordingId, recordingInfo);
1567                     }
1568                 } catch (RemoteException e) {
1569                     Log.w(TAG, "error in setTvRecordingInfo", e);
1570                 }
1571             });
1572         }
1573 
1574         /**
1575          * Gets the recording info for the specified recording
1576          * @param recordingId The ID of the recording to set the info for. This is provided by the
1577          *                    TV app in
1578          *                    {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
1579          */
1580         @CallSuper
requestTvRecordingInfo(@onNull String recordingId)1581         public void requestTvRecordingInfo(@NonNull String recordingId) {
1582             executeOrPostRunnableOnMainThread(() -> {
1583                 try {
1584                     if (DEBUG) {
1585                         Log.d(TAG, "requestTvRecordingInfo");
1586                     }
1587                     if (mSessionCallback != null) {
1588                         mSessionCallback.onRequestTvRecordingInfo(recordingId);
1589                     }
1590                 } catch (RemoteException e) {
1591                     Log.w(TAG, "error in requestTvRecordingInfo", e);
1592                 }
1593             });
1594         }
1595 
1596         /**
1597          * Gets a list of {@link TvRecordingInfo} for the specified recording type.
1598          *
1599          * @param type The type of recording to retrieve.
1600          */
1601         @CallSuper
requestTvRecordingInfoList(@vRecordingInfo.TvRecordingListType int type)1602         public void requestTvRecordingInfoList(@TvRecordingInfo.TvRecordingListType int type) {
1603             executeOrPostRunnableOnMainThread(() -> {
1604                 try {
1605                     if (DEBUG) {
1606                         Log.d(TAG, "requestTvRecordingInfoList");
1607                     }
1608                     if (mSessionCallback != null) {
1609                         mSessionCallback.onRequestTvRecordingInfoList(type);
1610                     }
1611                 } catch (RemoteException e) {
1612                     Log.w(TAG, "error in requestTvRecordingInfoList", e);
1613                 }
1614             });
1615         }
1616 
1617         /**
1618          * Requests signing of the given data.
1619          *
1620          * <p>This is used when the corresponding server of the broadcast-independent interactive
1621          * app requires signing during handshaking, and the interactive app service doesn't have
1622          * the built-in private key. The private key is provided by the content providers and
1623          * pre-built in the related app, such as TV app.
1624          *
1625          * @param signingId the ID to identify the request. When a result is received, this ID can
1626          *                  be used to correlate the result with the request.
1627          * @param algorithm the standard name of the signature algorithm requested, such as
1628          *                  MD5withRSA, SHA256withDSA, etc. The name is from standards like
1629          *                  FIPS PUB 186-4 and PKCS #1.
1630          * @param alias the alias of the corresponding {@link java.security.KeyStore}.
1631          * @param data the original bytes to be signed.
1632          *
1633          * @see #onSigningResult(String, byte[])
1634          * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle)
1635          * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS
1636          */
1637         @CallSuper
requestSigning(@onNull String signingId, @NonNull String algorithm, @NonNull String alias, @NonNull byte[] data)1638         public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
1639                 @NonNull String alias, @NonNull byte[] data) {
1640             executeOrPostRunnableOnMainThread(new Runnable() {
1641                 @MainThread
1642                 @Override
1643                 public void run() {
1644                     try {
1645                         if (DEBUG) {
1646                             Log.d(TAG, "requestSigning");
1647                         }
1648                         if (mSessionCallback != null) {
1649                             mSessionCallback.onRequestSigning(signingId, algorithm, alias, data);
1650                         }
1651                     } catch (RemoteException e) {
1652                         Log.w(TAG, "error in requestSigning", e);
1653                     }
1654                 }
1655             });
1656         }
1657 
1658         /**
1659          * Requests signing of the given data.
1660          *
1661          * <p>This is used when the corresponding server of the broadcast-independent interactive
1662          * app requires signing during handshaking, and the interactive app service doesn't have
1663          * the built-in private key. The private key is provided by the content providers and
1664          * pre-built in the related app, such as TV app.
1665          *
1666          * @param signingId the ID to identify the request. When a result is received, this ID can
1667          *                  be used to correlate the result with the request.
1668          * @param algorithm the standard name of the signature algorithm requested, such as
1669          *                  MD5withRSA, SHA256withDSA, etc. The name is from standards like
1670          *                  FIPS PUB 186-4 and PKCS #1.
1671          * @param host the host of the SSL client authentication server.
1672          * @param port the port of the SSL client authentication server.
1673          * @param data the original bytes to be signed.
1674          *
1675          * @see #onSigningResult(String, byte[])
1676          * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle)
1677          * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS
1678          */
1679         @CallSuper
1680         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
requestSigning(@onNull String signingId, @NonNull String algorithm, @NonNull String host, int port, @NonNull byte[] data)1681         public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
1682                 @NonNull String host, int port, @NonNull byte[] data) {
1683             executeOrPostRunnableOnMainThread(new Runnable() {
1684                 @MainThread
1685                 @Override
1686                 public void run() {
1687                     try {
1688                         if (DEBUG) {
1689                             Log.d(TAG, "requestSigning");
1690                         }
1691                         if (mSessionCallback != null) {
1692                             mSessionCallback.onRequestSigning2(signingId, algorithm,
1693                                     host, port, data);
1694                         }
1695                     } catch (RemoteException e) {
1696                         Log.w(TAG, "error in requestSigning", e);
1697                     }
1698                 }
1699             });
1700         }
1701 
1702         /**
1703          * Requests a SSL certificate for client validation.
1704          *
1705          * @param host the host name of the SSL authentication server.
1706          * @param port the port of the SSL authentication server. E.g., 443
1707          */
1708         @CallSuper
1709         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
requestCertificate(@onNull String host, int port)1710         public void requestCertificate(@NonNull String host, int port) {
1711             executeOrPostRunnableOnMainThread(new Runnable() {
1712                 @MainThread
1713                 @Override
1714                 public void run() {
1715                     try {
1716                         if (DEBUG) {
1717                             Log.d(TAG, "requestCertificate");
1718                         }
1719                         if (mSessionCallback != null) {
1720                             mSessionCallback.onRequestCertificate(host, port);
1721                         }
1722                     } catch (RemoteException e) {
1723                         Log.w(TAG, "error in requestCertificate", e);
1724                     }
1725                 }
1726             });
1727         }
1728 
1729         /**
1730          * Sends an advertisement request to be processed by the related TV input.
1731          *
1732          * @param request The advertisement request
1733          */
1734         @CallSuper
requestAd(@onNull final AdRequest request)1735         public void requestAd(@NonNull final AdRequest request) {
1736             executeOrPostRunnableOnMainThread(new Runnable() {
1737                 @MainThread
1738                 @Override
1739                 public void run() {
1740                     try {
1741                         if (DEBUG) {
1742                             Log.d(TAG, "requestAd (id=" + request.getId() + ")");
1743                         }
1744                         if (mSessionCallback != null) {
1745                             mSessionCallback.onAdRequest(request);
1746                         }
1747                     } catch (RemoteException e) {
1748                         Log.w(TAG, "error in requestAd", e);
1749                     }
1750                 }
1751             });
1752         }
1753 
startInteractiveApp()1754         void startInteractiveApp() {
1755             onStartInteractiveApp();
1756         }
1757 
stopInteractiveApp()1758         void stopInteractiveApp() {
1759             onStopInteractiveApp();
1760         }
1761 
resetInteractiveApp()1762         void resetInteractiveApp() {
1763             onResetInteractiveApp();
1764         }
1765 
createBiInteractiveApp(@onNull Uri biIAppUri, @Nullable Bundle params)1766         void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
1767             onCreateBiInteractiveAppRequest(biIAppUri, params);
1768         }
1769 
destroyBiInteractiveApp(@onNull String biIAppId)1770         void destroyBiInteractiveApp(@NonNull String biIAppId) {
1771             onDestroyBiInteractiveAppRequest(biIAppId);
1772         }
1773 
setTeletextAppEnabled(boolean enable)1774         void setTeletextAppEnabled(boolean enable) {
1775             onSetTeletextAppEnabled(enable);
1776         }
1777 
sendCurrentVideoBounds(@onNull Rect bounds)1778         void sendCurrentVideoBounds(@NonNull Rect bounds) {
1779             onCurrentVideoBounds(bounds);
1780         }
1781 
sendCurrentChannelUri(@ullable Uri channelUri)1782         void sendCurrentChannelUri(@Nullable Uri channelUri) {
1783             onCurrentChannelUri(channelUri);
1784         }
1785 
sendCurrentChannelLcn(int lcn)1786         void sendCurrentChannelLcn(int lcn) {
1787             onCurrentChannelLcn(lcn);
1788         }
1789 
sendStreamVolume(float volume)1790         void sendStreamVolume(float volume) {
1791             onStreamVolume(volume);
1792         }
1793 
sendTrackInfoList(@onNull List<TvTrackInfo> tracks)1794         void sendTrackInfoList(@NonNull List<TvTrackInfo> tracks) {
1795             onTrackInfoList(tracks);
1796         }
1797 
sendCurrentTvInputId(@ullable String inputId)1798         void sendCurrentTvInputId(@Nullable String inputId) {
1799             onCurrentTvInputId(inputId);
1800         }
1801 
sendTimeShiftMode(int mode)1802         void sendTimeShiftMode(int mode) {
1803             onTimeShiftMode(mode);
1804         }
1805 
sendAvailableSpeeds(@onNull float[] speeds)1806         void sendAvailableSpeeds(@NonNull float[] speeds) {
1807             onAvailableSpeeds(speeds);
1808         }
1809 
sendTvRecordingInfo(@ullable TvRecordingInfo recordingInfo)1810         void sendTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) {
1811             onTvRecordingInfo(recordingInfo);
1812         }
1813 
sendTvRecordingInfoList(@ullable List<TvRecordingInfo> recordingInfoList)1814         void sendTvRecordingInfoList(@Nullable List<TvRecordingInfo> recordingInfoList) {
1815             onTvRecordingInfoList(recordingInfoList);
1816         }
1817 
sendSigningResult(String signingId, byte[] result)1818         void sendSigningResult(String signingId, byte[] result) {
1819             onSigningResult(signingId, result);
1820         }
1821 
sendCertificate(String host, int port, Bundle certBundle)1822         void sendCertificate(String host, int port, Bundle certBundle) {
1823             SslCertificate cert = SslCertificate.restoreState(certBundle);
1824             onCertificate(host, port, cert);
1825         }
1826 
notifyError(String errMsg, Bundle params)1827         void notifyError(String errMsg, Bundle params) {
1828             onError(errMsg, params);
1829         }
1830 
release()1831         void release() {
1832             onRelease();
1833             if (mSurface != null) {
1834                 mSurface.release();
1835                 mSurface = null;
1836             }
1837             synchronized (mLock) {
1838                 mSessionCallback = null;
1839                 mPendingActions.clear();
1840             }
1841             // Removes the media view lastly so that any hanging on the main thread can be handled
1842             // in {@link #scheduleMediaViewCleanup}.
1843             removeMediaView(true);
1844         }
1845 
notifyTuned(Uri channelUri)1846         void notifyTuned(Uri channelUri) {
1847             if (DEBUG) {
1848                 Log.d(TAG, "notifyTuned (channelUri=" + channelUri + ")");
1849             }
1850             onTuned(channelUri);
1851         }
1852 
notifyTrackSelected(int type, String trackId)1853         void notifyTrackSelected(int type, String trackId) {
1854             if (DEBUG) {
1855                 Log.d(TAG, "notifyTrackSelected (type=" + type + "trackId=" + trackId + ")");
1856             }
1857             // TvInputService accepts a Null String, but onTrackSelected expects NonNull.
1858             if (trackId == null) {
1859                 trackId = "";
1860             }
1861             onTrackSelected(type, trackId);
1862         }
1863 
notifyTracksChanged(List<TvTrackInfo> tracks)1864         void notifyTracksChanged(List<TvTrackInfo> tracks) {
1865             if (DEBUG) {
1866                 Log.d(TAG, "notifyTracksChanged (tracks=" + tracks + ")");
1867             }
1868             onTracksChanged(tracks);
1869         }
1870 
notifyVideoAvailable()1871         void notifyVideoAvailable() {
1872             if (DEBUG) {
1873                 Log.d(TAG, "notifyVideoAvailable");
1874             }
1875             onVideoAvailable();
1876         }
1877 
notifyVideoUnavailable(int reason)1878         void notifyVideoUnavailable(int reason) {
1879             if (DEBUG) {
1880                 Log.d(TAG, "notifyVideoAvailable (reason=" + reason + ")");
1881             }
1882             onVideoUnavailable(reason);
1883         }
1884 
notifyVideoFreezeUpdated(boolean isFrozen)1885         void notifyVideoFreezeUpdated(boolean isFrozen) {
1886             if (DEBUG) {
1887                 Log.d(TAG, "notifyVideoFreezeUpdated (isFrozen=" + isFrozen + ")");
1888             }
1889             onVideoFreezeUpdated(isFrozen);
1890         }
1891 
notifyContentAllowed()1892         void notifyContentAllowed() {
1893             if (DEBUG) {
1894                 Log.d(TAG, "notifyContentAllowed");
1895             }
1896             onContentAllowed();
1897         }
1898 
notifyContentBlocked(TvContentRating rating)1899         void notifyContentBlocked(TvContentRating rating) {
1900             if (DEBUG) {
1901                 Log.d(TAG, "notifyContentBlocked (rating=" + rating.flattenToString() + ")");
1902             }
1903             onContentBlocked(rating);
1904         }
1905 
notifySignalStrength(int strength)1906         void notifySignalStrength(int strength) {
1907             if (DEBUG) {
1908                 Log.d(TAG, "notifySignalStrength (strength=" + strength + ")");
1909             }
1910             onSignalStrength(strength);
1911         }
1912 
1913         /**
1914          * Calls {@link #onBroadcastInfoResponse}.
1915          */
notifyBroadcastInfoResponse(BroadcastInfoResponse response)1916         void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
1917             if (DEBUG) {
1918                 Log.d(TAG, "notifyBroadcastInfoResponse (requestId="
1919                         + response.getRequestId() + ")");
1920             }
1921             onBroadcastInfoResponse(response);
1922         }
1923 
1924         /**
1925          * Calls {@link #onAdResponse}.
1926          */
notifyAdResponse(AdResponse response)1927         void notifyAdResponse(AdResponse response) {
1928             if (DEBUG) {
1929                 Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")");
1930             }
1931             onAdResponse(response);
1932         }
1933 
notifyTvMessage(int type, Bundle data)1934         void notifyTvMessage(int type, Bundle data) {
1935             if (DEBUG) {
1936                 Log.d(TAG, "notifyTvMessage (type=" + type + ", data= " + data + ")");
1937             }
1938             onTvMessage(type, data);
1939         }
1940 
sendSelectedTrackInfo(List<TvTrackInfo> tracks)1941         void sendSelectedTrackInfo(List<TvTrackInfo> tracks) {
1942             if (DEBUG) {
1943                 Log.d(TAG, "notifySelectedTrackInfo (tracks= " + tracks + ")");
1944             }
1945             onSelectedTrackInfo(tracks);
1946         }
1947 
1948         /**
1949          * Calls {@link #onAdBufferConsumed}.
1950          */
notifyAdBufferConsumed(AdBuffer buffer)1951         void notifyAdBufferConsumed(AdBuffer buffer) {
1952             if (DEBUG) {
1953                 Log.d(TAG,
1954                         "notifyAdBufferConsumed (buffer=" + buffer + ")");
1955             }
1956             onAdBufferConsumed(buffer);
1957         }
1958 
1959         /**
1960          * Calls {@link #onRecordingStarted(String, String)}.
1961          */
notifyRecordingStarted(String recordingId, String requestId)1962         void notifyRecordingStarted(String recordingId, String requestId) {
1963             onRecordingStarted(recordingId, requestId);
1964         }
1965 
1966         /**
1967          * Calls {@link #onRecordingStopped(String)}.
1968          */
notifyRecordingStopped(String recordingId)1969         void notifyRecordingStopped(String recordingId) {
1970             onRecordingStopped(recordingId);
1971         }
1972 
1973         /**
1974          * Calls {@link #onRecordingConnectionFailed(String, String)}.
1975          */
notifyRecordingConnectionFailed(String recordingId, String inputId)1976         void notifyRecordingConnectionFailed(String recordingId, String inputId) {
1977             onRecordingConnectionFailed(recordingId, inputId);
1978         }
1979 
1980         /**
1981          * Calls {@link #onRecordingDisconnected(String, String)}.
1982          */
notifyRecordingDisconnected(String recordingId, String inputId)1983         void notifyRecordingDisconnected(String recordingId, String inputId) {
1984             onRecordingDisconnected(recordingId, inputId);
1985         }
1986 
1987         /**
1988          * Calls {@link #onRecordingTuned(String, Uri)}.
1989          */
notifyRecordingTuned(String recordingId, Uri channelUri)1990         void notifyRecordingTuned(String recordingId, Uri channelUri) {
1991             onRecordingTuned(recordingId, channelUri);
1992         }
1993 
1994         /**
1995          * Calls {@link #onRecordingError(String, int)}.
1996          */
notifyRecordingError(String recordingId, int err)1997         void notifyRecordingError(String recordingId, int err) {
1998             onRecordingError(recordingId, err);
1999         }
2000 
2001         /**
2002          * Calls {@link #onRecordingScheduled(String, String)}.
2003          */
notifyRecordingScheduled(String recordingId, String requestId)2004         void notifyRecordingScheduled(String recordingId, String requestId) {
2005             onRecordingScheduled(recordingId, requestId);
2006         }
2007 
2008         /**
2009          * Calls {@link #onTimeShiftPlaybackParams(PlaybackParams)}.
2010          */
notifyTimeShiftPlaybackParams(PlaybackParams params)2011         void notifyTimeShiftPlaybackParams(PlaybackParams params) {
2012             onTimeShiftPlaybackParams(params);
2013         }
2014 
2015         /**
2016          * Calls {@link #onTimeShiftStatusChanged(String, int)}.
2017          */
notifyTimeShiftStatusChanged(String inputId, int status)2018         void notifyTimeShiftStatusChanged(String inputId, int status) {
2019             onTimeShiftStatusChanged(inputId, status);
2020         }
2021 
2022         /**
2023          * Calls {@link #onTimeShiftStartPositionChanged(String, long)}.
2024          */
notifyTimeShiftStartPositionChanged(String inputId, long timeMs)2025         void notifyTimeShiftStartPositionChanged(String inputId, long timeMs) {
2026             onTimeShiftStartPositionChanged(inputId, timeMs);
2027         }
2028 
2029         /**
2030          * Calls {@link #onTimeShiftCurrentPositionChanged(String, long)}.
2031          */
notifyTimeShiftCurrentPositionChanged(String inputId, long timeMs)2032         void notifyTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
2033             onTimeShiftCurrentPositionChanged(inputId, timeMs);
2034         }
2035 
2036         /**
2037          * Notifies when the session state is changed.
2038          *
2039          * @param state the current session state.
2040          * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
2041          *            used when the state is not
2042          *            {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
2043          */
2044         @CallSuper
notifySessionStateChanged( @vInteractiveAppManager.InteractiveAppState int state, @TvInteractiveAppManager.ErrorCode int err)2045         public void notifySessionStateChanged(
2046                 @TvInteractiveAppManager.InteractiveAppState int state,
2047                 @TvInteractiveAppManager.ErrorCode int err) {
2048             executeOrPostRunnableOnMainThread(new Runnable() {
2049                 @MainThread
2050                 @Override
2051                 public void run() {
2052                     try {
2053                         if (DEBUG) {
2054                             Log.d(TAG, "notifySessionStateChanged (state="
2055                                     + state + "; err=" + err + ")");
2056                         }
2057                         if (mSessionCallback != null) {
2058                             mSessionCallback.onSessionStateChanged(state, err);
2059                         }
2060                     } catch (RemoteException e) {
2061                         Log.w(TAG, "error in notifySessionStateChanged", e);
2062                     }
2063                 }
2064             });
2065         }
2066 
2067         /**
2068          * Notifies the broadcast-independent(BI) interactive application has been created.
2069          *
2070          * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
2071          *                 app. {@code null} if it's not created successfully.
2072          *
2073          * @see #onCreateBiInteractiveAppRequest(Uri, Bundle)
2074          */
2075         @CallSuper
notifyBiInteractiveAppCreated( @onNull Uri biIAppUri, @Nullable String biIAppId)2076         public final void notifyBiInteractiveAppCreated(
2077                 @NonNull Uri biIAppUri, @Nullable String biIAppId) {
2078             executeOrPostRunnableOnMainThread(new Runnable() {
2079                 @MainThread
2080                 @Override
2081                 public void run() {
2082                     try {
2083                         if (DEBUG) {
2084                             Log.d(TAG, "notifyBiInteractiveAppCreated (biIAppId="
2085                                     + biIAppId + ")");
2086                         }
2087                         if (mSessionCallback != null) {
2088                             mSessionCallback.onBiInteractiveAppCreated(biIAppUri, biIAppId);
2089                         }
2090                     } catch (RemoteException e) {
2091                         Log.w(TAG, "error in notifyBiInteractiveAppCreated", e);
2092                     }
2093                 }
2094             });
2095         }
2096 
2097         /**
2098          * Notifies when the digital teletext app state is changed.
2099          * @param state the current state.
2100          */
2101         @CallSuper
notifyTeletextAppStateChanged( @vInteractiveAppManager.TeletextAppState int state)2102         public final void notifyTeletextAppStateChanged(
2103                 @TvInteractiveAppManager.TeletextAppState int state) {
2104             executeOrPostRunnableOnMainThread(new Runnable() {
2105                 @MainThread
2106                 @Override
2107                 public void run() {
2108                     try {
2109                         if (DEBUG) {
2110                             Log.d(TAG, "notifyTeletextAppState (state="
2111                                     + state + ")");
2112                         }
2113                         if (mSessionCallback != null) {
2114                             mSessionCallback.onTeletextAppStateChanged(state);
2115                         }
2116                     } catch (RemoteException e) {
2117                         Log.w(TAG, "error in notifyTeletextAppState", e);
2118                     }
2119                 }
2120             });
2121         }
2122 
2123 
2124         /**
2125          * Notifies when the advertisement buffer is filled and ready to be read.
2126          *
2127          * @param buffer The {@link AdBuffer} to be received
2128          */
2129         @CallSuper
notifyAdBufferReady(@onNull AdBuffer buffer)2130         public void notifyAdBufferReady(@NonNull AdBuffer buffer) {
2131             AdBuffer dupBuffer;
2132             try {
2133                 dupBuffer = AdBuffer.dupAdBuffer(buffer);
2134             } catch (IOException e) {
2135                 Log.w(TAG, "dup AdBuffer error in notifyAdBufferReady:", e);
2136                 return;
2137             }
2138             executeOrPostRunnableOnMainThread(new Runnable() {
2139                 @MainThread
2140                 @Override
2141                 public void run() {
2142                     try {
2143                         if (DEBUG) {
2144                             Log.d(TAG,
2145                                     "notifyAdBufferReady(buffer=" + buffer + ")");
2146                         }
2147                         if (mSessionCallback != null) {
2148                             mSessionCallback.onAdBufferReady(dupBuffer);
2149                         }
2150                     } catch (RemoteException e) {
2151                         Log.w(TAG, "error in notifyAdBuffer", e);
2152                     } finally {
2153                         if (dupBuffer != null) {
2154                             dupBuffer.getSharedMemory().close();
2155                         }
2156                     }
2157                 }
2158             });
2159         }
2160 
2161 
2162         /**
2163          * Takes care of dispatching incoming input events and tells whether the event was handled.
2164          */
dispatchInputEvent(InputEvent event, InputEventReceiver receiver)2165         int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
2166             if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
2167             if (event instanceof KeyEvent) {
2168                 KeyEvent keyEvent = (KeyEvent) event;
2169                 if (keyEvent.dispatch(this, mDispatcherState, this)) {
2170                     return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
2171                 }
2172 
2173                 // TODO: special handlings of navigation keys and media keys
2174             } else if (event instanceof MotionEvent) {
2175                 MotionEvent motionEvent = (MotionEvent) event;
2176                 final int source = motionEvent.getSource();
2177                 if (motionEvent.isTouchEvent()) {
2178                     if (onTouchEvent(motionEvent)) {
2179                         return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
2180                     }
2181                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
2182                     if (onTrackballEvent(motionEvent)) {
2183                         return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
2184                     }
2185                 } else {
2186                     if (onGenericMotionEvent(motionEvent)) {
2187                         return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
2188                     }
2189                 }
2190             }
2191             // TODO: handle overlay view
2192             return TvInteractiveAppManager.Session.DISPATCH_NOT_HANDLED;
2193         }
2194 
initialize(ITvInteractiveAppSessionCallback callback)2195         private void initialize(ITvInteractiveAppSessionCallback callback) {
2196             synchronized (mLock) {
2197                 mSessionCallback = callback;
2198                 for (Runnable runnable : mPendingActions) {
2199                     runnable.run();
2200                 }
2201                 mPendingActions.clear();
2202             }
2203         }
2204 
2205         /**
2206          * Calls {@link #onSetSurface}.
2207          */
setSurface(Surface surface)2208         void setSurface(Surface surface) {
2209             onSetSurface(surface);
2210             if (mSurface != null) {
2211                 mSurface.release();
2212             }
2213             mSurface = surface;
2214             // TODO: Handle failure.
2215         }
2216 
2217         /**
2218          * Calls {@link #onSurfaceChanged}.
2219          */
dispatchSurfaceChanged(int format, int width, int height)2220         void dispatchSurfaceChanged(int format, int width, int height) {
2221             if (DEBUG) {
2222                 Log.d(TAG, "dispatchSurfaceChanged(format=" + format + ", width=" + width
2223                         + ", height=" + height + ")");
2224             }
2225             onSurfaceChanged(format, width, height);
2226         }
2227 
executeOrPostRunnableOnMainThread(Runnable action)2228         private void executeOrPostRunnableOnMainThread(Runnable action) {
2229             synchronized (mLock) {
2230                 if (mSessionCallback == null) {
2231                     // The session is not initialized yet.
2232                     mPendingActions.add(action);
2233                 } else {
2234                     if (mHandler.getLooper().isCurrentThread()) {
2235                         action.run();
2236                     } else {
2237                         // Posts the runnable if this is not called from the main thread
2238                         mHandler.post(action);
2239                     }
2240                 }
2241             }
2242         }
2243 
2244         /**
2245          * Creates an media view. This calls {@link #onCreateMediaView} to get a view to attach
2246          * to the media window.
2247          *
2248          * @param windowToken A window token of the application.
2249          * @param frame A position of the media view.
2250          */
createMediaView(IBinder windowToken, Rect frame)2251         void createMediaView(IBinder windowToken, Rect frame) {
2252             if (mMediaViewContainer != null) {
2253                 removeMediaView(false);
2254             }
2255             if (DEBUG) Log.d(TAG, "create media view(" + frame + ")");
2256             mWindowToken = windowToken;
2257             mMediaFrame = frame;
2258             onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
2259             if (!mMediaViewEnabled) {
2260                 return;
2261             }
2262             mMediaView = onCreateMediaView();
2263             if (mMediaView == null) {
2264                 return;
2265             }
2266             if (mMediaViewCleanUpTask != null) {
2267                 mMediaViewCleanUpTask.cancel(true);
2268                 mMediaViewCleanUpTask = null;
2269             }
2270             // Creates a container view to check hanging on the media view detaching.
2271             // Adding/removing the media view to/from the container make the view attach/detach
2272             // logic run on the main thread.
2273             mMediaViewContainer = new FrameLayout(mContext.getApplicationContext());
2274             mMediaViewContainer.addView(mMediaView);
2275 
2276             int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
2277             // We make the overlay view non-focusable and non-touchable so that
2278             // the application that owns the window token can decide whether to consume or
2279             // dispatch the input events.
2280             int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
2281                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
2282                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
2283             if (ActivityManager.isHighEndGfx()) {
2284                 flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
2285             }
2286             mWindowParams = new WindowManager.LayoutParams(
2287                     frame.right - frame.left, frame.bottom - frame.top,
2288                     frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT);
2289             mWindowParams.privateFlags |=
2290                     WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
2291             mWindowParams.gravity = Gravity.START | Gravity.TOP;
2292             mWindowParams.token = windowToken;
2293             mWindowManager.addView(mMediaViewContainer, mWindowParams);
2294         }
2295 
2296         /**
2297          * Relayouts the current media view.
2298          *
2299          * @param frame A new position of the media view.
2300          */
relayoutMediaView(Rect frame)2301         void relayoutMediaView(Rect frame) {
2302             if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")");
2303             if (mMediaFrame == null || mMediaFrame.width() != frame.width()
2304                     || mMediaFrame.height() != frame.height()) {
2305                 // Note: relayoutMediaView is called whenever TvInteractiveAppView's layout is
2306                 // changed regardless of setMediaViewEnabled.
2307                 onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
2308             }
2309             mMediaFrame = frame;
2310             if (!mMediaViewEnabled || mMediaViewContainer == null) {
2311                 return;
2312             }
2313             mWindowParams.x = frame.left;
2314             mWindowParams.y = frame.top;
2315             mWindowParams.width = frame.right - frame.left;
2316             mWindowParams.height = frame.bottom - frame.top;
2317             mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams);
2318         }
2319 
2320         /**
2321          * Removes the current media view.
2322          */
removeMediaView(boolean clearWindowToken)2323         void removeMediaView(boolean clearWindowToken) {
2324             if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")");
2325             if (clearWindowToken) {
2326                 mWindowToken = null;
2327                 mMediaFrame = null;
2328             }
2329             if (mMediaViewContainer != null) {
2330                 // Removes the media view from the view hierarchy in advance so that it can be
2331                 // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is
2332                 // hanging.
2333                 mMediaViewContainer.removeView(mMediaView);
2334                 mMediaView = null;
2335                 mWindowManager.removeView(mMediaViewContainer);
2336                 mMediaViewContainer = null;
2337                 mWindowParams = null;
2338             }
2339         }
2340 
2341         /**
2342          * Schedules a task which checks whether the media view is detached and kills the process
2343          * if it is not. Note that this method is expected to be called in a non-main thread.
2344          */
scheduleMediaViewCleanup()2345         void scheduleMediaViewCleanup() {
2346             View mediaViewParent = mMediaViewContainer;
2347             if (mediaViewParent != null) {
2348                 mMediaViewCleanUpTask = new MediaViewCleanUpTask();
2349                 mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
2350                         mediaViewParent);
2351             }
2352         }
2353     }
2354 
2355     private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> {
2356         @Override
doInBackground(View... views)2357         protected Void doInBackground(View... views) {
2358             View mediaViewParent = views[0];
2359             try {
2360                 Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS);
2361             } catch (InterruptedException e) {
2362                 return null;
2363             }
2364             if (isCancelled()) {
2365                 return null;
2366             }
2367             if (mediaViewParent.isAttachedToWindow()) {
2368                 Log.e(TAG, "Time out on releasing media view. Killing "
2369                         + mediaViewParent.getContext().getPackageName());
2370                 android.os.Process.killProcess(Process.myPid());
2371             }
2372             return null;
2373         }
2374     }
2375 
2376     @SuppressLint("HandlerLeak")
2377     private final class ServiceHandler extends Handler {
2378         private static final int DO_CREATE_SESSION = 1;
2379         private static final int DO_NOTIFY_SESSION_CREATED = 2;
2380         private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3;
2381 
broadcastRteStateChanged(int type, int state, int error)2382         private void broadcastRteStateChanged(int type, int state, int error) {
2383             int n = mCallbacks.beginBroadcast();
2384             for (int i = 0; i < n; ++i) {
2385                 try {
2386                     mCallbacks.getBroadcastItem(i).onStateChanged(type, state, error);
2387                 } catch (RemoteException e) {
2388                     Log.e(TAG, "error in broadcastRteStateChanged", e);
2389                 }
2390             }
2391             mCallbacks.finishBroadcast();
2392         }
2393 
2394         @Override
handleMessage(Message msg)2395         public void handleMessage(Message msg) {
2396             switch (msg.what) {
2397                 case DO_CREATE_SESSION: {
2398                     SomeArgs args = (SomeArgs) msg.obj;
2399                     InputChannel channel = (InputChannel) args.arg1;
2400                     ITvInteractiveAppSessionCallback cb =
2401                             (ITvInteractiveAppSessionCallback) args.arg2;
2402                     String iAppServiceId = (String) args.arg3;
2403                     int type = (int) args.arg4;
2404                     args.recycle();
2405                     Session sessionImpl = onCreateSession(iAppServiceId, type);
2406                     if (sessionImpl == null) {
2407                         try {
2408                             // Failed to create a session.
2409                             cb.onSessionCreated(null);
2410                         } catch (RemoteException e) {
2411                             Log.e(TAG, "error in onSessionCreated", e);
2412                         }
2413                         return;
2414                     }
2415                     ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper(
2416                             TvInteractiveAppService.this, sessionImpl, channel);
2417 
2418                     SomeArgs someArgs = SomeArgs.obtain();
2419                     someArgs.arg1 = sessionImpl;
2420                     someArgs.arg2 = stub;
2421                     someArgs.arg3 = cb;
2422                     mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED,
2423                             someArgs).sendToTarget();
2424                     return;
2425                 }
2426                 case DO_NOTIFY_SESSION_CREATED: {
2427                     SomeArgs args = (SomeArgs) msg.obj;
2428                     Session sessionImpl = (Session) args.arg1;
2429                     ITvInteractiveAppSession stub = (ITvInteractiveAppSession) args.arg2;
2430                     ITvInteractiveAppSessionCallback cb =
2431                             (ITvInteractiveAppSessionCallback) args.arg3;
2432                     try {
2433                         cb.onSessionCreated(stub);
2434                     } catch (RemoteException e) {
2435                         Log.e(TAG, "error in onSessionCreated", e);
2436                     }
2437                     if (sessionImpl != null) {
2438                         sessionImpl.initialize(cb);
2439                     }
2440                     args.recycle();
2441                     return;
2442                 }
2443                 case DO_NOTIFY_RTE_STATE_CHANGED: {
2444                     SomeArgs args = (SomeArgs) msg.obj;
2445                     int type = (int) args.arg1;
2446                     int state = (int) args.arg2;
2447                     int error = (int) args.arg3;
2448                     broadcastRteStateChanged(type, state, error);
2449                     return;
2450                 }
2451                 default: {
2452                     Log.w(TAG, "Unhandled message code: " + msg.what);
2453                     return;
2454                 }
2455             }
2456         }
2457 
2458     }
2459 }
2460