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