• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.tv;
18 
19 import android.annotation.CallbackExecutor;
20 import android.annotation.FlaggedApi;
21 import android.annotation.IntDef;
22 import android.annotation.IntRange;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.StringDef;
27 import android.annotation.SystemApi;
28 import android.annotation.SystemService;
29 import android.annotation.TestApi;
30 import android.annotation.UserIdInt;
31 import android.content.AttributionSource;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.graphics.Rect;
35 import android.media.AudioDeviceInfo;
36 import android.media.AudioFormat.Encoding;
37 import android.media.AudioPresentation;
38 import android.media.PlaybackParams;
39 import android.media.tv.ad.TvAdManager;
40 import android.media.tv.flags.Flags;
41 import android.media.tv.interactive.TvInteractiveAppManager;
42 import android.net.Uri;
43 import android.os.Binder;
44 import android.os.Bundle;
45 import android.os.Handler;
46 import android.os.IBinder;
47 import android.os.Looper;
48 import android.os.Message;
49 import android.os.ParcelFileDescriptor;
50 import android.os.RemoteException;
51 import android.text.TextUtils;
52 import android.util.ArrayMap;
53 import android.util.Log;
54 import android.util.Pools.Pool;
55 import android.util.Pools.SimplePool;
56 import android.util.SparseArray;
57 import android.view.InputChannel;
58 import android.view.InputEvent;
59 import android.view.InputEventSender;
60 import android.view.KeyEvent;
61 import android.view.Surface;
62 import android.view.View;
63 
64 import com.android.internal.util.Preconditions;
65 
66 import java.lang.annotation.Retention;
67 import java.lang.annotation.RetentionPolicy;
68 import java.util.ArrayList;
69 import java.util.Iterator;
70 import java.util.List;
71 import java.util.Map;
72 import java.util.Objects;
73 import java.util.concurrent.Executor;
74 
75 /**
76  * Central system API to the overall TV input framework (TIF) architecture, which arbitrates
77  * interaction between applications and the selected TV inputs.
78  *
79  * <p>There are three primary parties involved in the TV input framework (TIF) architecture:
80  *
81  * <ul>
82  * <li>The <strong>TV input manager</strong> as expressed by this class is the central point of the
83  * system that manages interaction between all other parts. It is expressed as the client-side API
84  * here which exists in each application context and communicates with a global system service that
85  * manages the interaction across all processes.
86  * <li>A <strong>TV input</strong> implemented by {@link TvInputService} represents an input source
87  * of TV, which can be a pass-through input such as HDMI, or a tuner input which provides broadcast
88  * TV programs. The system binds to the TV input per application’s request.
89  * on implementing TV inputs.
90  * <li><strong>Applications</strong> talk to the TV input manager to list TV inputs and check their
91  * status. Once an application find the input to use, it uses {@link TvView} or
92  * {@link TvRecordingClient} for further interaction such as watching and recording broadcast TV
93  * programs.
94  * </ul>
95  */
96 @SystemService(Context.TV_INPUT_SERVICE)
97 public final class TvInputManager {
98     private static final String TAG = "TvInputManager";
99 
100     static final int DVB_DEVICE_START = 0;
101     static final int DVB_DEVICE_END = 2;
102 
103     /**
104      * A demux device of DVB API for controlling the filters of DVB hardware/software.
105      * @hide
106      */
107     public static final int DVB_DEVICE_DEMUX = DVB_DEVICE_START;
108      /**
109      * A DVR device of DVB API for reading transport streams.
110      * @hide
111      */
112     public static final int DVB_DEVICE_DVR = 1;
113     /**
114      * A frontend device of DVB API for controlling the tuner and DVB demodulator hardware.
115      * @hide
116      */
117     public static final int DVB_DEVICE_FRONTEND = DVB_DEVICE_END;
118 
119     /** @hide */
120     @Retention(RetentionPolicy.SOURCE)
121     @IntDef({DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_FRONTEND})
122     public @interface DvbDeviceType {}
123 
124 
125     /** @hide */
126     @Retention(RetentionPolicy.SOURCE)
127     @IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING,
128             VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING,
129             VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY, VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED,
130             VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE,
131             VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION,
132             VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED,
133             VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE, VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED,
134             VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION, VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING,
135             VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD, VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE,
136             VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID, VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT,
137             VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN,
138             VIDEO_UNAVAILABLE_REASON_STOPPED})
139     public @interface VideoUnavailableReason {}
140 
141     /** Indicates that this TV message contains watermarking data */
142     public static final int TV_MESSAGE_TYPE_WATERMARK = 1;
143 
144     /** Indicates that this TV message contains Closed Captioning data */
145     public static final int TV_MESSAGE_TYPE_CLOSED_CAPTION = 2;
146 
147     /** Indicates that this TV message contains other data */
148     public static final int TV_MESSAGE_TYPE_OTHER = 1000;
149 
150     /** @hide */
151     @Retention(RetentionPolicy.SOURCE)
152     @IntDef({TV_MESSAGE_TYPE_WATERMARK, TV_MESSAGE_TYPE_CLOSED_CAPTION, TV_MESSAGE_TYPE_OTHER})
153     public @interface TvMessageType {}
154 
155     /**
156      * This constant is used as a {@link Bundle} key for TV messages. The value of the key
157      * identifies the stream on the TV input source for which the watermark event is relevant to.
158      *
159      * <p> Type: String
160      */
161     public static final String TV_MESSAGE_KEY_STREAM_ID =
162             "android.media.tv.TvInputManager.stream_id";
163 
164     /**
165      * This value for {@link #TV_MESSAGE_KEY_GROUP_ID} denotes that the message doesn't
166      * belong to any group.
167      */
168     public static final long TV_MESSAGE_GROUP_ID_NONE = -1;
169 
170     /**
171      * This constant is used as a {@link Bundle} key for TV messages. This is used to
172      * optionally identify messages that belong together, such as headers and bodies
173      * of the same event. For messages that do not have a group, this value
174      * should be {@link #TV_MESSAGE_GROUP_ID_NONE}.
175      *
176      * <p> As -1 is a reserved value, -1 should not be used as a valid groupId.
177      *
178      * <p> Type: long
179      */
180     public static final String TV_MESSAGE_KEY_GROUP_ID =
181             "android.media.tv.TvInputManager.group_id";
182 
183     /**
184      * This is a subtype for TV messages that can be potentially found as a value
185      * at {@link #TV_MESSAGE_KEY_SUBTYPE}. It identifies the subtype of the message
186      * as the watermarking format ATSC A/335.
187      */
188     public static final String TV_MESSAGE_SUBTYPE_WATERMARKING_A335 = "ATSC A/335";
189 
190     /**
191      * This is a subtype for TV messages that can be potentially found as a value
192      * at {@link #TV_MESSAGE_KEY_SUBTYPE}. It identifies the subtype of the message
193      * as the CC format CTA 608-E.
194      */
195     public static final String TV_MESSAGE_SUBTYPE_CC_608E = "CTA 608-E";
196 
197     /**
198      * This constant is used as a {@link Bundle} key for TV messages. The value of the key
199      * identifies the subtype of the data, such as the format of the CC data. The format
200      * found at this key can then be used to identify how to parse the data at
201      * {@link #TV_MESSAGE_KEY_RAW_DATA}.
202      *
203      * <p> To parse the raw data based on the subtype, please refer to the official
204      * documentation of the concerning subtype. For example, for the subtype
205      * {@link #TV_MESSAGE_SUBTYPE_WATERMARKING_A335}, the document for A/335 from the ATSC
206      * standard details how this data is formatted. Similarly, the subtype
207      * {@link #TV_MESSAGE_SUBTYPE_CC_608E} is documented in the ANSI/CTA standard for
208      * 608-E. These subtypes are examples of common formats for their respective uses
209      * and other subtypes may exist.
210      *
211      * <p> Type: String
212      */
213     public static final String TV_MESSAGE_KEY_SUBTYPE =
214             "android.media.tv.TvInputManager.subtype";
215 
216     /**
217      * This constant is used as a {@link Bundle} key for TV messages. The value of the key
218      * stores the raw data contained in this TV message. The format of this data is determined
219      * by the format defined by the subtype, found using the key at
220      * {@link #TV_MESSAGE_KEY_SUBTYPE}. See {@link #TV_MESSAGE_KEY_SUBTYPE} for more
221      * information on how to parse this data.
222      *
223      * <p> Type: byte[]
224      */
225     public static final String TV_MESSAGE_KEY_RAW_DATA =
226             "android.media.tv.TvInputManager.raw_data";
227 
228     static final int VIDEO_UNAVAILABLE_REASON_START = 0;
229     static final int VIDEO_UNAVAILABLE_REASON_END = 18;
230 
231     /**
232      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
233      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable due to
234      * an unspecified error.
235      */
236     public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = VIDEO_UNAVAILABLE_REASON_START;
237     /**
238      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
239      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
240      * the corresponding TV input is in the middle of tuning to a new channel.
241      */
242     public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1;
243     /**
244      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
245      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable due to
246      * weak TV signal.
247      */
248     public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2;
249     /**
250      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
251      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
252      * the corresponding TV input has stopped playback temporarily to buffer more data.
253      */
254     public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3;
255     /**
256      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
257      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
258      * the current TV program is audio-only.
259      */
260     public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4;
261     /**
262      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
263      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
264      * the source is not physically connected, for example the HDMI cable is not connected.
265      */
266     public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5;
267     /**
268      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
269      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
270      * the resource is not enough to meet requirement.
271      */
272     public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6;
273     /**
274      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
275      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
276      * the output protection level enabled on the device is not sufficient to meet the requirements
277      * in the license policy.
278      */
279     public static final int VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION = 7;
280     /**
281      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
282      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
283      * the PVR record is not allowed by the license policy.
284      */
285     public static final int VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED = 8;
286     /**
287      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
288      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
289      * no license keys have been provided.
290      * @hide
291      */
292     public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE = 9;
293     /**
294      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
295      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
296      * Using a license in whhich the keys have expired.
297      */
298     public static final int VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED = 10;
299     /**
300      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
301      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
302      * the device need be activated.
303      */
304     public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION = 11;
305     /**
306      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
307      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
308      * the device need be paired.
309      */
310     public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING = 12;
311     /**
312      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
313      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
314      * smart card is missed.
315      */
316     public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD = 13;
317     /**
318      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
319      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
320      * smart card is muted.
321      */
322     public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE = 14;
323     /**
324      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
325      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
326      * smart card is invalid.
327      */
328     public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID = 15;
329     /**
330      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
331      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
332      * of a geographical blackout.
333      */
334     public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16;
335     /**
336      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
337      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
338      * CAS system is rebooting.
339      */
340     public static final int VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING = 17;
341     /**
342      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
343      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
344      * of unknown CAS error.
345      */
346     public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END;
347 
348     /**
349      * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and
350      * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because
351      * it has been stopped by {@link TvView#stopPlayback(int)}.
352      */
353     @FlaggedApi(Flags.FLAG_TIAF_V_APIS)
354     public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19;
355 
356     /** @hide */
357     @Retention(RetentionPolicy.SOURCE)
358     @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED,
359             TIME_SHIFT_STATUS_UNAVAILABLE, TIME_SHIFT_STATUS_AVAILABLE})
360     public @interface TimeShiftStatus {}
361 
362     /**
363      * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and
364      * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Unknown status. Also
365      * the status prior to calling {@code notifyTimeShiftStatusChanged}.
366      */
367     public static final int TIME_SHIFT_STATUS_UNKNOWN = 0;
368 
369     /**
370      * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and
371      * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: The current TV input
372      * does not support time shifting.
373      */
374     public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1;
375 
376     /**
377      * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and
378      * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Time shifting is
379      * currently unavailable but might work again later.
380      */
381     public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2;
382 
383     /**
384      * Status for {@link TvInputService.Session#notifyTimeShiftStatusChanged(int)} and
385      * {@link TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)}: Time shifting is
386      * currently available. In this status, the application assumes it can pause/resume playback,
387      * seek to a specified time position and set playback rate and audio mode.
388      */
389     public static final int TIME_SHIFT_STATUS_AVAILABLE = 3;
390 
391     /**
392      * Value returned by {@link TvInputService.Session#onTimeShiftGetCurrentPosition()} and
393      * {@link TvInputService.Session#onTimeShiftGetStartPosition()} when time shifting has not
394      * yet started.
395      */
396     public static final long TIME_SHIFT_INVALID_TIME = Long.MIN_VALUE;
397 
398     /** @hide */
399     @Retention(RetentionPolicy.SOURCE)
400     @IntDef(flag = false, prefix = "TIME_SHIFT_MODE_", value = {
401             TIME_SHIFT_MODE_OFF,
402             TIME_SHIFT_MODE_LOCAL,
403             TIME_SHIFT_MODE_NETWORK,
404             TIME_SHIFT_MODE_AUTO})
405     public @interface TimeShiftMode {}
406     /**
407      * Time shift mode: off.
408      * <p>Time shift is disabled.
409      */
410     public static final int TIME_SHIFT_MODE_OFF = 1;
411     /**
412      * Time shift mode: local.
413      * <p>Time shift is handle locally, using on-device data. E.g. playing a local file.
414      */
415     public static final int TIME_SHIFT_MODE_LOCAL = 2;
416     /**
417      * Time shift mode: network.
418      * <p>Time shift is handle remotely via network. E.g. online streaming.
419      */
420     public static final int TIME_SHIFT_MODE_NETWORK = 3;
421     /**
422      * Time shift mode: auto.
423      * <p>Time shift mode is handled automatically.
424      */
425     public static final int TIME_SHIFT_MODE_AUTO = 4;
426 
427     /** @hide */
428     @Retention(RetentionPolicy.SOURCE)
429     @IntDef({RECORDING_ERROR_UNKNOWN, RECORDING_ERROR_INSUFFICIENT_SPACE,
430             RECORDING_ERROR_RESOURCE_BUSY})
431     public @interface RecordingError {}
432 
433     static final int RECORDING_ERROR_START = 0;
434     static final int RECORDING_ERROR_END = 2;
435 
436     /**
437      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
438      * {@link TvRecordingClient.RecordingCallback#onError(int)}: The requested operation cannot be
439      * completed due to a problem that does not fit under any other error codes, or the error code
440      * for the problem is defined on the higher version than application's
441      * <code>android:targetSdkVersion</code>.
442      */
443     public static final int RECORDING_ERROR_UNKNOWN = RECORDING_ERROR_START;
444 
445     /**
446      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
447      * {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed due to
448      * insufficient storage space.
449      */
450     public static final int RECORDING_ERROR_INSUFFICIENT_SPACE = 1;
451 
452     /**
453      * Error for {@link TvInputService.RecordingSession#notifyError(int)} and
454      * {@link TvRecordingClient.RecordingCallback#onError(int)}: Recording cannot proceed because
455      * a required recording resource was not able to be allocated.
456      */
457     public static final int RECORDING_ERROR_RESOURCE_BUSY = RECORDING_ERROR_END;
458 
459     /** @hide */
460     @Retention(RetentionPolicy.SOURCE)
461     @IntDef({INPUT_STATE_CONNECTED, INPUT_STATE_CONNECTED_STANDBY, INPUT_STATE_DISCONNECTED})
462     public @interface InputState {}
463 
464     /**
465      * State for {@link #getInputState(String)} and
466      * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is connected.
467      *
468      * <p>This state indicates that a source device is connected to the input port and is in the
469      * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input.
470      * Non-hardware inputs are considered connected all the time.
471      */
472     public static final int INPUT_STATE_CONNECTED = 0;
473 
474     /**
475      * State for {@link #getInputState(String)} and
476      * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is connected but
477      * in standby mode.
478      *
479      * <p>This state indicates that a source device is connected to the input port but is in standby
480      * or low power mode. It is mostly relevant to hardware inputs such as HDMI input and Component
481      * inputs.
482      */
483     public static final int INPUT_STATE_CONNECTED_STANDBY = 1;
484 
485     /**
486      * State for {@link #getInputState(String)} and
487      * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is disconnected.
488      *
489      * <p>This state indicates that a source device is disconnected from the input port. It is
490      * mostly relevant to hardware inputs such as HDMI input.
491      *
492      */
493     public static final int INPUT_STATE_DISCONNECTED = 2;
494 
495     /** @hide */
496     @Retention(RetentionPolicy.SOURCE)
497     @IntDef(
498             prefix = "BROADCAST_INFO_TYPE_",
499             value = {
500                 BROADCAST_INFO_TYPE_TS,
501                 BROADCAST_INFO_TYPE_TABLE,
502                 BROADCAST_INFO_TYPE_SECTION,
503                 BROADCAST_INFO_TYPE_PES,
504                 BROADCAST_INFO_STREAM_EVENT,
505                 BROADCAST_INFO_TYPE_DSMCC,
506                 BROADCAST_INFO_TYPE_COMMAND,
507                 BROADCAST_INFO_TYPE_TIMELINE,
508                 BROADCAST_INFO_TYPE_SIGNALING_DATA
509             })
510     public @interface BroadcastInfoType {}
511 
512     public static final int BROADCAST_INFO_TYPE_TS = 1;
513     public static final int BROADCAST_INFO_TYPE_TABLE = 2;
514     public static final int BROADCAST_INFO_TYPE_SECTION = 3;
515     public static final int BROADCAST_INFO_TYPE_PES = 4;
516     public static final int BROADCAST_INFO_STREAM_EVENT = 5;
517     public static final int BROADCAST_INFO_TYPE_DSMCC = 6;
518     public static final int BROADCAST_INFO_TYPE_COMMAND = 7;
519     public static final int BROADCAST_INFO_TYPE_TIMELINE = 8;
520 
521     /** @hide */
522     public static final int BROADCAST_INFO_TYPE_SIGNALING_DATA = 9;
523 
524     /** @hide */
525     @Retention(RetentionPolicy.SOURCE)
526     @IntDef(prefix = "SIGNAL_STRENGTH_",
527             value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG})
528     public @interface SignalStrength {}
529 
530     /**
531      * Signal lost.
532      */
533     public static final int SIGNAL_STRENGTH_LOST = 1;
534     /**
535      * Weak signal.
536      */
537     public static final int SIGNAL_STRENGTH_WEAK = 2;
538     /**
539      * Strong signal.
540      */
541     public static final int SIGNAL_STRENGTH_STRONG = 3;
542 
543     /** @hide */
544     @Retention(RetentionPolicy.SOURCE)
545     @StringDef(prefix = "SESSION_DATA_TYPE_", value = {
546             SESSION_DATA_TYPE_TUNED,
547             SESSION_DATA_TYPE_TRACK_SELECTED,
548             SESSION_DATA_TYPE_TRACKS_CHANGED,
549             SESSION_DATA_TYPE_VIDEO_AVAILABLE,
550             SESSION_DATA_TYPE_VIDEO_UNAVAILABLE,
551             SESSION_DATA_TYPE_BROADCAST_INFO_RESPONSE,
552             SESSION_DATA_TYPE_AD_RESPONSE,
553             SESSION_DATA_TYPE_AD_BUFFER_CONSUMED,
554             SESSION_DATA_TYPE_TV_MESSAGE})
555     public @interface SessionDataType {}
556 
557     /**
558      * Informs the application that the session has been tuned to the given channel.
559      *
560      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
561      * @see SESSION_DATA_KEY_CHANNEL_URI
562      */
563     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
564     public static final String SESSION_DATA_TYPE_TUNED = "tuned";
565 
566     /**
567      * Sends the type and ID of a selected track. This is used to inform the application that a
568      * specific track is selected.
569      *
570      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
571      * @see SESSION_DATA_KEY_TRACK_TYPE
572      * @see SESSION_DATA_KEY_TRACK_ID
573      */
574     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
575     public static final String SESSION_DATA_TYPE_TRACK_SELECTED = "track_selected";
576 
577     /**
578      * Sends the list of all audio/video/subtitle tracks.
579      *
580      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
581      * @see SESSION_DATA_KEY_TRACKS
582      */
583     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
584     public static final String SESSION_DATA_TYPE_TRACKS_CHANGED = "tracks_changed";
585 
586     /**
587      * Informs the application that the video is now available for watching.
588      *
589      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
590      */
591     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
592     public static final String SESSION_DATA_TYPE_VIDEO_AVAILABLE = "video_available";
593 
594     /**
595      * Informs the application that the video became unavailable for some reason.
596      *
597      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
598      * @see SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON
599      */
600     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
601     public static final String SESSION_DATA_TYPE_VIDEO_UNAVAILABLE = "video_unavailable";
602 
603     /**
604      * Notifies response for broadcast info.
605      *
606      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
607      * @see SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE
608      */
609     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
610     public static final String SESSION_DATA_TYPE_BROADCAST_INFO_RESPONSE =
611             "broadcast_info_response";
612 
613     /**
614      * Notifies response for advertisement.
615      *
616      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
617      * @see SESSION_DATA_KEY_AD_RESPONSE
618      */
619     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
620     public static final String SESSION_DATA_TYPE_AD_RESPONSE = "ad_response";
621 
622     /**
623      * Notifies the advertisement buffer is consumed.
624      *
625      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
626      * @see SESSION_DATA_KEY_AD_BUFFER
627      */
628     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
629     public static final String SESSION_DATA_TYPE_AD_BUFFER_CONSUMED = "ad_buffer_consumed";
630 
631     /**
632      * Sends the TV message.
633      *
634      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
635      * @see TvInputService.Session#notifyTvMessage(int, Bundle)
636      * @see SESSION_DATA_KEY_TV_MESSAGE_TYPE
637      */
638     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
639     public static final String SESSION_DATA_TYPE_TV_MESSAGE = "tv_message";
640 
641 
642     /** @hide */
643     @Retention(RetentionPolicy.SOURCE)
644     @StringDef(prefix = "SESSION_DATA_KEY_", value = {
645             SESSION_DATA_KEY_CHANNEL_URI,
646             SESSION_DATA_KEY_TRACK_TYPE,
647             SESSION_DATA_KEY_TRACK_ID,
648             SESSION_DATA_KEY_TRACKS,
649             SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON,
650             SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE,
651             SESSION_DATA_KEY_AD_RESPONSE,
652             SESSION_DATA_KEY_AD_BUFFER,
653             SESSION_DATA_KEY_TV_MESSAGE_TYPE})
654     public @interface SessionDataKey {}
655 
656     /**
657      * The URI of a channel.
658      *
659      * <p> Type: android.net.Uri
660      *
661      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
662      */
663     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
664     public static final String SESSION_DATA_KEY_CHANNEL_URI = "channel_uri";
665 
666     /**
667      * The type of the track.
668      *
669      * <p>One of {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO},
670      * {@link TvTrackInfo#TYPE_SUBTITLE}.
671      *
672      * <p> Type: Integer
673      *
674      * @see TvTrackInfo#getType()
675      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
676      */
677     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
678     public static final String SESSION_DATA_KEY_TRACK_TYPE = "track_type";
679 
680     /**
681      * The ID of the track.
682      *
683      * <p> Type: String
684      *
685      * @see TvTrackInfo#getId()
686      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
687      */
688     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
689     public static final String SESSION_DATA_KEY_TRACK_ID = "track_id";
690 
691     /**
692      * A list which includes track information.
693      *
694      * <p> Type: {@code java.util.List<android.media.tv.TvTrackInfo> }
695      *
696      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
697      */
698     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
699     public static final String SESSION_DATA_KEY_TRACKS = "tracks";
700 
701     /**
702      * The reason why the video became unavailable.
703      * <p>The value can be {@link VIDEO_UNAVAILABLE_REASON_BUFFERING},
704      * {@link VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}, etc.
705      *
706      * <p> Type: Integer
707      *
708      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
709      */
710     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
711     public static final String SESSION_DATA_KEY_VIDEO_UNAVAILABLE_REASON =
712             "video_unavailable_reason";
713 
714     /**
715      * An object of {@link BroadcastInfoResponse}.
716      *
717      * <p> Type: android.media.tv.BroadcastInfoResponse
718      *
719      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
720      */
721     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
722     public static final String SESSION_DATA_KEY_BROADCAST_INFO_RESPONSE = "broadcast_info_response";
723 
724     /**
725      * An object of {@link AdResponse}.
726      *
727      * <p> Type: android.media.tv.AdResponse
728      *
729      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
730      */
731     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
732     public static final String SESSION_DATA_KEY_AD_RESPONSE = "ad_response";
733 
734     /**
735      * An object of {@link AdBuffer}.
736      *
737      * <p> Type: android.media.tv.AdBuffer
738      *
739      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
740      */
741     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
742     public static final String SESSION_DATA_KEY_AD_BUFFER = "ad_buffer";
743 
744     /**
745      * The type of TV message.
746      * <p>It can be one of {@link TV_MESSAGE_TYPE_WATERMARK},
747      * {@link TV_MESSAGE_TYPE_CLOSED_CAPTION}, {@link TV_MESSAGE_TYPE_OTHER}
748      *
749      * <p> Type: Integer
750      *
751      * @see TvInputService.Session#sendTvInputSessionData(String, Bundle)
752      */
753     @FlaggedApi(Flags.FLAG_ENABLE_AD_SERVICE_FW)
754     public static final String SESSION_DATA_KEY_TV_MESSAGE_TYPE = "tv_message_type";
755 
756 
757     /**
758      * An unknown state of the client pid gets from the TvInputManager. Client gets this value when
759      * query through {@link getClientPid(String sessionId)} fails.
760      *
761      * @hide
762      */
763     public static final int UNKNOWN_CLIENT_PID = -1;
764 
765     /**
766      * An unknown state of the client userId gets from the TvInputManager. Client gets this value
767      * when query through {@link #getClientUserId(String sessionId)} fails.
768      *
769      * @hide
770      */
771     @FlaggedApi(Flags.FLAG_KIDS_MODE_TVDB_SHARING)
772     public static final int UNKNOWN_CLIENT_USER_ID = -1;
773 
774     /**
775      * Broadcast intent action when the user blocked content ratings change. For use with the
776      * {@link #isRatingBlocked}.
777      */
778     public static final String ACTION_BLOCKED_RATINGS_CHANGED =
779             "android.media.tv.action.BLOCKED_RATINGS_CHANGED";
780 
781     /**
782      * Broadcast intent action when the parental controls enabled state changes. For use with the
783      * {@link #isParentalControlsEnabled}.
784      */
785     public static final String ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED =
786             "android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED";
787 
788     /**
789      * Broadcast intent action used to query available content rating systems.
790      *
791      * <p>The TV input manager service locates available content rating systems by querying
792      * broadcast receivers that are registered for this action. An application can offer additional
793      * content rating systems to the user by declaring a suitable broadcast receiver in its
794      * manifest.
795      *
796      * <p>Here is an example broadcast receiver declaration that an application might include in its
797      * AndroidManifest.xml to advertise custom content rating systems. The meta-data specifies a
798      * resource that contains a description of each content rating system that is provided by the
799      * application.
800      *
801      * <p><pre class="prettyprint">
802      * {@literal
803      * <receiver android:name=".TvInputReceiver">
804      *     <intent-filter>
805      *         <action android:name=
806      *                 "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS" />
807      *     </intent-filter>
808      *     <meta-data
809      *             android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
810      *             android:resource="@xml/tv_content_rating_systems" />
811      * </receiver>}</pre>
812      *
813      * <p>In the above example, the <code>@xml/tv_content_rating_systems</code> resource refers to an
814      * XML resource whose root element is <code>&lt;rating-system-definitions&gt;</code> that
815      * contains zero or more <code>&lt;rating-system-definition&gt;</code> elements. Each <code>
816      * &lt;rating-system-definition&gt;</code> element specifies the ratings, sub-ratings and rating
817      * orders of a particular content rating system.
818      *
819      * @see TvContentRating
820      */
821     public static final String ACTION_QUERY_CONTENT_RATING_SYSTEMS =
822             "android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS";
823 
824     /**
825      * Content rating systems metadata associated with {@link #ACTION_QUERY_CONTENT_RATING_SYSTEMS}.
826      *
827      * <p>Specifies the resource ID of an XML resource that describes the content rating systems
828      * that are provided by the application.
829      */
830     public static final String META_DATA_CONTENT_RATING_SYSTEMS =
831             "android.media.tv.metadata.CONTENT_RATING_SYSTEMS";
832 
833     /**
834      * Activity action to set up channel sources i.e.&nbsp;TV inputs of type
835      * {@link TvInputInfo#TYPE_TUNER}. When invoked, the system will display an appropriate UI for
836      * the user to initiate the individual setup flow provided by
837      * {@link android.R.attr#setupActivity} of each TV input service.
838      */
839     public static final String ACTION_SETUP_INPUTS = "android.media.tv.action.SETUP_INPUTS";
840 
841     /**
842      * Activity action to display the recording schedules. When invoked, the system will display an
843      * appropriate UI to browse the schedules.
844      */
845     public static final String ACTION_VIEW_RECORDING_SCHEDULES =
846             "android.media.tv.action.VIEW_RECORDING_SCHEDULES";
847 
848     private final ITvInputManager mService;
849 
850     private final Object mLock = new Object();
851 
852     // @GuardedBy("mLock")
853     private final List<TvInputCallbackRecord> mCallbackRecords = new ArrayList<>();
854 
855     // A mapping from TV input ID to the state of corresponding input.
856     // @GuardedBy("mLock")
857     private final Map<String, Integer> mStateMap = new ArrayMap<>();
858 
859     // A mapping from the sequence number of a session to its SessionCallbackRecord.
860     private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
861             new SparseArray<>();
862 
863     // A sequence number for the next session to be created. Should be protected by a lock
864     // {@code mSessionCallbackRecordMap}.
865     private int mNextSeq;
866 
867     private final ITvInputClient mClient;
868 
869     private final int mUserId;
870 
871     /**
872      * Interface used to receive the created session.
873      * @hide
874      */
875     public abstract static class SessionCallback {
876         /**
877          * This is called after {@link TvInputManager#createSession} has been processed.
878          *
879          * @param session A {@link TvInputManager.Session} instance created. This can be
880          *            {@code null} if the creation request failed.
881          */
onSessionCreated(@ullable Session session)882         public void onSessionCreated(@Nullable Session session) {
883         }
884 
885         /**
886          * This is called when {@link TvInputManager.Session} is released.
887          * This typically happens when the process hosting the session has crashed or been killed.
888          *
889          * @param session A {@link TvInputManager.Session} instance released.
890          */
onSessionReleased(Session session)891         public void onSessionReleased(Session session) {
892         }
893 
894         /**
895          * This is called when the channel of this session is changed by the underlying TV input
896          * without any {@link TvInputManager.Session#tune(Uri)} request.
897          *
898          * @param session A {@link TvInputManager.Session} associated with this callback.
899          * @param channelUri The URI of a channel.
900          */
onChannelRetuned(Session session, Uri channelUri)901         public void onChannelRetuned(Session session, Uri channelUri) {
902         }
903 
904         /**
905          * This is called when the audio presentation information of the session has been changed.
906          *
907          * @param session A {@link TvInputManager.Session} associated with this callback.
908          * @param audioPresentations An updated list of selectable audio presentations.
909          */
onAudioPresentationsChanged(Session session, List<AudioPresentation> audioPresentations)910         public void onAudioPresentationsChanged(Session session,
911                 List<AudioPresentation> audioPresentations) {
912         }
913 
914         /**
915          * This is called when an audio presentation is selected.
916          *
917          * @param session A {@link TvInputManager.Session} associated with this callback.
918          * @param presentationId The ID of the selected audio presentation.
919          * @param programId The ID of the program providing the selected audio presentation.
920          */
onAudioPresentationSelected(Session session, int presentationId, int programId)921         public void onAudioPresentationSelected(Session session, int presentationId,
922                 int programId) {
923         }
924 
925         /**
926          * This is called when the track information of the session has been changed.
927          *
928          * @param session A {@link TvInputManager.Session} associated with this callback.
929          * @param tracks A list which includes track information.
930          */
onTracksChanged(Session session, List<TvTrackInfo> tracks)931         public void onTracksChanged(Session session, List<TvTrackInfo> tracks) {
932         }
933 
934         /**
935          * This is called when a track for a given type is selected.
936          *
937          * @param session A {@link TvInputManager.Session} associated with this callback.
938          * @param type The type of the selected track. The type can be
939          *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
940          *            {@link TvTrackInfo#TYPE_SUBTITLE}.
941          * @param trackId The ID of the selected track. When {@code null} the currently selected
942          *            track for a given type should be unselected.
943          */
onTrackSelected(Session session, int type, @Nullable String trackId)944         public void onTrackSelected(Session session, int type, @Nullable String trackId) {
945         }
946 
947         /**
948          * This is invoked when the video size has been changed. It is also called when the first
949          * time video size information becomes available after the session is tuned to a specific
950          * channel.
951          *
952          * @param session A {@link TvInputManager.Session} associated with this callback.
953          * @param width The width of the video.
954          * @param height The height of the video.
955          */
onVideoSizeChanged(Session session, int width, int height)956         public void onVideoSizeChanged(Session session, int width, int height) {
957         }
958 
959         /**
960          * This is called when the video is available, so the TV input starts the playback.
961          *
962          * @param session A {@link TvInputManager.Session} associated with this callback.
963          */
onVideoAvailable(Session session)964         public void onVideoAvailable(Session session) {
965         }
966 
967         /**
968          * This is called when the video is not available, so the TV input stops the playback.
969          *
970          * @param session A {@link TvInputManager.Session} associated with this callback.
971          * @param reason The reason why the TV input stopped the playback:
972          * <ul>
973          * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_UNKNOWN}
974          * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING}
975          * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL}
976          * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_BUFFERING}
977          * <li>{@link TvInputManager#VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}
978          * </ul>
979          */
onVideoUnavailable(Session session, int reason)980         public void onVideoUnavailable(Session session, int reason) {
981         }
982 
983         /**
984          * This is called when the video freeze state has been updated.
985          * If {@code true}, the video is frozen on the last frame while audio playback continues.
986          * @param session A {@link TvInputManager.Session} associated with this callback.
987          * @param isFrozen Whether the video is frozen
988          */
onVideoFreezeUpdated(Session session, boolean isFrozen)989         public void onVideoFreezeUpdated(Session session, boolean isFrozen) {
990         }
991 
992         /**
993          * This is called when the current program content turns out to be allowed to watch since
994          * its content rating is not blocked by parental controls.
995          *
996          * @param session A {@link TvInputManager.Session} associated with this callback.
997          */
onContentAllowed(Session session)998         public void onContentAllowed(Session session) {
999         }
1000 
1001         /**
1002          * This is called when the current program content turns out to be not allowed to watch
1003          * since its content rating is blocked by parental controls.
1004          *
1005          * @param session A {@link TvInputManager.Session} associated with this callback.
1006          * @param rating The content ration of the blocked program.
1007          */
onContentBlocked(Session session, TvContentRating rating)1008         public void onContentBlocked(Session session, TvContentRating rating) {
1009         }
1010 
1011         /**
1012          * This is called when {@link TvInputService.Session#layoutSurface} is called to change the
1013          * layout of surface.
1014          *
1015          * @param session A {@link TvInputManager.Session} associated with this callback.
1016          * @param left Left position.
1017          * @param top Top position.
1018          * @param right Right position.
1019          * @param bottom Bottom position.
1020          */
onLayoutSurface(Session session, int left, int top, int right, int bottom)1021         public void onLayoutSurface(Session session, int left, int top, int right, int bottom) {
1022         }
1023 
1024         /**
1025          * This is called when a custom event has been sent from this session.
1026          *
1027          * @param session A {@link TvInputManager.Session} associated with this callback
1028          * @param eventType The type of the event.
1029          * @param eventArgs Optional arguments of the event.
1030          */
onSessionEvent(Session session, String eventType, Bundle eventArgs)1031         public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
1032         }
1033 
1034         /**
1035          * This is called when the time shift status is changed.
1036          *
1037          * @param session A {@link TvInputManager.Session} associated with this callback.
1038          * @param status The current time shift status. Should be one of the followings.
1039          * <ul>
1040          * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
1041          * <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
1042          * <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
1043          * </ul>
1044          */
onTimeShiftStatusChanged(Session session, int status)1045         public void onTimeShiftStatusChanged(Session session, int status) {
1046         }
1047 
1048         /**
1049          * This is called when the start position for time shifting has changed.
1050          *
1051          * @param session A {@link TvInputManager.Session} associated with this callback.
1052          * @param timeMs The start position for time shifting, in milliseconds since the epoch.
1053          */
onTimeShiftStartPositionChanged(Session session, long timeMs)1054         public void onTimeShiftStartPositionChanged(Session session, long timeMs) {
1055         }
1056 
1057         /**
1058          * This is called when the current position for time shifting is changed.
1059          *
1060          * @param session A {@link TvInputManager.Session} associated with this callback.
1061          * @param timeMs The current position for time shifting, in milliseconds since the epoch.
1062          */
onTimeShiftCurrentPositionChanged(Session session, long timeMs)1063         public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) {
1064         }
1065 
1066         /**
1067          * This is called when AIT info is updated.
1068          * @param session A {@link TvInputManager.Session} associated with this callback.
1069          * @param aitInfo The current AIT info.
1070          */
onAitInfoUpdated(Session session, AitInfo aitInfo)1071         public void onAitInfoUpdated(Session session, AitInfo aitInfo) {
1072         }
1073 
1074         /**
1075          * This is called when signal strength is updated.
1076          * @param session A {@link TvInputManager.Session} associated with this callback.
1077          * @param strength The current signal strength.
1078          */
onSignalStrengthUpdated(Session session, @SignalStrength int strength)1079         public void onSignalStrengthUpdated(Session session, @SignalStrength int strength) {
1080         }
1081 
1082         /**
1083          * This is called when cueing message becomes available or unavailable.
1084          * @param session A {@link TvInputManager.Session} associated with this callback.
1085          * @param available The current availability of cueing message. {@code true} if cueing
1086          *                  message is available; {@code false} if it becomes unavailable.
1087          */
onCueingMessageAvailability(Session session, boolean available)1088         public void onCueingMessageAvailability(Session session, boolean available) {
1089         }
1090 
1091         /**
1092          * This is called when time shift mode is set or updated.
1093          * @param session A {@link TvInputManager.Session} associated with this callback.
1094          * @param mode The current time shift mode. The value is one of the following:
1095          * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
1096          * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
1097          * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
1098          */
onTimeShiftMode(Session session, @TimeShiftMode int mode)1099         public void onTimeShiftMode(Session session, @TimeShiftMode int mode) {
1100         }
1101 
1102         /**
1103          * Informs the app available speeds for time-shifting.
1104          * @param session A {@link TvInputManager.Session} associated with this callback.
1105          * @param speeds An ordered array of playback speeds, expressed as values relative to the
1106          *               normal playback speed (1.0), at which the current content can be played as
1107          *               a time-shifted broadcast. This is an empty array if the supported playback
1108          *               speeds are unknown or the video/broadcast is not in time shift mode. If
1109          *               currently in time shift mode, this array will normally include at least
1110          *               the values 1.0 (normal speed) and 0.0 (paused).
1111          * @see PlaybackParams#getSpeed()
1112          */
onAvailableSpeeds(Session session, float[] speeds)1113         public void onAvailableSpeeds(Session session, float[] speeds) {
1114         }
1115 
1116         /**
1117          * This is called when the session has been tuned to the given channel.
1118          *
1119          * @param channelUri The URI of a channel.
1120          */
onTuned(Session session, Uri channelUri)1121         public void onTuned(Session session, Uri channelUri) {
1122         }
1123 
1124         /**
1125          * This is called when the session receives a new TV Message
1126          *
1127          * @param session A {@link TvInputManager.Session} associated with this callback.
1128          * @param type The type of message received, such as {@link #TV_MESSAGE_TYPE_WATERMARK}
1129          * @param data The raw data of the message. The bundle keys are:
1130          *             {@link TvInputManager#TV_MESSAGE_KEY_STREAM_ID},
1131          *             {@link TvInputManager#TV_MESSAGE_KEY_GROUP_ID},
1132          *             {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE},
1133          *             {@link TvInputManager#TV_MESSAGE_KEY_RAW_DATA}.
1134          *             See {@link TvInputManager#TV_MESSAGE_KEY_SUBTYPE} for more information on
1135          *             how to parse this data.
1136          *
1137          */
onTvMessage(Session session, @TvInputManager.TvMessageType int type, Bundle data)1138         public void onTvMessage(Session session, @TvInputManager.TvMessageType int type,
1139                 Bundle data) {
1140         }
1141 
1142         // For the recording session only
1143         /**
1144          * This is called when the current recording session has stopped recording and created a
1145          * new data entry in the {@link TvContract.RecordedPrograms} table that describes the newly
1146          * recorded program.
1147          *
1148          * @param recordedProgramUri The URI for the newly recorded program.
1149          **/
onRecordingStopped(Session session, Uri recordedProgramUri)1150         void onRecordingStopped(Session session, Uri recordedProgramUri) {
1151         }
1152 
1153         // For the recording session only
1154         /**
1155          * This is called when an issue has occurred. It may be called at any time after the current
1156          * recording session is created until it is released.
1157          *
1158          * @param error The error code.
1159          */
onError(Session session, @TvInputManager.RecordingError int error)1160         void onError(Session session, @TvInputManager.RecordingError int error) {
1161         }
1162     }
1163 
1164     private static final class SessionCallbackRecord {
1165         private final SessionCallback mSessionCallback;
1166         private final Handler mHandler;
1167         private Session mSession;
1168 
SessionCallbackRecord(SessionCallback sessionCallback, Handler handler)1169         SessionCallbackRecord(SessionCallback sessionCallback,
1170                 Handler handler) {
1171             mSessionCallback = sessionCallback;
1172             mHandler = handler;
1173         }
1174 
postSessionCreated(final Session session)1175         void postSessionCreated(final Session session) {
1176             mSession = session;
1177             mHandler.post(new Runnable() {
1178                 @Override
1179                 public void run() {
1180                     mSessionCallback.onSessionCreated(session);
1181                 }
1182             });
1183         }
1184 
postSessionReleased()1185         void postSessionReleased() {
1186             mHandler.post(new Runnable() {
1187                 @Override
1188                 public void run() {
1189                     mSessionCallback.onSessionReleased(mSession);
1190                 }
1191             });
1192         }
1193 
postChannelRetuned(final Uri channelUri)1194         void postChannelRetuned(final Uri channelUri) {
1195             mHandler.post(new Runnable() {
1196                 @Override
1197                 public void run() {
1198                     mSessionCallback.onChannelRetuned(mSession, channelUri);
1199                 }
1200             });
1201         }
1202 
postAudioPresentationsChanged(final List<AudioPresentation> audioPresentations)1203         void postAudioPresentationsChanged(final List<AudioPresentation> audioPresentations) {
1204             mHandler.post(new Runnable() {
1205                 @Override
1206                 public void run() {
1207                     mSessionCallback.onAudioPresentationsChanged(mSession, audioPresentations);
1208                 }
1209             });
1210         }
1211 
postAudioPresentationSelected(final int presentationId, final int programId)1212         void postAudioPresentationSelected(final int presentationId, final int programId) {
1213             mHandler.post(new Runnable() {
1214                 @Override
1215                 public void run() {
1216                     mSessionCallback.onAudioPresentationSelected(mSession, presentationId,
1217                             programId);
1218                 }
1219             });
1220         }
1221 
postTracksChanged(final List<TvTrackInfo> tracks)1222         void postTracksChanged(final List<TvTrackInfo> tracks) {
1223             mHandler.post(new Runnable() {
1224                 @Override
1225                 public void run() {
1226                     mSessionCallback.onTracksChanged(mSession, tracks);
1227                     if (mSession.mIAppNotificationEnabled
1228                             && mSession.getInteractiveAppSession() != null) {
1229                         mSession.getInteractiveAppSession().notifyTracksChanged(tracks);
1230                     }
1231                 }
1232             });
1233         }
1234 
postTrackSelected(final int type, final String trackId)1235         void postTrackSelected(final int type, final String trackId) {
1236             mHandler.post(new Runnable() {
1237                 @Override
1238                 public void run() {
1239                     mSessionCallback.onTrackSelected(mSession, type, trackId);
1240                     if (mSession.mIAppNotificationEnabled
1241                             && mSession.getInteractiveAppSession() != null) {
1242                         mSession.getInteractiveAppSession().notifyTrackSelected(type, trackId);
1243                     }
1244                 }
1245             });
1246         }
1247 
postVideoSizeChanged(final int width, final int height)1248         void postVideoSizeChanged(final int width, final int height) {
1249             mHandler.post(new Runnable() {
1250                 @Override
1251                 public void run() {
1252                     mSessionCallback.onVideoSizeChanged(mSession, width, height);
1253                 }
1254             });
1255         }
1256 
postVideoAvailable()1257         void postVideoAvailable() {
1258             mHandler.post(new Runnable() {
1259                 @Override
1260                 public void run() {
1261                     mSessionCallback.onVideoAvailable(mSession);
1262                     if (mSession.mIAppNotificationEnabled
1263                             && mSession.getInteractiveAppSession() != null) {
1264                         mSession.getInteractiveAppSession().notifyVideoAvailable();
1265                     }
1266                 }
1267             });
1268         }
1269 
postVideoUnavailable(final int reason)1270         void postVideoUnavailable(final int reason) {
1271             mHandler.post(new Runnable() {
1272                 @Override
1273                 public void run() {
1274                     mSessionCallback.onVideoUnavailable(mSession, reason);
1275                     if (mSession.mIAppNotificationEnabled
1276                             && mSession.getInteractiveAppSession() != null) {
1277                         mSession.getInteractiveAppSession().notifyVideoUnavailable(reason);
1278                     }
1279                 }
1280             });
1281         }
1282 
postVideoFreezeUpdated(boolean isFrozen)1283         void postVideoFreezeUpdated(boolean isFrozen) {
1284             mHandler.post(new Runnable() {
1285                 @Override
1286                 public void run() {
1287                     mSessionCallback.onVideoFreezeUpdated(mSession, isFrozen);
1288                     if (mSession.mIAppNotificationEnabled
1289                             && mSession.getInteractiveAppSession() != null) {
1290                         mSession.getInteractiveAppSession().notifyVideoFreezeUpdated(isFrozen);
1291                     }
1292                 }
1293             });
1294         }
1295 
postContentAllowed()1296         void postContentAllowed() {
1297             mHandler.post(new Runnable() {
1298                 @Override
1299                 public void run() {
1300                     mSessionCallback.onContentAllowed(mSession);
1301                     if (mSession.mIAppNotificationEnabled
1302                             && mSession.getInteractiveAppSession() != null) {
1303                         mSession.getInteractiveAppSession().notifyContentAllowed();
1304                     }
1305                 }
1306             });
1307         }
1308 
postContentBlocked(final TvContentRating rating)1309         void postContentBlocked(final TvContentRating rating) {
1310             mHandler.post(new Runnable() {
1311                 @Override
1312                 public void run() {
1313                     mSessionCallback.onContentBlocked(mSession, rating);
1314                     if (mSession.mIAppNotificationEnabled
1315                             && mSession.getInteractiveAppSession() != null) {
1316                         mSession.getInteractiveAppSession().notifyContentBlocked(rating);
1317                     }
1318                 }
1319             });
1320         }
1321 
postLayoutSurface(final int left, final int top, final int right, final int bottom)1322         void postLayoutSurface(final int left, final int top, final int right,
1323                 final int bottom) {
1324             mHandler.post(new Runnable() {
1325                 @Override
1326                 public void run() {
1327                     mSessionCallback.onLayoutSurface(mSession, left, top, right, bottom);
1328                 }
1329             });
1330         }
1331 
postSessionEvent(final String eventType, final Bundle eventArgs)1332         void postSessionEvent(final String eventType, final Bundle eventArgs) {
1333             mHandler.post(new Runnable() {
1334                 @Override
1335                 public void run() {
1336                     mSessionCallback.onSessionEvent(mSession, eventType, eventArgs);
1337                 }
1338             });
1339         }
1340 
postTimeShiftStatusChanged(final int status)1341         void postTimeShiftStatusChanged(final int status) {
1342             mHandler.post(new Runnable() {
1343                 @Override
1344                 public void run() {
1345                     mSessionCallback.onTimeShiftStatusChanged(mSession, status);
1346                 }
1347             });
1348         }
1349 
postTimeShiftStartPositionChanged(final long timeMs)1350         void postTimeShiftStartPositionChanged(final long timeMs) {
1351             mHandler.post(new Runnable() {
1352                 @Override
1353                 public void run() {
1354                     mSessionCallback.onTimeShiftStartPositionChanged(mSession, timeMs);
1355                 }
1356             });
1357         }
1358 
postTimeShiftCurrentPositionChanged(final long timeMs)1359         void postTimeShiftCurrentPositionChanged(final long timeMs) {
1360             mHandler.post(new Runnable() {
1361                 @Override
1362                 public void run() {
1363                     mSessionCallback.onTimeShiftCurrentPositionChanged(mSession, timeMs);
1364                 }
1365             });
1366         }
1367 
postAitInfoUpdated(final AitInfo aitInfo)1368         void postAitInfoUpdated(final AitInfo aitInfo) {
1369             mHandler.post(new Runnable() {
1370                 @Override
1371                 public void run() {
1372                     mSessionCallback.onAitInfoUpdated(mSession, aitInfo);
1373                 }
1374             });
1375         }
1376 
postSignalStrength(final int strength)1377         void postSignalStrength(final int strength) {
1378             mHandler.post(new Runnable() {
1379                 @Override
1380                 public void run() {
1381                     mSessionCallback.onSignalStrengthUpdated(mSession, strength);
1382                     if (mSession.mIAppNotificationEnabled
1383                             && mSession.getInteractiveAppSession() != null) {
1384                         mSession.getInteractiveAppSession().notifySignalStrength(strength);
1385                     }
1386                 }
1387             });
1388         }
1389 
postCueingMessageAvailability(final boolean available)1390         void postCueingMessageAvailability(final boolean available) {
1391             mHandler.post(new Runnable() {
1392                 @Override
1393                 public void run() {
1394                     mSessionCallback.onCueingMessageAvailability(mSession, available);
1395                 }
1396             });
1397         }
1398 
postTimeShiftMode(final int mode)1399         void postTimeShiftMode(final int mode) {
1400             mHandler.post(new Runnable() {
1401                 @Override
1402                 public void run() {
1403                     mSessionCallback.onTimeShiftMode(mSession, mode);
1404                 }
1405             });
1406         }
1407 
postAvailableSpeeds(float[] speeds)1408         void postAvailableSpeeds(float[] speeds) {
1409             mHandler.post(new Runnable() {
1410                 @Override
1411                 public void run() {
1412                     mSessionCallback.onAvailableSpeeds(mSession, speeds);
1413                 }
1414             });
1415         }
1416 
postTuned(final Uri channelUri)1417         void postTuned(final Uri channelUri) {
1418             mHandler.post(new Runnable() {
1419                 @Override
1420                 public void run() {
1421                     mSessionCallback.onTuned(mSession, channelUri);
1422                     if (mSession.mIAppNotificationEnabled
1423                             && mSession.getInteractiveAppSession() != null) {
1424                         mSession.getInteractiveAppSession().notifyTuned(channelUri);
1425                     }
1426                 }
1427             });
1428         }
1429 
postTvMessage(int type, Bundle data)1430         void postTvMessage(int type, Bundle data) {
1431             mHandler.post(new Runnable() {
1432                 @Override
1433                 public void run() {
1434                     mSessionCallback.onTvMessage(mSession, type, data);
1435                     if (mSession.mIAppNotificationEnabled
1436                             && mSession.getInteractiveAppSession() != null) {
1437                         mSession.getInteractiveAppSession().notifyTvMessage(type, data);
1438                     }
1439                 }
1440             });
1441         }
1442 
1443         // For the recording session only
postRecordingStopped(final Uri recordedProgramUri)1444         void postRecordingStopped(final Uri recordedProgramUri) {
1445             mHandler.post(new Runnable() {
1446                 @Override
1447                 public void run() {
1448                     mSessionCallback.onRecordingStopped(mSession, recordedProgramUri);
1449                 }
1450             });
1451         }
1452 
1453         // For the recording session only
postError(final int error)1454         void postError(final int error) {
1455             mHandler.post(new Runnable() {
1456                 @Override
1457                 public void run() {
1458                     mSessionCallback.onError(mSession, error);
1459                 }
1460             });
1461         }
1462 
postBroadcastInfoResponse(final BroadcastInfoResponse response)1463         void postBroadcastInfoResponse(final BroadcastInfoResponse response) {
1464             if (mSession.mIAppNotificationEnabled) {
1465                 mHandler.post(new Runnable() {
1466                     @Override
1467                     public void run() {
1468                         if (mSession.getInteractiveAppSession() != null) {
1469                             mSession.getInteractiveAppSession()
1470                                     .notifyBroadcastInfoResponse(response);
1471                         }
1472                     }
1473                 });
1474             }
1475         }
1476 
postAdResponse(final AdResponse response)1477         void postAdResponse(final AdResponse response) {
1478             if (mSession.mIAppNotificationEnabled) {
1479                 mHandler.post(new Runnable() {
1480                     @Override
1481                     public void run() {
1482                         if (mSession.getInteractiveAppSession() != null) {
1483                             mSession.getInteractiveAppSession().notifyAdResponse(response);
1484                         }
1485                     }
1486                 });
1487             }
1488         }
1489 
postAdBufferConsumed(AdBuffer buffer)1490         void postAdBufferConsumed(AdBuffer buffer) {
1491             if (mSession.mIAppNotificationEnabled) {
1492                 mHandler.post(new Runnable() {
1493                     @Override
1494                     public void run() {
1495                         if (mSession.getInteractiveAppSession() != null) {
1496                             mSession.getInteractiveAppSession().notifyAdBufferConsumed(buffer);
1497                         }
1498                     }
1499                 });
1500             }
1501         }
1502 
postTvInputSessionData(String type, Bundle data)1503         void postTvInputSessionData(String type, Bundle data) {
1504             mHandler.post(new Runnable() {
1505                 @Override
1506                 public void run() {
1507                     if (mSession.getAdSession() != null) {
1508                         mSession.getAdSession().notifyTvInputSessionData(type, data);
1509                     }
1510                 }
1511             });
1512         }
1513     }
1514 
1515     /**
1516      * Callback used to monitor status of the TV inputs.
1517      */
1518     public abstract static class TvInputCallback {
1519         /**
1520          * This is called when the state of a given TV input is changed.
1521          *
1522          * @param inputId The ID of the TV input.
1523          * @param state State of the TV input. The value is one of the following:
1524          * <ul>
1525          * <li>{@link TvInputManager#INPUT_STATE_CONNECTED}
1526          * <li>{@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY}
1527          * <li>{@link TvInputManager#INPUT_STATE_DISCONNECTED}
1528          * </ul>
1529          */
onInputStateChanged(String inputId, @InputState int state)1530         public void onInputStateChanged(String inputId, @InputState int state) {
1531         }
1532 
1533         /**
1534          * This is called when a TV input is added to the system.
1535          *
1536          * <p>Normally it happens when the user installs a new TV input package that implements
1537          * {@link TvInputService} interface.
1538          *
1539          * @param inputId The ID of the TV input.
1540          */
onInputAdded(String inputId)1541         public void onInputAdded(String inputId) {
1542         }
1543 
1544         /**
1545          * This is called when a TV input is removed from the system.
1546          *
1547          * <p>Normally it happens when the user uninstalls the previously installed TV input
1548          * package.
1549          *
1550          * @param inputId The ID of the TV input.
1551          */
onInputRemoved(String inputId)1552         public void onInputRemoved(String inputId) {
1553         }
1554 
1555         /**
1556          * This is called when a TV input is updated on the system.
1557          *
1558          * <p>Normally it happens when a previously installed TV input package is re-installed or
1559          * the media on which a newer version of the package exists becomes available/unavailable.
1560          *
1561          * @param inputId The ID of the TV input.
1562          */
onInputUpdated(String inputId)1563         public void onInputUpdated(String inputId) {
1564         }
1565 
1566         /**
1567          * This is called when the information about an existing TV input has been updated.
1568          *
1569          * <p>Because the system automatically creates a <code>TvInputInfo</code> object for each TV
1570          * input based on the information collected from the <code>AndroidManifest.xml</code>, this
1571          * method is only called back when such information has changed dynamically.
1572          *
1573          * @param inputInfo The <code>TvInputInfo</code> object that contains new information.
1574          */
onTvInputInfoUpdated(TvInputInfo inputInfo)1575         public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
1576         }
1577 
1578         /**
1579          * This is called when the information about current tuned information has been updated.
1580          *
1581          * @param tunedInfos a list of {@link TunedInfo} objects of new tuned information.
1582          * @hide
1583          */
1584         @SystemApi
1585         @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO)
onCurrentTunedInfosUpdated(@onNull List<TunedInfo> tunedInfos)1586         public void onCurrentTunedInfosUpdated(@NonNull List<TunedInfo> tunedInfos) {
1587         }
1588     }
1589 
1590     private static final class TvInputCallbackRecord {
1591         private final TvInputCallback mCallback;
1592         private final Handler mHandler;
1593 
TvInputCallbackRecord(TvInputCallback callback, Handler handler)1594         public TvInputCallbackRecord(TvInputCallback callback, Handler handler) {
1595             mCallback = callback;
1596             mHandler = handler;
1597         }
1598 
getCallback()1599         public TvInputCallback getCallback() {
1600             return mCallback;
1601         }
1602 
postInputAdded(final String inputId)1603         public void postInputAdded(final String inputId) {
1604             mHandler.post(new Runnable() {
1605                 @Override
1606                 public void run() {
1607                     mCallback.onInputAdded(inputId);
1608                 }
1609             });
1610         }
1611 
postInputRemoved(final String inputId)1612         public void postInputRemoved(final String inputId) {
1613             mHandler.post(new Runnable() {
1614                 @Override
1615                 public void run() {
1616                     mCallback.onInputRemoved(inputId);
1617                 }
1618             });
1619         }
1620 
postInputUpdated(final String inputId)1621         public void postInputUpdated(final String inputId) {
1622             mHandler.post(new Runnable() {
1623                 @Override
1624                 public void run() {
1625                     mCallback.onInputUpdated(inputId);
1626                 }
1627             });
1628         }
1629 
postInputStateChanged(final String inputId, final int state)1630         public void postInputStateChanged(final String inputId, final int state) {
1631             mHandler.post(new Runnable() {
1632                 @Override
1633                 public void run() {
1634                     mCallback.onInputStateChanged(inputId, state);
1635                 }
1636             });
1637         }
1638 
postTvInputInfoUpdated(final TvInputInfo inputInfo)1639         public void postTvInputInfoUpdated(final TvInputInfo inputInfo) {
1640             mHandler.post(new Runnable() {
1641                 @Override
1642                 public void run() {
1643                     mCallback.onTvInputInfoUpdated(inputInfo);
1644                 }
1645             });
1646         }
1647 
postCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos)1648         public void postCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos) {
1649             mHandler.post(new Runnable() {
1650                 @Override
1651                 public void run() {
1652                     mCallback.onCurrentTunedInfosUpdated(currentTunedInfos);
1653                 }
1654             });
1655         }
1656     }
1657 
1658     /**
1659      * Interface used to receive events from Hardware objects.
1660      *
1661      * @hide
1662      */
1663     @SystemApi
1664     public abstract static class HardwareCallback {
1665         /**
1666          * This is called when {@link Hardware} is no longer available for the client.
1667          */
onReleased()1668         public abstract void onReleased();
1669 
1670         /**
1671          * This is called when the underlying {@link TvStreamConfig} has been changed.
1672          *
1673          * @param configs The new {@link TvStreamConfig}s.
1674          */
onStreamConfigChanged(TvStreamConfig[] configs)1675         public abstract void onStreamConfigChanged(TvStreamConfig[] configs);
1676     }
1677 
1678     /**
1679      * @hide
1680      */
TvInputManager(ITvInputManager service, int userId)1681     public TvInputManager(ITvInputManager service, int userId) {
1682         mService = service;
1683         mUserId = userId;
1684         mClient = new ITvInputClient.Stub() {
1685             @Override
1686             public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
1687                     int seq) {
1688                 synchronized (mSessionCallbackRecordMap) {
1689                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1690                     if (record == null) {
1691                         Log.e(TAG, "Callback not found for " + token);
1692                         return;
1693                     }
1694                     Session session = null;
1695                     if (token != null) {
1696                         session = new Session(token, channel, mService, mUserId, seq,
1697                                 mSessionCallbackRecordMap);
1698                     } else {
1699                         mSessionCallbackRecordMap.delete(seq);
1700                     }
1701                     record.postSessionCreated(session);
1702                 }
1703             }
1704 
1705             @Override
1706             public void onSessionReleased(int seq) {
1707                 synchronized (mSessionCallbackRecordMap) {
1708                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1709                     mSessionCallbackRecordMap.delete(seq);
1710                     if (record == null) {
1711                         Log.e(TAG, "Callback not found for seq:" + seq);
1712                         return;
1713                     }
1714                     record.mSession.releaseInternal();
1715                     record.postSessionReleased();
1716                 }
1717             }
1718 
1719             @Override
1720             public void onChannelRetuned(Uri channelUri, int seq) {
1721                 synchronized (mSessionCallbackRecordMap) {
1722                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1723                     if (record == null) {
1724                         Log.e(TAG, "Callback not found for seq " + seq);
1725                         return;
1726                     }
1727                     record.postChannelRetuned(channelUri);
1728                 }
1729             }
1730             @Override
1731             public void onAudioPresentationsChanged(List<AudioPresentation> audioPresentations,
1732                     int seq) {
1733                 synchronized (mSessionCallbackRecordMap) {
1734                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1735                     if (record == null) {
1736                         Log.e(TAG, "Callback not found for seq " + seq);
1737                         return;
1738                     }
1739                     if (record.mSession.updateAudioPresentations(audioPresentations)) {
1740                         record.postAudioPresentationsChanged(audioPresentations);
1741                     }
1742                 }
1743             }
1744 
1745             @Override
1746             public void onAudioPresentationSelected(int presentationId, int programId, int seq) {
1747                 synchronized (mSessionCallbackRecordMap) {
1748                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1749                     if (record == null) {
1750                         Log.e(TAG, "Callback not found for seq " + seq);
1751                         return;
1752                     }
1753                     if (record.mSession.updateAudioPresentationSelection(presentationId,
1754                             programId)) {
1755                         record.postAudioPresentationSelected(presentationId, programId);
1756                     }
1757                 }
1758             }
1759 
1760 
1761             @Override
1762             public void onTracksChanged(List<TvTrackInfo> tracks, int seq) {
1763                 synchronized (mSessionCallbackRecordMap) {
1764                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1765                     if (record == null) {
1766                         Log.e(TAG, "Callback not found for seq " + seq);
1767                         return;
1768                     }
1769                     if (record.mSession.updateTracks(tracks)) {
1770                         record.postTracksChanged(tracks);
1771                         postVideoSizeChangedIfNeededLocked(record);
1772                     }
1773                 }
1774             }
1775 
1776             @Override
1777             public void onTrackSelected(int type, String trackId, int seq) {
1778                 synchronized (mSessionCallbackRecordMap) {
1779                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1780                     if (record == null) {
1781                         Log.e(TAG, "Callback not found for seq " + seq);
1782                         return;
1783                     }
1784                     if (record.mSession.updateTrackSelection(type, trackId)) {
1785                         record.postTrackSelected(type, trackId);
1786                         postVideoSizeChangedIfNeededLocked(record);
1787                     }
1788                 }
1789             }
1790 
1791             private void postVideoSizeChangedIfNeededLocked(SessionCallbackRecord record) {
1792                 TvTrackInfo track = record.mSession.getVideoTrackToNotify();
1793                 if (track != null) {
1794                     record.postVideoSizeChanged(track.getVideoWidth(), track.getVideoHeight());
1795                 }
1796             }
1797 
1798             @Override
1799             public void onVideoAvailable(int seq) {
1800                 synchronized (mSessionCallbackRecordMap) {
1801                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1802                     if (record == null) {
1803                         Log.e(TAG, "Callback not found for seq " + seq);
1804                         return;
1805                     }
1806                     record.postVideoAvailable();
1807                 }
1808             }
1809 
1810             @Override
1811             public void onVideoUnavailable(int reason, int seq) {
1812                 synchronized (mSessionCallbackRecordMap) {
1813                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1814                     if (record == null) {
1815                         Log.e(TAG, "Callback not found for seq " + seq);
1816                         return;
1817                     }
1818                     record.postVideoUnavailable(reason);
1819                 }
1820             }
1821 
1822             @Override
1823             public void onVideoFreezeUpdated(boolean isFrozen, int seq) {
1824                 synchronized (mSessionCallbackRecordMap) {
1825                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1826                     if (record == null) {
1827                         Log.e(TAG, "Callback not found for seq " + seq);
1828                         return;
1829                     }
1830                     record.postVideoFreezeUpdated(isFrozen);
1831                 }
1832             }
1833 
1834             @Override
1835             public void onContentAllowed(int seq) {
1836                 synchronized (mSessionCallbackRecordMap) {
1837                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1838                     if (record == null) {
1839                         Log.e(TAG, "Callback not found for seq " + seq);
1840                         return;
1841                     }
1842                     record.postContentAllowed();
1843                 }
1844             }
1845 
1846             @Override
1847             public void onContentBlocked(String rating, int seq) {
1848                 synchronized (mSessionCallbackRecordMap) {
1849                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1850                     if (record == null) {
1851                         Log.e(TAG, "Callback not found for seq " + seq);
1852                         return;
1853                     }
1854                     record.postContentBlocked(TvContentRating.unflattenFromString(rating));
1855                 }
1856             }
1857 
1858             @Override
1859             public void onLayoutSurface(int left, int top, int right, int bottom, int seq) {
1860                 synchronized (mSessionCallbackRecordMap) {
1861                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1862                     if (record == null) {
1863                         Log.e(TAG, "Callback not found for seq " + seq);
1864                         return;
1865                     }
1866                     record.postLayoutSurface(left, top, right, bottom);
1867                 }
1868             }
1869 
1870             @Override
1871             public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
1872                 synchronized (mSessionCallbackRecordMap) {
1873                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1874                     if (record == null) {
1875                         Log.e(TAG, "Callback not found for seq " + seq);
1876                         return;
1877                     }
1878                     record.postSessionEvent(eventType, eventArgs);
1879                 }
1880             }
1881 
1882             @Override
1883             public void onTimeShiftStatusChanged(int status, int seq) {
1884                 synchronized (mSessionCallbackRecordMap) {
1885                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1886                     if (record == null) {
1887                         Log.e(TAG, "Callback not found for seq " + seq);
1888                         return;
1889                     }
1890                     record.postTimeShiftStatusChanged(status);
1891                 }
1892             }
1893 
1894             @Override
1895             public void onTimeShiftStartPositionChanged(long timeMs, int seq) {
1896                 synchronized (mSessionCallbackRecordMap) {
1897                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1898                     if (record == null) {
1899                         Log.e(TAG, "Callback not found for seq " + seq);
1900                         return;
1901                     }
1902                     record.postTimeShiftStartPositionChanged(timeMs);
1903                 }
1904             }
1905 
1906             @Override
1907             public void onTimeShiftCurrentPositionChanged(long timeMs, int seq) {
1908                 synchronized (mSessionCallbackRecordMap) {
1909                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1910                     if (record == null) {
1911                         Log.e(TAG, "Callback not found for seq " + seq);
1912                         return;
1913                     }
1914                     record.postTimeShiftCurrentPositionChanged(timeMs);
1915                 }
1916             }
1917 
1918             @Override
1919             public void onAitInfoUpdated(AitInfo aitInfo, int seq) {
1920                 synchronized (mSessionCallbackRecordMap) {
1921                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1922                     if (record == null) {
1923                         Log.e(TAG, "Callback not found for seq " + seq);
1924                         return;
1925                     }
1926                     record.postAitInfoUpdated(aitInfo);
1927                 }
1928             }
1929 
1930             @Override
1931             public void onSignalStrength(int strength, int seq) {
1932                 synchronized (mSessionCallbackRecordMap) {
1933                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1934                     if (record == null) {
1935                         Log.e(TAG, "Callback not found for seq " + seq);
1936                         return;
1937                     }
1938                     record.postSignalStrength(strength);
1939                 }
1940             }
1941 
1942             @Override
1943             public void onCueingMessageAvailability(boolean available, int seq) {
1944                 synchronized (mSessionCallbackRecordMap) {
1945                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1946                     if (record == null) {
1947                         Log.e(TAG, "Callback not found for seq " + seq);
1948                         return;
1949                     }
1950                     record.postCueingMessageAvailability(available);
1951                 }
1952             }
1953 
1954             @Override
1955             public void onTimeShiftMode(int mode, int seq) {
1956                 synchronized (mSessionCallbackRecordMap) {
1957                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1958                     if (record == null) {
1959                         Log.e(TAG, "Callback not found for seq " + seq);
1960                         return;
1961                     }
1962                     record.postTimeShiftMode(mode);
1963                 }
1964             }
1965 
1966             @Override
1967             public void onAvailableSpeeds(float[] speeds, int seq) {
1968                 synchronized (mSessionCallbackRecordMap) {
1969                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1970                     if (record == null) {
1971                         Log.e(TAG, "Callback not found for seq " + seq);
1972                         return;
1973                     }
1974                     record.postAvailableSpeeds(speeds);
1975                 }
1976             }
1977 
1978             @Override
1979             public void onTuned(Uri channelUri, int seq) {
1980                 synchronized (mSessionCallbackRecordMap) {
1981                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1982                     if (record == null) {
1983                         Log.e(TAG, "Callback not found for seq " + seq);
1984                         return;
1985                     }
1986                     record.postTuned(channelUri);
1987                     // TODO: synchronized and wrap the channelUri
1988                 }
1989             }
1990 
1991             @Override
1992             public void onTvMessage(int type, Bundle data, int seq) {
1993                 synchronized (mSessionCallbackRecordMap) {
1994                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
1995                     if (record == null) {
1996                         Log.e(TAG, "Callback not found for seq " + seq);
1997                         return;
1998                     }
1999                     record.postTvMessage(type, data);
2000                 }
2001             }
2002 
2003             @Override
2004             public void onRecordingStopped(Uri recordedProgramUri, int seq) {
2005                 synchronized (mSessionCallbackRecordMap) {
2006                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
2007                     if (record == null) {
2008                         Log.e(TAG, "Callback not found for seq " + seq);
2009                         return;
2010                     }
2011                     record.postRecordingStopped(recordedProgramUri);
2012                 }
2013             }
2014 
2015             @Override
2016             public void onError(int error, int seq) {
2017                 synchronized (mSessionCallbackRecordMap) {
2018                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
2019                     if (record == null) {
2020                         Log.e(TAG, "Callback not found for seq " + seq);
2021                         return;
2022                     }
2023                     record.postError(error);
2024                 }
2025             }
2026 
2027             @Override
2028             public void onBroadcastInfoResponse(BroadcastInfoResponse response, int seq) {
2029                 synchronized (mSessionCallbackRecordMap) {
2030                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
2031                     if (record == null) {
2032                         Log.e(TAG, "Callback not found for seq " + seq);
2033                         return;
2034                     }
2035                     record.postBroadcastInfoResponse(response);
2036                 }
2037             }
2038 
2039             @Override
2040             public void onAdResponse(AdResponse response, int seq) {
2041                 synchronized (mSessionCallbackRecordMap) {
2042                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
2043                     if (record == null) {
2044                         Log.e(TAG, "Callback not found for seq " + seq);
2045                         return;
2046                     }
2047                     record.postAdResponse(response);
2048                 }
2049             }
2050 
2051             @Override
2052             public void onAdBufferConsumed(AdBuffer buffer, int seq) {
2053                 synchronized (mSessionCallbackRecordMap) {
2054                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
2055                     if (record == null) {
2056                         Log.e(TAG, "Callback not found for seq " + seq);
2057                         return;
2058                     }
2059                     record.postAdBufferConsumed(buffer);
2060                 }
2061             }
2062 
2063             @Override
2064             public void onTvInputSessionData(String type, Bundle data, int seq) {
2065                 synchronized (mSessionCallbackRecordMap) {
2066                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
2067                     if (record == null) {
2068                         Log.e(TAG, "Callback not found for seq " + seq);
2069                         return;
2070                     }
2071                     record.postTvInputSessionData(type, data);
2072                 }
2073             }
2074         };
2075         ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
2076             @Override
2077             public void onInputAdded(String inputId) {
2078                 synchronized (mLock) {
2079                     mStateMap.put(inputId, INPUT_STATE_CONNECTED);
2080                     for (TvInputCallbackRecord record : mCallbackRecords) {
2081                         record.postInputAdded(inputId);
2082                     }
2083                 }
2084             }
2085 
2086             @Override
2087             public void onInputRemoved(String inputId) {
2088                 synchronized (mLock) {
2089                     mStateMap.remove(inputId);
2090                     for (TvInputCallbackRecord record : mCallbackRecords) {
2091                         record.postInputRemoved(inputId);
2092                     }
2093                 }
2094             }
2095 
2096             @Override
2097             public void onInputUpdated(String inputId) {
2098                 synchronized (mLock) {
2099                     for (TvInputCallbackRecord record : mCallbackRecords) {
2100                         record.postInputUpdated(inputId);
2101                     }
2102                 }
2103             }
2104 
2105             @Override
2106             public void onInputStateChanged(String inputId, int state) {
2107                 synchronized (mLock) {
2108                     mStateMap.put(inputId, state);
2109                     for (TvInputCallbackRecord record : mCallbackRecords) {
2110                         record.postInputStateChanged(inputId, state);
2111                     }
2112                 }
2113             }
2114 
2115             @Override
2116             public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
2117                 synchronized (mLock) {
2118                     for (TvInputCallbackRecord record : mCallbackRecords) {
2119                         record.postTvInputInfoUpdated(inputInfo);
2120                     }
2121                 }
2122             }
2123 
2124             @Override
2125             public void onCurrentTunedInfosUpdated(List<TunedInfo> currentTunedInfos) {
2126                 synchronized (mLock) {
2127                     for (TvInputCallbackRecord record : mCallbackRecords) {
2128                         record.postCurrentTunedInfosUpdated(currentTunedInfos);
2129                     }
2130                 }
2131             }
2132         };
2133         try {
2134             if (mService != null) {
2135                 mService.registerCallback(managerCallback, mUserId);
2136                 List<TvInputInfo> infos = mService.getTvInputList(mUserId);
2137                 synchronized (mLock) {
2138                     for (TvInputInfo info : infos) {
2139                         String inputId = info.getId();
2140                         mStateMap.put(inputId, mService.getTvInputState(inputId, mUserId));
2141                     }
2142                 }
2143             }
2144         } catch (RemoteException e) {
2145             throw e.rethrowFromSystemServer();
2146         }
2147     }
2148 
2149     /**
2150      * Returns the complete list of TV inputs on the system.
2151      *
2152      * @return List of {@link TvInputInfo} for each TV input that describes its meta information.
2153      */
getTvInputList()2154     public List<TvInputInfo> getTvInputList() {
2155         try {
2156             return mService.getTvInputList(mUserId);
2157         } catch (RemoteException e) {
2158             throw e.rethrowFromSystemServer();
2159         }
2160     }
2161 
2162     /**
2163      * Returns the {@link TvInputInfo} for a given TV input.
2164      *
2165      * @param inputId The ID of the TV input.
2166      * @return the {@link TvInputInfo} for a given TV input. {@code null} if not found.
2167      */
2168     @Nullable
getTvInputInfo(@onNull String inputId)2169     public TvInputInfo getTvInputInfo(@NonNull String inputId) {
2170         Preconditions.checkNotNull(inputId);
2171         try {
2172             return mService.getTvInputInfo(inputId, mUserId);
2173         } catch (RemoteException e) {
2174             throw e.rethrowFromSystemServer();
2175         }
2176     }
2177 
2178     /**
2179      * Updates the <code>TvInputInfo</code> for an existing TV input. A TV input service
2180      * implementation may call this method to pass the application and system an up-to-date
2181      * <code>TvInputInfo</code> object that describes itself.
2182      *
2183      * <p>The system automatically creates a <code>TvInputInfo</code> object for each TV input,
2184      * based on the information collected from the <code>AndroidManifest.xml</code>, thus it is not
2185      * necessary to call this method unless such information has changed dynamically.
2186      * Use {@link TvInputInfo.Builder} to build a new <code>TvInputInfo</code> object.
2187      *
2188      * <p>Attempting to change information about a TV input that the calling package does not own
2189      * does nothing.
2190      *
2191      * @param inputInfo The <code>TvInputInfo</code> object that contains new information.
2192      * @throws IllegalArgumentException if the argument is {@code null}.
2193      * @see TvInputCallback#onTvInputInfoUpdated(TvInputInfo)
2194      */
updateTvInputInfo(@onNull TvInputInfo inputInfo)2195     public void updateTvInputInfo(@NonNull TvInputInfo inputInfo) {
2196         Preconditions.checkNotNull(inputInfo);
2197         try {
2198             mService.updateTvInputInfo(inputInfo, mUserId);
2199         } catch (RemoteException e) {
2200             throw e.rethrowFromSystemServer();
2201         }
2202     }
2203 
2204     /**
2205      * Returns the state of a given TV input.
2206      *
2207      * <p>The state is one of the following:
2208      * <ul>
2209      * <li>{@link #INPUT_STATE_CONNECTED}
2210      * <li>{@link #INPUT_STATE_CONNECTED_STANDBY}
2211      * <li>{@link #INPUT_STATE_DISCONNECTED}
2212      * </ul>
2213      *
2214      * @param inputId The ID of the TV input.
2215      * @throws IllegalArgumentException if the argument is {@code null}.
2216      */
2217     @InputState
getInputState(@onNull String inputId)2218     public int getInputState(@NonNull String inputId) {
2219         Preconditions.checkNotNull(inputId);
2220         synchronized (mLock) {
2221             Integer state = mStateMap.get(inputId);
2222             if (state == null) {
2223                 Log.w(TAG, "Unrecognized input ID: " + inputId);
2224                 return INPUT_STATE_DISCONNECTED;
2225             }
2226             return state;
2227         }
2228     }
2229 
2230     /**
2231      * Returns available extension interfaces of a given hardware TV input. This can be used to
2232      * provide domain-specific features that are only known between certain hardware TV inputs
2233      * and their clients.
2234      *
2235      * @param inputId The ID of the TV input.
2236      * @return a non-null list of extension interface names available to the caller. An empty
2237      *         list indicates the given TV input is not found, or the given TV input is not a
2238      *         hardware TV input, or the given TV input doesn't support any extension
2239      *         interfaces, or the caller doesn't hold the required permission for the extension
2240      *         interfaces supported by the given TV input.
2241      * @see #getExtensionInterface
2242      * @hide
2243      */
2244     @SystemApi
2245     @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE)
2246     @NonNull
getAvailableExtensionInterfaceNames(@onNull String inputId)2247     public List<String> getAvailableExtensionInterfaceNames(@NonNull String inputId) {
2248         Preconditions.checkNotNull(inputId);
2249         try {
2250             return mService.getAvailableExtensionInterfaceNames(inputId, mUserId);
2251         } catch (RemoteException e) {
2252             throw e.rethrowFromSystemServer();
2253         }
2254     }
2255 
2256     /**
2257      * Returns an extension interface of a given hardware TV input. This can be used to provide
2258      * domain-specific features that are only known between certain hardware TV inputs and
2259      * their clients.
2260      *
2261      * @param inputId The ID of the TV input.
2262      * @param name The extension interface name.
2263      * @return an {@link IBinder} for the given extension interface, {@code null} if the given TV
2264      *         input is not found, or if the given TV input is not a hardware TV input, or if the
2265      *         given TV input doesn't support the given extension interface, or if the caller
2266      *         doesn't hold the required permission for the given extension interface.
2267      * @see #getAvailableExtensionInterfaceNames
2268      * @hide
2269      */
2270     @SystemApi
2271     @RequiresPermission(android.Manifest.permission.TIS_EXTENSION_INTERFACE)
2272     @Nullable
getExtensionInterface(@onNull String inputId, @NonNull String name)2273     public IBinder getExtensionInterface(@NonNull String inputId, @NonNull String name) {
2274         Preconditions.checkNotNull(inputId);
2275         Preconditions.checkNotNull(name);
2276         try {
2277             return mService.getExtensionInterface(inputId, name, mUserId);
2278         } catch (RemoteException e) {
2279             throw e.rethrowFromSystemServer();
2280         }
2281     }
2282 
2283     /**
2284      * Registers a {@link TvInputCallback}.
2285      *
2286      * @param callback A callback used to monitor status of the TV inputs.
2287      * @param handler A {@link Handler} that the status change will be delivered to.
2288      */
registerCallback(@onNull TvInputCallback callback, @NonNull Handler handler)2289     public void registerCallback(@NonNull TvInputCallback callback, @NonNull Handler handler) {
2290         Preconditions.checkNotNull(callback);
2291         Preconditions.checkNotNull(handler);
2292         synchronized (mLock) {
2293             mCallbackRecords.add(new TvInputCallbackRecord(callback, handler));
2294         }
2295     }
2296 
2297     /**
2298      * Unregisters the existing {@link TvInputCallback}.
2299      *
2300      * @param callback The existing callback to remove.
2301      */
unregisterCallback(@onNull final TvInputCallback callback)2302     public void unregisterCallback(@NonNull final TvInputCallback callback) {
2303         Preconditions.checkNotNull(callback);
2304         synchronized (mLock) {
2305             for (Iterator<TvInputCallbackRecord> it = mCallbackRecords.iterator();
2306                     it.hasNext(); ) {
2307                 TvInputCallbackRecord record = it.next();
2308                 if (record.getCallback() == callback) {
2309                     it.remove();
2310                     break;
2311                 }
2312             }
2313         }
2314     }
2315 
2316     /**
2317      * Returns the user's parental controls enabled state.
2318      *
2319      * @return {@code true} if the user enabled the parental controls, {@code false} otherwise.
2320      */
isParentalControlsEnabled()2321     public boolean isParentalControlsEnabled() {
2322         try {
2323             return mService.isParentalControlsEnabled(mUserId);
2324         } catch (RemoteException e) {
2325             throw e.rethrowFromSystemServer();
2326         }
2327     }
2328 
2329     /**
2330      * Sets the user's parental controls enabled state.
2331      *
2332      * @param enabled The user's parental controls enabled state. {@code true} if the user enabled
2333      *            the parental controls, {@code false} otherwise.
2334      * @see #isParentalControlsEnabled
2335      * @hide
2336      */
2337     @SystemApi
2338     @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
setParentalControlsEnabled(boolean enabled)2339     public void setParentalControlsEnabled(boolean enabled) {
2340         try {
2341             mService.setParentalControlsEnabled(enabled, mUserId);
2342         } catch (RemoteException e) {
2343             throw e.rethrowFromSystemServer();
2344         }
2345     }
2346 
2347     /**
2348      * Checks whether a given TV content rating is blocked by the user.
2349      *
2350      * @param rating The TV content rating to check. Can be {@link TvContentRating#UNRATED}.
2351      * @return {@code true} if the given TV content rating is blocked, {@code false} otherwise.
2352      */
isRatingBlocked(@onNull TvContentRating rating)2353     public boolean isRatingBlocked(@NonNull TvContentRating rating) {
2354         Preconditions.checkNotNull(rating);
2355         try {
2356             return mService.isRatingBlocked(rating.flattenToString(), mUserId);
2357         } catch (RemoteException e) {
2358             throw e.rethrowFromSystemServer();
2359         }
2360     }
2361 
2362     /**
2363      * Returns the list of blocked content ratings.
2364      *
2365      * @return the list of content ratings blocked by the user.
2366      */
getBlockedRatings()2367     public List<TvContentRating> getBlockedRatings() {
2368         try {
2369             List<TvContentRating> ratings = new ArrayList<>();
2370             for (String rating : mService.getBlockedRatings(mUserId)) {
2371                 ratings.add(TvContentRating.unflattenFromString(rating));
2372             }
2373             return ratings;
2374         } catch (RemoteException e) {
2375             throw e.rethrowFromSystemServer();
2376         }
2377     }
2378 
2379     /**
2380      * Adds a user blocked content rating.
2381      *
2382      * @param rating The content rating to block.
2383      * @see #isRatingBlocked
2384      * @see #removeBlockedRating
2385      * @hide
2386      */
2387     @SystemApi
2388     @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
addBlockedRating(@onNull TvContentRating rating)2389     public void addBlockedRating(@NonNull TvContentRating rating) {
2390         Preconditions.checkNotNull(rating);
2391         try {
2392             mService.addBlockedRating(rating.flattenToString(), mUserId);
2393         } catch (RemoteException e) {
2394             throw e.rethrowFromSystemServer();
2395         }
2396     }
2397 
2398     /**
2399      * Removes a user blocked content rating.
2400      *
2401      * @param rating The content rating to unblock.
2402      * @see #isRatingBlocked
2403      * @see #addBlockedRating
2404      * @hide
2405      */
2406     @SystemApi
2407     @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
removeBlockedRating(@onNull TvContentRating rating)2408     public void removeBlockedRating(@NonNull TvContentRating rating) {
2409         Preconditions.checkNotNull(rating);
2410         try {
2411             mService.removeBlockedRating(rating.flattenToString(), mUserId);
2412         } catch (RemoteException e) {
2413             throw e.rethrowFromSystemServer();
2414         }
2415     }
2416 
2417     /**
2418      * Returns the list of all TV content rating systems defined.
2419      * @hide
2420      */
2421     @SystemApi
2422     @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS)
getTvContentRatingSystemList()2423     public List<TvContentRatingSystemInfo> getTvContentRatingSystemList() {
2424         try {
2425             return mService.getTvContentRatingSystemList(mUserId);
2426         } catch (RemoteException e) {
2427             throw e.rethrowFromSystemServer();
2428         }
2429     }
2430 
2431     /**
2432      * Notifies the TV input of the given preview program that the program's browsable state is
2433      * disabled.
2434      * @hide
2435      */
2436     @SystemApi
2437     @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
notifyPreviewProgramBrowsableDisabled(String packageName, long programId)2438     public void notifyPreviewProgramBrowsableDisabled(String packageName, long programId) {
2439         Intent intent = new Intent();
2440         intent.setAction(TvContract.ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED);
2441         intent.putExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, programId);
2442         intent.setPackage(packageName);
2443         try {
2444             mService.sendTvInputNotifyIntent(intent, mUserId);
2445         } catch (RemoteException e) {
2446             throw e.rethrowFromSystemServer();
2447         }
2448     }
2449 
2450     /**
2451      * Notifies the TV input of the given watch next program that the program's browsable state is
2452      * disabled.
2453      * @hide
2454      */
2455     @SystemApi
2456     @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
notifyWatchNextProgramBrowsableDisabled(String packageName, long programId)2457     public void notifyWatchNextProgramBrowsableDisabled(String packageName, long programId) {
2458         Intent intent = new Intent();
2459         intent.setAction(TvContract.ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED);
2460         intent.putExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, programId);
2461         intent.setPackage(packageName);
2462         try {
2463             mService.sendTvInputNotifyIntent(intent, mUserId);
2464         } catch (RemoteException e) {
2465             throw e.rethrowFromSystemServer();
2466         }
2467     }
2468 
2469     /**
2470      * Notifies the TV input of the given preview program that the program is added to watch next.
2471      * @hide
2472      */
2473     @SystemApi
2474     @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)
notifyPreviewProgramAddedToWatchNext(String packageName, long previewProgramId, long watchNextProgramId)2475     public void notifyPreviewProgramAddedToWatchNext(String packageName, long previewProgramId,
2476             long watchNextProgramId) {
2477         Intent intent = new Intent();
2478         intent.setAction(TvContract.ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT);
2479         intent.putExtra(TvContract.EXTRA_PREVIEW_PROGRAM_ID, previewProgramId);
2480         intent.putExtra(TvContract.EXTRA_WATCH_NEXT_PROGRAM_ID, watchNextProgramId);
2481         intent.setPackage(packageName);
2482         try {
2483             mService.sendTvInputNotifyIntent(intent, mUserId);
2484         } catch (RemoteException e) {
2485             throw e.rethrowFromSystemServer();
2486         }
2487     }
2488 
2489     /**
2490      * Creates a {@link Session} for a given TV input.
2491      *
2492      * <p>The number of sessions that can be created at the same time is limited by the capability
2493      * of the given TV input.
2494      *
2495      * @param inputId The ID of the TV input.
2496      * @param tvAppAttributionSource The Attribution Source of the TV App.
2497      * @param callback A callback used to receive the created session.
2498      * @param handler A {@link Handler} that the session creation will be delivered to.
2499      * @hide
2500      */
createSession(@onNull String inputId, @NonNull AttributionSource tvAppAttributionSource, @NonNull final SessionCallback callback, @NonNull Handler handler)2501     public void createSession(@NonNull String inputId,
2502             @NonNull AttributionSource tvAppAttributionSource,
2503             @NonNull final SessionCallback callback, @NonNull Handler handler) {
2504         createSessionInternal(inputId, tvAppAttributionSource, false, callback, handler);
2505     }
2506 
2507     /**
2508      * Get a the client pid when creating the session with the session id provided.
2509      *
2510      * @param sessionId a String of session id that is used to query the client pid.
2511      * @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_PID}
2512      *         if the call fails.
2513      *
2514      * @hide
2515      */
2516     @SystemApi
2517     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
getClientPid(@onNull String sessionId)2518     public int getClientPid(@NonNull String sessionId) {
2519         return getClientPidInternal(sessionId);
2520     };
2521 
2522     /**
2523      * Get a the client user id when creating the session with the session id provided.
2524      *
2525      * @param sessionId a String of session id that is used to query the client user id.
2526      * @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_USER_ID}
2527      *         if the call fails.
2528      *
2529      * @hide
2530      */
2531     @SystemApi
2532     @FlaggedApi(Flags.FLAG_KIDS_MODE_TVDB_SHARING)
2533     @RequiresPermission(android.Manifest.permission.SINGLE_USER_TIS_ACCESS)
getClientUserId(@onNull String sessionId)2534     public @UserIdInt int getClientUserId(@NonNull String sessionId) {
2535         return getClientUserIdInternal(sessionId);
2536     }
2537 
2538     /**
2539      * Returns a priority for the given use case type and the client's foreground or background
2540      * status.
2541      *
2542      * @param useCase the use case type of the client.
2543      *        {@see TvInputService#PriorityHintUseCaseType}.
2544      * @param sessionId the unique id of the session owned by the client.
2545      *        {@see TvInputService#onCreateSession(String, String, AttributionSource)}.
2546      *
2547      * @return the use case priority value for the given use case type and the client's foreground
2548      *         or background status.
2549      *
2550      * @hide
2551      */
2552     @SystemApi
2553     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
getClientPriority(@vInputService.PriorityHintUseCaseType int useCase, @NonNull String sessionId)2554     public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase,
2555             @NonNull String sessionId) {
2556         Preconditions.checkNotNull(sessionId);
2557         if (!isValidUseCase(useCase)) {
2558             throw new IllegalArgumentException("Invalid use case: " + useCase);
2559         }
2560         return getClientPriorityInternal(useCase, sessionId);
2561     };
2562 
2563     /**
2564      * Returns a priority for the given use case type and the caller's foreground or background
2565      * status.
2566      *
2567      * @param useCase the use case type of the caller.
2568      *        {@see TvInputService#PriorityHintUseCaseType}.
2569      *
2570      * @return the use case priority value for the given use case type and the caller's foreground
2571      *         or background status.
2572      *
2573      * @hide
2574      */
2575     @SystemApi
2576     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
getClientPriority(@vInputService.PriorityHintUseCaseType int useCase)2577     public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase) {
2578         if (!isValidUseCase(useCase)) {
2579             throw new IllegalArgumentException("Invalid use case: " + useCase);
2580         }
2581         return getClientPriorityInternal(useCase, null);
2582     };
2583     /**
2584      * Creates a recording {@link Session} for a given TV input.
2585      *
2586      * <p>The number of sessions that can be created at the same time is limited by the capability
2587      * of the given TV input.
2588      *
2589      * @param inputId The ID of the TV input.
2590      * @param callback A callback used to receive the created session.
2591      * @param handler A {@link Handler} that the session creation will be delivered to.
2592      * @hide
2593      */
createRecordingSession(@onNull String inputId, @NonNull final SessionCallback callback, @NonNull Handler handler)2594     public void createRecordingSession(@NonNull String inputId,
2595             @NonNull final SessionCallback callback, @NonNull Handler handler) {
2596         createSessionInternal(inputId, null, true, callback, handler);
2597     }
2598 
createSessionInternal(String inputId, AttributionSource tvAppAttributionSource, boolean isRecordingSession, SessionCallback callback, Handler handler)2599     private void createSessionInternal(String inputId, AttributionSource tvAppAttributionSource,
2600             boolean isRecordingSession, SessionCallback callback, Handler handler) {
2601         Preconditions.checkNotNull(inputId);
2602         Preconditions.checkNotNull(callback);
2603         Preconditions.checkNotNull(handler);
2604         SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
2605         synchronized (mSessionCallbackRecordMap) {
2606             int seq = mNextSeq++;
2607             mSessionCallbackRecordMap.put(seq, record);
2608             try {
2609                 mService.createSession(
2610                         mClient, inputId, tvAppAttributionSource, isRecordingSession, seq, mUserId);
2611             } catch (RemoteException e) {
2612                 throw e.rethrowFromSystemServer();
2613             }
2614         }
2615     }
2616 
getClientPidInternal(String sessionId)2617     private int getClientPidInternal(String sessionId) {
2618         Preconditions.checkNotNull(sessionId);
2619         int clientPid = UNKNOWN_CLIENT_PID;
2620         try {
2621             clientPid = mService.getClientPid(sessionId);
2622         } catch (RemoteException e) {
2623             throw e.rethrowFromSystemServer();
2624         }
2625         return clientPid;
2626     }
2627 
2628     @FlaggedApi(Flags.FLAG_KIDS_MODE_TVDB_SHARING)
getClientUserIdInternal(String sessionId)2629     private int getClientUserIdInternal(String sessionId) {
2630         Preconditions.checkNotNull(sessionId);
2631         int clientUserId = UNKNOWN_CLIENT_USER_ID;
2632         try {
2633             clientUserId = mService.getClientUserId(sessionId);
2634         } catch (RemoteException e) {
2635             throw e.rethrowFromSystemServer();
2636         }
2637         return clientUserId;
2638     }
2639 
getClientPriorityInternal(int useCase, String sessionId)2640     private int getClientPriorityInternal(int useCase, String sessionId) {
2641         try {
2642             return mService.getClientPriority(useCase, sessionId);
2643         } catch (RemoteException e) {
2644             throw e.rethrowFromSystemServer();
2645         }
2646     }
2647 
isValidUseCase(int useCase)2648     private boolean isValidUseCase(int useCase) {
2649         return useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND
2650             || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN
2651             || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK
2652             || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE
2653             || useCase == TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD;
2654     }
2655 
2656     /**
2657      * Returns the TvStreamConfig list of the given TV input.
2658      *
2659      * If you are using {@link Hardware} object from {@link
2660      * #acquireTvInputHardware}, you should get the list of available streams
2661      * from {@link HardwareCallback#onStreamConfigChanged} method, not from
2662      * here. This method is designed to be used with {@link #captureFrame} in
2663      * capture scenarios specifically and not suitable for any other use.
2664      *
2665      * @param inputId The ID of the TV input.
2666      * @return List of {@link TvStreamConfig} which is available for capturing
2667      *   of the given TV input.
2668      * @hide
2669      */
2670     @SystemApi
2671     @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT)
getAvailableTvStreamConfigList(String inputId)2672     public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId) {
2673         try {
2674             return mService.getAvailableTvStreamConfigList(inputId, mUserId);
2675         } catch (RemoteException e) {
2676             throw e.rethrowFromSystemServer();
2677         }
2678     }
2679 
2680     /**
2681      * Take a snapshot of the given TV input into the provided Surface.
2682      *
2683      * @param inputId The ID of the TV input.
2684      * @param surface the {@link Surface} to which the snapshot is captured.
2685      * @param config the {@link TvStreamConfig} which is used for capturing.
2686      * @return true when the {@link Surface} is ready to be captured.
2687      * @hide
2688      */
2689     @SystemApi
2690     @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT)
captureFrame(String inputId, Surface surface, TvStreamConfig config)2691     public boolean captureFrame(String inputId, Surface surface, TvStreamConfig config) {
2692         try {
2693             return mService.captureFrame(inputId, surface, config, mUserId);
2694         } catch (RemoteException e) {
2695             throw e.rethrowFromSystemServer();
2696         }
2697     }
2698 
2699     /**
2700      * Returns true if there is only a single TV input session.
2701      *
2702      * @hide
2703      */
2704     @SystemApi
2705     @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT)
isSingleSessionActive()2706     public boolean isSingleSessionActive() {
2707         try {
2708             return mService.isSingleSessionActive(mUserId);
2709         } catch (RemoteException e) {
2710             throw e.rethrowFromSystemServer();
2711         }
2712     }
2713 
2714     /**
2715      * Returns a list of TvInputHardwareInfo objects representing available hardware.
2716      *
2717      * @hide
2718      */
2719     @SystemApi
2720     @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
getHardwareList()2721     public List<TvInputHardwareInfo> getHardwareList() {
2722         try {
2723             return mService.getHardwareList();
2724         } catch (RemoteException e) {
2725             throw e.rethrowFromSystemServer();
2726         }
2727     }
2728 
2729     /**
2730      * Acquires {@link Hardware} object for the given device ID.
2731      *
2732      * <p>A subsequent call to this method on the same {@code deviceId} will release the currently
2733      * acquired Hardware.
2734      *
2735      * @param deviceId The device ID to acquire Hardware for.
2736      * @param callback A callback to receive updates on Hardware.
2737      * @param info The TV input which will use the acquired Hardware.
2738      * @return Hardware on success, {@code null} otherwise.
2739      *
2740      * @hide
2741      * @removed
2742      */
2743     @SystemApi
2744     @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
acquireTvInputHardware(int deviceId, final HardwareCallback callback, TvInputInfo info)2745     public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback,
2746             TvInputInfo info) {
2747         return acquireTvInputHardware(deviceId, info, callback);
2748     }
2749 
2750     /**
2751      * Acquires {@link Hardware} object for the given device ID.
2752      *
2753      * <p>A subsequent call to this method on the same {@code deviceId} could release the currently
2754      * acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current
2755      * request.
2756      *
2757      * <p>If the client would like to provide information for the TRM to compare, use
2758      * {@link #acquireTvInputHardware(int, TvInputInfo, HardwareCallback, String, int)} instead.
2759      *
2760      * <p>Otherwise default priority will be applied.
2761      *
2762      * @param deviceId The device ID to acquire Hardware for.
2763      * @param info The TV input which will use the acquired Hardware.
2764      * @param callback A callback to receive updates on Hardware.
2765      * @return Hardware on success, {@code null} otherwise.
2766      *
2767      * @hide
2768      */
2769     @SystemApi
2770     @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info, @NonNull final HardwareCallback callback)2771     public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info,
2772             @NonNull final HardwareCallback callback) {
2773         Preconditions.checkNotNull(info);
2774         Preconditions.checkNotNull(callback);
2775         return acquireTvInputHardwareInternal(deviceId, info, null,
2776                 TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, new Executor() {
2777                     public void execute(Runnable r) {
2778                         r.run();
2779                     }
2780                 }, callback);
2781     }
2782 
2783     /**
2784      * Acquires {@link Hardware} object for the given device ID.
2785      *
2786      * <p>A subsequent call to this method on the same {@code deviceId} could release the currently
2787      * acquired Hardware if TunerResourceManager(TRM) detects higher priority from the current
2788      * request.
2789      *
2790      * @param deviceId The device ID to acquire Hardware for.
2791      * @param info The TV input which will use the acquired Hardware.
2792      * @param tvInputSessionId a String returned to TIS when the session was created.
2793      *        {@see TvInputService#onCreateSession(String, String, AttributionSource)}. If null, the
2794      *        client will be treated as a background app.
2795      * @param priorityHint The use case of the client. {@see TvInputService#PriorityHintUseCaseType}
2796      * @param executor the executor on which the listener would be invoked.
2797      * @param callback A callback to receive updates on Hardware.
2798      * @return Hardware on success, {@code null} otherwise. When the TRM decides to not grant
2799      *         resource, null is returned and the {@link IllegalStateException} is thrown with
2800      *         "No enough resources".
2801      *
2802      * @hide
2803      */
2804     @SystemApi
2805     @Nullable
2806     @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2807     public Hardware acquireTvInputHardware(int deviceId, @NonNull TvInputInfo info,
2808             @Nullable String tvInputSessionId,
2809             @TvInputService.PriorityHintUseCaseType int priorityHint,
2810             @NonNull @CallbackExecutor Executor executor,
2811             @NonNull final HardwareCallback callback) {
2812         Preconditions.checkNotNull(info);
2813         Preconditions.checkNotNull(callback);
2814         return acquireTvInputHardwareInternal(deviceId, info, tvInputSessionId, priorityHint,
2815                 executor, callback);
2816     }
2817 
2818     /**
2819      * API to add a hardware device in the TvInputHardwareManager for CTS testing
2820      * purpose.
2821      *
2822      * @param deviceId Id of the adding hardware device.
2823      *
2824      * @hide
2825      */
2826     @TestApi
2827     public void addHardwareDevice(int deviceId) {
2828         try {
2829             mService.addHardwareDevice(deviceId);
2830         } catch (RemoteException e) {
2831             throw e.rethrowFromSystemServer();
2832         }
2833     }
2834 
2835     /**
2836      * API to remove a hardware device in the TvInputHardwareManager for CTS testing
2837      * purpose.
2838      *
2839      * @param deviceId Id of the removing hardware device.
2840      *
2841      * @hide
2842      */
2843     @TestApi
2844     public void removeHardwareDevice(int deviceId) {
2845         try {
2846             mService.removeHardwareDevice(deviceId);
2847         } catch (RemoteException e) {
2848             throw e.rethrowFromSystemServer();
2849         }
2850     }
2851 
2852     private Hardware acquireTvInputHardwareInternal(int deviceId, TvInputInfo info,
2853             String tvInputSessionId, int priorityHint,
2854             Executor executor, final HardwareCallback callback) {
2855         try {
2856             ITvInputHardware hardware =
2857                     mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() {
2858                 @Override
2859                 public void onReleased() {
2860                             final long identity = Binder.clearCallingIdentity();
2861                             try {
2862                                 executor.execute(() -> callback.onReleased());
2863                             } finally {
2864                                 Binder.restoreCallingIdentity(identity);
2865                             }
2866                 }
2867 
2868                 @Override
2869                 public void onStreamConfigChanged(TvStreamConfig[] configs) {
2870                             final long identity = Binder.clearCallingIdentity();
2871                             try {
2872                                 executor.execute(() -> callback.onStreamConfigChanged(configs));
2873                             } finally {
2874                                 Binder.restoreCallingIdentity(identity);
2875                             }
2876                 }
2877                     }, info, mUserId, tvInputSessionId, priorityHint);
2878             if (hardware == null) {
2879                 return null;
2880             }
2881             return new Hardware(hardware);
2882         } catch (RemoteException e) {
2883             throw e.rethrowFromSystemServer();
2884         }
2885     }
2886 
2887     /**
2888      * Releases previously acquired hardware object.
2889      *
2890      * @param deviceId The device ID this Hardware was acquired for
2891      * @param hardware Hardware to release.
2892      *
2893      * @hide
2894      */
2895     @SystemApi
2896     @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
2897     public void releaseTvInputHardware(int deviceId, Hardware hardware) {
2898         try {
2899             mService.releaseTvInputHardware(deviceId, hardware.getInterface(), mUserId);
2900         } catch (RemoteException e) {
2901             throw e.rethrowFromSystemServer();
2902         }
2903     }
2904 
2905     /**
2906      * Returns the list of currently available DVB frontend devices on the system.
2907      *
2908      * @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
2909      * @hide
2910      */
2911     @SystemApi
2912     @RequiresPermission(android.Manifest.permission.DVB_DEVICE)
2913     @NonNull
2914     public List<DvbDeviceInfo> getDvbDeviceList() {
2915         try {
2916             return mService.getDvbDeviceList();
2917         } catch (RemoteException e) {
2918             throw e.rethrowFromSystemServer();
2919         }
2920     }
2921 
2922     /**
2923      * Returns a {@link ParcelFileDescriptor} of a specified DVB device of a given type for a given
2924      * {@link DvbDeviceInfo}.
2925      *
2926      * @param info A {@link DvbDeviceInfo} to open a DVB device.
2927      * @param deviceType A DVB device type.
2928      * @return a {@link ParcelFileDescriptor} of a specified DVB device for a given
2929      * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
2930      * failed to open.
2931      * @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found.
2932 
2933      * @see <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB API v3</a>
2934      * @hide
2935      */
2936     @SystemApi
2937     @RequiresPermission(android.Manifest.permission.DVB_DEVICE)
2938     @Nullable
2939     public ParcelFileDescriptor openDvbDevice(@NonNull DvbDeviceInfo info,
2940             @DvbDeviceType int deviceType) {
2941         try {
2942             if (DVB_DEVICE_START > deviceType || DVB_DEVICE_END < deviceType) {
2943                 throw new IllegalArgumentException("Invalid DVB device: " + deviceType);
2944             }
2945             return mService.openDvbDevice(info, deviceType);
2946         } catch (RemoteException e) {
2947             throw e.rethrowFromSystemServer();
2948         }
2949     }
2950 
2951     /**
2952      * Requests to make a channel browsable.
2953      *
2954      * <p>Once called, the system will review the request and make the channel browsable based on
2955      * its policy. The first request from a package is guaranteed to be approved.
2956      *
2957      * @param channelUri The URI for the channel to be browsable.
2958      * @hide
2959      */
2960     public void requestChannelBrowsable(Uri channelUri) {
2961         try {
2962             mService.requestChannelBrowsable(channelUri, mUserId);
2963         } catch (RemoteException e) {
2964             throw e.rethrowFromSystemServer();
2965         }
2966     }
2967 
2968     /**
2969      * Returns the list of session information for {@link TvInputService.Session} that are
2970      * currently in use.
2971      * <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get
2972      * the channel URIs. If the permission is not granted,
2973      * {@link TunedInfo#getChannelUri()} returns {@code null}.
2974      * @hide
2975      */
2976     @SystemApi
2977     @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO)
2978     @NonNull
2979     public List<TunedInfo> getCurrentTunedInfos() {
2980         try {
2981             return mService.getCurrentTunedInfos(mUserId);
2982         } catch (RemoteException e) {
2983             throw e.rethrowFromSystemServer();
2984         }
2985     }
2986 
2987     /**
2988      * The Session provides the per-session functionality of TV inputs.
2989      * @hide
2990      */
2991     public static final class Session {
2992         static final int DISPATCH_IN_PROGRESS = -1;
2993         static final int DISPATCH_NOT_HANDLED = 0;
2994         static final int DISPATCH_HANDLED = 1;
2995 
2996         private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
2997 
2998         private final ITvInputManager mService;
2999         private final int mUserId;
3000         private final int mSeq;
3001 
3002         // For scheduling input event handling on the main thread. This also serves as a lock to
3003         // protect pending input events and the input channel.
3004         private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
3005 
3006         private final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
3007         private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
3008         private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
3009 
3010         private IBinder mToken;
3011         private TvInputEventSender mSender;
3012         private InputChannel mChannel;
3013 
3014         private final Object mMetadataLock = new Object();
3015         // @GuardedBy("mMetadataLock")
3016         private final List<AudioPresentation> mAudioPresentations = new ArrayList<>();
3017         // @GuardedBy("mMetadataLock")
3018         private final List<TvTrackInfo> mAudioTracks = new ArrayList<>();
3019         // @GuardedBy("mMetadataLock")
3020         private final List<TvTrackInfo> mVideoTracks = new ArrayList<>();
3021         // @GuardedBy("mMetadataLock")
3022         private final List<TvTrackInfo> mSubtitleTracks = new ArrayList<>();
3023         // @GuardedBy("mMetadataLock")
3024         private int mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
3025         // @GuardedBy("mMetadataLock")
3026         private int mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
3027         // @GuardedBy("mMetadataLock")
3028         private String mSelectedAudioTrackId;
3029         // @GuardedBy("mMetadataLock")
3030         private String mSelectedVideoTrackId;
3031         // @GuardedBy("mMetadataLock")
3032         private String mSelectedSubtitleTrackId;
3033         // @GuardedBy("mMetadataLock")
3034         private int mVideoWidth;
3035         // @GuardedBy("mMetadataLock")
3036         private int mVideoHeight;
3037 
3038         private TvInteractiveAppManager.Session mIAppSession;
3039         private TvAdManager.Session mAdSession;
3040         private boolean mIAppNotificationEnabled = false;
3041 
3042         private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
3043                 int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
3044             mToken = token;
3045             mChannel = channel;
3046             mService = service;
3047             mUserId = userId;
3048             mSeq = seq;
3049             mSessionCallbackRecordMap = sessionCallbackRecordMap;
3050         }
3051 
3052         public TvInteractiveAppManager.Session getInteractiveAppSession() {
3053             return mIAppSession;
3054         }
3055 
3056         public void setInteractiveAppSession(TvInteractiveAppManager.Session iAppSession) {
3057             this.mIAppSession = iAppSession;
3058         }
3059 
3060         public TvAdManager.Session getAdSession() {
3061             return mAdSession;
3062         }
3063 
3064         public void setAdSession(TvAdManager.Session adSession) {
3065             this.mAdSession = adSession;
3066         }
3067 
3068         /**
3069          * Releases this session.
3070          */
3071         public void release() {
3072             if (mToken == null) {
3073                 Log.w(TAG, "The session has been already released");
3074                 return;
3075             }
3076             try {
3077                 mService.releaseSession(mToken, mUserId);
3078             } catch (RemoteException e) {
3079                 throw e.rethrowFromSystemServer();
3080             }
3081 
3082             releaseInternal();
3083         }
3084 
3085         /**
3086          * Sets this as the main session. The main session is a session whose corresponding TV
3087          * input determines the HDMI-CEC active source device.
3088          *
3089          * @see TvView#setMain
3090          */
3091         void setMain() {
3092             if (mToken == null) {
3093                 Log.w(TAG, "The session has been already released");
3094                 return;
3095             }
3096             try {
3097                 mService.setMainSession(mToken, mUserId);
3098             } catch (RemoteException e) {
3099                 throw e.rethrowFromSystemServer();
3100             }
3101         }
3102 
3103         /**
3104          * Sets the {@link android.view.Surface} for this session.
3105          *
3106          * @param surface A {@link android.view.Surface} used to render video.
3107          */
3108         public void setSurface(Surface surface) {
3109             if (mToken == null) {
3110                 Log.w(TAG, "The session has been already released");
3111                 return;
3112             }
3113             // surface can be null.
3114             try {
3115                 mService.setSurface(mToken, surface, mUserId);
3116             } catch (RemoteException e) {
3117                 throw e.rethrowFromSystemServer();
3118             }
3119         }
3120 
3121         /**
3122          * Notifies of any structural changes (format or size) of the surface passed in
3123          * {@link #setSurface}.
3124          *
3125          * @param format The new PixelFormat of the surface.
3126          * @param width The new width of the surface.
3127          * @param height The new height of the surface.
3128          */
3129         public void dispatchSurfaceChanged(int format, int width, int height) {
3130             if (mToken == null) {
3131                 Log.w(TAG, "The session has been already released");
3132                 return;
3133             }
3134             try {
3135                 mService.dispatchSurfaceChanged(mToken, format, width, height, mUserId);
3136             } catch (RemoteException e) {
3137                 throw e.rethrowFromSystemServer();
3138             }
3139         }
3140 
3141         /**
3142          * Sets the relative stream volume of this session to handle a change of audio focus.
3143          *
3144          * @param volume A volume value between 0.0f to 1.0f.
3145          * @throws IllegalArgumentException if the volume value is out of range.
3146          */
3147         public void setStreamVolume(float volume) {
3148             if (mToken == null) {
3149                 Log.w(TAG, "The session has been already released");
3150                 return;
3151             }
3152             try {
3153                 if (volume < 0.0f || volume > 1.0f) {
3154                     throw new IllegalArgumentException("volume should be between 0.0f and 1.0f");
3155                 }
3156                 mService.setVolume(mToken, volume, mUserId);
3157             } catch (RemoteException e) {
3158                 throw e.rethrowFromSystemServer();
3159             }
3160         }
3161 
3162         /**
3163          * Tunes to a given channel.
3164          *
3165          * @param channelUri The URI of a channel.
3166          */
3167         public void tune(Uri channelUri) {
3168             tune(channelUri, null);
3169         }
3170 
3171         /**
3172          * Tunes to a given channel.
3173          *
3174          * @param channelUri The URI of a channel.
3175          * @param params A set of extra parameters which might be handled with this tune event.
3176          */
3177         public void tune(@NonNull Uri channelUri, Bundle params) {
3178             Preconditions.checkNotNull(channelUri);
3179             if (mToken == null) {
3180                 Log.w(TAG, "The session has been already released");
3181                 return;
3182             }
3183             synchronized (mMetadataLock) {
3184                 mAudioPresentations.clear();
3185                 mAudioTracks.clear();
3186                 mVideoTracks.clear();
3187                 mSubtitleTracks.clear();
3188                 mSelectedAudioProgramId = AudioPresentation.PROGRAM_ID_UNKNOWN;
3189                 mSelectedAudioPresentationId = AudioPresentation.PRESENTATION_ID_UNKNOWN;
3190                 mSelectedAudioTrackId = null;
3191                 mSelectedVideoTrackId = null;
3192                 mSelectedSubtitleTrackId = null;
3193                 mVideoWidth = 0;
3194                 mVideoHeight = 0;
3195             }
3196             try {
3197                 mService.tune(mToken, channelUri, params, mUserId);
3198             } catch (RemoteException e) {
3199                 throw e.rethrowFromSystemServer();
3200             }
3201         }
3202 
3203         /**
3204          * Enables or disables the caption for this session.
3205          *
3206          * @param enabled {@code true} to enable, {@code false} to disable.
3207          */
3208         public void setCaptionEnabled(boolean enabled) {
3209             if (mToken == null) {
3210                 Log.w(TAG, "The session has been already released");
3211                 return;
3212             }
3213             try {
3214                 mService.setCaptionEnabled(mToken, enabled, mUserId);
3215             } catch (RemoteException e) {
3216                 throw e.rethrowFromSystemServer();
3217             }
3218         }
3219 
3220         /**
3221          * Selects an audio presentation
3222          *
3223          * @param presentationId The ID of the audio presentation to select.
3224          * @param programId The ID of the program offering the selected audio presentation.
3225          * @see #getAudioPresentations
3226          */
3227         public void selectAudioPresentation(int presentationId, int programId) {
3228             synchronized (mMetadataLock) {
3229                 if (presentationId != AudioPresentation.PRESENTATION_ID_UNKNOWN
3230                         && !containsAudioPresentation(mAudioPresentations, presentationId)) {
3231                     Log.w(TAG, "Invalid audio presentation id: " + presentationId);
3232                     return;
3233                 }
3234             }
3235             if (mToken == null) {
3236                 Log.w(TAG, "The session has been already released");
3237                 return;
3238             }
3239             try {
3240                 mService.selectAudioPresentation(mToken, presentationId, programId, mUserId);
3241             } catch (RemoteException e) {
3242                 throw e.rethrowFromSystemServer();
3243             }
3244         }
3245 
3246         private boolean containsAudioPresentation(List<AudioPresentation> audioPresentations,
3247                     int presentationId) {
3248             synchronized (mMetadataLock) {
3249                 for (AudioPresentation audioPresentation : audioPresentations) {
3250                     if (audioPresentation.getPresentationId() == presentationId) {
3251                         return true;
3252                     }
3253                 }
3254                 return false;
3255             }
3256         }
3257 
3258         /**
3259          * Returns a list of audio presentations.
3260          *
3261          * @return the list of audio presentations.
3262          * Returns empty AudioPresentation list if no presentations are available.
3263          */
3264         public List<AudioPresentation> getAudioPresentations() {
3265             synchronized (mMetadataLock) {
3266                 if (mAudioPresentations == null) {
3267                     return new ArrayList<AudioPresentation>();
3268                 }
3269                 return new ArrayList<AudioPresentation>(mAudioPresentations);
3270             }
3271         }
3272 
3273         /**
3274          * Returns the program ID of the selected audio presentation.
3275          *
3276          * @return The ID of the program providing the selected audio presentation.
3277          * Returns {@value AudioPresentation.PROGRAM_ID_UNKNOWN} if no audio presentation has
3278          * been selected from a program.
3279          * @see #selectAudioPresentation
3280          */
3281         public int getSelectedProgramId() {
3282             synchronized (mMetadataLock) {
3283                 return mSelectedAudioProgramId;
3284             }
3285         }
3286 
3287         /**
3288          * Returns the presentation ID of the selected audio presentation.
3289          *
3290          * @return The ID of the selected audio presentation.
3291          * Returns {@value AudioPresentation.PRESENTATION_ID_UNKNOWN} if no audio presentation
3292          * has been selected.
3293          * @see #selectAudioPresentation
3294          */
3295         public int getSelectedAudioPresentationId() {
3296             synchronized (mMetadataLock) {
3297                 return mSelectedAudioPresentationId;
3298             }
3299         }
3300 
3301         /**
3302          * Responds to onAudioPresentationsChanged() and updates the internal audio presentation
3303          * information.
3304          * @return true if there is an update.
3305          */
3306         boolean updateAudioPresentations(List<AudioPresentation> audioPresentations) {
3307             synchronized (mMetadataLock) {
3308                 mAudioPresentations.clear();
3309                 for (AudioPresentation presentation : audioPresentations) {
3310                     mAudioPresentations.add(presentation);
3311                 }
3312                 return !mAudioPresentations.isEmpty();
3313             }
3314         }
3315 
3316         /**
3317          * Responds to onAudioPresentationSelected() and updates the internal audio presentation
3318          * selection information.
3319          * @return true if there is an update.
3320          */
3321         boolean updateAudioPresentationSelection(int presentationId, int programId) {
3322             synchronized (mMetadataLock) {
3323                 if ((programId != mSelectedAudioProgramId)
3324                         || (presentationId != mSelectedAudioPresentationId)) {
3325                     mSelectedAudioPresentationId = presentationId;
3326                     mSelectedAudioProgramId = programId;
3327                     return true;
3328                 }
3329             }
3330             return false;
3331         }
3332 
3333         /**
3334          * Selects a track.
3335          *
3336          * @param type The type of the track to select. The type can be
3337          *            {@link TvTrackInfo#TYPE_AUDIO}, {@link TvTrackInfo#TYPE_VIDEO} or
3338          *            {@link TvTrackInfo#TYPE_SUBTITLE}.
3339          * @param trackId The ID of the track to select. When {@code null}, the currently selected
3340          *            track of the given type will be unselected.
3341          * @see #getTracks
3342          */
3343         public void selectTrack(int type, @Nullable String trackId) {
3344             synchronized (mMetadataLock) {
3345                 if (type == TvTrackInfo.TYPE_AUDIO) {
3346                     if (trackId != null && !containsTrack(mAudioTracks, trackId)) {
3347                         Log.w(TAG, "Invalid audio trackId: " + trackId);
3348                         return;
3349                     }
3350                 } else if (type == TvTrackInfo.TYPE_VIDEO) {
3351                     if (trackId != null && !containsTrack(mVideoTracks, trackId)) {
3352                         Log.w(TAG, "Invalid video trackId: " + trackId);
3353                         return;
3354                     }
3355                 } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
3356                     if (trackId != null && !containsTrack(mSubtitleTracks, trackId)) {
3357                         Log.w(TAG, "Invalid subtitle trackId: " + trackId);
3358                         return;
3359                     }
3360                 } else {
3361                     throw new IllegalArgumentException("invalid type: " + type);
3362                 }
3363             }
3364             if (mToken == null) {
3365                 Log.w(TAG, "The session has been already released");
3366                 return;
3367             }
3368             try {
3369                 mService.selectTrack(mToken, type, trackId, mUserId);
3370             } catch (RemoteException e) {
3371                 throw e.rethrowFromSystemServer();
3372             }
3373         }
3374 
3375         private boolean containsTrack(List<TvTrackInfo> tracks, String trackId) {
3376             for (TvTrackInfo track : tracks) {
3377                 if (track.getId().equals(trackId)) {
3378                     return true;
3379                 }
3380             }
3381             return false;
3382         }
3383 
3384         /**
3385          * Returns the list of tracks for a given type. Returns {@code null} if the information is
3386          * not available.
3387          *
3388          * @param type The type of the tracks. The type can be {@link TvTrackInfo#TYPE_AUDIO},
3389          *            {@link TvTrackInfo#TYPE_VIDEO} or {@link TvTrackInfo#TYPE_SUBTITLE}.
3390          * @return the list of tracks for the given type.
3391          */
3392         @Nullable
3393         public List<TvTrackInfo> getTracks(int type) {
3394             synchronized (mMetadataLock) {
3395                 if (type == TvTrackInfo.TYPE_AUDIO) {
3396                     if (mAudioTracks == null) {
3397                         return null;
3398                     }
3399                     return new ArrayList<>(mAudioTracks);
3400                 } else if (type == TvTrackInfo.TYPE_VIDEO) {
3401                     if (mVideoTracks == null) {
3402                         return null;
3403                     }
3404                     return new ArrayList<>(mVideoTracks);
3405                 } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
3406                     if (mSubtitleTracks == null) {
3407                         return null;
3408                     }
3409                     return new ArrayList<>(mSubtitleTracks);
3410                 }
3411             }
3412             throw new IllegalArgumentException("invalid type: " + type);
3413         }
3414 
3415         /**
3416          * Returns the selected track for a given type. Returns {@code null} if the information is
3417          * not available or any of the tracks for the given type is not selected.
3418          *
3419          * @return The ID of the selected track.
3420          * @see #selectTrack
3421          */
3422         @Nullable
3423         public String getSelectedTrack(int type) {
3424             synchronized (mMetadataLock) {
3425                 if (type == TvTrackInfo.TYPE_AUDIO) {
3426                     return mSelectedAudioTrackId;
3427                 } else if (type == TvTrackInfo.TYPE_VIDEO) {
3428                     return mSelectedVideoTrackId;
3429                 } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
3430                     return mSelectedSubtitleTrackId;
3431                 }
3432             }
3433             throw new IllegalArgumentException("invalid type: " + type);
3434         }
3435 
3436         /**
3437          * Enables interactive app notification.
3438          *
3439          * @param enabled {@code true} if you want to enable interactive app notifications.
3440          *                {@code false} otherwise.
3441          */
3442         public void setInteractiveAppNotificationEnabled(boolean enabled) {
3443             if (mToken == null) {
3444                 Log.w(TAG, "The session has been already released");
3445                 return;
3446             }
3447             try {
3448                 mService.setInteractiveAppNotificationEnabled(mToken, enabled, mUserId);
3449                 mIAppNotificationEnabled = enabled;
3450             } catch (RemoteException e) {
3451                 throw e.rethrowFromSystemServer();
3452             }
3453         }
3454 
3455         /**
3456          * Responds to onTracksChanged() and updates the internal track information. Returns true if
3457          * there is an update.
3458          */
3459         boolean updateTracks(List<TvTrackInfo> tracks) {
3460             synchronized (mMetadataLock) {
3461                 mAudioTracks.clear();
3462                 mVideoTracks.clear();
3463                 mSubtitleTracks.clear();
3464                 for (TvTrackInfo track : tracks) {
3465                     if (track.getType() == TvTrackInfo.TYPE_AUDIO) {
3466                         mAudioTracks.add(track);
3467                     } else if (track.getType() == TvTrackInfo.TYPE_VIDEO) {
3468                         mVideoTracks.add(track);
3469                     } else if (track.getType() == TvTrackInfo.TYPE_SUBTITLE) {
3470                         mSubtitleTracks.add(track);
3471                     }
3472                 }
3473                 return !mAudioTracks.isEmpty() || !mVideoTracks.isEmpty()
3474                         || !mSubtitleTracks.isEmpty();
3475             }
3476         }
3477 
3478         /**
3479          * Responds to onTrackSelected() and updates the internal track selection information.
3480          * Returns true if there is an update.
3481          */
3482         boolean updateTrackSelection(int type, String trackId) {
3483             synchronized (mMetadataLock) {
3484                 if (type == TvTrackInfo.TYPE_AUDIO
3485                         && !TextUtils.equals(trackId, mSelectedAudioTrackId)) {
3486                     mSelectedAudioTrackId = trackId;
3487                     return true;
3488                 } else if (type == TvTrackInfo.TYPE_VIDEO
3489                         && !TextUtils.equals(trackId, mSelectedVideoTrackId)) {
3490                     mSelectedVideoTrackId = trackId;
3491                     return true;
3492                 } else if (type == TvTrackInfo.TYPE_SUBTITLE
3493                         && !TextUtils.equals(trackId, mSelectedSubtitleTrackId)) {
3494                     mSelectedSubtitleTrackId = trackId;
3495                     return true;
3496                 }
3497             }
3498             return false;
3499         }
3500 
3501         /**
3502          * Returns the new/updated video track that contains new video size information. Returns
3503          * null if there is no video track to notify. Subsequent calls of this method results in a
3504          * non-null video track returned only by the first call and null returned by following
3505          * calls. The caller should immediately notify of the video size change upon receiving the
3506          * track.
3507          */
3508         TvTrackInfo getVideoTrackToNotify() {
3509             synchronized (mMetadataLock) {
3510                 if (!mVideoTracks.isEmpty() && mSelectedVideoTrackId != null) {
3511                     for (TvTrackInfo track : mVideoTracks) {
3512                         if (track.getId().equals(mSelectedVideoTrackId)) {
3513                             int videoWidth = track.getVideoWidth();
3514                             int videoHeight = track.getVideoHeight();
3515                             if (mVideoWidth != videoWidth || mVideoHeight != videoHeight) {
3516                                 mVideoWidth = videoWidth;
3517                                 mVideoHeight = videoHeight;
3518                                 return track;
3519                             }
3520                         }
3521                     }
3522                 }
3523             }
3524             return null;
3525         }
3526 
3527         /**
3528          * Plays a given recorded TV program.
3529          */
3530         void timeShiftPlay(Uri recordedProgramUri) {
3531             if (mToken == null) {
3532                 Log.w(TAG, "The session has been already released");
3533                 return;
3534             }
3535             try {
3536                 mService.timeShiftPlay(mToken, recordedProgramUri, mUserId);
3537             } catch (RemoteException e) {
3538                 throw e.rethrowFromSystemServer();
3539             }
3540         }
3541 
3542         /**
3543          * Pauses the playback. Call {@link #timeShiftResume()} to restart the playback.
3544          */
3545         void timeShiftPause() {
3546             if (mToken == null) {
3547                 Log.w(TAG, "The session has been already released");
3548                 return;
3549             }
3550             try {
3551                 mService.timeShiftPause(mToken, mUserId);
3552             } catch (RemoteException e) {
3553                 throw e.rethrowFromSystemServer();
3554             }
3555         }
3556 
3557         /**
3558          * Resumes the playback. No-op if it is already playing the channel.
3559          */
3560         void timeShiftResume() {
3561             if (mToken == null) {
3562                 Log.w(TAG, "The session has been already released");
3563                 return;
3564             }
3565             try {
3566                 mService.timeShiftResume(mToken, mUserId);
3567             } catch (RemoteException e) {
3568                 throw e.rethrowFromSystemServer();
3569             }
3570         }
3571 
3572         /**
3573          * Seeks to a specified time position.
3574          *
3575          * <p>Normally, the position is given within range between the start and the current time,
3576          * inclusively.
3577          *
3578          * @param timeMs The time position to seek to, in milliseconds since the epoch.
3579          * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged
3580          */
3581         void timeShiftSeekTo(long timeMs) {
3582             if (mToken == null) {
3583                 Log.w(TAG, "The session has been already released");
3584                 return;
3585             }
3586             try {
3587                 mService.timeShiftSeekTo(mToken, timeMs, mUserId);
3588             } catch (RemoteException e) {
3589                 throw e.rethrowFromSystemServer();
3590             }
3591         }
3592 
3593         /**
3594          * Sets playback rate using {@link android.media.PlaybackParams}.
3595          *
3596          * @param params The playback params.
3597          */
3598         void timeShiftSetPlaybackParams(PlaybackParams params) {
3599             if (mToken == null) {
3600                 Log.w(TAG, "The session has been already released");
3601                 return;
3602             }
3603             try {
3604                 mService.timeShiftSetPlaybackParams(mToken, params, mUserId);
3605             } catch (RemoteException e) {
3606                 throw e.rethrowFromSystemServer();
3607             }
3608         }
3609 
3610         /**
3611          * Sets time shift mode.
3612          *
3613          * @param mode The time shift mode. The value is one of the following:
3614          * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
3615          * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
3616          * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
3617          * @hide
3618          */
3619         void timeShiftSetMode(@TimeShiftMode int mode) {
3620             if (mToken == null) {
3621                 Log.w(TAG, "The session has been already released");
3622                 return;
3623             }
3624             try {
3625                 mService.timeShiftSetMode(mToken, mode, mUserId);
3626             } catch (RemoteException e) {
3627                 throw e.rethrowFromSystemServer();
3628             }
3629         }
3630 
3631         /**
3632          * Enable/disable position tracking.
3633          *
3634          * @param enable {@code true} to enable tracking, {@code false} otherwise.
3635          */
3636         void timeShiftEnablePositionTracking(boolean enable) {
3637             if (mToken == null) {
3638                 Log.w(TAG, "The session has been already released");
3639                 return;
3640             }
3641             try {
3642                 mService.timeShiftEnablePositionTracking(mToken, enable, mUserId);
3643             } catch (RemoteException e) {
3644                 throw e.rethrowFromSystemServer();
3645             }
3646         }
3647 
3648         void stopPlayback(int mode) {
3649             if (mToken == null) {
3650                 Log.w(TAG, "The session has been already released");
3651                 return;
3652             }
3653             try {
3654                 mService.stopPlayback(mToken, mode, mUserId);
3655             } catch (RemoteException e) {
3656                 throw e.rethrowFromSystemServer();
3657             }
3658         }
3659 
3660         void resumePlayback() {
3661             if (mToken == null) {
3662                 Log.w(TAG, "The session has been already released");
3663                 return;
3664             }
3665             try {
3666                 mService.resumePlayback(mToken, mUserId);
3667             } catch (RemoteException e) {
3668                 throw e.rethrowFromSystemServer();
3669             }
3670         }
3671 
3672         void setVideoFrozen(boolean isFrozen) {
3673             if (mToken == null) {
3674                 Log.w(TAG, "The session has been already released");
3675                 return;
3676             }
3677             try {
3678                 mService.setVideoFrozen(mToken, isFrozen, mUserId);
3679             } catch (RemoteException e) {
3680                 throw e.rethrowFromSystemServer();
3681             }
3682         }
3683 
3684         /**
3685          * Sends TV messages to the service for testing purposes
3686          */
3687         public void notifyTvMessage(int type, Bundle data) {
3688             try {
3689                 mService.notifyTvMessage(mToken, type, data, mUserId);
3690             } catch (RemoteException e) {
3691                 throw e.rethrowFromSystemServer();
3692             }
3693         }
3694 
3695         /**
3696          * Sets whether the TV message of the specific type should be enabled.
3697          */
3698         public void setTvMessageEnabled(int type, boolean enabled) {
3699             try {
3700                 mService.setTvMessageEnabled(mToken, type, enabled, mUserId);
3701             } catch (RemoteException e) {
3702                 throw e.rethrowFromSystemServer();
3703             }
3704         }
3705 
3706         /**
3707          * Starts TV program recording in the current recording session.
3708          *
3709          * @param programUri The URI for the TV program to record as a hint, built by
3710          *            {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
3711          */
3712         void startRecording(@Nullable Uri programUri) {
3713             startRecording(programUri, null);
3714         }
3715 
3716         /**
3717          * Starts TV program recording in the current recording session.
3718          *
3719          * @param programUri The URI for the TV program to record as a hint, built by
3720          *            {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
3721          * @param params A set of extra parameters which might be handled with this event.
3722          */
3723         void startRecording(@Nullable Uri programUri, @Nullable Bundle params) {
3724             if (mToken == null) {
3725                 Log.w(TAG, "The session has been already released");
3726                 return;
3727             }
3728             try {
3729                 mService.startRecording(mToken, programUri, params, mUserId);
3730             } catch (RemoteException e) {
3731                 throw e.rethrowFromSystemServer();
3732             }
3733         }
3734 
3735         /**
3736          * Stops TV program recording in the current recording session.
3737          */
3738         void stopRecording() {
3739             if (mToken == null) {
3740                 Log.w(TAG, "The session has been already released");
3741                 return;
3742             }
3743             try {
3744                 mService.stopRecording(mToken, mUserId);
3745             } catch (RemoteException e) {
3746                 throw e.rethrowFromSystemServer();
3747             }
3748         }
3749 
3750         /**
3751          * Pauses TV program recording in the current recording session.
3752          *
3753          * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped
3754          *            name, i.e. prefixed with a package name you own, so that different developers
3755          *            will not create conflicting keys.
3756          *        {@link TvRecordingClient#pauseRecording(Bundle)}.
3757          */
3758         void pauseRecording(@NonNull Bundle params) {
3759             if (mToken == null) {
3760                 Log.w(TAG, "The session has been already released");
3761                 return;
3762             }
3763             try {
3764                 mService.pauseRecording(mToken, params, mUserId);
3765             } catch (RemoteException e) {
3766                 throw e.rethrowFromSystemServer();
3767             }
3768         }
3769 
3770         /**
3771          * Resumes TV program recording in the current recording session.
3772          *
3773          * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped
3774          *            name, i.e. prefixed with a package name you own, so that different developers
3775          *            will not create conflicting keys.
3776          *        {@link TvRecordingClient#resumeRecording(Bundle)}.
3777          */
3778         void resumeRecording(@NonNull Bundle params) {
3779             if (mToken == null) {
3780                 Log.w(TAG, "The session has been already released");
3781                 return;
3782             }
3783             try {
3784                 mService.resumeRecording(mToken, params, mUserId);
3785             } catch (RemoteException e) {
3786                 throw e.rethrowFromSystemServer();
3787             }
3788         }
3789 
3790         /**
3791          * Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle)
3792          * TvInputService.Session.appPrivateCommand()} on the current TvView.
3793          *
3794          * @param action Name of the command to be performed. This <em>must</em> be a scoped name,
3795          *            i.e. prefixed with a package name you own, so that different developers will
3796          *            not create conflicting commands.
3797          * @param data Any data to include with the command.
3798          */
3799         public void sendAppPrivateCommand(String action, Bundle data) {
3800             if (mToken == null) {
3801                 Log.w(TAG, "The session has been already released");
3802                 return;
3803             }
3804             try {
3805                 mService.sendAppPrivateCommand(mToken, action, data, mUserId);
3806             } catch (RemoteException e) {
3807                 throw e.rethrowFromSystemServer();
3808             }
3809         }
3810 
3811         /**
3812          * Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
3813          * should be called whenever the layout of its containing view is changed.
3814          * {@link #removeOverlayView()} should be called to remove the overlay view.
3815          * Since a session can have only one overlay view, this method should be called only once
3816          * or it can be called again after calling {@link #removeOverlayView()}.
3817          *
3818          * @param view A view playing TV.
3819          * @param frame A position of the overlay view.
3820          * @throws IllegalStateException if {@code view} is not attached to a window.
3821          */
3822         void createOverlayView(@NonNull View view, @NonNull Rect frame) {
3823             Preconditions.checkNotNull(view);
3824             Preconditions.checkNotNull(frame);
3825             if (view.getWindowToken() == null) {
3826                 throw new IllegalStateException("view must be attached to a window");
3827             }
3828             if (mToken == null) {
3829                 Log.w(TAG, "The session has been already released");
3830                 return;
3831             }
3832             try {
3833                 mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
3834             } catch (RemoteException e) {
3835                 throw e.rethrowFromSystemServer();
3836             }
3837         }
3838 
3839         /**
3840          * Relayouts the current overlay view.
3841          *
3842          * @param frame A new position of the overlay view.
3843          */
3844         void relayoutOverlayView(@NonNull Rect frame) {
3845             Preconditions.checkNotNull(frame);
3846             if (mToken == null) {
3847                 Log.w(TAG, "The session has been already released");
3848                 return;
3849             }
3850             try {
3851                 mService.relayoutOverlayView(mToken, frame, mUserId);
3852             } catch (RemoteException e) {
3853                 throw e.rethrowFromSystemServer();
3854             }
3855         }
3856 
3857         /**
3858          * Removes the current overlay view.
3859          */
3860         void removeOverlayView() {
3861             if (mToken == null) {
3862                 Log.w(TAG, "The session has been already released");
3863                 return;
3864             }
3865             try {
3866                 mService.removeOverlayView(mToken, mUserId);
3867             } catch (RemoteException e) {
3868                 throw e.rethrowFromSystemServer();
3869             }
3870         }
3871 
3872         /**
3873          * Requests to unblock content blocked by parental controls.
3874          */
3875         void unblockContent(@NonNull TvContentRating unblockedRating) {
3876             Preconditions.checkNotNull(unblockedRating);
3877             if (mToken == null) {
3878                 Log.w(TAG, "The session has been already released");
3879                 return;
3880             }
3881             try {
3882                 mService.unblockContent(mToken, unblockedRating.flattenToString(), mUserId);
3883             } catch (RemoteException e) {
3884                 throw e.rethrowFromSystemServer();
3885             }
3886         }
3887 
3888         /**
3889          * Dispatches an input event to this session.
3890          *
3891          * @param event An {@link InputEvent} to dispatch. Cannot be {@code null}.
3892          * @param token A token used to identify the input event later in the callback.
3893          * @param callback A callback used to receive the dispatch result. Cannot be {@code null}.
3894          * @param handler A {@link Handler} that the dispatch result will be delivered to. Cannot be
3895          *            {@code null}.
3896          * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
3897          *         {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
3898          *         {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
3899          *         be invoked later.
3900          * @hide
3901          */
3902         public int dispatchInputEvent(@NonNull InputEvent event, Object token,
3903                 @NonNull FinishedInputEventCallback callback, @NonNull Handler handler) {
3904             Preconditions.checkNotNull(event);
3905             Preconditions.checkNotNull(callback);
3906             Preconditions.checkNotNull(handler);
3907             synchronized (mHandler) {
3908                 if (mChannel == null) {
3909                     return DISPATCH_NOT_HANDLED;
3910                 }
3911                 PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
3912                 if (Looper.myLooper() == Looper.getMainLooper()) {
3913                     // Already running on the main thread so we can send the event immediately.
3914                     return sendInputEventOnMainLooperLocked(p);
3915                 }
3916 
3917                 // Post the event to the main thread.
3918                 Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
3919                 msg.setAsynchronous(true);
3920                 mHandler.sendMessage(msg);
3921                 return DISPATCH_IN_PROGRESS;
3922             }
3923         }
3924 
3925         /**
3926          * Callback that is invoked when an input event that was dispatched to this session has been
3927          * finished.
3928          *
3929          * @hide
3930          */
3931         public interface FinishedInputEventCallback {
3932             /**
3933              * Called when the dispatched input event is finished.
3934              *
3935              * @param token A token passed to {@link #dispatchInputEvent}.
3936              * @param handled {@code true} if the dispatched input event was handled properly.
3937              *            {@code false} otherwise.
3938              */
3939             void onFinishedInputEvent(Object token, boolean handled);
3940         }
3941 
3942         // Must be called on the main looper
3943         private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
3944             synchronized (mHandler) {
3945                 int result = sendInputEventOnMainLooperLocked(p);
3946                 if (result == DISPATCH_IN_PROGRESS) {
3947                     return;
3948                 }
3949             }
3950 
3951             invokeFinishedInputEventCallback(p, false);
3952         }
3953 
3954         private int sendInputEventOnMainLooperLocked(PendingEvent p) {
3955             if (mChannel != null) {
3956                 if (mSender == null) {
3957                     mSender = new TvInputEventSender(mChannel, mHandler.getLooper());
3958                 }
3959 
3960                 final InputEvent event = p.mEvent;
3961                 final int seq = event.getSequenceNumber();
3962                 if (mSender.sendInputEvent(seq, event)) {
3963                     mPendingEvents.put(seq, p);
3964                     Message msg = mHandler.obtainMessage(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
3965                     msg.setAsynchronous(true);
3966                     mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
3967                     return DISPATCH_IN_PROGRESS;
3968                 }
3969 
3970                 Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
3971                         + event);
3972             }
3973             return DISPATCH_NOT_HANDLED;
3974         }
3975 
3976         void finishedInputEvent(int seq, boolean handled, boolean timeout) {
3977             final PendingEvent p;
3978             synchronized (mHandler) {
3979                 int index = mPendingEvents.indexOfKey(seq);
3980                 if (index < 0) {
3981                     return; // spurious, event already finished or timed out
3982                 }
3983 
3984                 p = mPendingEvents.valueAt(index);
3985                 mPendingEvents.removeAt(index);
3986 
3987                 if (timeout) {
3988                     Log.w(TAG, "Timeout waiting for session to handle input event after "
3989                             + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
3990                 } else {
3991                     mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
3992                 }
3993             }
3994 
3995             invokeFinishedInputEventCallback(p, handled);
3996         }
3997 
3998         // Assumes the event has already been removed from the queue.
3999         void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
4000             p.mHandled = handled;
4001             if (p.mEventHandler.getLooper().isCurrentThread()) {
4002                 // Already running on the callback handler thread so we can send the callback
4003                 // immediately.
4004                 p.run();
4005             } else {
4006                 // Post the event to the callback handler thread.
4007                 // In this case, the callback will be responsible for recycling the event.
4008                 Message msg = Message.obtain(p.mEventHandler, p);
4009                 msg.setAsynchronous(true);
4010                 msg.sendToTarget();
4011             }
4012         }
4013 
4014         private void flushPendingEventsLocked() {
4015             mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
4016 
4017             final int count = mPendingEvents.size();
4018             for (int i = 0; i < count; i++) {
4019                 int seq = mPendingEvents.keyAt(i);
4020                 Message msg = mHandler.obtainMessage(InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
4021                 msg.setAsynchronous(true);
4022                 msg.sendToTarget();
4023             }
4024         }
4025 
4026         private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
4027                 FinishedInputEventCallback callback, Handler handler) {
4028             PendingEvent p = mPendingEventPool.acquire();
4029             if (p == null) {
4030                 p = new PendingEvent();
4031             }
4032             p.mEvent = event;
4033             p.mEventToken = token;
4034             p.mCallback = callback;
4035             p.mEventHandler = handler;
4036             return p;
4037         }
4038 
4039         private void recyclePendingEventLocked(PendingEvent p) {
4040             p.recycle();
4041             mPendingEventPool.release(p);
4042         }
4043 
4044         IBinder getToken() {
4045             return mToken;
4046         }
4047 
4048         private void releaseInternal() {
4049             mToken = null;
4050             synchronized (mHandler) {
4051                 if (mChannel != null) {
4052                     if (mSender != null) {
4053                         flushPendingEventsLocked();
4054                         mSender.dispose();
4055                         mSender = null;
4056                     }
4057                     mChannel.dispose();
4058                     mChannel = null;
4059                 }
4060             }
4061             synchronized (mSessionCallbackRecordMap) {
4062                 mSessionCallbackRecordMap.delete(mSeq);
4063             }
4064         }
4065 
4066         public void requestBroadcastInfo(BroadcastInfoRequest request) {
4067             if (mToken == null) {
4068                 Log.w(TAG, "The session has been already released");
4069                 return;
4070             }
4071             try {
4072                 mService.requestBroadcastInfo(mToken, request, mUserId);
4073             } catch (RemoteException e) {
4074                 throw e.rethrowFromSystemServer();
4075             }
4076         }
4077 
4078         /**
4079          * Removes broadcast info.
4080          * @param requestId the corresponding request ID sent from
4081          *                  {@link #requestBroadcastInfo(android.media.tv.BroadcastInfoRequest)}
4082          */
4083         public void removeBroadcastInfo(int requestId) {
4084             if (mToken == null) {
4085                 Log.w(TAG, "The session has been already released");
4086                 return;
4087             }
4088             try {
4089                 mService.removeBroadcastInfo(mToken, requestId, mUserId);
4090             } catch (RemoteException e) {
4091                 throw e.rethrowFromSystemServer();
4092             }
4093         }
4094 
4095         public void requestAd(AdRequest request) {
4096             if (mToken == null) {
4097                 Log.w(TAG, "The session has been already released");
4098                 return;
4099             }
4100             try {
4101                 mService.requestAd(mToken, request, mUserId);
4102             } catch (RemoteException e) {
4103                 throw e.rethrowFromSystemServer();
4104             }
4105         }
4106 
4107         /**
4108          * Notifies when the advertisement buffer is filled and ready to be read.
4109          */
4110         public void notifyAdBufferReady(AdBuffer buffer) {
4111             if (mToken == null) {
4112                 Log.w(TAG, "The session has been already released");
4113                 return;
4114             }
4115             try {
4116                 mService.notifyAdBufferReady(mToken, buffer, mUserId);
4117             } catch (RemoteException e) {
4118                 throw e.rethrowFromSystemServer();
4119             } finally {
4120                 if (buffer != null) {
4121                     buffer.getSharedMemory().close();
4122                 }
4123             }
4124         }
4125 
4126         /**
4127          * Notifies data from session of linked TvAdService.
4128          */
4129         public void notifyTvAdSessionData(String type, Bundle data) {
4130             if (mToken == null) {
4131                 Log.w(TAG, "The session has been already released");
4132                 return;
4133             }
4134             try {
4135                 mService.notifyTvAdSessionData(mToken, type, data, mUserId);
4136             } catch (RemoteException e) {
4137                 throw e.rethrowFromSystemServer();
4138             }
4139         }
4140 
4141         private final class InputEventHandler extends Handler {
4142             public static final int MSG_SEND_INPUT_EVENT = 1;
4143             public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
4144             public static final int MSG_FLUSH_INPUT_EVENT = 3;
4145 
4146             InputEventHandler(Looper looper) {
4147                 super(looper, null, true);
4148             }
4149 
4150             @Override
4151             public void handleMessage(Message msg) {
4152                 switch (msg.what) {
4153                     case MSG_SEND_INPUT_EVENT: {
4154                         sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
4155                         return;
4156                     }
4157                     case MSG_TIMEOUT_INPUT_EVENT: {
4158                         finishedInputEvent(msg.arg1, false, true);
4159                         return;
4160                     }
4161                     case MSG_FLUSH_INPUT_EVENT: {
4162                         finishedInputEvent(msg.arg1, false, false);
4163                         return;
4164                     }
4165                 }
4166             }
4167         }
4168 
4169         private final class TvInputEventSender extends InputEventSender {
4170             public TvInputEventSender(InputChannel inputChannel, Looper looper) {
4171                 super(inputChannel, looper);
4172             }
4173 
4174             @Override
4175             public void onInputEventFinished(int seq, boolean handled) {
4176                 finishedInputEvent(seq, handled, false);
4177             }
4178         }
4179 
4180         private final class PendingEvent implements Runnable {
4181             public InputEvent mEvent;
4182             public Object mEventToken;
4183             public FinishedInputEventCallback mCallback;
4184             public Handler mEventHandler;
4185             public boolean mHandled;
4186 
4187             public void recycle() {
4188                 mEvent = null;
4189                 mEventToken = null;
4190                 mCallback = null;
4191                 mEventHandler = null;
4192                 mHandled = false;
4193             }
4194 
4195             @Override
4196             public void run() {
4197                 mCallback.onFinishedInputEvent(mEventToken, mHandled);
4198 
4199                 synchronized (mEventHandler) {
4200                     recyclePendingEventLocked(this);
4201                 }
4202             }
4203         }
4204     }
4205 
4206     /**
4207      * The Hardware provides the per-hardware functionality of TV hardware.
4208      *
4209      * <p>TV hardware is physical hardware attached to the Android device; for example, HDMI ports,
4210      * Component/Composite ports, etc. Specifically, logical devices such as HDMI CEC logical
4211      * devices don't fall into this category.
4212      *
4213      * @hide
4214      */
4215     @SystemApi
4216     public final static class Hardware {
4217         private final ITvInputHardware mInterface;
4218 
4219         private Hardware(ITvInputHardware hardwareInterface) {
4220             mInterface = hardwareInterface;
4221         }
4222 
4223         private ITvInputHardware getInterface() {
4224             return mInterface;
4225         }
4226 
4227         public boolean setSurface(Surface surface, TvStreamConfig config) {
4228             try {
4229                 return mInterface.setSurface(surface, config);
4230             } catch (RemoteException e) {
4231                 throw new RuntimeException(e);
4232             }
4233         }
4234 
4235         public void setStreamVolume(float volume) {
4236             try {
4237                 mInterface.setStreamVolume(volume);
4238             } catch (RemoteException e) {
4239                 throw new RuntimeException(e);
4240             }
4241         }
4242 
4243         /** @removed */
4244         @SystemApi
4245         public boolean dispatchKeyEventToHdmi(KeyEvent event) {
4246             return false;
4247         }
4248 
4249         /**
4250          * Override default audio sink from audio policy.
4251          *
4252          * @param audioType device type of the audio sink to override with.
4253          * @param audioAddress device address of the audio sink to override with.
4254          * @param samplingRate desired sampling rate. Use default when it's 0.
4255          * @param channelMask desired channel mask. Use default when it's
4256          *        AudioFormat.CHANNEL_OUT_DEFAULT.
4257          * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
4258          */
4259         public void overrideAudioSink(@AudioDeviceInfo.AudioDeviceType int audioType,
4260                 String audioAddress, int samplingRate, int channelMask, int format) {
4261             try {
4262                 mInterface.overrideAudioSink(audioType, audioAddress, samplingRate, channelMask,
4263                         format);
4264             } catch (RemoteException e) {
4265                 throw new RuntimeException(e);
4266             }
4267         }
4268 
4269         /**
4270          * Override default audio sink from audio policy.
4271          *
4272          * @param device {@link android.media.AudioDeviceInfo} to use.
4273          * @param samplingRate desired sampling rate. Use default when it's 0.
4274          * @param channelMask desired channel mask. Use default when it's
4275          *        AudioFormat.CHANNEL_OUT_DEFAULT.
4276          * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
4277          */
4278         public void overrideAudioSink(@NonNull AudioDeviceInfo device,
4279                 @IntRange(from = 0) int samplingRate,
4280                 int channelMask, @Encoding int format) {
4281             Objects.requireNonNull(device);
4282             try {
4283                 mInterface.overrideAudioSink(
4284                         AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
4285                         device.getAddress(), samplingRate, channelMask, format);
4286             } catch (RemoteException e) {
4287                 throw new RuntimeException(e);
4288             }
4289         }
4290     }
4291 }
4292