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