• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.CallbackExecutor;
20 import android.annotation.FlaggedApi;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.content.res.Resources;
25 import android.content.res.XmlResourceParser;
26 import android.graphics.PixelFormat;
27 import android.graphics.Rect;
28 import android.graphics.RectF;
29 import android.media.PlaybackParams;
30 import android.media.tv.TvInputManager;
31 import android.media.tv.TvRecordingInfo;
32 import android.media.tv.TvTrackInfo;
33 import android.media.tv.TvView;
34 import android.media.tv.flags.Flags;
35 import android.media.tv.interactive.TvInteractiveAppManager.Session;
36 import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
37 import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
38 import android.net.Uri;
39 import android.net.http.SslCertificate;
40 import android.os.Bundle;
41 import android.os.Handler;
42 import android.util.AttributeSet;
43 import android.util.Log;
44 import android.util.Xml;
45 import android.view.InputEvent;
46 import android.view.KeyEvent;
47 import android.view.Surface;
48 import android.view.SurfaceHolder;
49 import android.view.SurfaceView;
50 import android.view.View;
51 import android.view.ViewGroup;
52 import android.view.ViewRootImpl;
53 
54 import java.security.KeyStore;
55 import java.util.Arrays;
56 import java.util.List;
57 import java.util.concurrent.Executor;
58 
59 /**
60  * Displays contents of interactive TV applications.
61  */
62 public class TvInteractiveAppView extends ViewGroup {
63     private static final String TAG = "TvInteractiveAppView";
64     private static final boolean DEBUG = false;
65 
66     private static final int SET_TVVIEW_SUCCESS = 1;
67     private static final int SET_TVVIEW_FAIL = 2;
68     private static final int UNSET_TVVIEW_SUCCESS = 3;
69     private static final int UNSET_TVVIEW_FAIL = 4;
70 
71     /**
72      * Used to share client {@link java.security.cert.Certificate} with
73      * {@link TvInteractiveAppService}.
74      * @see #createBiInteractiveApp(Uri, Bundle)
75      * @see java.security.cert.Certificate
76      */
77     public static final String BI_INTERACTIVE_APP_KEY_CERTIFICATE = "certificate";
78     /**
79      * Used to share the {@link KeyStore} alias with {@link TvInteractiveAppService}.
80      * @see #createBiInteractiveApp(Uri, Bundle)
81      * @see KeyStore#aliases()
82      */
83     public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias";
84     /**
85      * Used to share the {@link java.security.PrivateKey} with {@link TvInteractiveAppService}.
86      * <p>The private key is optional. It is used to encrypt data when necessary.
87      *
88      * @see #createBiInteractiveApp(Uri, Bundle)
89      * @see java.security.PrivateKey
90      */
91     public static final String BI_INTERACTIVE_APP_KEY_PRIVATE_KEY = "private_key";
92     /**
93      * Additional HTTP headers to be used by {@link TvInteractiveAppService} to load the
94      * broadcast-independent interactive application.
95      * @see #createBiInteractiveApp(Uri, Bundle)
96      */
97     public static final String BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS =
98             "http_additional_headers";
99     /**
100      * HTTP user agent to be used by {@link TvInteractiveAppService} for broadcast-independent
101      * interactive application.
102      * @see #createBiInteractiveApp(Uri, Bundle)
103      */
104     public static final String BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT = "http_user_agent";
105 
106     /**
107      * The name of the method where the error happened, if applicable. For example, if there is an
108      * error during signing, the request name is "onRequestSigning".
109      * @see #notifyError(String, Bundle)
110      */
111     public static final String ERROR_KEY_METHOD_NAME = "method_name";
112 
113     private final TvInteractiveAppManager mTvInteractiveAppManager;
114     private final Handler mHandler = new Handler();
115     private final Object mCallbackLock = new Object();
116     private Session mSession;
117     private MySessionCallback mSessionCallback;
118     private TvInteractiveAppCallback mCallback;
119     private Executor mCallbackExecutor;
120     private SurfaceView mSurfaceView;
121     private Surface mSurface;
122 
123     private boolean mSurfaceChanged;
124     private int mSurfaceFormat;
125     private int mSurfaceWidth;
126     private int mSurfaceHeight;
127 
128     private boolean mUseRequestedSurfaceLayout;
129     private int mSurfaceViewLeft;
130     private int mSurfaceViewRight;
131     private int mSurfaceViewTop;
132     private int mSurfaceViewBottom;
133 
134     private boolean mMediaViewCreated;
135     private Rect mMediaViewFrame;
136 
137     private final AttributeSet mAttrs;
138     private final int mDefStyleAttr;
139     private final XmlResourceParser mParser;
140     private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
141 
142     private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
143         @Override
144         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
145             if (DEBUG) {
146                 Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format
147                         + ", width=" + width + ", height=" + height + ")");
148             }
149             mSurfaceFormat = format;
150             mSurfaceWidth = width;
151             mSurfaceHeight = height;
152             mSurfaceChanged = true;
153             dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
154         }
155 
156         @Override
157         public void surfaceCreated(SurfaceHolder holder) {
158             mSurface = holder.getSurface();
159             setSessionSurface(mSurface);
160         }
161 
162         @Override
163         public void surfaceDestroyed(SurfaceHolder holder) {
164             mSurface = null;
165             mSurfaceChanged = false;
166             setSessionSurface(null);
167         }
168     };
169 
TvInteractiveAppView(@onNull Context context)170     public TvInteractiveAppView(@NonNull Context context) {
171         this(context, null, 0);
172     }
173 
TvInteractiveAppView(@onNull Context context, @Nullable AttributeSet attrs)174     public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
175         this(context, attrs, 0);
176     }
177 
TvInteractiveAppView(@onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)178     public TvInteractiveAppView(@NonNull Context context, @Nullable AttributeSet attrs,
179             int defStyleAttr) {
180         super(context, attrs, defStyleAttr);
181         int sourceResId = Resources.getAttributeSetSourceResId(attrs);
182         if (sourceResId != Resources.ID_NULL) {
183             Log.d(TAG, "Build local AttributeSet");
184             mParser  = context.getResources().getXml(sourceResId);
185             mAttrs = Xml.asAttributeSet(mParser);
186         } else {
187             Log.d(TAG, "Use passed in AttributeSet");
188             mParser = null;
189             mAttrs = attrs;
190         }
191         mDefStyleAttr = defStyleAttr;
192         resetSurfaceView();
193         mTvInteractiveAppManager = (TvInteractiveAppManager) getContext().getSystemService(
194                 Context.TV_INTERACTIVE_APP_SERVICE);
195     }
196 
197     /**
198      * Sets the callback to be invoked when an event is dispatched to this TvInteractiveAppView.
199      *
200      * @param callback the callback to receive events. MUST NOT be {@code null}.
201      *
202      * @see #clearCallback()
203      */
setCallback( @onNull @allbackExecutor Executor executor, @NonNull TvInteractiveAppCallback callback)204     public void setCallback(
205             @NonNull @CallbackExecutor Executor executor,
206             @NonNull TvInteractiveAppCallback callback) {
207         com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, callback);
208         synchronized (mCallbackLock) {
209             mCallbackExecutor = executor;
210             mCallback = callback;
211         }
212     }
213 
214     /**
215      * Clears the callback.
216      *
217      * @see #setCallback(Executor, TvInteractiveAppCallback)
218      */
clearCallback()219     public void clearCallback() {
220         synchronized (mCallbackLock) {
221             mCallback = null;
222             mCallbackExecutor = null;
223         }
224     }
225 
226     @Override
onAttachedToWindow()227     public void onAttachedToWindow() {
228         super.onAttachedToWindow();
229         createSessionMediaView();
230     }
231 
232     @Override
onDetachedFromWindow()233     public void onDetachedFromWindow() {
234         removeSessionMediaView();
235         super.onDetachedFromWindow();
236     }
237 
238     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)239     public void onLayout(boolean changed, int left, int top, int right, int bottom) {
240         if (DEBUG) {
241             Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
242                     + ", bottom=" + bottom + ",)");
243         }
244         if (mUseRequestedSurfaceLayout) {
245             mSurfaceView.layout(mSurfaceViewLeft, mSurfaceViewTop, mSurfaceViewRight,
246                     mSurfaceViewBottom);
247         } else {
248             mSurfaceView.layout(0, 0, right - left, bottom - top);
249         }
250     }
251 
252     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)253     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
254         mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
255         int width = mSurfaceView.getMeasuredWidth();
256         int height = mSurfaceView.getMeasuredHeight();
257         int childState = mSurfaceView.getMeasuredState();
258         setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
259                 resolveSizeAndState(height, heightMeasureSpec,
260                         childState << MEASURED_HEIGHT_STATE_SHIFT));
261     }
262 
263     @Override
onVisibilityChanged(@onNull View changedView, int visibility)264     public void onVisibilityChanged(@NonNull View changedView, int visibility) {
265         super.onVisibilityChanged(changedView, visibility);
266         mSurfaceView.setVisibility(visibility);
267         if (visibility == View.VISIBLE) {
268             createSessionMediaView();
269         } else {
270             removeSessionMediaView();
271         }
272     }
273 
resetSurfaceView()274     private void resetSurfaceView() {
275         if (mSurfaceView != null) {
276             mSurfaceView.getHolder().removeCallback(mSurfaceHolderCallback);
277             removeView(mSurfaceView);
278         }
279         mSurface = null;
280         mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
281             @Override
282             protected void updateSurface() {
283                 super.updateSurface();
284                 relayoutSessionMediaView();
285             }};
286         // The surface view's content should be treated as secure all the time.
287         mSurfaceView.setSecure(true);
288         mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
289         mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
290 
291         mSurfaceView.setZOrderOnTop(false);
292         mSurfaceView.setZOrderMediaOverlay(true);
293 
294         addView(mSurfaceView);
295     }
296 
297     /**
298      * Resets this TvInteractiveAppView to release its resources.
299      *
300      * <p>It can be reused by call {@link #prepareInteractiveApp(String, int)}.
301      */
reset()302     public void reset() {
303         if (DEBUG) Log.d(TAG, "reset()");
304         resetInternal();
305     }
306 
createSessionMediaView()307     private void createSessionMediaView() {
308         // TODO: handle z-order
309         if (mSession == null || !isAttachedToWindow() || mMediaViewCreated) {
310             return;
311         }
312         mMediaViewFrame = getViewFrameOnScreen();
313         mSession.createMediaView(this, mMediaViewFrame);
314         mMediaViewCreated = true;
315     }
316 
removeSessionMediaView()317     private void removeSessionMediaView() {
318         if (mSession == null || !mMediaViewCreated) {
319             return;
320         }
321         mSession.removeMediaView();
322         mMediaViewCreated = false;
323         mMediaViewFrame = null;
324     }
325 
relayoutSessionMediaView()326     private void relayoutSessionMediaView() {
327         if (mSession == null || !isAttachedToWindow() || !mMediaViewCreated) {
328             return;
329         }
330         Rect viewFrame = getViewFrameOnScreen();
331         if (viewFrame.equals(mMediaViewFrame)) {
332             return;
333         }
334         mSession.relayoutMediaView(viewFrame);
335         mMediaViewFrame = viewFrame;
336     }
337 
getViewFrameOnScreen()338     private Rect getViewFrameOnScreen() {
339         Rect frame = new Rect();
340         getGlobalVisibleRect(frame);
341         RectF frameF = new RectF(frame);
342         getMatrix().mapRect(frameF);
343         frameF.round(frame);
344         return frame;
345     }
346 
setSessionSurface(Surface surface)347     private void setSessionSurface(Surface surface) {
348         if (mSession == null) {
349             return;
350         }
351         mSession.setSurface(surface);
352     }
353 
dispatchSurfaceChanged(int format, int width, int height)354     private void dispatchSurfaceChanged(int format, int width, int height) {
355         if (mSession == null) {
356             return;
357         }
358         mSession.dispatchSurfaceChanged(format, width, height);
359     }
360 
361     private final FinishedInputEventCallback mFinishedInputEventCallback =
362             new FinishedInputEventCallback() {
363                 @Override
364                 public void onFinishedInputEvent(Object token, boolean handled) {
365                     if (DEBUG) {
366                         Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled="
367                                 + handled + ")");
368                     }
369                     if (handled) {
370                         return;
371                     }
372                     // TODO: Re-order unhandled events.
373                     InputEvent event = (InputEvent) token;
374                     if (dispatchUnhandledInputEvent(event)) {
375                         return;
376                     }
377                     ViewRootImpl viewRootImpl = getViewRootImpl();
378                     if (viewRootImpl != null) {
379                         viewRootImpl.dispatchUnhandledInputEvent(event);
380                     }
381                 }
382             };
383 
384     /**
385      * Dispatches an unhandled input event to the next receiver.
386      *
387      * It gives the host application a chance to dispatch the unhandled input events.
388      *
389      * @param event The input event.
390      * @return {@code true} if the event was handled by the view, {@code false} otherwise.
391      */
dispatchUnhandledInputEvent(@onNull InputEvent event)392     public boolean dispatchUnhandledInputEvent(@NonNull InputEvent event) {
393         if (mOnUnhandledInputEventListener != null) {
394             if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
395                 return true;
396             }
397         }
398         return onUnhandledInputEvent(event);
399     }
400 
401     /**
402      * Called when an unhandled input event also has not been handled by the user provided
403      * callback. This is the last chance to handle the unhandled input event in the
404      * TvInteractiveAppView.
405      *
406      * @param event The input event.
407      * @return If you handled the event, return {@code true}. If you want to allow the event to be
408      *         handled by the next receiver, return {@code false}.
409      */
onUnhandledInputEvent(@onNull InputEvent event)410     public boolean onUnhandledInputEvent(@NonNull InputEvent event) {
411         return false;
412     }
413 
414     /**
415      * Sets a listener to be invoked when an input event is not handled
416      * by the TV Interactive App.
417      *
418      * @param listener The callback to be invoked when the unhandled input event is received.
419      */
setOnUnhandledInputEventListener( @onNull @allbackExecutor Executor executor, @NonNull OnUnhandledInputEventListener listener)420     public void setOnUnhandledInputEventListener(
421             @NonNull @CallbackExecutor Executor executor,
422             @NonNull OnUnhandledInputEventListener listener) {
423         mOnUnhandledInputEventListener = listener;
424         // TODO: handle CallbackExecutor
425     }
426 
427     /**
428      * Gets the {@link OnUnhandledInputEventListener}.
429      * <p>Returns {@code null} if the listener is not set or is cleared.
430      *
431      * @see #setOnUnhandledInputEventListener(Executor, OnUnhandledInputEventListener)
432      * @see #clearOnUnhandledInputEventListener()
433      */
434     @Nullable
getOnUnhandledInputEventListener()435     public OnUnhandledInputEventListener getOnUnhandledInputEventListener() {
436         return mOnUnhandledInputEventListener;
437     }
438 
439     /**
440      * Clears the {@link OnUnhandledInputEventListener}.
441      */
clearOnUnhandledInputEventListener()442     public void clearOnUnhandledInputEventListener() {
443         mOnUnhandledInputEventListener = null;
444     }
445 
446     @Override
dispatchKeyEvent(@onNull KeyEvent event)447     public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
448         if (super.dispatchKeyEvent(event)) {
449             return true;
450         }
451         if (mSession == null) {
452             return false;
453         }
454         InputEvent copiedEvent = event.copy();
455         int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
456                 mHandler);
457         return ret != Session.DISPATCH_NOT_HANDLED;
458     }
459 
460     /**
461      * Prepares the interactive application runtime environment of corresponding
462      * {@link TvInteractiveAppService}.
463      *
464      * @param iAppServiceId the interactive app service ID, which can be found in
465      *                      {@link TvInteractiveAppServiceInfo#getId()}.
466      *
467      * @see android.media.tv.interactive.TvInteractiveAppManager#getTvInteractiveAppServiceList()
468      */
prepareInteractiveApp( @onNull String iAppServiceId, @TvInteractiveAppServiceInfo.InteractiveAppType int type)469     public void prepareInteractiveApp(
470             @NonNull String iAppServiceId,
471             @TvInteractiveAppServiceInfo.InteractiveAppType int type) {
472         // TODO: document and handle the cases that this method is called multiple times.
473         if (DEBUG) {
474             Log.d(TAG, "prepareInteractiveApp");
475         }
476         mSessionCallback = new MySessionCallback(iAppServiceId, type);
477         if (mTvInteractiveAppManager != null) {
478             mTvInteractiveAppManager.createSession(iAppServiceId, type, mSessionCallback, mHandler);
479         }
480     }
481 
482     /**
483      * Starts the interactive application.
484      */
startInteractiveApp()485     public void startInteractiveApp() {
486         if (DEBUG) {
487             Log.d(TAG, "startInteractiveApp");
488         }
489         if (mSession != null) {
490             mSession.startInteractiveApp();
491         }
492     }
493 
494     /**
495      * Stops the interactive application.
496      */
stopInteractiveApp()497     public void stopInteractiveApp() {
498         if (DEBUG) {
499             Log.d(TAG, "stopInteractiveApp");
500         }
501         if (mSession != null) {
502             mSession.stopInteractiveApp();
503         }
504     }
505 
506     /**
507      * Resets the interactive application.
508      *
509      * <p>This releases the resources of the corresponding {@link TvInteractiveAppService.Session}.
510      */
resetInteractiveApp()511     public void resetInteractiveApp() {
512         if (DEBUG) {
513             Log.d(TAG, "resetInteractiveApp");
514         }
515         if (mSession != null) {
516             mSession.resetInteractiveApp();
517         }
518     }
519 
520     /**
521      * Sends current video bounds to related TV interactive app.
522      *
523      * @param bounds the rectangle area for rendering the current video.
524      */
sendCurrentVideoBounds(@onNull Rect bounds)525     public void sendCurrentVideoBounds(@NonNull Rect bounds) {
526         if (DEBUG) {
527             Log.d(TAG, "sendCurrentVideoBounds");
528         }
529         if (mSession != null) {
530             mSession.sendCurrentVideoBounds(bounds);
531         }
532     }
533 
534     /**
535      * Sends current channel URI to related TV interactive app.
536      *
537      * @param channelUri The current channel URI; {@code null} if there is no currently tuned
538      *                   channel.
539      */
sendCurrentChannelUri(@ullable Uri channelUri)540     public void sendCurrentChannelUri(@Nullable Uri channelUri) {
541         if (DEBUG) {
542             Log.d(TAG, "sendCurrentChannelUri");
543         }
544         if (mSession != null) {
545             mSession.sendCurrentChannelUri(channelUri);
546         }
547     }
548 
549     /**
550      * Sends current channel logical channel number (LCN) to related TV interactive app.
551      */
sendCurrentChannelLcn(int lcn)552     public void sendCurrentChannelLcn(int lcn) {
553         if (DEBUG) {
554             Log.d(TAG, "sendCurrentChannelLcn");
555         }
556         if (mSession != null) {
557             mSession.sendCurrentChannelLcn(lcn);
558         }
559     }
560 
561     /**
562      * Sends stream volume to related TV interactive app.
563      *
564      * @param volume a volume value between {@code 0.0f} and {@code 1.0f}, inclusive.
565      */
sendStreamVolume(float volume)566     public void sendStreamVolume(float volume) {
567         if (DEBUG) {
568             Log.d(TAG, "sendStreamVolume");
569         }
570         if (mSession != null) {
571             mSession.sendStreamVolume(volume);
572         }
573     }
574 
575     /**
576      * Sends track info list to related TV interactive app.
577      */
sendTrackInfoList(@ullable List<TvTrackInfo> tracks)578     public void sendTrackInfoList(@Nullable List<TvTrackInfo> tracks) {
579         if (DEBUG) {
580             Log.d(TAG, "sendTrackInfoList");
581         }
582         if (mSession != null) {
583             mSession.sendTrackInfoList(tracks);
584         }
585     }
586 
587     /**
588      * Sends the currently selected track info to the TV Interactive App in response to a
589      * {@link TvInteractiveAppCallback#onRequestSelectedTrackInfo(String)} request.
590      *
591      * @param tracks list of {@link TvTrackInfo} of the currently selected track(s)
592      */
593     @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
sendSelectedTrackInfo(@ullable List<TvTrackInfo> tracks)594     public void sendSelectedTrackInfo(@Nullable List<TvTrackInfo> tracks) {
595         if (DEBUG) {
596             Log.d(TAG, "sendSelectedTrackInfo");
597         }
598         if (mSession != null) {
599             mSession.sendSelectedTrackInfo(tracks);
600         }
601     }
602 
603     /**
604      * Sends current TV input ID to related TV interactive app.
605      *
606      * @param inputId The current TV input ID whose channel is tuned. {@code null} if no channel is
607      *                tuned.
608      * @see android.media.tv.TvInputInfo
609      */
sendCurrentTvInputId(@ullable String inputId)610     public void sendCurrentTvInputId(@Nullable String inputId) {
611         if (DEBUG) {
612             Log.d(TAG, "sendCurrentTvInputId");
613         }
614         if (mSession != null) {
615             mSession.sendCurrentTvInputId(inputId);
616         }
617     }
618 
619     /**
620      * Sends the current time shift mode to the TV interactive app bound to this view
621      *
622      * @param mode The current time shift mode. The value is one of the following:
623      * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
624      * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
625      * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
626      */
sendTimeShiftMode(@ndroid.media.tv.TvInputManager.TimeShiftMode int mode)627     public void sendTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
628         if (DEBUG) {
629             Log.d(TAG, "sendTimeShiftMode");
630         }
631         if (mSession != null) {
632             mSession.sendTimeShiftMode(mode);
633         }
634     }
635 
636     /**
637      * Sends the available supported playback speeds to the TV interactive app bound to this view.
638      *
639      * @param speeds An ordered array of playback speeds, expressed as values relative to the
640      *               normal playback speed (1.0), at which the current content can be played as
641      *               a time-shifted broadcast. This is an empty array if the supported playback
642      *               speeds are unknown or the video/broadcast is not in time shift mode. If
643      *               currently in time shift mode, this array will normally include at least
644      *               the values 1.0 (normal speed) and 0.0 (paused).
645      * @see PlaybackParams#getSpeed()
646      */
sendAvailableSpeeds(@onNull float[] speeds)647     public void sendAvailableSpeeds(@NonNull float[] speeds) {
648         if (DEBUG) {
649             Log.d(TAG, "sendAvailableSpeeds");
650         }
651         if (mSession != null) {
652             Arrays.sort(speeds);
653             mSession.sendAvailableSpeeds(speeds);
654         }
655     }
656 
657     /**
658      * Sends the requested {@link android.media.tv.TvRecordingInfo}.
659      *
660      * @see TvInteractiveAppService.Session#requestTvRecordingInfo(String)
661      * @param recordingInfo The recording info requested. {@code null} if no recording found.
662      */
sendTvRecordingInfo(@ullable TvRecordingInfo recordingInfo)663     public void sendTvRecordingInfo(@Nullable TvRecordingInfo recordingInfo) {
664         if (DEBUG) {
665             Log.d(TAG, "sendTvRecordingInfo");
666         }
667         if (mSession != null) {
668             mSession.sendTvRecordingInfo(recordingInfo);
669         }
670     }
671 
672     /**
673      * Sends the requested {@link android.media.tv.TvRecordingInfo}.
674      *
675      * @see TvInteractiveAppService.Session#requestTvRecordingInfoList(int)
676      * @param recordingInfoList The list of recording info requested. Returns an empty list if no
677      *                          matching recording info found.
678      */
sendTvRecordingInfoList(@onNull List<TvRecordingInfo> recordingInfoList)679     public void sendTvRecordingInfoList(@NonNull List<TvRecordingInfo> recordingInfoList) {
680         if (DEBUG) {
681             Log.d(TAG, "sendTvRecordingInfoList");
682         }
683         if (mSession != null) {
684             mSession.sendTvRecordingInfoList(recordingInfoList);
685         }
686     }
687 
688     /**
689      * Alerts the related TV interactive app service that a recording has been started.
690      *
691      * @param recordingId The ID of the recording started. This ID is created and maintained by the
692      *                    TV app and is used to identify the recording in the future.
693      *
694      * @param requestId The ID of the request when
695      *                  {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)}
696      *                  is called. {@code null} if the recording is not triggered by a request.
697      *                  This ID should be created by the {@link TvInteractiveAppService} and
698      *                  can be any string.
699      * @see TvInteractiveAppView#notifyRecordingStopped(String)
700      */
notifyRecordingStarted(@onNull String recordingId, @Nullable String requestId)701     public void notifyRecordingStarted(@NonNull String recordingId, @Nullable String requestId) {
702         if (DEBUG) {
703             Log.d(TAG, "notifyRecordingStarted");
704         }
705         if (mSession != null) {
706             mSession.notifyRecordingStarted(recordingId, requestId);
707         }
708     }
709 
710     /**
711      * Alerts the TV interactive app that a recording has been stopped.
712      *
713      * @param recordingId The ID of the recording stopped. This ID is created and maintained
714      *                    by the TV app when a recording is started.
715      * @see TvInteractiveAppView#notifyRecordingStarted(String, String)
716      */
notifyRecordingStopped(@onNull String recordingId)717     public void notifyRecordingStopped(@NonNull String recordingId) {
718         if (DEBUG) {
719             Log.d(TAG, "notifyRecordingStopped");
720         }
721         if (mSession != null) {
722             mSession.notifyRecordingStopped(recordingId);
723         }
724     }
725 
726     /**
727      * Alerts the TV Interactive app that the video freeze state has been updated. If {@code true},
728      * the video is frozen on the last frame while audio playback continues.
729      *
730      * @param isFrozen Whether the video is frozen.
731      */
732     @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
notifyVideoFreezeUpdated(boolean isFrozen)733     public void notifyVideoFreezeUpdated(boolean isFrozen) {
734         if (DEBUG) {
735             Log.d(TAG, "notifyVideoFreezeUpdated");
736         }
737         if (mSession != null) {
738             mSession.notifyVideoFreezeUpdated(isFrozen);
739         }
740     }
741 
742     /**
743      * Sends signing result to related TV interactive app.
744      *
745      * <p>This is used when the corresponding server of the broadcast-independent interactive
746      * app requires signing during handshaking, and the interactive app service doesn't have
747      * the built-in private key. The private key is provided by the content providers and
748      * pre-built in the related app, such as TV app.
749      *
750      * @param signingId the ID to identify the request. It's the same as the corresponding ID in
751      *        {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])}
752      * @param result the signed result.
753      */
sendSigningResult(@onNull String signingId, @NonNull byte[] result)754     public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) {
755         if (DEBUG) {
756             Log.d(TAG, "sendSigningResult");
757         }
758         if (mSession != null) {
759             mSession.sendSigningResult(signingId, result);
760         }
761     }
762 
763     /**
764      * Sends the requested SSL certificate to the TV Interactive App
765      * @param host the host name of the SSL authentication server.
766      * @param port the port of the SSL authentication server. E.g., 443
767      * @param cert the SSL certificate requested
768      */
769     @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
sendCertificate(@onNull String host, int port, @NonNull SslCertificate cert)770     public void sendCertificate(@NonNull String host, int port, @NonNull SslCertificate cert) {
771         if (DEBUG) {
772             Log.d(TAG, "sendCertificate");
773         }
774         if (mSession != null) {
775             mSession.sendCertificate(host, port, cert);
776         }
777     }
778 
779     /**
780      * Notifies the corresponding {@link TvInteractiveAppService} when there is an error.
781      *
782      * @param errMsg the message of the error.
783      * @param params additional parameters of the error. For example, the signingId of {@link
784      *     TvInteractiveAppCallback#onRequestSigning(String, String, String, String, byte[])} can be
785      *     included to identify the related signing request, and the method name "onRequestSigning"
786      *     can also be added to the params.
787      *
788      * @see #ERROR_KEY_METHOD_NAME
789      */
notifyError(@onNull String errMsg, @NonNull Bundle params)790     public void notifyError(@NonNull String errMsg, @NonNull Bundle params) {
791         if (DEBUG) {
792             Log.d(TAG, "notifyError msg=" + errMsg + "; params=" + params);
793         }
794         if (mSession != null) {
795             mSession.notifyError(errMsg, params);
796         }
797     }
798 
799     /**
800      * Notifies the corresponding {@link TvInteractiveAppService} when a time shift
801      * {@link android.media.PlaybackParams} is set or changed.
802      *
803      * @see TvView#timeShiftSetPlaybackParams(PlaybackParams)
804      * @param params The new {@link PlaybackParams} that was set or changed.
805      */
notifyTimeShiftPlaybackParams(@onNull PlaybackParams params)806     public void notifyTimeShiftPlaybackParams(@NonNull PlaybackParams params) {
807         if (DEBUG) {
808             Log.d(TAG, "notifyTimeShiftPlaybackParams params=" + params);
809         }
810         if (mSession != null) {
811             mSession.notifyTimeShiftPlaybackParams(params);
812         }
813     }
814 
815     /**
816      * Notifies the corresponding {@link TvInteractiveAppService} when time shift
817      * status is changed.
818      *
819      * @see TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)
820      * @see android.media.tv.TvInputService.Session#notifyTimeShiftStatusChanged(int)
821      * @param inputId The ID of the input for which the time shift status has changed.
822      * @param status The status of which the input has changed to. Should be one of the
823      *               following.
824      *               <ul>
825      *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}
826      *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
827      *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
828      *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
829      *               </ul>
830      */
notifyTimeShiftStatusChanged( @onNull String inputId, @TvInputManager.TimeShiftStatus int status)831     public void notifyTimeShiftStatusChanged(
832             @NonNull String inputId, @TvInputManager.TimeShiftStatus int status) {
833         if (DEBUG) {
834             Log.d(TAG,
835                     "notifyTimeShiftStatusChanged inputId=" + inputId + "; status=" + status);
836         }
837         if (mSession != null) {
838             mSession.notifyTimeShiftStatusChanged(inputId, status);
839         }
840     }
841 
842     /**
843      * Notifies the corresponding {@link TvInteractiveAppService} when time shift
844      * start position is changed.
845      *
846      * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged(String, long)
847      * @param inputId The ID of the input for which the time shift start position has changed.
848      * @param timeMs The start position for time shifting, in milliseconds since the epoch.
849      */
notifyTimeShiftStartPositionChanged(@onNull String inputId, long timeMs)850     public void notifyTimeShiftStartPositionChanged(@NonNull String inputId, long timeMs) {
851         if (DEBUG) {
852             Log.d(TAG, "notifyTimeShiftStartPositionChanged inputId=" + inputId
853                     + "; timeMs=" + timeMs);
854         }
855         if (mSession != null) {
856             mSession.notifyTimeShiftStartPositionChanged(inputId, timeMs);
857         }
858     }
859 
860     /**
861      * Notifies the corresponding {@link TvInteractiveAppService} when time shift
862      * current position is changed.
863      *
864      * @see TvView.TimeShiftPositionCallback#onTimeShiftCurrentPositionChanged(String, long)
865      * @param inputId The ID of the input for which the time shift current position has changed.
866      * @param timeMs The current position for time shifting, in milliseconds since the epoch.
867      */
notifyTimeShiftCurrentPositionChanged(@onNull String inputId, long timeMs)868     public void notifyTimeShiftCurrentPositionChanged(@NonNull String inputId, long timeMs) {
869         if (DEBUG) {
870             Log.d(TAG, "notifyTimeShiftCurrentPositionChanged inputId=" + inputId
871                     + "; timeMs=" + timeMs);
872         }
873         if (mSession != null) {
874             mSession.notifyTimeShiftCurrentPositionChanged(inputId, timeMs);
875         }
876     }
877 
878     /**
879      * This is called to notify the corresponding interactive app service when an error occurred
880      * while establishing a connection to the recording session for the corresponding TV input.
881      *
882      * @param recordingId The ID of the related recording which is sent via
883      *                    {@link #notifyRecordingStarted(String, String)}
884      * @param inputId The ID of the TV input bound to the current TvRecordingClient.
885      * @see android.media.tv.TvRecordingClient.RecordingCallback#onConnectionFailed(String)
886      * @hide
887      */
notifyRecordingConnectionFailed( @onNull String recordingId, @NonNull String inputId)888     public void notifyRecordingConnectionFailed(
889             @NonNull String recordingId, @NonNull String inputId) {
890         if (DEBUG) {
891             Log.d(TAG, "notifyRecordingConnectionFailed recordingId=" + recordingId
892                     + "; inputId=" + inputId);
893         }
894         if (mSession != null) {
895             mSession.notifyRecordingConnectionFailed(recordingId, inputId);
896         }
897     }
898 
899     /**
900      * This is called to notify the corresponding interactive app service when the connection to
901      * the current recording session is lost.
902      *
903      * @param recordingId The ID of the related recording which is sent via
904      *                    {@link #notifyRecordingStarted(String, String)}
905      * @param inputId The ID of the TV input bound to the current TvRecordingClient.
906      * @see android.media.tv.TvRecordingClient.RecordingCallback#onDisconnected(String)
907      * @hide
908      */
notifyRecordingDisconnected( @onNull String recordingId, @NonNull String inputId)909     public void notifyRecordingDisconnected(
910             @NonNull String recordingId, @NonNull String inputId) {
911         if (DEBUG) {
912             Log.d(TAG, "notifyRecordingDisconnected recordingId=" + recordingId
913                     + "; inputId=" + inputId);
914         }
915         if (mSession != null) {
916             mSession.notifyRecordingDisconnected(recordingId, inputId);
917         }
918     }
919 
920     /**
921      * This is called to notify the corresponding interactive app service when the recording session
922      * has been tuned to the given channel and is ready to start recording.
923      *
924      * @param recordingId The ID of the related recording which is sent via
925      *                    {@link #notifyRecordingStarted(String, String)}
926      * @param channelUri The URI of the tuned channel.
927      * @see android.media.tv.TvRecordingClient.RecordingCallback#onTuned(Uri)
928      * @hide
929      */
notifyRecordingTuned( @onNull String recordingId, @NonNull Uri channelUri)930     public void notifyRecordingTuned(
931             @NonNull String recordingId, @NonNull Uri channelUri) {
932         if (DEBUG) {
933             Log.d(TAG, "notifyRecordingTuned recordingId=" + recordingId
934                     + "; channelUri=" + channelUri);
935         }
936         if (mSession != null) {
937             mSession.notifyRecordingTuned(recordingId, channelUri);
938         }
939     }
940 
941     /**
942      * This is called to notify the corresponding interactive app service when an issue has
943      * occurred. It may be called at any time after the current recording session is created until
944      * it is released.
945      *
946      * @param recordingId The ID of the related recording which is sent via
947      *                    {@link #notifyRecordingStarted(String, String)}
948      * @param err The error code. Should be one of the following.
949      * <ul>
950      * <li>{@link TvInputManager#RECORDING_ERROR_UNKNOWN}
951      * <li>{@link TvInputManager#RECORDING_ERROR_INSUFFICIENT_SPACE}
952      * <li>{@link TvInputManager#RECORDING_ERROR_RESOURCE_BUSY}
953      * </ul>
954      * @see android.media.tv.TvRecordingClient.RecordingCallback#onError(int)
955      * @hide
956      */
notifyRecordingError( @onNull String recordingId, @TvInputManager.RecordingError int err)957     public void notifyRecordingError(
958             @NonNull String recordingId, @TvInputManager.RecordingError int err) {
959         if (DEBUG) {
960             Log.d(TAG, "notifyRecordingError recordingId=" + recordingId
961                     + "; err=" + err);
962         }
963         if (mSession != null) {
964             mSession.notifyRecordingError(recordingId, err);
965         }
966     }
967 
968     /**
969      * This is called to notify the corresponding interactive app service when the recording has
970      * been scheduled.
971      *
972      * @param recordingId The ID assigned to this recording by the app. It can be used to send
973      *                    recording related requests such as
974      *                    {@link TvInteractiveAppService.Session#requestStopRecording(String)}.
975      * @param requestId The ID of the request when
976      *                  {@link TvInteractiveAppService.Session#requestScheduleRecording} is called.
977      *                  {@code null} if the recording is not triggered by a request.
978      *                  This ID should be created by the {@link TvInteractiveAppService} and
979      *                  can be any string.
980      */
notifyRecordingScheduled( @onNull String recordingId, @Nullable String requestId)981     public void notifyRecordingScheduled(
982             @NonNull String recordingId, @Nullable String requestId) {
983         if (DEBUG) {
984             Log.d(TAG, "notifyRecordingScheduled recordingId=" + recordingId
985                     + "; requestId=" + requestId);
986         }
987         if (mSession != null) {
988             mSession.notifyRecordingScheduled(recordingId, requestId);
989         }
990     }
991 
992     /**
993      * This is called to notify the corresponding interactive app service when a new TV message
994      * is received.
995      *
996      * @param type The type of message received, such as
997      * {@link TvInputManager#TV_MESSAGE_TYPE_WATERMARK}
998      * @param data The raw data of the message. The bundle keys are:
999      *             {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID},
1000      *             {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID},
1001      *             {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE},
1002      *             {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}.
1003      *             See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on
1004      *             how to parse this data.
1005      */
notifyTvMessage(@onNull @vInputManager.TvMessageType int type, @NonNull Bundle data)1006     public void notifyTvMessage(@NonNull @TvInputManager.TvMessageType int type,
1007             @NonNull Bundle data) {
1008         if (DEBUG) {
1009             Log.d(TAG, "notifyTvMessage type=" + type
1010                     + "; data=" + data);
1011         }
1012         if (mSession != null) {
1013             mSession.notifyTvMessage(type, data);
1014         }
1015     }
1016 
resetInternal()1017     private void resetInternal() {
1018         mSessionCallback = null;
1019         if (mSession != null) {
1020             setSessionSurface(null);
1021             removeSessionMediaView();
1022             mUseRequestedSurfaceLayout = false;
1023             mSession.release();
1024             mSession = null;
1025             resetSurfaceView();
1026         }
1027     }
1028 
1029     /**
1030      * Creates broadcast-independent(BI) interactive application.
1031      *
1032      * <p>{@link TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String)} will be
1033      * called for the result.
1034      *
1035      * @param biIAppUri URI associated this BI interactive app.
1036      * @param params optional parameters for broadcast-independent interactive application, such as
1037      *               {@link #BI_INTERACTIVE_APP_KEY_CERTIFICATE}.
1038      *
1039      * @see TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String)
1040      * @see #BI_INTERACTIVE_APP_KEY_CERTIFICATE
1041      * @see #BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS
1042      * @see #BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT
1043      */
createBiInteractiveApp(@onNull Uri biIAppUri, @Nullable Bundle params)1044     public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) {
1045         if (DEBUG) {
1046             Log.d(TAG, "createBiInteractiveApp Uri=" + biIAppUri + ", params=" + params);
1047         }
1048         if (mSession != null) {
1049             mSession.createBiInteractiveApp(biIAppUri, params);
1050         }
1051     }
1052 
1053     /**
1054      * Destroys broadcast-independent(BI) interactive application.
1055      *
1056      * @param biIAppId the BI interactive app ID from {@link #createBiInteractiveApp(Uri, Bundle)}
1057      *
1058      * @see #createBiInteractiveApp(Uri, Bundle)
1059      */
destroyBiInteractiveApp(@onNull String biIAppId)1060     public void destroyBiInteractiveApp(@NonNull String biIAppId) {
1061         if (DEBUG) {
1062             Log.d(TAG, "destroyBiInteractiveApp biIAppId=" + biIAppId);
1063         }
1064         if (mSession != null) {
1065             mSession.destroyBiInteractiveApp(biIAppId);
1066         }
1067     }
1068 
1069     /** @hide */
getInteractiveAppSession()1070     public Session getInteractiveAppSession() {
1071         return mSession;
1072     }
1073 
1074     /**
1075      * Sets the TvInteractiveAppView to receive events from TIS. This method links the session of
1076      * TvInteractiveAppManager to TvInputManager session, so the TIAS can get the TIS events.
1077      *
1078      * @param tvView the TvView to be linked to this TvInteractiveAppView via linking of Sessions.
1079      * @return The result of the operation.
1080      */
setTvView(@ullable TvView tvView)1081     public int setTvView(@Nullable TvView tvView) {
1082         if (tvView == null) {
1083             return unsetTvView();
1084         }
1085         TvInputManager.Session inputSession = tvView.getInputSession();
1086         if (inputSession == null || mSession == null) {
1087             return SET_TVVIEW_FAIL;
1088         }
1089         mSession.setInputSession(inputSession);
1090         inputSession.setInteractiveAppSession(mSession);
1091         return SET_TVVIEW_SUCCESS;
1092     }
1093 
unsetTvView()1094     private int unsetTvView() {
1095         if (mSession == null || mSession.getInputSession() == null) {
1096             return UNSET_TVVIEW_FAIL;
1097         }
1098         mSession.getInputSession().setInteractiveAppSession(null);
1099         mSession.setInputSession(null);
1100         return UNSET_TVVIEW_SUCCESS;
1101     }
1102 
1103     /**
1104      * To toggle Digital Teletext Application if there is one in AIT app list.
1105      *
1106      * <p>A Teletext Application is a broadcast-related application to display text and basic
1107      * graphics.
1108      *
1109      * @param enable {@code true} to enable Teletext app; {@code false} to disable it.
1110      */
setTeletextAppEnabled(boolean enable)1111     public void setTeletextAppEnabled(boolean enable) {
1112         if (DEBUG) {
1113             Log.d(TAG, "setTeletextAppEnabled enable=" + enable);
1114         }
1115         if (mSession != null) {
1116             mSession.setTeletextAppEnabled(enable);
1117         }
1118     }
1119 
1120     /**
1121      * Callback used to receive various status updates on the {@link TvInteractiveAppView}.
1122      */
1123     public abstract static class TvInteractiveAppCallback {
1124         // TODO: unhide the following public APIs
1125 
1126         /**
1127          * This is called when a playback command is requested to be processed by the related TV
1128          * input.
1129          *
1130          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1131          * @param cmdType type of the command
1132          * @param parameters parameters of the command
1133          */
onPlaybackCommandRequest( @onNull String iAppServiceId, @NonNull @TvInteractiveAppService.PlaybackCommandType String cmdType, @NonNull Bundle parameters)1134         public void onPlaybackCommandRequest(
1135                 @NonNull String iAppServiceId,
1136                 @NonNull @TvInteractiveAppService.PlaybackCommandType String cmdType,
1137                 @NonNull Bundle parameters) {
1138         }
1139 
1140         /**
1141          * This is called when a time shift command is requested to be processed by the related TV
1142          * input.
1143          *
1144          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1145          * @param cmdType type of the command
1146          * @param parameters parameters of the command
1147          */
onTimeShiftCommandRequest( @onNull String iAppServiceId, @NonNull @TvInteractiveAppService.TimeShiftCommandType String cmdType, @NonNull Bundle parameters)1148         public void onTimeShiftCommandRequest(
1149                 @NonNull String iAppServiceId,
1150                 @NonNull @TvInteractiveAppService.TimeShiftCommandType String cmdType,
1151                 @NonNull Bundle parameters) {
1152         }
1153 
1154         /**
1155          * This is called when the state of corresponding interactive app is changed.
1156          *
1157          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1158          * @param state the current state.
1159          * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE}
1160          *              is used when the state is not
1161          *              {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
1162          */
onStateChanged( @onNull String iAppServiceId, @TvInteractiveAppManager.InteractiveAppState int state, @TvInteractiveAppManager.ErrorCode int err)1163         public void onStateChanged(
1164                 @NonNull String iAppServiceId,
1165                 @TvInteractiveAppManager.InteractiveAppState int state,
1166                 @TvInteractiveAppManager.ErrorCode int err) {
1167         }
1168 
1169         /**
1170          * This is called when broadcast-independent (BI) interactive app is created.
1171          *
1172          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1173          * @param biIAppUri URI associated this BI interactive app. This is the same URI in
1174          *                  {@link #createBiInteractiveApp(Uri, Bundle)}
1175          * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
1176          *                 app. {@code null} if it's not created successfully.
1177          *
1178          * @see #createBiInteractiveApp(Uri, Bundle)
1179          * @see #destroyBiInteractiveApp(String)
1180          */
onBiInteractiveAppCreated(@onNull String iAppServiceId, @NonNull Uri biIAppUri, @Nullable String biIAppId)1181         public void onBiInteractiveAppCreated(@NonNull String iAppServiceId, @NonNull Uri biIAppUri,
1182                 @Nullable String biIAppId) {
1183         }
1184 
1185         /**
1186          * This is called when the digital teletext app state is changed.
1187          *
1188          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1189          * @param state digital teletext app current state.
1190          */
onTeletextAppStateChanged( @onNull String iAppServiceId, @TvInteractiveAppManager.TeletextAppState int state)1191         public void onTeletextAppStateChanged(
1192                 @NonNull String iAppServiceId,
1193                 @TvInteractiveAppManager.TeletextAppState int state) {
1194         }
1195 
1196         /**
1197          * This is called when {@link TvInteractiveAppService.Session#setVideoBounds(Rect)} is
1198          * called.
1199          *
1200          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1201          */
onSetVideoBounds(@onNull String iAppServiceId, @NonNull Rect rect)1202         public void onSetVideoBounds(@NonNull String iAppServiceId, @NonNull Rect rect) {
1203         }
1204 
1205         /**
1206          * This is called when {@link TvInteractiveAppService.Session#requestCurrentVideoBounds()}
1207          * is called.
1208          *
1209          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1210          */
onRequestCurrentVideoBounds(@onNull String iAppServiceId)1211         public void onRequestCurrentVideoBounds(@NonNull String iAppServiceId) {
1212         }
1213 
1214         /**
1215          * This is called when {@link TvInteractiveAppService.Session#requestCurrentChannelUri()} is
1216          * called.
1217          *
1218          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1219          */
onRequestCurrentChannelUri(@onNull String iAppServiceId)1220         public void onRequestCurrentChannelUri(@NonNull String iAppServiceId) {
1221         }
1222 
1223         /**
1224          * This is called when {@link TvInteractiveAppService.Session#requestCurrentChannelLcn()} is
1225          * called.
1226          *
1227          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1228          */
onRequestCurrentChannelLcn(@onNull String iAppServiceId)1229         public void onRequestCurrentChannelLcn(@NonNull String iAppServiceId) {
1230         }
1231 
1232         /**
1233          * This is called when {@link TvInteractiveAppService.Session#requestStreamVolume()} is
1234          * called.
1235          *
1236          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1237          */
onRequestStreamVolume(@onNull String iAppServiceId)1238         public void onRequestStreamVolume(@NonNull String iAppServiceId) {
1239         }
1240 
1241         /**
1242          * This is called when {@link TvInteractiveAppService.Session#requestTrackInfoList()} is
1243          * called.
1244          *
1245          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1246          */
onRequestTrackInfoList(@onNull String iAppServiceId)1247         public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
1248         }
1249 
1250         /**
1251          * This is called when {@link TvInteractiveAppService.Session#requestSelectedTrackInfo()} is
1252          * called.
1253          *
1254          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1255          */
1256         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
onRequestSelectedTrackInfo(@onNull String iAppServiceId)1257         public void onRequestSelectedTrackInfo(@NonNull String iAppServiceId) {
1258         }
1259 
1260         /**
1261          * This is called when {@link TvInteractiveAppService.Session#requestCurrentTvInputId()} is
1262          * called.
1263          *
1264          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1265          */
onRequestCurrentTvInputId(@onNull String iAppServiceId)1266         public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) {
1267         }
1268 
1269         /**
1270          * This is called when {@link TvInteractiveAppService.Session#requestTimeShiftMode()} is
1271          * called.
1272          *
1273          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1274          */
onRequestTimeShiftMode(@onNull String iAppServiceId)1275         public void onRequestTimeShiftMode(@NonNull String iAppServiceId) {
1276         }
1277 
1278         /**
1279          * This is called when {@link TvInteractiveAppService.Session#requestAvailableSpeeds()} is
1280          * called.
1281          *
1282          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1283          */
onRequestAvailableSpeeds(@onNull String iAppServiceId)1284         public void onRequestAvailableSpeeds(@NonNull String iAppServiceId) {
1285         }
1286 
1287         /**
1288          * This is called when
1289          * {@link TvInteractiveAppService.Session#requestStartRecording(String, Uri)} is called.
1290          *
1291          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1292          * @param requestId The ID of this request which is used to match the corresponding
1293          *                  response. The request ID in
1294          *                  {@link #notifyRecordingStarted(String, String)}  for this request is the
1295          *                  same as the ID sent here. This should be defined by the
1296          *                  TIAS and can be any string. Should this API be called with the
1297          *                  same requestId twice, both requests should be handled regardless
1298          *                  by the TV application.
1299          * @param programUri The URI of the program to record
1300          *
1301          */
onRequestStartRecording(@onNull String iAppServiceId, @NonNull String requestId, @Nullable Uri programUri)1302         public void onRequestStartRecording(@NonNull String iAppServiceId,
1303                 @NonNull String requestId, @Nullable Uri programUri) {
1304         }
1305 
1306         /**
1307          * This is called when {@link TvInteractiveAppService.Session#requestStopRecording(String)}
1308          * is called.
1309          *
1310          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1311          * @param recordingId The ID of the recording to stop. This is provided by the TV app in
1312          *                    {@link #notifyRecordingStarted(String, String)}
1313          * @see #notifyRecordingStarted(String, String)
1314          * @see #notifyRecordingStopped(String)
1315          */
onRequestStopRecording( @onNull String iAppServiceId, @NonNull String recordingId)1316         public void onRequestStopRecording(
1317                 @NonNull String iAppServiceId,
1318                 @NonNull String recordingId) {
1319         }
1320 
1321         /**
1322          * This is called when
1323          * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, String, Uri, Uri, Bundle)}
1324          * is called.
1325          *
1326          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1327          * @param requestId The ID of this request which is used to match the corresponding
1328          *                  response. The request ID in
1329          *                  {@link #notifyRecordingScheduled(String, String)} for this request is
1330          *                  the same as the ID sent here. This should be defined by the
1331          *                  TIAS and can be any string. Should this API be called with the
1332          *                  same requestId twice, both requests should be handled regardless
1333          *                  by the TV application.
1334          * @param inputId The ID of the TV input for the given channel.
1335          * @param channelUri The URI of a channel to be recorded.
1336          * @param programUri The URI of the TV program to be recorded.
1337          * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
1338          *            name, i.e. prefixed with a package name you own, so that different developers
1339          *            will not create conflicting keys.
1340          * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle)
1341          * @see android.media.tv.TvRecordingClient#startRecording(Uri)
1342          */
onRequestScheduleRecording(@onNull String iAppServiceId, @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, @NonNull Uri programUri, @NonNull Bundle params)1343         public void onRequestScheduleRecording(@NonNull String iAppServiceId,
1344                 @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri,
1345                 @NonNull Uri programUri, @NonNull Bundle params) {
1346         }
1347 
1348         /**
1349          * This is called when
1350          * {@link TvInteractiveAppService.Session#requestScheduleRecording(String, String, Uri, long, long, int, Bundle)}
1351          * is called.
1352          *
1353          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1354          * @param requestId The ID of this request which is used to match the corresponding
1355          *                  response. The request ID in
1356          *                  {@link #notifyRecordingScheduled(String, String)} for this request is
1357          *                  the same as the ID sent here. This should be defined by the
1358          *                  TIAS and can be any string. Should this API be called with the
1359          *                  same requestId twice, both requests should be handled regardless
1360          *                  by the TV application.
1361          * @param inputId The ID of the TV input for the given channel.
1362          * @param channelUri The URI of a channel to be recorded.
1363          * @param startTime The start time of the recording in milliseconds since epoch.
1364          * @param duration The duration of the recording in milliseconds.
1365          * @param repeatDays The repeated days. 0 if not repeated.
1366          * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
1367          *            name, i.e. prefixed with a package name you own, so that different developers
1368          *            will not create conflicting keys.
1369          * @see android.media.tv.TvRecordingClient#tune(String, Uri, Bundle)
1370          * @see android.media.tv.TvRecordingClient#startRecording(Uri)
1371          */
onRequestScheduleRecording(@onNull String iAppServiceId, @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration, int repeatDays, @NonNull Bundle params)1372         public void onRequestScheduleRecording(@NonNull String iAppServiceId,
1373                 @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri,
1374                 long startTime, long duration, int repeatDays, @NonNull Bundle params) {
1375         }
1376 
1377         /**
1378          * This is called when
1379          * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is
1380          * called.
1381          *
1382          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1383          * @param signingId the ID to identify the request.
1384          * @param algorithm the standard name of the signature algorithm requested, such as
1385          *                  MD5withRSA, SHA256withDSA, etc.
1386          * @param alias the alias of the corresponding {@link java.security.KeyStore}.
1387          * @param data the original bytes to be signed.
1388          */
onRequestSigning(@onNull String iAppServiceId, @NonNull String signingId, @NonNull String algorithm, @NonNull String alias, @NonNull byte[] data)1389         public void onRequestSigning(@NonNull String iAppServiceId, @NonNull String signingId,
1390                 @NonNull String algorithm, @NonNull String alias, @NonNull byte[] data) {
1391         }
1392 
1393         /**
1394          * This is called when
1395          * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, int, byte[])}
1396          * is called.
1397          *
1398          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1399          * @param signingId the ID to identify the request.
1400          * @param algorithm the standard name of the signature algorithm requested, such as
1401          *                  MD5withRSA, SHA256withDSA, etc.
1402          * @param host The hostname of the SSL authentication server.
1403          * @param port The port of the SSL authentication server.
1404          * @param data the original bytes to be signed.
1405          */
1406         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
onRequestSigning(@onNull String iAppServiceId, @NonNull String signingId, @NonNull String algorithm, @NonNull String host, int port, @NonNull byte[] data)1407         public void onRequestSigning(@NonNull String iAppServiceId, @NonNull String signingId,
1408                 @NonNull String algorithm, @NonNull String host, int port, @NonNull byte[] data) {
1409         }
1410 
1411         /**
1412          * This is called when
1413          * {@link TvInteractiveAppService.Session#requestCertificate(String, int)} is called.
1414          *
1415          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1416          * @param host The hostname of the SSL authentication server.
1417          * @param port The port of the SSL authentication server.
1418          */
1419         @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
onRequestCertificate(@onNull String iAppServiceId, @NonNull String host, int port)1420         public void onRequestCertificate(@NonNull String iAppServiceId, @NonNull String host,
1421                 int port) {
1422         }
1423 
1424         /**
1425          * This is called when {@link TvInteractiveAppService.Session#setTvRecordingInfo(String,
1426          * TvRecordingInfo)} is called.
1427          *
1428          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1429          * @param recordingId The ID of the recording to set the info for. This is provided by the
1430          *     TV app in {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
1431          * @param recordingInfo The {@link TvRecordingInfo} to set to the recording.
1432          */
onSetTvRecordingInfo( @onNull String iAppServiceId, @NonNull String recordingId, @NonNull TvRecordingInfo recordingInfo)1433         public void onSetTvRecordingInfo(
1434                 @NonNull String iAppServiceId,
1435                 @NonNull String recordingId,
1436                 @NonNull TvRecordingInfo recordingInfo) {
1437         }
1438 
1439         /**
1440          * This is called when
1441          * {@link TvInteractiveAppService.Session#requestTvRecordingInfo(String)} is
1442          * called.
1443          *
1444          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1445          * @param recordingId The ID of the recording to get the info for. This is provided by the
1446          *                    TV app in
1447          *                    {@link TvInteractiveAppView#notifyRecordingStarted(String, String)}
1448          */
onRequestTvRecordingInfo( @onNull String iAppServiceId, @NonNull String recordingId)1449         public void onRequestTvRecordingInfo(
1450                 @NonNull String iAppServiceId,
1451                 @NonNull String recordingId) {
1452         }
1453 
1454         /**
1455          * This is called when
1456          * {@link TvInteractiveAppService.Session#requestTvRecordingInfoList(int)} is
1457          * called.
1458          *
1459          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
1460          * @param type The type of recording requested to retrieve.
1461          */
onRequestTvRecordingInfoList( @onNull String iAppServiceId, @TvRecordingInfo.TvRecordingListType int type)1462         public void onRequestTvRecordingInfoList(
1463                 @NonNull String iAppServiceId,
1464                 @TvRecordingInfo.TvRecordingListType int type) {
1465         }
1466     }
1467 
1468     /**
1469      * Interface definition for a callback to be invoked when the unhandled input event is received.
1470      */
1471     public interface OnUnhandledInputEventListener {
1472         /**
1473          * Called when an input event was not handled by the TV Interactive App.
1474          *
1475          * <p>This is called asynchronously from where the event is dispatched. It gives the host
1476          * application a chance to handle the unhandled input events.
1477          *
1478          * @param event The input event.
1479          * @return If you handled the event, return {@code true}. If you want to allow the event to
1480          *         be handled by the next receiver, return {@code false}.
1481          */
onUnhandledInputEvent(@onNull InputEvent event)1482         boolean onUnhandledInputEvent(@NonNull InputEvent event);
1483     }
1484 
1485     private class MySessionCallback extends SessionCallback {
1486         final String mIAppServiceId;
1487         int mType;
1488 
MySessionCallback(String iAppServiceId, int type)1489         MySessionCallback(String iAppServiceId, int type) {
1490             mIAppServiceId = iAppServiceId;
1491             mType = type;
1492         }
1493 
1494         @Override
onSessionCreated(Session session)1495         public void onSessionCreated(Session session) {
1496             if (DEBUG) {
1497                 Log.d(TAG, "onSessionCreated()");
1498             }
1499             if (this != mSessionCallback) {
1500                 Log.w(TAG, "onSessionCreated - session already created");
1501                 // This callback is obsolete.
1502                 if (session != null) {
1503                     session.release();
1504                 }
1505                 return;
1506             }
1507             mSession = session;
1508             if (session != null) {
1509                 // mSurface may not be ready yet as soon as starting an application.
1510                 // In the case, we don't send Session.setSurface(null) unnecessarily.
1511                 // setSessionSurface will be called in surfaceCreated.
1512                 if (mSurface != null) {
1513                     setSessionSurface(mSurface);
1514                     if (mSurfaceChanged) {
1515                         dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
1516                     }
1517                 }
1518                 createSessionMediaView();
1519             } else {
1520                 // Failed to create
1521                 // Todo: forward error to Tv App
1522                 mSessionCallback = null;
1523             }
1524         }
1525 
1526         @Override
onSessionReleased(Session session)1527         public void onSessionReleased(Session session) {
1528             if (DEBUG) {
1529                 Log.d(TAG, "onSessionReleased()");
1530             }
1531             if (this != mSessionCallback) {
1532                 Log.w(TAG, "onSessionReleased - session not created");
1533                 return;
1534             }
1535             mMediaViewCreated = false;
1536             mMediaViewFrame = null;
1537             mSessionCallback = null;
1538             mSession = null;
1539         }
1540 
1541         @Override
onLayoutSurface(Session session, int left, int top, int right, int bottom)1542         public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
1543             if (DEBUG) {
1544                 Log.d(TAG, "onLayoutSurface (left=" + left + ", top=" + top + ", right="
1545                         + right + ", bottom=" + bottom + ",)");
1546             }
1547             if (this != mSessionCallback) {
1548                 Log.w(TAG, "onLayoutSurface - session not created");
1549                 return;
1550             }
1551             mSurfaceViewLeft = left;
1552             mSurfaceViewTop = top;
1553             mSurfaceViewRight = right;
1554             mSurfaceViewBottom = bottom;
1555             mUseRequestedSurfaceLayout = true;
1556             requestLayout();
1557         }
1558 
1559         @Override
onCommandRequest( Session session, @TvInteractiveAppService.PlaybackCommandType String cmdType, Bundle parameters)1560         public void onCommandRequest(
1561                 Session session,
1562                 @TvInteractiveAppService.PlaybackCommandType String cmdType,
1563                 Bundle parameters) {
1564             if (DEBUG) {
1565                 Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
1566                         + parameters.toString() + ")");
1567             }
1568             if (this != mSessionCallback) {
1569                 Log.w(TAG, "onCommandRequest - session not created");
1570                 return;
1571             }
1572             synchronized (mCallbackLock) {
1573                 if (mCallbackExecutor != null) {
1574                     mCallbackExecutor.execute(() -> {
1575                         synchronized (mCallbackLock) {
1576                             if (mCallback != null) {
1577                                 mCallback.onPlaybackCommandRequest(
1578                                         mIAppServiceId, cmdType, parameters);
1579                             }
1580                         }
1581                     });
1582                 }
1583             }
1584         }
1585 
1586         @Override
onTimeShiftCommandRequest( Session session, @TvInteractiveAppService.TimeShiftCommandType String cmdType, Bundle parameters)1587         public void onTimeShiftCommandRequest(
1588                 Session session,
1589                 @TvInteractiveAppService.TimeShiftCommandType String cmdType,
1590                 Bundle parameters) {
1591             if (DEBUG) {
1592                 Log.d(TAG, "onTimeShiftCommandRequest (cmdType=" + cmdType + ", parameters="
1593                         + parameters.toString() + ")");
1594             }
1595             if (this != mSessionCallback) {
1596                 Log.w(TAG, "onTimeShiftCommandRequest - session not created");
1597                 return;
1598             }
1599             synchronized (mCallbackLock) {
1600                 if (mCallbackExecutor != null) {
1601                     mCallbackExecutor.execute(() -> {
1602                         synchronized (mCallbackLock) {
1603                             if (mCallback != null) {
1604                                 mCallback.onTimeShiftCommandRequest(
1605                                         mIAppServiceId, cmdType, parameters);
1606                             }
1607                         }
1608                     });
1609                 }
1610             }
1611         }
1612 
1613         @Override
onSessionStateChanged( Session session, @TvInteractiveAppManager.InteractiveAppState int state, @TvInteractiveAppManager.ErrorCode int err)1614         public void onSessionStateChanged(
1615                 Session session,
1616                 @TvInteractiveAppManager.InteractiveAppState int state,
1617                 @TvInteractiveAppManager.ErrorCode int err) {
1618             if (DEBUG) {
1619                 Log.d(TAG, "onSessionStateChanged (state=" + state + "; err=" + err + ")");
1620             }
1621             if (this != mSessionCallback) {
1622                 Log.w(TAG, "onSessionStateChanged - session not created");
1623                 return;
1624             }
1625             synchronized (mCallbackLock) {
1626                 if (mCallbackExecutor != null) {
1627                     mCallbackExecutor.execute(() -> {
1628                         synchronized (mCallbackLock) {
1629                             if (mCallback != null) {
1630                                 mCallback.onStateChanged(mIAppServiceId, state, err);
1631                             }
1632                         }
1633                     });
1634                 }
1635             }
1636         }
1637 
1638         @Override
onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId)1639         public void onBiInteractiveAppCreated(Session session, Uri biIAppUri, String biIAppId) {
1640             if (DEBUG) {
1641                 Log.d(TAG, "onBiInteractiveAppCreated (biIAppUri=" + biIAppUri + ", biIAppId="
1642                         + biIAppId + ")");
1643             }
1644             if (this != mSessionCallback) {
1645                 Log.w(TAG, "onBiInteractiveAppCreated - session not created");
1646                 return;
1647             }
1648             synchronized (mCallbackLock) {
1649                 if (mCallbackExecutor != null) {
1650                     mCallbackExecutor.execute(() -> {
1651                         synchronized (mCallbackLock) {
1652                             if (mCallback != null) {
1653                                 mCallback.onBiInteractiveAppCreated(
1654                                         mIAppServiceId, biIAppUri, biIAppId);
1655                             }
1656                         }
1657                     });
1658                 }
1659             }
1660         }
1661 
1662         @Override
onTeletextAppStateChanged(Session session, int state)1663         public void onTeletextAppStateChanged(Session session, int state) {
1664             if (DEBUG) {
1665                 Log.d(TAG, "onTeletextAppStateChanged (state=" + state +  ")");
1666             }
1667             if (this != mSessionCallback) {
1668                 Log.w(TAG, "onTeletextAppStateChanged - session not created");
1669                 return;
1670             }
1671             if (mCallback != null) {
1672                 mCallback.onTeletextAppStateChanged(mIAppServiceId, state);
1673             }
1674         }
1675 
1676         @Override
onSetVideoBounds(Session session, Rect rect)1677         public void onSetVideoBounds(Session session, Rect rect) {
1678             if (DEBUG) {
1679                 Log.d(TAG, "onSetVideoBounds (rect=" + rect + ")");
1680             }
1681             if (this != mSessionCallback) {
1682                 Log.w(TAG, "onSetVideoBounds - session not created");
1683                 return;
1684             }
1685             synchronized (mCallbackLock) {
1686                 if (mCallbackExecutor != null) {
1687                     mCallbackExecutor.execute(() -> {
1688                         synchronized (mCallbackLock) {
1689                             if (mCallback != null) {
1690                                 mCallback.onSetVideoBounds(mIAppServiceId, rect);
1691                             }
1692                         }
1693                     });
1694                 }
1695             }
1696         }
1697 
1698         @Override
onRequestCurrentVideoBounds(Session session)1699         public void onRequestCurrentVideoBounds(Session session) {
1700             if (DEBUG) {
1701                 Log.d(TAG, "onRequestCurrentVideoBounds");
1702             }
1703             if (this != mSessionCallback) {
1704                 Log.w(TAG, "onRequestCurrentVideoBounds - session not created");
1705                 return;
1706             }
1707             synchronized (mCallbackLock) {
1708                 if (mCallbackExecutor != null) {
1709                     mCallbackExecutor.execute(() -> {
1710                         synchronized (mCallbackLock) {
1711                             if (mCallback != null) {
1712                                 mCallback.onRequestCurrentVideoBounds(mIAppServiceId);
1713                             }
1714                         }
1715                     });
1716                 }
1717             }
1718         }
1719 
1720         @Override
onRequestCurrentChannelUri(Session session)1721         public void onRequestCurrentChannelUri(Session session) {
1722             if (DEBUG) {
1723                 Log.d(TAG, "onRequestCurrentChannelUri");
1724             }
1725             if (this != mSessionCallback) {
1726                 Log.w(TAG, "onRequestCurrentChannelUri - session not created");
1727                 return;
1728             }
1729             synchronized (mCallbackLock) {
1730                 if (mCallbackExecutor != null) {
1731                     mCallbackExecutor.execute(() -> {
1732                         synchronized (mCallbackLock) {
1733                             if (mCallback != null) {
1734                                 mCallback.onRequestCurrentChannelUri(mIAppServiceId);
1735                             }
1736                         }
1737                     });
1738                 }
1739             }
1740         }
1741 
1742         @Override
onRequestCurrentChannelLcn(Session session)1743         public void onRequestCurrentChannelLcn(Session session) {
1744             if (DEBUG) {
1745                 Log.d(TAG, "onRequestCurrentChannelLcn");
1746             }
1747             if (this != mSessionCallback) {
1748                 Log.w(TAG, "onRequestCurrentChannelLcn - session not created");
1749                 return;
1750             }
1751             synchronized (mCallbackLock) {
1752                 if (mCallbackExecutor != null) {
1753                     mCallbackExecutor.execute(() -> {
1754                         synchronized (mCallbackLock) {
1755                             if (mCallback != null) {
1756                                 mCallback.onRequestCurrentChannelLcn(mIAppServiceId);
1757                             }
1758                         }
1759                     });
1760                 }
1761             }
1762         }
1763 
1764         @Override
onRequestStreamVolume(Session session)1765         public void onRequestStreamVolume(Session session) {
1766             if (DEBUG) {
1767                 Log.d(TAG, "onRequestStreamVolume");
1768             }
1769             if (this != mSessionCallback) {
1770                 Log.w(TAG, "onRequestStreamVolume - session not created");
1771                 return;
1772             }
1773             synchronized (mCallbackLock) {
1774                 if (mCallbackExecutor != null) {
1775                     mCallbackExecutor.execute(() -> {
1776                         synchronized (mCallbackLock) {
1777                             if (mCallback != null) {
1778                                 mCallback.onRequestStreamVolume(mIAppServiceId);
1779                             }
1780                         }
1781                     });
1782                 }
1783             }
1784         }
1785 
1786         @Override
onRequestTrackInfoList(Session session)1787         public void onRequestTrackInfoList(Session session) {
1788             if (DEBUG) {
1789                 Log.d(TAG, "onRequestTrackInfoList");
1790             }
1791             if (this != mSessionCallback) {
1792                 Log.w(TAG, "onRequestTrackInfoList - session not created");
1793                 return;
1794             }
1795             synchronized (mCallbackLock) {
1796                 if (mCallbackExecutor != null) {
1797                     mCallbackExecutor.execute(() -> {
1798                         synchronized (mCallbackLock) {
1799                             if (mCallback != null) {
1800                                 mCallback.onRequestTrackInfoList(mIAppServiceId);
1801                             }
1802                         }
1803                     });
1804                 }
1805             }
1806         }
1807 
1808         @Override
onRequestSelectedTrackInfo(Session session)1809         public void onRequestSelectedTrackInfo(Session session) {
1810             if (DEBUG) {
1811                 Log.d(TAG, "onRequestSelectedTrackInfo");
1812             }
1813             if (this != mSessionCallback) {
1814                 Log.w(TAG, "onRequestSelectedTrackInfo - session not created");
1815                 return;
1816             }
1817             synchronized (mCallbackLock) {
1818                 if (mCallbackExecutor != null) {
1819                     mCallbackExecutor.execute(() -> {
1820                         synchronized (mCallbackLock) {
1821                             if (mCallback != null) {
1822                                 mCallback.onRequestSelectedTrackInfo(mIAppServiceId);
1823                             }
1824                         }
1825                     });
1826                 }
1827             }
1828         }
1829 
1830         @Override
onRequestCurrentTvInputId(Session session)1831         public void onRequestCurrentTvInputId(Session session) {
1832             if (DEBUG) {
1833                 Log.d(TAG, "onRequestCurrentTvInputId");
1834             }
1835             if (this != mSessionCallback) {
1836                 Log.w(TAG, "onRequestCurrentTvInputId - session not created");
1837                 return;
1838             }
1839             if (mCallback != null) {
1840                 mCallback.onRequestCurrentTvInputId(mIAppServiceId);
1841             }
1842         }
1843 
1844         @Override
onRequestTimeShiftMode(Session session)1845         public void onRequestTimeShiftMode(Session session) {
1846             if (DEBUG) {
1847                 Log.d(TAG, "onRequestTimeShiftMode");
1848             }
1849             if (this != mSessionCallback) {
1850                 Log.w(TAG, "onRequestTimeShiftMode - session not created");
1851                 return;
1852             }
1853             if (mCallback != null) {
1854                 mCallback.onRequestTimeShiftMode(mIAppServiceId);
1855             }
1856         }
1857 
1858         @Override
onRequestAvailableSpeeds(Session session)1859         public void onRequestAvailableSpeeds(Session session) {
1860             if (DEBUG) {
1861                 Log.d(TAG, "onRequestAvailableSpeeds");
1862             }
1863             if (this != mSessionCallback) {
1864                 Log.w(TAG, "onRequestAvailableSpeeds - session not created");
1865                 return;
1866             }
1867             if (mCallback != null) {
1868                 mCallback.onRequestAvailableSpeeds(mIAppServiceId);
1869             }
1870         }
1871 
1872         @Override
onRequestStartRecording(Session session, String requestId, Uri programUri)1873         public void onRequestStartRecording(Session session, String requestId, Uri programUri) {
1874             if (DEBUG) {
1875                 Log.d(TAG, "onRequestStartRecording");
1876             }
1877             if (this != mSessionCallback) {
1878                 Log.w(TAG, "onRequestStartRecording - session not created");
1879                 return;
1880             }
1881             if (mCallback != null) {
1882                 mCallback.onRequestStartRecording(mIAppServiceId, requestId, programUri);
1883             }
1884         }
1885 
1886         @Override
onRequestStopRecording(Session session, String recordingId)1887         public void onRequestStopRecording(Session session, String recordingId) {
1888             if (DEBUG) {
1889                 Log.d(TAG, "onRequestStopRecording");
1890             }
1891             if (this != mSessionCallback) {
1892                 Log.w(TAG, "onRequestStopRecording - session not created");
1893                 return;
1894             }
1895             if (mCallback != null) {
1896                 mCallback.onRequestStopRecording(mIAppServiceId, recordingId);
1897             }
1898         }
1899 
1900         @Override
onSetTvRecordingInfo( Session session, String recordingId, TvRecordingInfo recordingInfo)1901         public void onSetTvRecordingInfo(
1902                 Session session, String recordingId, TvRecordingInfo recordingInfo) {
1903             if (DEBUG) {
1904                 Log.d(TAG, "onSetRecordingInfo");
1905             }
1906             if (this != mSessionCallback) {
1907                 Log.w(TAG, "onSetRecordingInfo - session not created");
1908                 return;
1909             }
1910             if (mCallback != null) {
1911                 mCallback.onSetTvRecordingInfo(mIAppServiceId, recordingId, recordingInfo);
1912             }
1913         }
1914 
1915         @Override
onRequestScheduleRecording(Session session, @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, Uri programUri, @NonNull Bundle params)1916         public void onRequestScheduleRecording(Session session, @NonNull String requestId,
1917                 @NonNull String inputId, @NonNull Uri channelUri, Uri programUri,
1918                 @NonNull Bundle params) {
1919             if (DEBUG) {
1920                 Log.d(TAG, "onRequestScheduleRecording");
1921             }
1922             if (this != mSessionCallback) {
1923                 Log.w(TAG, "onRequestScheduleRecording - session not created");
1924                 return;
1925             }
1926             if (mCallback != null) {
1927                 mCallback.onRequestScheduleRecording(mIAppServiceId, requestId, inputId, channelUri,
1928                         programUri, params);
1929             }
1930         }
1931 
onRequestScheduleRecording(Session session, @NonNull String requestId, @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration, int repeatDays, @NonNull Bundle params)1932         public void onRequestScheduleRecording(Session session, @NonNull String requestId,
1933                 @NonNull String inputId, @NonNull Uri channelUri, long startTime, long duration,
1934                 int repeatDays, @NonNull Bundle params) {
1935             if (DEBUG) {
1936                 Log.d(TAG, "onRequestScheduleRecording");
1937             }
1938             if (this != mSessionCallback) {
1939                 Log.w(TAG, "onRequestScheduleRecording - session not created");
1940                 return;
1941             }
1942             if (mCallback != null) {
1943                 mCallback.onRequestScheduleRecording(mIAppServiceId, requestId, inputId, channelUri,
1944                         startTime, duration, repeatDays, params);
1945             }
1946         }
1947 
1948         @Override
onRequestTvRecordingInfo(Session session, String recordingId)1949         public void onRequestTvRecordingInfo(Session session,
1950                 String recordingId) {
1951             if (DEBUG) {
1952                 Log.d(TAG, "onRequestRecordingInfo");
1953             }
1954             if (this != mSessionCallback) {
1955                 Log.w(TAG, "onRequestRecordingInfo - session not created");
1956                 return;
1957             }
1958             if (mCallback != null) {
1959                 mCallback.onRequestTvRecordingInfo(mIAppServiceId, recordingId);
1960             }
1961         }
1962 
1963         @Override
onRequestTvRecordingInfoList(Session session, int type)1964         public void onRequestTvRecordingInfoList(Session session,
1965                 int type) {
1966             if (DEBUG) {
1967                 Log.d(TAG, "onRequestRecordingInfoList");
1968             }
1969             if (this != mSessionCallback) {
1970                 Log.w(TAG, "onRequestRecordingInfoList - session not created");
1971                 return;
1972             }
1973             if (mCallback != null) {
1974                 mCallback.onRequestTvRecordingInfoList(mIAppServiceId, type);
1975             }
1976         }
1977 
1978         @Override
onRequestSigning( Session session, String id, String algorithm, String alias, byte[] data)1979         public void onRequestSigning(
1980                 Session session, String id, String algorithm, String alias, byte[] data) {
1981             if (DEBUG) {
1982                 Log.d(TAG, "onRequestSigning");
1983             }
1984             if (this != mSessionCallback) {
1985                 Log.w(TAG, "onRequestSigning - session not created");
1986                 return;
1987             }
1988             if (mCallback != null) {
1989                 mCallback.onRequestSigning(mIAppServiceId, id, algorithm, alias, data);
1990             }
1991         }
1992 
1993         @Override
onRequestSigning( Session session, String id, String algorithm, String host, int port, byte[] data)1994         public void onRequestSigning(
1995                 Session session, String id, String algorithm, String host, int port, byte[] data) {
1996             if (DEBUG) {
1997                 Log.d(TAG, "onRequestSigning");
1998             }
1999             if (this != mSessionCallback) {
2000                 Log.w(TAG, "onRequestSigning - session not created");
2001                 return;
2002             }
2003             if (mCallback != null && Flags.tiafVApis()) {
2004                 mCallback.onRequestSigning(mIAppServiceId, id, algorithm, host, port, data);
2005             }
2006         }
2007 
2008         @Override
onRequestCertificate(Session session, String host, int port)2009         public void onRequestCertificate(Session session, String host, int port) {
2010             if (DEBUG) {
2011                 Log.d(TAG, "onRequestCertificate");
2012             }
2013             if (this != mSessionCallback) {
2014                 Log.w(TAG, "onRequestCertificate - session not created");
2015                 return;
2016             }
2017             if (mCallback != null && Flags.tiafVApis()) {
2018                 mCallback.onRequestCertificate(mIAppServiceId, host, port);
2019             }
2020         }
2021     }
2022 }
2023