• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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.tuner;
18 
19 import static android.media.tv.flags.Flags.FLAG_SET_RESOURCE_HOLDER_RETAIN;
20 
21 import android.annotation.BytesLong;
22 import android.annotation.CallbackExecutor;
23 import android.annotation.FlaggedApi;
24 import android.annotation.IntDef;
25 import android.annotation.IntRange;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.RequiresPermission;
29 import android.annotation.SuppressLint;
30 import android.annotation.SystemApi;
31 import android.content.Context;
32 import android.content.pm.PackageManager;
33 import android.hardware.tv.tuner.Constant;
34 import android.hardware.tv.tuner.Constant64Bit;
35 import android.hardware.tv.tuner.FrontendScanType;
36 import android.media.MediaCodec;
37 import android.media.tv.TvInputService;
38 import android.media.tv.flags.Flags;
39 import android.media.tv.tuner.dvr.DvrPlayback;
40 import android.media.tv.tuner.dvr.DvrRecorder;
41 import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener;
42 import android.media.tv.tuner.dvr.OnRecordStatusChangedListener;
43 import android.media.tv.tuner.filter.Filter;
44 import android.media.tv.tuner.filter.Filter.Subtype;
45 import android.media.tv.tuner.filter.Filter.Type;
46 import android.media.tv.tuner.filter.FilterCallback;
47 import android.media.tv.tuner.filter.SharedFilter;
48 import android.media.tv.tuner.filter.SharedFilterCallback;
49 import android.media.tv.tuner.filter.TimeFilter;
50 import android.media.tv.tuner.frontend.Atsc3PlpInfo;
51 import android.media.tv.tuner.frontend.FrontendInfo;
52 import android.media.tv.tuner.frontend.FrontendSettings;
53 import android.media.tv.tuner.frontend.FrontendStatus;
54 import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
55 import android.media.tv.tuner.frontend.FrontendStatusReadiness;
56 import android.media.tv.tuner.frontend.OnTuneEventListener;
57 import android.media.tv.tuner.frontend.ScanCallback;
58 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
59 import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
60 import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
61 import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
62 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
63 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
64 import android.media.tv.tunerresourcemanager.TunerResourceManager;
65 import android.os.Handler;
66 import android.os.Looper;
67 import android.os.Message;
68 import android.os.Process;
69 import android.util.Log;
70 
71 import com.android.internal.util.FrameworkStatsLog;
72 
73 import java.lang.annotation.Retention;
74 import java.lang.annotation.RetentionPolicy;
75 import java.lang.ref.WeakReference;
76 import java.util.ArrayList;
77 import java.util.Arrays;
78 import java.util.Collections;
79 import java.util.HashMap;
80 import java.util.Iterator;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.Objects;
84 import java.util.concurrent.Executor;
85 import java.util.concurrent.locks.ReentrantLock;
86 
87 /**
88  * This class is used to interact with hardware tuners devices.
89  *
90  * <p> Each TvInputService Session should create one instance of this class.
91  *
92  * <p> This class controls the TIS interaction with Tuner HAL.
93  *
94  * @hide
95  */
96 @SystemApi
97 public class Tuner implements AutoCloseable  {
98     /**
99      * Invalid TS packet ID.
100      */
101     public static final int INVALID_TS_PID = Constant.INVALID_TS_PID;
102     /**
103      * Invalid stream ID.
104      */
105     public static final int INVALID_STREAM_ID = Constant.INVALID_STREAM_ID;
106     /**
107      * Invalid filter ID.
108      */
109     public static final int INVALID_FILTER_ID = Constant.INVALID_FILTER_ID;
110     /**
111      * Invalid AV Sync ID.
112      */
113     public static final int INVALID_AV_SYNC_ID = Constant.INVALID_AV_SYNC_ID;
114     /**
115      * Invalid timestamp.
116      *
117      * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
118      * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()},
119      * {@link Tuner#getAvSyncTime(int)} or {@link TsRecordEvent#getPts()} and
120      * {@link MmtpRecordEvent#getPts()} when the requested timestamp is not available.
121      *
122      * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
123      * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
124      * @see Tuner#getAvSyncTime(int)
125      * @see android.media.tv.tuner.filter.TsRecordEvent#getPts()
126      * @see android.media.tv.tuner.filter.MmtpRecordEvent#getPts()
127      */
128     public static final long INVALID_TIMESTAMP =
129             Constant64Bit.INVALID_PRESENTATION_TIME_STAMP;
130     /**
131      * Invalid mpu sequence number in MmtpRecordEvent.
132      *
133      * <p>Returned by {@link MmtpRecordEvent#getMpuSequenceNumber()} when the requested sequence
134      * number is not available.
135      *
136      * @see android.media.tv.tuner.filter.MmtpRecordEvent#getMpuSequenceNumber()
137      */
138     public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM =
139             Constant.INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM;
140     /**
141      * Invalid first macroblock address in MmtpRecordEvent and TsRecordEvent.
142      *
143      * <p>Returned by {@link MmtpRecordEvent#getMbInSlice()} and
144      * {@link TsRecordEvent#getMbInSlice()} when the requested sequence number is not available.
145      *
146      * @see android.media.tv.tuner.filter.MmtpRecordEvent#getMbInSlice()
147      * @see android.media.tv.tuner.filter.TsRecordEvent#getMbInSlice()
148      */
149     public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE =
150             Constant.INVALID_FIRST_MACROBLOCK_IN_SLICE;
151     /**
152      * Invalid local transport stream id.
153      *
154      * <p>Returned by {@link #linkFrontendToCiCam(int)} when the requested failed
155      * or the hal implementation does not support the operation.
156      *
157      * @see #linkFrontendToCiCam(int)
158      */
159     public static final int INVALID_LTS_ID = Constant.INVALID_LTS_ID;
160     /**
161      * Invalid 64-bit filter ID.
162      */
163     public static final long INVALID_FILTER_ID_LONG = Constant64Bit.INVALID_FILTER_ID_64BIT;
164     /**
165      * Invalid frequency that is used as the default frontend frequency setting.
166      */
167     public static final int INVALID_FRONTEND_SETTING_FREQUENCY =
168             Constant.INVALID_FRONTEND_SETTING_FREQUENCY;
169     /**
170      * Invalid frontend id.
171      */
172     public static final int INVALID_FRONTEND_ID = Constant.INVALID_FRONTEND_ID;
173     /**
174      * Invalid LNB id.
175      *
176      * @hide
177      */
178     public static final int INVALID_LNB_ID = Constant.INVALID_LNB_ID;
179     /**
180      * A void key token. It is used to remove the current key from descrambler.
181      *
182      * <p>If the current keyToken comes from a MediaCas session, App is recommended to
183      * to use this constant to remove current key before closing MediaCas session.
184      */
185     @NonNull
186     public static final byte[] VOID_KEYTOKEN = {Constant.INVALID_KEYTOKEN};
187 
188     /** @hide */
189     @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
190     @Retention(RetentionPolicy.SOURCE)
191     public @interface ScanType {}
192     /**
193      * Scan type undefined.
194      */
195     public static final int SCAN_TYPE_UNDEFINED = FrontendScanType.SCAN_UNDEFINED;
196     /**
197      * Scan type auto.
198      *
199      * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
200      */
201     public static final int SCAN_TYPE_AUTO = FrontendScanType.SCAN_AUTO;
202     /**
203      * Blind scan.
204      *
205      * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
206      * implementation specific range.
207      */
208     public static final int SCAN_TYPE_BLIND = FrontendScanType.SCAN_BLIND;
209 
210 
211     /** @hide */
212     @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
213             RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
214     @Retention(RetentionPolicy.SOURCE)
215     public @interface Result {}
216 
217     /**
218      * Operation succeeded.
219      */
220     public static final int RESULT_SUCCESS = android.hardware.tv.tuner.Result.SUCCESS;
221     /**
222      * Operation failed because the corresponding resources are not available.
223      */
224     public static final int RESULT_UNAVAILABLE = android.hardware.tv.tuner.Result.UNAVAILABLE;
225     /**
226      * Operation failed because the corresponding resources are not initialized.
227      */
228     public static final int RESULT_NOT_INITIALIZED =
229             android.hardware.tv.tuner.Result.NOT_INITIALIZED;
230     /**
231      * Operation failed because it's not in a valid state.
232      */
233     public static final int RESULT_INVALID_STATE = android.hardware.tv.tuner.Result.INVALID_STATE;
234     /**
235      * Operation failed because there are invalid arguments.
236      */
237     public static final int RESULT_INVALID_ARGUMENT =
238             android.hardware.tv.tuner.Result.INVALID_ARGUMENT;
239     /**
240      * Memory allocation failed.
241      */
242     public static final int RESULT_OUT_OF_MEMORY = android.hardware.tv.tuner.Result.OUT_OF_MEMORY;
243     /**
244      * Operation failed due to unknown errors.
245      */
246     public static final int RESULT_UNKNOWN_ERROR = android.hardware.tv.tuner.Result.UNKNOWN_ERROR;
247 
248 
249 
250     private static final String TAG = "MediaTvTuner";
251     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
252 
253     private static final int MSG_RESOURCE_LOST = 1;
254     private static final int MSG_ON_FILTER_EVENT = 2;
255     private static final int MSG_ON_FILTER_STATUS = 3;
256     private static final int MSG_ON_LNB_EVENT = 4;
257 
258     private static final int FILTER_CLEANUP_THRESHOLD = 256;
259 
260     /** @hide */
261     @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
262     @Retention(RetentionPolicy.SOURCE)
263     public @interface DvrType {}
264 
265     /**
266      * DVR for recording.
267      * @hide
268      */
269     public static final int DVR_TYPE_RECORD = android.hardware.tv.tuner.DvrType.RECORD;
270     /**
271      * DVR for playback of recorded programs.
272      * @hide
273      */
274     public static final int DVR_TYPE_PLAYBACK = android.hardware.tv.tuner.DvrType.PLAYBACK;
275 
276     static {
277         try {
278             System.loadLibrary("media_tv_tuner");
nativeInit()279             nativeInit();
280             // Load and initialize MediaCodec to avoid flaky cts test result.
MediaCodec.class.getName()281             Class.forName(MediaCodec.class.getName());
282         } catch (UnsatisfiedLinkError e) {
283             Log.d(TAG, "tuner JNI library not found!");
284         } catch (ClassNotFoundException e) {
285             Log.e(TAG, "MediaCodec class not found!", e);
286         }
287     }
288 
289     private final Context mContext;
290     private final TunerResourceManager mTunerResourceManager;
291     private final int mClientId;
292     private static int sTunerVersion = TunerVersionChecker.TUNER_VERSION_UNKNOWN;
293     private DemuxInfo mDesiredDemuxInfo = new DemuxInfo(Filter.TYPE_UNDEFINED);
294 
295     private boolean mClosed = false;
296     private Frontend mFrontend;
297     private EventHandler mHandler;
298     @Nullable
299     private FrontendInfo mFrontendInfo;
300     private Long mFrontendHandle;
301     private Tuner mFeOwnerTuner = null;
302     private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
303     private Integer mDesiredFrontendId = null;
304     private int mUserId;
305     private Lnb mLnb;
306     private Long mLnbHandle;
307     @Nullable
308     private OnTuneEventListener mOnTuneEventListener;
309     @Nullable
310     private Executor mOnTuneEventExecutor;
311     @Nullable
312     private ScanCallback mScanCallback;
313     @Nullable
314     private Executor mScanCallbackExecutor;
315     @Nullable
316     private OnResourceLostListener mOnResourceLostListener;
317     @Nullable
318     private Executor mOnResourceLostListenerExecutor;
319 
320     private final Object mOnTuneEventLock = new Object();
321     private final Object mScanCallbackLock = new Object();
322     private final Object mOnResourceLostListenerLock = new Object();
323     private final ReentrantLock mFrontendLock = new ReentrantLock();
324     private final ReentrantLock mLnbLock = new ReentrantLock();
325     private final ReentrantLock mFrontendCiCamLock = new ReentrantLock();
326     private final ReentrantLock mDemuxLock = new ReentrantLock();
327     private int mRequestedCiCamId;
328 
329     private Long mDemuxHandle;
330     private Long mFrontendCiCamHandle;
331     private Integer mFrontendCiCamId;
332     private Map<Long, WeakReference<Descrambler>> mDescramblers = new HashMap<>();
333     private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>();
334 
335     private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
336             new TunerResourceManager.ResourcesReclaimListener() {
337                 @Override
338                 public void onReclaimResources() {
339                     if (mFrontend != null) {
340                         FrameworkStatsLog
341                                 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
342                                     FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
343                     }
344                     releaseAll();
345                     mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST));
346                 }
347             };
348 
349     /**
350      * Constructs a Tuner instance.
351      *
352      * @param context the context of the caller.
353      * @param tvInputSessionId the session ID of the TV input.
354      * @param useCase the use case of this Tuner instance.
355      */
356     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
Tuner(@onNull Context context, @Nullable String tvInputSessionId, @TvInputService.PriorityHintUseCaseType int useCase)357     public Tuner(@NonNull Context context, @Nullable String tvInputSessionId,
358             @TvInputService.PriorityHintUseCaseType int useCase) {
359         mContext = context;
360         mTunerResourceManager = mContext.getSystemService(TunerResourceManager.class);
361 
362         // The Tuner Resource Manager is only started when the device has the tuner feature.
363         if (mTunerResourceManager == null) {
364             throw new IllegalStateException(
365                     "Tuner instance is created, but the device doesn't have tuner feature");
366         }
367 
368         // This code will start tuner server if the device is running on the lazy tuner HAL.
369         nativeSetup();
370         sTunerVersion = nativeGetTunerVersion();
371         if (sTunerVersion == TunerVersionChecker.TUNER_VERSION_UNKNOWN) {
372             Log.e(TAG, "Unknown Tuner version!");
373         } else {
374             Log.d(TAG, "Current Tuner version is "
375                     + TunerVersionChecker.getMajorVersion(sTunerVersion) + "."
376                     + TunerVersionChecker.getMinorVersion(sTunerVersion) + ".");
377         }
378         if (mHandler == null) {
379             mHandler = createEventHandler();
380         }
381 
382         int[] clientId = new int[1];
383         ResourceClientProfile profile = new ResourceClientProfile();
384         profile.tvInputSessionId = tvInputSessionId;
385         profile.useCase = useCase;
386         mTunerResourceManager.registerClientProfile(
387                 profile, Runnable::run, mResourceListener, clientId);
388         mClientId = clientId[0];
389 
390         mUserId = Process.myUid();
391     }
392 
393     /**
394      * Get frontend info list from native and build them into a {@link FrontendInfo} list. Any
395      * {@code null} FrontendInfo element would be removed.
396      */
getFrontendInfoListInternal()397     private FrontendInfo[] getFrontendInfoListInternal() {
398         List<Integer> ids = getFrontendIds();
399         if (ids == null) {
400             return null;
401         }
402         FrontendInfo[] infos = new FrontendInfo[ids.size()];
403         for (int i = 0; i < ids.size(); i++) {
404             int id = ids.get(i);
405             FrontendInfo frontendInfo = getFrontendInfoById(id);
406             if (frontendInfo == null) {
407                 Log.e(TAG, "Failed to get a FrontendInfo on frontend id:" + id + "!");
408                 continue;
409             }
410             infos[i] = frontendInfo;
411         }
412         return Arrays.stream(infos).filter(Objects::nonNull).toArray(FrontendInfo[]::new);
413     }
414 
415     /** @hide */
getTunerVersion()416     public static int getTunerVersion() {
417         return sTunerVersion;
418     }
419 
420     /** @hide */
getFrontendIds()421     public List<Integer> getFrontendIds() {
422         mFrontendLock.lock();
423         try {
424             return nativeGetFrontendIds();
425         } finally {
426             mFrontendLock.unlock();
427         }
428     }
429 
430     /**
431      * Sets the listener for resource lost.
432      *
433      * @param executor the executor on which the listener should be invoked.
434      * @param listener the listener that will be run.
435      */
setResourceLostListener(@onNull @allbackExecutor Executor executor, @NonNull OnResourceLostListener listener)436     public void setResourceLostListener(@NonNull @CallbackExecutor Executor executor,
437             @NonNull OnResourceLostListener listener) {
438         synchronized (mOnResourceLostListenerLock) {
439             Objects.requireNonNull(executor, "OnResourceLostListener must not be null");
440             Objects.requireNonNull(listener, "executor must not be null");
441             mOnResourceLostListener = listener;
442             mOnResourceLostListenerExecutor = executor;
443         }
444     }
445 
446     /**
447      * Removes the listener for resource lost.
448      */
clearResourceLostListener()449     public void clearResourceLostListener() {
450         synchronized (mOnResourceLostListenerLock) {
451             mOnResourceLostListener = null;
452             mOnResourceLostListenerExecutor = null;
453         }
454     }
455 
456     /**
457      * Shares the frontend resource with another Tuner instance
458      *
459      * @param tuner the Tuner instance to share frontend resource with.
460      */
shareFrontendFromTuner(@onNull Tuner tuner)461     public void shareFrontendFromTuner(@NonNull Tuner tuner) {
462         acquireTRMSLock("shareFrontendFromTuner()");
463         mFrontendLock.lock();
464         try {
465             if (mFeOwnerTuner != null) {
466                 // unregister self from the Frontend callback
467                 mFeOwnerTuner.unregisterFrontendCallbackListener(this);
468                 mFeOwnerTuner = null;
469                 nativeUnshareFrontend();
470             }
471             mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId);
472             mFeOwnerTuner = tuner;
473             mFeOwnerTuner.registerFrontendCallbackListener(this);
474             mFrontendHandle = mFeOwnerTuner.mFrontendHandle;
475             mFrontend = mFeOwnerTuner.mFrontend;
476             nativeShareFrontend(mFrontend.mId);
477         } finally {
478             releaseTRMSLock();
479             mFrontendLock.unlock();
480         }
481     }
482 
483     /**
484      * Transfers the ownership of shared frontend and its associated resources.
485      *
486      * @param newOwner the Tuner instance to be the new owner.
487      *
488      * @return result status of tune operation.
489      */
transferOwner(@onNull Tuner newOwner)490     public int transferOwner(@NonNull Tuner newOwner) {
491         acquireTRMSLock("transferOwner()");
492         mFrontendLock.lock();
493         mFrontendCiCamLock.lock();
494         mLnbLock.lock();
495         try {
496 
497             if (!isFrontendOwner() || !isNewOwnerQualifiedForTransfer(newOwner)) {
498                 return RESULT_INVALID_STATE;
499             }
500 
501             int res = transferFeOwner(newOwner);
502             if (res != RESULT_SUCCESS) {
503                 return res;
504             }
505 
506             res = transferCiCamOwner(newOwner);
507             if (res != RESULT_SUCCESS) {
508                 return res;
509             }
510 
511             res = transferLnbOwner(newOwner);
512             if (res != RESULT_SUCCESS) {
513                 return res;
514             }
515         } finally {
516             mFrontendLock.unlock();
517             mFrontendCiCamLock.unlock();
518             mLnbLock.unlock();
519             releaseTRMSLock();
520         }
521         return RESULT_SUCCESS;
522     }
523 
524     /**
525      * Resets or copies Frontend related settings.
526      */
replicateFrontendSettings(@ullable Tuner src)527     private void replicateFrontendSettings(@Nullable Tuner src) {
528         mFrontendLock.lock();
529         try {
530             if (src == null) {
531                 if (DEBUG) {
532                     Log.d(TAG, "resetting Frontend params for " + mClientId);
533                 }
534                 mFrontend = null;
535                 mFrontendHandle = null;
536                 mFrontendInfo = null;
537                 mFrontendType = FrontendSettings.TYPE_UNDEFINED;
538             } else {
539                 if (DEBUG) {
540                     Log.d(TAG, "copying Frontend params from " + src.mClientId
541                             + " to " + mClientId);
542                 }
543                 mFrontend = src.mFrontend;
544                 mFrontendHandle = src.mFrontendHandle;
545                 mFrontendInfo = src.mFrontendInfo;
546                 mFrontendType = src.mFrontendType;
547             }
548         } finally {
549             mFrontendLock.unlock();
550         }
551     }
552 
553     /**
554      * Sets the frontend owner. mFeOwnerTuner should be null for the owner Tuner instance.
555      */
setFrontendOwner(Tuner owner)556     private void setFrontendOwner(Tuner owner) {
557         mFrontendLock.lock();
558         try {
559             mFeOwnerTuner = owner;
560         } finally {
561             mFrontendLock.unlock();
562         }
563     }
564 
565     /**
566      * Resets or copies the CiCam related settings.
567      */
replicateCiCamSettings(@ullable Tuner src)568     private void replicateCiCamSettings(@Nullable Tuner src) {
569         mFrontendCiCamLock.lock();
570         try {
571             if (src == null) {
572                 if (DEBUG) {
573                     Log.d(TAG, "resetting CiCamParams: " + mClientId);
574                 }
575                 mFrontendCiCamHandle = null;
576                 mFrontendCiCamId = null;
577             } else {
578                 if (DEBUG) {
579                     Log.d(TAG, "copying CiCamParams from " + src.mClientId + " to " + mClientId);
580                     Log.d(TAG, "mFrontendCiCamHandle:" + src.mFrontendCiCamHandle + ", "
581                             + "mFrontendCiCamId:" + src.mFrontendCiCamId);
582                 }
583                 mFrontendCiCamHandle = src.mFrontendCiCamHandle;
584                 mFrontendCiCamId = src.mFrontendCiCamId;
585             }
586         } finally {
587             mFrontendCiCamLock.unlock();
588         }
589     }
590 
591     /**
592      * Resets or copies Lnb related settings.
593      */
replicateLnbSettings(@ullable Tuner src)594     private void replicateLnbSettings(@Nullable Tuner src) {
595         mLnbLock.lock();
596         try {
597             if (src == null) {
598                 if (DEBUG) {
599                     Log.d(TAG, "resetting Lnb params");
600                 }
601                 mLnb = null;
602                 mLnbHandle = null;
603             } else {
604                 if (DEBUG) {
605                     Log.d(TAG, "copying Lnb params from " + src.mClientId + " to " + mClientId);
606                 }
607                 mLnb = src.mLnb;
608                 mLnbHandle = src.mLnbHandle;
609             }
610         } finally {
611             mLnbLock.unlock();
612         }
613     }
614 
615     /**
616      * Checks if it is a frontend resource owner.
617      * Proper mutex must be held prior to calling this.
618      */
isFrontendOwner()619     private boolean isFrontendOwner() {
620         boolean notAnOwner = (mFeOwnerTuner != null);
621         if (notAnOwner) {
622             Log.e(TAG, "transferOwner() - cannot be called on the non-owner");
623             return false;
624         }
625         return true;
626     }
627 
628     /**
629      * Checks if the newOwner is qualified.
630      * Proper mutex must be held prior to calling this.
631      */
isNewOwnerQualifiedForTransfer(@onNull Tuner newOwner)632     private boolean isNewOwnerQualifiedForTransfer(@NonNull Tuner newOwner) {
633         // new owner must be the current sharee
634         boolean newOwnerIsTheCurrentSharee = (newOwner.mFeOwnerTuner == this)
635                 && (newOwner.mFrontendHandle.equals(mFrontendHandle));
636         if (!newOwnerIsTheCurrentSharee) {
637             Log.e(TAG, "transferOwner() - new owner must be the current sharee");
638             return false;
639         }
640 
641         // new owner must not be holding any of the to-be-shared resources
642         boolean newOwnerAlreadyHoldsToBeSharedResource =
643                 (newOwner.mFrontendCiCamHandle != null || newOwner.mLnb != null);
644         if (newOwnerAlreadyHoldsToBeSharedResource) {
645             Log.e(TAG, "transferOwner() - new owner cannot be holding CiCam"
646                     + " nor Lnb resource");
647             return false;
648         }
649 
650         return true;
651     }
652 
653     /**
654      * Transfers the ownership of the already held frontend resource.
655      * Proper mutex must be held prior to calling this.
656      */
transferFeOwner(@onNull Tuner newOwner)657     private int transferFeOwner(@NonNull Tuner newOwner) {
658         // handle native resource first
659         newOwner.nativeUpdateFrontend(getNativeContext());
660         nativeUpdateFrontend(0);
661 
662         // transfer frontend related settings
663         newOwner.replicateFrontendSettings(this);
664 
665         // transfer the frontend owner info
666         setFrontendOwner(newOwner);
667         newOwner.setFrontendOwner(null);
668 
669         // handle TRM
670         if (mTunerResourceManager.transferOwner(
671                 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
672                 mClientId, newOwner.mClientId)) {
673             return RESULT_SUCCESS;
674         } else {
675             return RESULT_UNKNOWN_ERROR;
676         }
677     }
678 
679     /**
680      * Transfers the ownership of CiCam resource.
681      * This is a no-op if the CiCam resource is not held.
682      * Proper mutex must be held prior to calling this.
683      */
transferCiCamOwner(Tuner newOwner)684     private int transferCiCamOwner(Tuner newOwner) {
685         boolean notAnOwner = (mFrontendCiCamHandle == null);
686         if (notAnOwner) {
687             // There is nothing to do here if there is no CiCam
688             return RESULT_SUCCESS;
689         }
690 
691         // no need to handle at native level
692 
693         // transfer the CiCam info at Tuner level
694         newOwner.replicateCiCamSettings(this);
695         replicateCiCamSettings(null);
696 
697         // handle TRM
698         if (mTunerResourceManager.transferOwner(
699                 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM,
700                 mClientId, newOwner.mClientId)) {
701             return RESULT_SUCCESS;
702         } else {
703             return RESULT_UNKNOWN_ERROR;
704         }
705     }
706 
707     /**
708      * Transfers the ownership of Lnb resource.
709      * This is a no-op if the Lnb resource is not held.
710      * Proper mutex must be held prior to calling this.
711      */
transferLnbOwner(Tuner newOwner)712     private int transferLnbOwner(Tuner newOwner) {
713         boolean notAnOwner = (mLnb == null);
714         if (notAnOwner) {
715             // There is nothing to do here if there is no Lnb
716             return RESULT_SUCCESS;
717         }
718 
719         // no need to handle at native level
720 
721         // set the new owner
722         mLnb.setOwner(newOwner);
723 
724         newOwner.replicateLnbSettings(this);
725         replicateLnbSettings(null);
726 
727         // handle TRM
728         if (mTunerResourceManager.transferOwner(
729                 TunerResourceManager.TUNER_RESOURCE_TYPE_LNB,
730                 mClientId, newOwner.mClientId)) {
731             return RESULT_SUCCESS;
732         } else {
733             return RESULT_UNKNOWN_ERROR;
734         }
735     }
736 
737     /**
738      * Updates client priority with an arbitrary value along with a nice value.
739      *
740      * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
741      * to reclaim insufficient resources from another client.
742      *
743      * <p>The nice value represents how much the client intends to give up the resource when an
744      * insufficient resource situation happens.
745      *
746      * @param priority the new priority. Any negative value would cause no-op on priority setting
747      *                 and the API would only process nice value setting in that case.
748      * @param niceValue the nice value.
749      */
750     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
updateResourcePriority(int priority, int niceValue)751     public void updateResourcePriority(int priority, int niceValue) {
752         mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue);
753     }
754 
755     /**
756      * Determines whether the resource holder retains ownership of the resource during a challenge
757      * scenario, when both resource holder and resource challenger have same processId and same
758      * priority.
759      *
760      * @param enabled Set to {@code true} to allow the resource holder to retain ownership,
761      *     or false to allow the resource challenger to acquire the resource.
762      *     If not explicitly set, enabled is set to {@code false}.
763      */
764     @FlaggedApi(FLAG_SET_RESOURCE_HOLDER_RETAIN)
765     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
setResourceOwnershipRetention(boolean enabled)766     public void setResourceOwnershipRetention(boolean enabled) {
767         mTunerResourceManager.setResourceOwnershipRetention(mClientId, enabled);
768     }
769 
770     /**
771      * Checks if there is an unused frontend resource available.
772      *
773      * @param frontendType {@link android.media.tv.tuner.frontend.FrontendSettings.Type} for the
774      * query to be done for.
775      */
776     @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
hasUnusedFrontend(int frontendType)777     public boolean hasUnusedFrontend(int frontendType) {
778         return mTunerResourceManager.hasUnusedFrontend(frontendType);
779     }
780 
781     /**
782      * Checks if the calling Tuner object has the lowest priority as a client to
783      * {@link TunerResourceManager}
784      *
785      * <p>The priority comparison is done against the current holders of the frontend resource.
786      *
787      * <p>The behavior of this function is independent of the availability of unused resources.
788      *
789      * <p>The function returns {@code true} in any of the following sceanrios:
790      * <ul>
791      * <li>The caller has the priority <= other clients</li>
792      * <li>No one is holding the frontend resource of the specified type</li>
793      * <li>The caller is the only one who is holding the resource</li>
794      * <li>The frontend resource of the specified type does not exist</li>
795      *
796      * </ul>
797      * @param frontendType {@link android.media.tv.tuner.frontend.FrontendSettings.Type} for the
798      * query to be done for.
799      *
800      * @return {@code false} only if someone else with strictly lower priority is holding the
801      *         resourece.
802      *         {@code true} otherwise.
803      */
isLowestPriority(int frontendType)804     public boolean isLowestPriority(int frontendType) {
805         return mTunerResourceManager.isLowestPriority(mClientId, frontendType);
806     }
807 
808     private long mNativeContext; // used by native jMediaTuner
809 
810     /**
811      * Registers a tuner as a listener for frontend callbacks.
812      */
registerFrontendCallbackListener(Tuner tuner)813     private void registerFrontendCallbackListener(Tuner tuner) {
814         nativeRegisterFeCbListener(tuner.getNativeContext());
815     }
816 
817     /**
818      * Unregisters a tuner as a listener for frontend callbacks.
819      */
unregisterFrontendCallbackListener(Tuner tuner)820     private void unregisterFrontendCallbackListener(Tuner tuner) {
821         nativeUnregisterFeCbListener(tuner.getNativeContext());
822     }
823 
824     /**
825      * Returns the pointer to the associated JTuner.
826      */
getNativeContext()827     long getNativeContext() {
828         return mNativeContext;
829     }
830 
831     /**
832      * Releases the Tuner instance.
833      */
834     @Override
close()835     public void close() {
836         if (mClosed) {
837             return;
838         }
839         acquireTRMSLock("close()");
840         try {
841             releaseAll();
842             mTunerResourceManager.unregisterClientProfile(mClientId);
843             TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner");
844         } finally {
845             releaseTRMSLock();
846             mClosed = true;
847         }
848     }
849 
850     /**
851      * Either unshares the frontend resource (for sharee) or release Frontend (for owner)
852      */
closeFrontend()853     public void closeFrontend() {
854         acquireTRMSLock("closeFrontend()");
855         try {
856             releaseFrontend();
857         } finally {
858             releaseTRMSLock();
859         }
860     }
861 
862     /**
863      * Releases frontend resource for the owner. Unshares frontend resource for the sharee.
864      */
releaseFrontend()865     private void releaseFrontend() {
866         if (DEBUG) {
867             Log.d(TAG, "Tuner#releaseFrontend");
868         }
869         mFrontendLock.lock();
870         try {
871             if (mFrontendHandle != null) {
872                 if (DEBUG) {
873                     Log.d(TAG, "mFrontendHandle not null");
874                 }
875                 if (mFeOwnerTuner != null) {
876                     if (DEBUG) {
877                         Log.d(TAG, "mFeOwnerTuner not null - sharee");
878                     }
879                     // unregister self from the Frontend callback
880                     mFeOwnerTuner.unregisterFrontendCallbackListener(this);
881                     mFeOwnerTuner = null;
882                     nativeUnshareFrontend();
883                 } else {
884                     if (DEBUG) {
885                         Log.d(TAG, "mFeOwnerTuner null - owner");
886                     }
887                     // close resource as owner
888                     int res = nativeCloseFrontend(mFrontendHandle);
889                     if (res != Tuner.RESULT_SUCCESS) {
890                         TunerUtils.throwExceptionForResult(res, "failed to close frontend");
891                     }
892                 }
893                 if (DEBUG) {
894                     Log.d(TAG, "call TRM#releaseFrontend :" + mFrontendHandle + ", " + mClientId);
895                 }
896                 mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
897                 FrameworkStatsLog
898                         .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
899                         FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
900                 replicateFrontendSettings(null);
901             }
902         } finally {
903             mFrontendLock.unlock();
904         }
905     }
906 
907     /**
908      * Releases CiCam resource if held. No-op otherwise.
909      */
releaseCiCam()910     private void releaseCiCam() {
911         mFrontendCiCamLock.lock();
912         try {
913             if (mFrontendCiCamHandle != null) {
914                 if (DEBUG) {
915                     Log.d(TAG, "releasing CiCam : " + mFrontendCiCamHandle + " for " +  mClientId);
916                 }
917                 nativeUnlinkCiCam(mFrontendCiCamId);
918                 mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
919                 replicateCiCamSettings(null);
920             } else {
921                 if (DEBUG) {
922                     Log.d(TAG, "NOT releasing CiCam : " + mClientId);
923                 }
924             }
925         } finally {
926             mFrontendCiCamLock.unlock();
927         }
928     }
929 
930     /**
931      * Releases Lnb resource if held. TRMS lock must be acquired prior to calling this function.
932      */
closeLnb()933     private void closeLnb() {
934         mLnbLock.lock();
935         try {
936             // mLnb will be non-null only for owner tuner
937             if (mLnb != null) {
938                 if (DEBUG) {
939                     Log.d(TAG, "calling mLnb.close() : " + mClientId);
940                 }
941                 mLnb.closeInternal();
942             } else {
943                 if (DEBUG) {
944                     Log.d(TAG, "NOT calling mLnb.close() : " + mClientId);
945                 }
946             }
947         } finally {
948             mLnbLock.unlock();
949         }
950     }
951 
releaseFilters()952     private void releaseFilters() {
953         synchronized (mFilters) {
954             if (!mFilters.isEmpty()) {
955                 for (WeakReference<Filter> weakFilter : mFilters) {
956                     Filter filter = weakFilter.get();
957                     if (filter != null) {
958                         filter.close();
959                     }
960                 }
961                 mFilters.clear();
962             }
963         }
964     }
965 
releaseDescramblers()966     private void releaseDescramblers() {
967         synchronized (mDescramblers) {
968             if (!mDescramblers.isEmpty()) {
969                 for (Map.Entry<Long, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
970                     Descrambler descrambler = d.getValue().get();
971                     if (descrambler != null) {
972                         descrambler.close();
973                     }
974                     mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId);
975                 }
976                 mDescramblers.clear();
977             }
978         }
979     }
980 
releaseDemux()981     private void releaseDemux() {
982         mDemuxLock.lock();
983         try {
984             if (mDemuxHandle != null) {
985                 int res = nativeCloseDemux(mDemuxHandle);
986                 if (res != Tuner.RESULT_SUCCESS) {
987                     TunerUtils.throwExceptionForResult(res, "failed to close demux");
988                 }
989                 mTunerResourceManager.releaseDemux(mDemuxHandle, mClientId);
990                 mDemuxHandle = null;
991             }
992         } finally {
993             mDemuxLock.unlock();
994         }
995     }
996 
releaseAll()997     private void releaseAll() {
998         // release CiCam before frontend because frontend handle is needed to unlink CiCam
999         releaseCiCam();
1000         releaseFrontend();
1001         closeLnb();
1002         releaseDescramblers();
1003         releaseFilters();
1004         releaseDemux();
1005     }
1006 
1007     /**
1008      * Native Initialization.
1009      */
nativeInit()1010     private static native void nativeInit();
1011 
1012     /**
1013      * Native setup.
1014      */
nativeSetup()1015     private native void nativeSetup();
1016 
1017     /**
1018      * Native method to get all frontend IDs.
1019      */
nativeGetTunerVersion()1020     private native int nativeGetTunerVersion();
1021 
1022     /**
1023      * Native method to get all frontend IDs.
1024      */
nativeGetFrontendIds()1025     private native List<Integer> nativeGetFrontendIds();
1026 
1027     /**
1028      * Native method to open frontend of the given ID.
1029      */
nativeOpenFrontendByHandle(long handle)1030     private native Frontend nativeOpenFrontendByHandle(long handle);
nativeShareFrontend(int id)1031     private native int nativeShareFrontend(int id);
nativeUnshareFrontend()1032     private native int nativeUnshareFrontend();
nativeRegisterFeCbListener(long nativeContext)1033     private native void nativeRegisterFeCbListener(long nativeContext);
nativeUnregisterFeCbListener(long nativeContext)1034     private native void nativeUnregisterFeCbListener(long nativeContext);
1035     // nativeUpdateFrontend must be called on the new owner first
nativeUpdateFrontend(long nativeContext)1036     private native void nativeUpdateFrontend(long nativeContext);
1037     @Result
nativeTune(int type, FrontendSettings settings)1038     private native int nativeTune(int type, FrontendSettings settings);
nativeStopTune()1039     private native int nativeStopTune();
nativeScan(int settingsType, FrontendSettings settings, int scanType)1040     private native int nativeScan(int settingsType, FrontendSettings settings, int scanType);
nativeStopScan()1041     private native int nativeStopScan();
nativeSetLnb(Lnb lnb)1042     private native int nativeSetLnb(Lnb lnb);
nativeIsLnaSupported()1043     private native boolean nativeIsLnaSupported();
nativeSetLna(boolean enable)1044     private native int nativeSetLna(boolean enable);
nativeGetFrontendStatus(int[] statusTypes)1045     private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes);
nativeGetAvSyncHwId(Filter filter)1046     private native Integer nativeGetAvSyncHwId(Filter filter);
nativeGetAvSyncTime(int avSyncId)1047     private native Long nativeGetAvSyncTime(int avSyncId);
nativeConnectCiCam(int ciCamId)1048     private native int nativeConnectCiCam(int ciCamId);
nativeLinkCiCam(int ciCamId)1049     private native int nativeLinkCiCam(int ciCamId);
nativeDisconnectCiCam()1050     private native int nativeDisconnectCiCam();
nativeUnlinkCiCam(int ciCamId)1051     private native int nativeUnlinkCiCam(int ciCamId);
nativeGetFrontendInfo(int id)1052     private native FrontendInfo nativeGetFrontendInfo(int id);
nativeOpenFilter(int type, int subType, long bufferSize)1053     private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
nativeOpenTimeFilter()1054     private native TimeFilter nativeOpenTimeFilter();
nativeGetFrontendHardwareInfo()1055     private native String nativeGetFrontendHardwareInfo();
nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber)1056     private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
nativeGetMaxNumberOfFrontends(int frontendType)1057     private native int nativeGetMaxNumberOfFrontends(int frontendType);
nativeRemoveOutputPid(int pid)1058     private native int nativeRemoveOutputPid(int pid);
nativeOpenLnbByHandle(long handle)1059     private native Lnb nativeOpenLnbByHandle(long handle);
nativeOpenLnbByName(String name)1060     private native Lnb nativeOpenLnbByName(String name);
nativeGetFrontendStatusReadiness(int[] statusTypes)1061     private native FrontendStatusReadiness[] nativeGetFrontendStatusReadiness(int[] statusTypes);
1062 
nativeOpenDescramblerByHandle(long handle)1063     private native Descrambler nativeOpenDescramblerByHandle(long handle);
nativeOpenDemuxByhandle(long handle)1064     private native int nativeOpenDemuxByhandle(long handle);
1065 
nativeOpenDvrRecorder(long bufferSize)1066     private native DvrRecorder nativeOpenDvrRecorder(long bufferSize);
nativeOpenDvrPlayback(long bufferSize)1067     private native DvrPlayback nativeOpenDvrPlayback(long bufferSize);
1068 
nativeGetDemuxCapabilities()1069     private native DemuxCapabilities nativeGetDemuxCapabilities();
nativeGetDemuxInfo(long demuxHandle)1070     private native DemuxInfo nativeGetDemuxInfo(long demuxHandle);
1071 
nativeCloseDemux(long handle)1072     private native int nativeCloseDemux(long handle);
nativeCloseFrontend(long handle)1073     private native int nativeCloseFrontend(long handle);
nativeClose()1074     private native int nativeClose();
1075 
nativeOpenSharedFilter(String token)1076     private static native SharedFilter nativeOpenSharedFilter(String token);
1077 
1078     /**
1079      * Listener for resource lost.
1080      *
1081      * <p>Insufficient resources are reclaimed by higher priority clients.
1082      */
1083     public interface OnResourceLostListener {
1084         /**
1085          * Invoked when resource lost.
1086          *
1087          * @param tuner the tuner instance whose resource is being reclaimed.
1088          */
onResourceLost(@onNull Tuner tuner)1089         void onResourceLost(@NonNull Tuner tuner);
1090     }
1091 
1092     @Nullable
createEventHandler()1093     private EventHandler createEventHandler() {
1094         Looper looper;
1095         if ((looper = Looper.myLooper()) != null) {
1096             return new EventHandler(looper);
1097         } else if ((looper = Looper.getMainLooper()) != null) {
1098             return new EventHandler(looper);
1099         }
1100         return null;
1101     }
1102 
1103     private class EventHandler extends Handler {
EventHandler(Looper looper)1104         private EventHandler(Looper looper) {
1105             super(looper);
1106         }
1107 
1108         @Override
handleMessage(Message msg)1109         public void handleMessage(Message msg) {
1110             switch (msg.what) {
1111                 case MSG_ON_FILTER_STATUS: {
1112                     Filter filter = (Filter) msg.obj;
1113                     if (filter.getCallback() != null) {
1114                         filter.getCallback().onFilterStatusChanged(filter, msg.arg1);
1115                     }
1116                     break;
1117                 }
1118                 case MSG_RESOURCE_LOST: {
1119                     synchronized (mOnResourceLostListenerLock) {
1120                         if (mOnResourceLostListener != null
1121                                 && mOnResourceLostListenerExecutor != null) {
1122                             mOnResourceLostListenerExecutor.execute(() -> {
1123                                 synchronized (mOnResourceLostListenerLock) {
1124                                     if (mOnResourceLostListener != null) {
1125                                         mOnResourceLostListener.onResourceLost(Tuner.this);
1126                                     }
1127                                 }
1128                             });
1129                         }
1130                     }
1131                     break;
1132                 }
1133                 default:
1134                     // fall through
1135             }
1136         }
1137     }
1138 
1139     private class Frontend {
1140         private int mId;
1141 
Frontend(int id)1142         private Frontend(int id) {
1143             mId = id;
1144         }
1145     }
1146 
1147     /**
1148      * Listens for tune events.
1149      *
1150      * <p>
1151      * Tuner events are started when {@link #tune(FrontendSettings)} is called and end when {@link
1152      * #cancelTuning()} is called.
1153      *
1154      * @param eventListener receives tune events.
1155      * @throws SecurityException if the caller does not have appropriate permissions.
1156      * @see #tune(FrontendSettings)
1157      */
setOnTuneEventListener(@onNull @allbackExecutor Executor executor, @NonNull OnTuneEventListener eventListener)1158     public void setOnTuneEventListener(@NonNull @CallbackExecutor Executor executor,
1159             @NonNull OnTuneEventListener eventListener) {
1160         synchronized (mOnTuneEventLock) {
1161             mOnTuneEventListener = eventListener;
1162             mOnTuneEventExecutor = executor;
1163         }
1164     }
1165 
1166     /**
1167      * Clears the {@link OnTuneEventListener} and its associated {@link Executor}.
1168      *
1169      * @throws SecurityException if the caller does not have appropriate permissions.
1170      * @see #setOnTuneEventListener(Executor, OnTuneEventListener)
1171      */
clearOnTuneEventListener()1172     public void clearOnTuneEventListener() {
1173         synchronized (mOnTuneEventLock) {
1174             mOnTuneEventListener = null;
1175             mOnTuneEventExecutor = null;
1176         }
1177     }
1178 
1179     /**
1180      * Tunes the frontend to using the settings given.
1181      *
1182      * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
1183      * to get frontend resource. If the client can't get the resource, this call returns {@link
1184      * #RESULT_UNAVAILABLE}.
1185      *
1186      * <p>
1187      * This locks the frontend to a frequency by providing signal
1188      * delivery information. If previous tuning isn't completed, this stop the previous tuning, and
1189      * start a new tuning.
1190      *
1191      * <p>
1192      * Tune is an async call, with {@link OnTuneEventListener#SIGNAL_LOCKED} and {@link
1193      * OnTuneEventListener#SIGNAL_NO_SIGNAL} events sent to the {@link OnTuneEventListener}
1194      * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}.
1195      *
1196      * <p>Tuning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only
1197      * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
1198      * TunerVersionChecker#getTunerVersion()} to get the version information.
1199      *
1200      * <p>Tuning with {@link
1201      * android.media.tv.tuner.frontend.IsdbtFrontendSettings.PartialReceptionFlag} or {@link
1202      * android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings} is only supported
1203      * in Tuner 2.0 or higher version. Unsupported version will cause no-op. Use {@link
1204      * TunerVersionChecker#getTunerVersion()} to get the version information.
1205      *
1206      * <p>Tuning with {@link
1207      * android.media.tv.tuner.frontend.IptvFrontendSettings} is only supported
1208      * in Tuner 3.0 or higher version. Unsupported version will cause no-op. Use {@link
1209      * TunerVersionChecker#getTunerVersion()} to get the version information.
1210      *
1211      * @param settings Signal delivery information the frontend uses to
1212      *                 search and lock the signal.
1213      * @return result status of tune operation.
1214      * @throws SecurityException if the caller does not have appropriate permissions.
1215      * @see #setOnTuneEventListener(Executor, OnTuneEventListener)
1216      */
1217     @Result
tune(@onNull FrontendSettings settings)1218     public int tune(@NonNull FrontendSettings settings) {
1219         mFrontendLock.lock();
1220         try {
1221             if (mFeOwnerTuner != null) {
1222                 Log.d(TAG, "Operation cannot be done by sharee of tuner");
1223                 return RESULT_INVALID_STATE;
1224             }
1225             final int type = settings.getType();
1226             if (mFrontendHandle != null && type != mFrontendType) {
1227                 Log.e(TAG, "Frontend was opened with type " + mFrontendType
1228                         + ", new type is " + type);
1229                 return RESULT_INVALID_STATE;
1230             }
1231             Log.d(TAG, "Tune to " + settings.getFrequencyLong());
1232             mFrontendType = type;
1233             if (mFrontendType == FrontendSettings.TYPE_DTMB) {
1234                 if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1235                         TunerVersionChecker.TUNER_VERSION_1_1, "Tuner with DTMB Frontend")) {
1236                     return RESULT_UNAVAILABLE;
1237                 }
1238             }
1239             if (mFrontendType == FrontendSettings.TYPE_IPTV) {
1240                 if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1241                         TunerVersionChecker.TUNER_VERSION_3_0, "Tuner with IPTV Frontend")) {
1242                     return RESULT_UNAVAILABLE;
1243                 }
1244             }
1245 
1246             if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
1247                 mFrontendInfo = null;
1248                 Log.d(TAG, "Write Stats Log for tuning.");
1249                 FrameworkStatsLog
1250                         .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
1251                             FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__TUNING);
1252                 int res = nativeTune(settings.getType(), settings);
1253                 return res;
1254             } else {
1255                 return RESULT_UNAVAILABLE;
1256             }
1257         } finally {
1258             mFrontendLock.unlock();
1259         }
1260     }
1261 
1262     /**
1263      * Stops a previous tuning.
1264      *
1265      * <p>If the method completes successfully, the frontend is no longer tuned and no data
1266      * will be sent to attached filters.
1267      *
1268      * @return result status of the operation.
1269      */
1270     @Result
cancelTuning()1271     public int cancelTuning() {
1272         mFrontendLock.lock();
1273         try {
1274             if (mFeOwnerTuner != null) {
1275                 Log.d(TAG, "Operation cannot be done by sharee of tuner");
1276                 return RESULT_INVALID_STATE;
1277             }
1278             return nativeStopTune();
1279         } finally {
1280             mFrontendLock.unlock();
1281         }
1282     }
1283 
1284     /**
1285      * Scan for channels.
1286      *
1287      * <p>Details for channels found are returned via {@link ScanCallback}.
1288      *
1289      * <p>Scanning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only
1290      * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link
1291      * TunerVersionChecker#getTunerVersion()} to get the version information.
1292      *
1293      * * <p>Scanning with {@link
1294      * android.media.tv.tuner.frontend.IsdbtFrontendSettings.PartialReceptionFlag} or {@link
1295      * android.media.tv.tuner.frontend.IsdbtFrontendSettings.IsdbtLayerSettings} is only supported
1296      * in Tuner 2.0 or higher version. Unsupported version will cause no-op. Use {@link
1297      * TunerVersionChecker#getTunerVersion()} to get the version information.
1298      *
1299      * @param settings A {@link FrontendSettings} to configure the frontend.
1300      * @param scanType The scan type.
1301      * @throws SecurityException     if the caller does not have appropriate permissions.
1302      * @throws IllegalStateException if {@code scan} is called again before
1303      *                               {@link #cancelScanning()} is called.
1304      */
1305     @Result
scan(@onNull FrontendSettings settings, @ScanType int scanType, @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback)1306     public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
1307             @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
1308 
1309         mFrontendLock.lock();
1310         try {
1311             if (mFeOwnerTuner != null) {
1312                 Log.d(TAG, "Operation cannot be done by sharee of tuner");
1313                 return RESULT_INVALID_STATE;
1314             }
1315             synchronized (mScanCallbackLock) {
1316                 // Scan can be called again for blink scan if scanCallback and executor are same as
1317                 //before.
1318                 if (((mScanCallback != null) && (mScanCallback != scanCallback))
1319                         || ((mScanCallbackExecutor != null)
1320                             && (mScanCallbackExecutor != executor))) {
1321                     throw new IllegalStateException(
1322                         "Different Scan session already in progress.  stopScan must be called "
1323                             + "before a new scan session can be " + "started.");
1324                 }
1325                 mFrontendType = settings.getType();
1326                 if (mFrontendType == FrontendSettings.TYPE_DTMB) {
1327                     if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1328                             TunerVersionChecker.TUNER_VERSION_1_1,
1329                             "Scan with DTMB Frontend")) {
1330                         return RESULT_UNAVAILABLE;
1331                     }
1332                 }
1333                 if (mFrontendType == FrontendSettings.TYPE_IPTV) {
1334                     if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1335                             TunerVersionChecker.TUNER_VERSION_3_0,
1336                             "Tuner with IPTV Frontend")) {
1337                         return RESULT_UNAVAILABLE;
1338                     }
1339                 }
1340                 if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
1341                           mFrontendLock)) {
1342                     mScanCallback = scanCallback;
1343                     mScanCallbackExecutor = executor;
1344                     mFrontendInfo = null;
1345                     FrameworkStatsLog
1346                             .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
1347                             FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCANNING);
1348                     return nativeScan(settings.getType(), settings, scanType);
1349                 }
1350                 return RESULT_UNAVAILABLE;
1351             }
1352         } finally {
1353             mFrontendLock.unlock();
1354         }
1355     }
1356 
1357     /**
1358      * Stops a previous scanning.
1359      *
1360      * <p>
1361      * The {@link ScanCallback} and it's {@link Executor} will be removed.
1362      *
1363      * <p>
1364      * If the method completes successfully, the frontend stopped previous scanning.
1365      *
1366      * @throws SecurityException if the caller does not have appropriate permissions.
1367      */
1368     @Result
cancelScanning()1369     public int cancelScanning() {
1370         mFrontendLock.lock();
1371         try {
1372             if (mFeOwnerTuner != null) {
1373                 Log.d(TAG, "Operation cannot be done by sharee of tuner");
1374                 return RESULT_INVALID_STATE;
1375             }
1376             synchronized (mScanCallbackLock) {
1377                 FrameworkStatsLog.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
1378                         FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCAN_STOPPED);
1379 
1380                 int retVal = nativeStopScan();
1381                 mScanCallback = null;
1382                 mScanCallbackExecutor = null;
1383                 return retVal;
1384             }
1385         } finally {
1386             mFrontendLock.unlock();
1387         }
1388     }
1389 
requestFrontend()1390     private boolean requestFrontend() {
1391         long[] feHandle = new long[1];
1392         boolean granted = false;
1393         try {
1394             TunerFrontendRequest request = new TunerFrontendRequest();
1395             request.clientId = mClientId;
1396             request.frontendType = mFrontendType;
1397             request.desiredId = mDesiredFrontendId == null
1398                     ? TunerFrontendRequest.DEFAULT_DESIRED_ID
1399                     : mDesiredFrontendId;
1400             granted = mTunerResourceManager.requestFrontend(request, feHandle);
1401         } finally {
1402             mDesiredFrontendId = null;
1403         }
1404         if (granted) {
1405             mFrontendHandle = feHandle[0];
1406             mFrontend = nativeOpenFrontendByHandle(mFrontendHandle);
1407         }
1408 
1409         // For satellite type, set Lnb if valid handle exists.
1410         // This is necessary as now that we support closeFrontend().
1411         if (mFrontendType == FrontendSettings.TYPE_DVBS
1412                 || mFrontendType == FrontendSettings.TYPE_ISDBS
1413                 || mFrontendType == FrontendSettings.TYPE_ISDBS3) {
1414             mLnbLock.lock();
1415             try {
1416                 if (mLnbHandle != null && mLnb != null) {
1417                     nativeSetLnb(mLnb);
1418                 }
1419             } finally {
1420                 mLnbLock.unlock();
1421             }
1422         }
1423         return granted;
1424     }
1425 
1426     /**
1427      * Sets Low-Noise Block downconverter (LNB) for satellite frontend.
1428      *
1429      * <p>This assigns a hardware LNB resource to the satellite tuner. It can be
1430      * called multiple times to update LNB assignment.
1431      *
1432      * @param lnb the LNB instance.
1433      *
1434      * @return result status of the operation.
1435      */
1436     @Result
setLnb(@onNull Lnb lnb)1437     private int setLnb(@NonNull Lnb lnb) {
1438         mLnbLock.lock();
1439         try {
1440             return nativeSetLnb(lnb);
1441         } finally {
1442             mLnbLock.unlock();
1443         }
1444     }
1445 
1446     /**
1447      * Is Low Noise Amplifier (LNA) supported by the Tuner.
1448      *
1449      * <p>This API is only supported by Tuner HAL 3.0 or higher.
1450      * Unsupported version would throw UnsupportedOperationException. Use
1451      * {@link TunerVersionChecker#getTunerVersion()} to check the version.
1452      *
1453      * @return {@code true} if supported, otherwise {@code false}.
1454      * @throws UnsupportedOperationException if the Tuner HAL version is lower than 3.0
1455      * @see android.media.tv.tuner.TunerVersionChecker#TUNER_VERSION_3_0
1456      */
isLnaSupported()1457     public boolean isLnaSupported() {
1458         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1459                 TunerVersionChecker.TUNER_VERSION_3_0, "isLnaSupported")) {
1460             throw new UnsupportedOperationException("Tuner HAL version "
1461                     + TunerVersionChecker.getTunerVersion() + " doesn't support this method.");
1462         }
1463         return nativeIsLnaSupported();
1464     }
1465 
1466     /**
1467      * Enable or Disable Low Noise Amplifier (LNA).
1468      *
1469      * @param enable {@code true} to activate LNA module; {@code false} to deactivate LNA.
1470      *
1471      * @return result status of the operation. {@link #RESULT_UNAVAILABLE} if the device doesn't
1472      *         support LNA.
1473      */
1474     @Result
setLnaEnabled(boolean enable)1475     public int setLnaEnabled(boolean enable) {
1476         return nativeSetLna(enable);
1477     }
1478 
1479     /**
1480      * Gets the statuses of the frontend.
1481      *
1482      * <p>This retrieve the statuses of the frontend for given status types.
1483      *
1484      * @param statusTypes an array of status types which the caller requests. Any types that are not
1485      *        in {@link FrontendInfo#getStatusCapabilities()} would be ignored.
1486      * @return statuses which response the caller's requests. {@code null} if the operation failed.
1487      */
1488     @Nullable
getFrontendStatus(@onNull @rontendStatusType int[] statusTypes)1489     public FrontendStatus getFrontendStatus(@NonNull @FrontendStatusType int[] statusTypes) {
1490         mFrontendLock.lock();
1491         try {
1492             if (mFrontend == null) {
1493                 throw new IllegalStateException("frontend is not initialized");
1494             }
1495             if (mFeOwnerTuner != null) {
1496                 throw new IllegalStateException("Operation cannot be done by sharee of tuner");
1497             }
1498             return nativeGetFrontendStatus(statusTypes);
1499         } finally {
1500             mFrontendLock.unlock();
1501         }
1502     }
1503 
1504     /**
1505      * Gets hardware sync ID for audio and video.
1506      *
1507      * @param filter the filter instance for the hardware sync ID.
1508      * @return the id of hardware A/V sync.
1509      */
getAvSyncHwId(@onNull Filter filter)1510     public int getAvSyncHwId(@NonNull Filter filter) {
1511         mDemuxLock.lock();
1512         try {
1513             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
1514                 return INVALID_AV_SYNC_ID;
1515             }
1516             Integer id = nativeGetAvSyncHwId(filter);
1517             return id == null ? INVALID_AV_SYNC_ID : id;
1518         } finally {
1519             mDemuxLock.unlock();
1520         }
1521     }
1522 
1523     /**
1524      * Gets the current timestamp for Audio/Video sync
1525      *
1526      * <p>The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is
1527      * the same as PTS (Presentation Time Stamp).
1528      *
1529      * @param avSyncHwId the hardware id of A/V sync.
1530      * @return the current timestamp of hardware A/V sync.
1531      */
getAvSyncTime(int avSyncHwId)1532     public long getAvSyncTime(int avSyncHwId) {
1533         mDemuxLock.lock();
1534         try {
1535             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
1536                 return INVALID_TIMESTAMP;
1537             }
1538             Long time = nativeGetAvSyncTime(avSyncHwId);
1539             return time == null ? INVALID_TIMESTAMP : time;
1540         } finally {
1541             mDemuxLock.unlock();
1542         }
1543     }
1544 
1545     /**
1546      * Connects Conditional Access Modules (CAM) through Common Interface (CI).
1547      *
1548      * <p>The demux uses the output from the frontend as the input by default, and must change to
1549      * use the output from CI-CAM as the input after this call.
1550      *
1551      * <p> Note that this API is used to connect the CI-CAM to the Demux module while
1552      * {@link #connectFrontendToCiCam(int)} is used to connect CI-CAM to the Frontend module.
1553      *
1554      * @param ciCamId specify CI-CAM Id to connect.
1555      * @return result status of the operation.
1556      */
1557     @Result
connectCiCam(int ciCamId)1558     public int connectCiCam(int ciCamId) {
1559         mDemuxLock.lock();
1560         try {
1561             if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
1562                 return nativeConnectCiCam(ciCamId);
1563             }
1564             return RESULT_UNAVAILABLE;
1565         } finally {
1566             mDemuxLock.unlock();
1567         }
1568     }
1569 
1570     /**
1571      * Connect Conditional Access Modules (CAM) Frontend to support Common Interface (CI)
1572      * by-pass mode.
1573      *
1574      * <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that
1575      * the CICAM also receives the TS concurrently from the frontend when the Demux is receiving
1576      * the TS directly from the frontend.
1577      *
1578      * <p> Note that this API is used to connect the CI-CAM to the Frontend module while
1579      * {@link #connectCiCam(int)} is used to connect CI-CAM to the Demux module.
1580      *
1581      * <p>Use {@link #disconnectFrontendToCiCam(int)} to disconnect.
1582      *
1583      * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
1584      * no-op and return {@link #INVALID_LTS_ID}. Use {@link TunerVersionChecker#getTunerVersion()}
1585      * to check the version.
1586      *
1587      * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM)
1588      *                Common Interface (CI), to link.
1589      * @return Local transport stream id when connection is successfully established. Failed
1590      *         operation returns {@link #INVALID_LTS_ID} while unsupported version also returns
1591      *         {@link #INVALID_LTS_ID}. Check the current HAL version using
1592      *         {@link TunerVersionChecker#getTunerVersion()}.
1593      */
connectFrontendToCiCam(int ciCamId)1594     public int connectFrontendToCiCam(int ciCamId) {
1595         // TODO: change this so TRMS lock is held only when the resource handles for
1596         // CiCam/Frontend is null. Current implementation can only handle one local lock for that.
1597         acquireTRMSLock("connectFrontendToCiCam()");
1598         mFrontendCiCamLock.lock();
1599         mFrontendLock.lock();
1600         try {
1601             if (mFrontendHandle == null) {
1602                 Log.d(TAG, "Operation cannot be done without frontend");
1603                 return RESULT_INVALID_STATE;
1604             }
1605             if (mFeOwnerTuner != null) {
1606                 Log.d(TAG, "Operation cannot be done by sharee of tuner");
1607                 return RESULT_INVALID_STATE;
1608             }
1609             if (TunerVersionChecker.checkHigherOrEqualVersionTo(
1610                     TunerVersionChecker.TUNER_VERSION_1_1,
1611                     "linkFrontendToCiCam")) {
1612                 mRequestedCiCamId = ciCamId;
1613                 // No need to unlock mFrontendCiCamLock and mFrontendLock below becauase
1614                 // TRMS lock is already acquired. Pass null to disable lock related operations
1615                 if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, null)
1616                         && checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, null)
1617                     ) {
1618                     return nativeLinkCiCam(ciCamId);
1619                 }
1620             }
1621             return INVALID_LTS_ID;
1622         } finally {
1623             releaseTRMSLock();
1624             mFrontendCiCamLock.unlock();
1625             mFrontendLock.unlock();
1626         }
1627     }
1628 
1629     /**
1630      * Disconnects Conditional Access Modules (CAM).
1631      *
1632      * <p>The demux will use the output from the frontend as the input after this call.
1633      *
1634      * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while
1635      * {@link #disconnectFrontendToCiCam(int)} is used to disconnect CI-CAM to the Frontend module.
1636      *
1637      * @return result status of the operation.
1638      */
1639     @Result
disconnectCiCam()1640     public int disconnectCiCam() {
1641         mDemuxLock.lock();
1642         try {
1643             if (mDemuxHandle != null) {
1644                 return nativeDisconnectCiCam();
1645             }
1646             return RESULT_UNAVAILABLE;
1647         } finally {
1648             mDemuxLock.unlock();
1649         }
1650     }
1651 
1652     /**
1653      * Disconnect Conditional Access Modules (CAM) Frontend.
1654      *
1655      * <p>It is used by the client to unlink CI-CAM to a Frontend.
1656      *
1657      * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while
1658      * {@link #disconnectCiCam(int)} is used to disconnect CI-CAM to the Frontend module.
1659      *
1660      * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
1661      * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
1662      *
1663      * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM)
1664      *                Common Interface (CI), to disconnect.
1665      * @return result status of the operation. Unsupported version would return
1666      *         {@link #RESULT_UNAVAILABLE}
1667      */
1668     @Result
disconnectFrontendToCiCam(int ciCamId)1669     public int disconnectFrontendToCiCam(int ciCamId) {
1670         acquireTRMSLock("disconnectFrontendToCiCam()");
1671         try {
1672             if (mFrontendHandle == null) {
1673                 Log.d(TAG, "Operation cannot be done without frontend");
1674                 return RESULT_INVALID_STATE;
1675             }
1676             if (mFeOwnerTuner != null) {
1677                 Log.d(TAG, "Operation cannot be done by sharee of tuner");
1678                 return RESULT_INVALID_STATE;
1679             }
1680             if (TunerVersionChecker.checkHigherOrEqualVersionTo(
1681                     TunerVersionChecker.TUNER_VERSION_1_1,
1682                     "unlinkFrontendToCiCam")) {
1683                 mFrontendCiCamLock.lock();
1684                 if (mFrontendCiCamHandle != null && mFrontendCiCamId != null
1685                         && mFrontendCiCamId == ciCamId) {
1686                     int result = nativeUnlinkCiCam(ciCamId);
1687                     mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
1688                     mFrontendCiCamId = null;
1689                     mFrontendCiCamHandle = null;
1690                     return result;
1691                 }
1692             }
1693             return RESULT_UNAVAILABLE;
1694         } finally {
1695             if (mFrontendCiCamLock.isLocked()) {
1696                 mFrontendCiCamLock.unlock();
1697             }
1698             releaseTRMSLock();
1699         }
1700     }
1701 
1702     /**
1703      * Remove PID (packet identifier) from frontend output.
1704      *
1705      * <p>It is used by the client to remove a video or audio PID of other program to reduce the
1706      * total amount of recorded TS.
1707      *
1708      * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would cause
1709      * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
1710      *
1711      * @return result status of the operation. Unsupported version or if current active frontend
1712      *         doesn’t support PID filtering out would return {@link #RESULT_UNAVAILABLE}.
1713      * @throws IllegalStateException if there is no active frontend currently.
1714      */
1715     @Result
removeOutputPid(@ntRangefrom = 0) int pid)1716     public int removeOutputPid(@IntRange(from = 0) int pid) {
1717         mFrontendLock.lock();
1718         try {
1719             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1720                         TunerVersionChecker.TUNER_VERSION_2_0, "Remove output PID")) {
1721                 return RESULT_UNAVAILABLE;
1722             }
1723             if (mFrontend == null) {
1724                 throw new IllegalStateException("frontend is not initialized");
1725             }
1726             if (mFeOwnerTuner != null) {
1727                 Log.d(TAG, "Operation cannot be done by sharee of tuner");
1728                 return RESULT_INVALID_STATE;
1729             }
1730             return nativeRemoveOutputPid(pid);
1731         } finally {
1732             mFrontendLock.unlock();
1733         }
1734     }
1735 
1736     /**
1737      * Gets Frontend Status Readiness statuses for given status types.
1738      *
1739      * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported versions would cause
1740      * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
1741      *
1742      * @param statusTypes an array of status types.
1743      *
1744      * @return a list of current readiness states. It is empty if the operation fails or unsupported
1745      *         versions.
1746      * @throws IllegalStateException if there is no active frontend currently.
1747      */
1748     @NonNull
getFrontendStatusReadiness( @onNull @rontendStatusType int[] statusTypes)1749     public List<FrontendStatusReadiness> getFrontendStatusReadiness(
1750             @NonNull @FrontendStatusType int[] statusTypes) {
1751         mFrontendLock.lock();
1752         try {
1753             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1754                         TunerVersionChecker.TUNER_VERSION_2_0, "Get fronted status readiness")) {
1755                 return Collections.EMPTY_LIST;
1756             }
1757             if (mFrontend == null) {
1758                 throw new IllegalStateException("frontend is not initialized");
1759             }
1760             if (mFeOwnerTuner != null) {
1761                 throw new IllegalStateException("Operation cannot be done by sharee of tuner");
1762             }
1763             FrontendStatusReadiness[] readiness = nativeGetFrontendStatusReadiness(statusTypes);
1764             if (readiness == null) {
1765                 return Collections.EMPTY_LIST;
1766             }
1767             return Arrays.asList(readiness);
1768         } finally {
1769             mFrontendLock.unlock();
1770         }
1771     }
1772 
1773     /**
1774      * Gets the currently initialized and activated frontend information. To get all the available
1775      * frontend info on the device, use {@link getAvailableFrontendInfos()}.
1776      *
1777      * @return The active frontend information. {@code null} if the operation failed.
1778      * @throws IllegalStateException if there is no active frontend currently.
1779      */
1780     @Nullable
getFrontendInfo()1781     public FrontendInfo getFrontendInfo() {
1782         mFrontendLock.lock();
1783         try {
1784             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
1785                 return null;
1786             }
1787             if (mFrontend == null) {
1788                 throw new IllegalStateException("frontend is not initialized");
1789             }
1790             if (mFrontendInfo == null) {
1791                 mFrontendInfo = getFrontendInfoById(mFrontend.mId);
1792             }
1793             return mFrontendInfo;
1794         } finally {
1795             mFrontendLock.unlock();
1796         }
1797     }
1798 
1799     /**
1800      * Gets a list of all the available frontend information on the device. To get the information
1801      * of the currently active frontend, use {@link getFrontendInfo()}. The active frontend
1802      * information is also included in the list of the available frontend information.
1803      *
1804      * @return The list of all the available frontend information. {@code null} if the operation
1805      * failed.
1806      */
1807     @Nullable
1808     @SuppressLint("NullableCollection")
getAvailableFrontendInfos()1809     public List<FrontendInfo> getAvailableFrontendInfos() {
1810         FrontendInfo[] feInfoList = getFrontendInfoListInternal();
1811         if (feInfoList == null) {
1812             return null;
1813         }
1814         return Arrays.asList(feInfoList);
1815     }
1816 
1817     /**
1818      * Gets the currently initialized and activated frontend hardware information. The return values
1819      * would differ per device makers. E.g. RF chip version, Demod chip version, detailed status of
1820      * dvbs blind scan, etc
1821      *
1822      * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would return
1823      * {@code null}. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
1824      *
1825      * @return The active frontend hardware information. {@code null} if the operation failed.
1826      * @throws IllegalStateException if there is no active frontend currently.
1827      */
1828     @Nullable
getCurrentFrontendHardwareInfo()1829     public String getCurrentFrontendHardwareInfo() {
1830         mFrontendLock.lock();
1831         try {
1832             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1833                         TunerVersionChecker.TUNER_VERSION_2_0, "Get Frontend hardware info")) {
1834                 return null;
1835             }
1836             if (mFrontend == null) {
1837                 throw new IllegalStateException("frontend is not initialized");
1838             }
1839             if (mFeOwnerTuner != null) {
1840                 throw new IllegalStateException("Operation cannot be done by sharee of tuner");
1841             }
1842             return nativeGetFrontendHardwareInfo();
1843         } finally {
1844             mFrontendLock.unlock();
1845         }
1846     }
1847 
1848     /**
1849      * Sets the maximum usable frontends number of a given frontend type. It is used to enable or
1850      * disable frontends when cable connection status is changed by user.
1851      *
1852      * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would return
1853      * {@link RESULT_UNAVAILABLE}. Use {@link TunerVersionChecker#getTunerVersion()} to check the
1854      * version.
1855      *
1856      * @param frontendType the {@link android.media.tv.tuner.frontend.FrontendSettings.Type} which
1857      *                     the maximum usable number will be set.
1858      * @param maxNumber the new maximum usable number.
1859      * @return result status of the operation.
1860      */
1861     @Result
setMaxNumberOfFrontends( @rontendSettings.Type int frontendType, @IntRange(from = 0) int maxNumber)1862     public int setMaxNumberOfFrontends(
1863             @FrontendSettings.Type int frontendType, @IntRange(from = 0) int maxNumber) {
1864         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1865                     TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) {
1866             return RESULT_UNAVAILABLE;
1867         }
1868         if (maxNumber < 0) {
1869             return RESULT_INVALID_ARGUMENT;
1870         }
1871         if (mFeOwnerTuner != null) {
1872             Log.d(TAG, "Operation cannot be done by sharee of tuner");
1873             return RESULT_INVALID_STATE;
1874         }
1875         int res = nativeSetMaxNumberOfFrontends(frontendType, maxNumber);
1876         if (res == RESULT_SUCCESS) {
1877             if (!mTunerResourceManager.setMaxNumberOfFrontends(frontendType, maxNumber)) {
1878                 res = RESULT_INVALID_ARGUMENT;
1879             }
1880         }
1881         return res;
1882     }
1883 
1884     /**
1885      * Get the maximum usable frontends number of a given frontend type.
1886      *
1887      * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would return
1888      * {@code -1}. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
1889      *
1890      * @param frontendType the {@link android.media.tv.tuner.frontend.FrontendSettings.Type} which
1891      *                     the maximum usable number will be queried.
1892      * @return the maximum usable number of the queried frontend type.
1893      */
1894     @IntRange(from = -1)
getMaxNumberOfFrontends(@rontendSettings.Type int frontendType)1895     public int getMaxNumberOfFrontends(@FrontendSettings.Type int frontendType) {
1896         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
1897                     TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) {
1898             return -1;
1899         }
1900         int maxNumFromHAL = nativeGetMaxNumberOfFrontends(frontendType);
1901         int maxNumFromTRM = mTunerResourceManager.getMaxNumberOfFrontends(frontendType);
1902         if (maxNumFromHAL != maxNumFromTRM) {
1903             Log.w(TAG, "max num of usable frontend is out-of-sync b/w " + maxNumFromHAL
1904                     + " != " + maxNumFromTRM);
1905         }
1906         return maxNumFromHAL;
1907     }
1908 
1909     /** @hide */
getFrontendInfoById(int id)1910     public FrontendInfo getFrontendInfoById(int id) {
1911         mFrontendLock.lock();
1912         try {
1913             return nativeGetFrontendInfo(id);
1914         } finally {
1915             mFrontendLock.unlock();
1916         }
1917     }
1918 
1919     /**
1920      * Gets Demux capabilities.
1921      *
1922      * @return A {@link DemuxCapabilities} instance that represents the demux capabilities.
1923      *         {@code null} if the operation failed.
1924      */
1925     @Nullable
getDemuxCapabilities()1926     public DemuxCapabilities getDemuxCapabilities() {
1927         mDemuxLock.lock();
1928         try {
1929             return nativeGetDemuxCapabilities();
1930         } finally {
1931             mDemuxLock.unlock();
1932         }
1933     }
1934 
1935     /**
1936      * Gets DemuxInfo of the currently held demux
1937      *
1938      * @return A {@link DemuxInfo} of currently held demux resource.
1939      *         Returns null if no demux resource is held.
1940      */
1941     @Nullable
getCurrentDemuxInfo()1942     public DemuxInfo getCurrentDemuxInfo() {
1943         mDemuxLock.lock();
1944         try {
1945             if (mDemuxHandle == null) {
1946                 return null;
1947             }
1948             return nativeGetDemuxInfo(mDemuxHandle);
1949         } finally {
1950             mDemuxLock.unlock();
1951         }
1952     }
1953 
1954     /** @hide */
getDesiredDemuxInfo()1955     public DemuxInfo getDesiredDemuxInfo() {
1956         return mDesiredDemuxInfo;
1957     }
1958 
onFrontendEvent(int eventType)1959     private void onFrontendEvent(int eventType) {
1960         Log.d(TAG, "Got event from tuning. Event type: " + eventType + " for " + this);
1961         synchronized (mOnTuneEventLock) {
1962             if (mOnTuneEventExecutor != null && mOnTuneEventListener != null) {
1963                 mOnTuneEventExecutor.execute(() -> {
1964                     synchronized (mOnTuneEventLock) {
1965                         if (mOnTuneEventListener != null) {
1966                             mOnTuneEventListener.onTuneEvent(eventType);
1967                         }
1968                     }
1969                 });
1970             }
1971         }
1972 
1973         Log.d(TAG, "Wrote Stats Log for the events from tuning.");
1974         if (eventType == OnTuneEventListener.SIGNAL_LOCKED) {
1975             FrameworkStatsLog
1976                     .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
1977                         FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED);
1978         } else if (eventType == OnTuneEventListener.SIGNAL_NO_SIGNAL) {
1979             FrameworkStatsLog
1980                     .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
1981                         FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__NOT_LOCKED);
1982         } else if (eventType == OnTuneEventListener.SIGNAL_LOST_LOCK) {
1983             FrameworkStatsLog
1984                     .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
1985                         FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SIGNAL_LOST);
1986         }
1987     }
1988 
onLocked()1989     private void onLocked() {
1990         Log.d(TAG, "Wrote Stats Log for locked event from scanning.");
1991         FrameworkStatsLog.write(
1992                 FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
1993                 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED);
1994 
1995         synchronized (mScanCallbackLock) {
1996             if (mScanCallbackExecutor != null && mScanCallback != null) {
1997                 mScanCallbackExecutor.execute(() -> {
1998                     synchronized (mScanCallbackLock) {
1999                         if (mScanCallback != null) {
2000                             mScanCallback.onLocked();
2001                         }
2002                     }
2003                 });
2004             }
2005         }
2006     }
2007 
onUnlocked()2008     private void onUnlocked() {
2009         Log.d(TAG, "Wrote Stats Log for unlocked event from scanning.");
2010         FrameworkStatsLog.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
2011                 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED);
2012 
2013         synchronized (mScanCallbackLock) {
2014             if (mScanCallbackExecutor != null && mScanCallback != null) {
2015                 mScanCallbackExecutor.execute(() -> {
2016                     synchronized (mScanCallbackLock) {
2017                         if (mScanCallback != null) {
2018                             mScanCallback.onUnlocked();
2019                         }
2020                     }
2021                 });
2022             }
2023         }
2024     }
2025 
onScanStopped()2026     private void onScanStopped() {
2027         synchronized (mScanCallbackLock) {
2028             if (mScanCallbackExecutor != null && mScanCallback != null) {
2029                 mScanCallbackExecutor.execute(() -> {
2030                     synchronized (mScanCallbackLock) {
2031                         if (mScanCallback != null) {
2032                             mScanCallback.onScanStopped();
2033                         }
2034                     }
2035                 });
2036             }
2037         }
2038     }
2039 
onProgress(int percent)2040     private void onProgress(int percent) {
2041         synchronized (mScanCallbackLock) {
2042             if (mScanCallbackExecutor != null && mScanCallback != null) {
2043                 mScanCallbackExecutor.execute(() -> {
2044                     synchronized (mScanCallbackLock) {
2045                         if (mScanCallback != null) {
2046                             mScanCallback.onProgress(percent);
2047                         }
2048                     }
2049                 });
2050             }
2051         }
2052     }
2053 
onFrequenciesReport(long[] frequencies)2054     private void onFrequenciesReport(long[] frequencies) {
2055         synchronized (mScanCallbackLock) {
2056             if (mScanCallbackExecutor != null && mScanCallback != null) {
2057                 mScanCallbackExecutor.execute(() -> {
2058                     synchronized (mScanCallbackLock) {
2059                         if (mScanCallback != null) {
2060                             mScanCallback.onFrequenciesLongReported(frequencies);
2061                         }
2062                     }
2063                 });
2064             }
2065         }
2066     }
2067 
onSymbolRates(int[] rate)2068     private void onSymbolRates(int[] rate) {
2069         synchronized (mScanCallbackLock) {
2070             if (mScanCallbackExecutor != null && mScanCallback != null) {
2071                 mScanCallbackExecutor.execute(() -> {
2072                     synchronized (mScanCallbackLock) {
2073                         if (mScanCallback != null) {
2074                             mScanCallback.onSymbolRatesReported(rate);
2075                         }
2076                     }
2077                 });
2078             }
2079         }
2080     }
2081 
onHierarchy(int hierarchy)2082     private void onHierarchy(int hierarchy) {
2083         synchronized (mScanCallbackLock) {
2084             if (mScanCallbackExecutor != null && mScanCallback != null) {
2085                 mScanCallbackExecutor.execute(() -> {
2086                     synchronized (mScanCallbackLock) {
2087                         if (mScanCallback != null) {
2088                             mScanCallback.onHierarchyReported(hierarchy);
2089                         }
2090                     }
2091                 });
2092             }
2093         }
2094     }
2095 
onSignalType(int signalType)2096     private void onSignalType(int signalType) {
2097         synchronized (mScanCallbackLock) {
2098             if (mScanCallbackExecutor != null && mScanCallback != null) {
2099                 mScanCallbackExecutor.execute(() -> {
2100                     synchronized (mScanCallbackLock) {
2101                         if (mScanCallback != null) {
2102                             mScanCallback.onSignalTypeReported(signalType);
2103                         }
2104                     }
2105                 });
2106             }
2107         }
2108     }
2109 
onPlpIds(int[] plpIds)2110     private void onPlpIds(int[] plpIds) {
2111         synchronized (mScanCallbackLock) {
2112             if (mScanCallbackExecutor != null && mScanCallback != null) {
2113                 mScanCallbackExecutor.execute(() -> {
2114                     synchronized (mScanCallbackLock) {
2115                         if (mScanCallback != null) {
2116                             mScanCallback.onPlpIdsReported(plpIds);
2117                         }
2118                     }
2119                 });
2120             }
2121         }
2122     }
2123 
onGroupIds(int[] groupIds)2124     private void onGroupIds(int[] groupIds) {
2125         synchronized (mScanCallbackLock) {
2126             if (mScanCallbackExecutor != null && mScanCallback != null) {
2127                 mScanCallbackExecutor.execute(() -> {
2128                     synchronized (mScanCallbackLock) {
2129                         if (mScanCallback != null) {
2130                             mScanCallback.onGroupIdsReported(groupIds);
2131                         }
2132                     }
2133                 });
2134             }
2135         }
2136     }
2137 
onInputStreamIds(int[] inputStreamIds)2138     private void onInputStreamIds(int[] inputStreamIds) {
2139         synchronized (mScanCallbackLock) {
2140             if (mScanCallbackExecutor != null && mScanCallback != null) {
2141                 mScanCallbackExecutor.execute(() -> {
2142                     synchronized (mScanCallbackLock) {
2143                         if (mScanCallback != null) {
2144                             mScanCallback.onInputStreamIdsReported(inputStreamIds);
2145                         }
2146                     }
2147                 });
2148             }
2149         }
2150     }
2151 
onDvbsStandard(int dvbsStandandard)2152     private void onDvbsStandard(int dvbsStandandard) {
2153         synchronized (mScanCallbackLock) {
2154             if (mScanCallbackExecutor != null && mScanCallback != null) {
2155                 mScanCallbackExecutor.execute(() -> {
2156                     synchronized (mScanCallbackLock) {
2157                         if (mScanCallback != null) {
2158                             mScanCallback.onDvbsStandardReported(dvbsStandandard);
2159                         }
2160                     }
2161                 });
2162             }
2163         }
2164     }
2165 
onDvbtStandard(int dvbtStandard)2166     private void onDvbtStandard(int dvbtStandard) {
2167         synchronized (mScanCallbackLock) {
2168             if (mScanCallbackExecutor != null && mScanCallback != null) {
2169                 mScanCallbackExecutor.execute(() -> {
2170                     synchronized (mScanCallbackLock) {
2171                         if (mScanCallback != null) {
2172                             mScanCallback.onDvbtStandardReported(dvbtStandard);
2173                         }
2174                     }
2175                 });
2176             }
2177         }
2178     }
2179 
onAnalogSifStandard(int sif)2180     private void onAnalogSifStandard(int sif) {
2181         synchronized (mScanCallbackLock) {
2182             if (mScanCallbackExecutor != null && mScanCallback != null) {
2183                 mScanCallbackExecutor.execute(() -> {
2184                     synchronized (mScanCallbackLock) {
2185                         if (mScanCallback != null) {
2186                             mScanCallback.onAnalogSifStandardReported(sif);
2187                         }
2188                     }
2189                 });
2190             }
2191         }
2192     }
2193 
onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos)2194     private void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos) {
2195         synchronized (mScanCallbackLock) {
2196             if (mScanCallbackExecutor != null && mScanCallback != null) {
2197                 mScanCallbackExecutor.execute(() -> {
2198                     synchronized (mScanCallbackLock) {
2199                         if (mScanCallback != null) {
2200                             mScanCallback.onAtsc3PlpInfosReported(atsc3PlpInfos);
2201                         }
2202                     }
2203                 });
2204             }
2205         }
2206     }
2207 
onModulationReported(int modulation)2208     private void onModulationReported(int modulation) {
2209         synchronized (mScanCallbackLock) {
2210             if (mScanCallbackExecutor != null && mScanCallback != null) {
2211                 mScanCallbackExecutor.execute(() -> {
2212                     synchronized (mScanCallbackLock) {
2213                         if (mScanCallback != null) {
2214                             mScanCallback.onModulationReported(modulation);
2215                         }
2216                     }
2217                 });
2218             }
2219         }
2220     }
2221 
onPriorityReported(boolean isHighPriority)2222     private void onPriorityReported(boolean isHighPriority) {
2223         synchronized (mScanCallbackLock) {
2224             if (mScanCallbackExecutor != null && mScanCallback != null) {
2225                 mScanCallbackExecutor.execute(() -> {
2226                     synchronized (mScanCallbackLock) {
2227                         if (mScanCallback != null) {
2228                             mScanCallback.onPriorityReported(isHighPriority);
2229                         }
2230                     }
2231                 });
2232             }
2233         }
2234     }
2235 
onDvbcAnnexReported(int dvbcAnnex)2236     private void onDvbcAnnexReported(int dvbcAnnex) {
2237         synchronized (mScanCallbackLock) {
2238             if (mScanCallbackExecutor != null && mScanCallback != null) {
2239                 mScanCallbackExecutor.execute(() -> {
2240                     synchronized (mScanCallbackLock) {
2241                         if (mScanCallback != null) {
2242                             mScanCallback.onDvbcAnnexReported(dvbcAnnex);
2243                         }
2244                     }
2245                 });
2246             }
2247         }
2248     }
2249 
onDvbtCellIdsReported(int[] dvbtCellIds)2250     private void onDvbtCellIdsReported(int[] dvbtCellIds) {
2251         synchronized (mScanCallbackLock) {
2252             if (mScanCallbackExecutor != null && mScanCallback != null) {
2253                 mScanCallbackExecutor.execute(() -> {
2254                     synchronized (mScanCallbackLock) {
2255                         if (mScanCallback != null) {
2256                             mScanCallback.onDvbtCellIdsReported(dvbtCellIds);
2257                         }
2258                     }
2259                 });
2260             }
2261         }
2262     }
2263 
2264     /**
2265      * Opens a filter object based on the given types and buffer size.
2266      *
2267      * <p>For TUNER_VERSION_3_0 and above, configureDemuxInternal() will be called with mainType.
2268      * However, unlike when configureDemux() is called directly, the desired filter types will not
2269      * be changed when previously set desired filter types are the superset of the newly desired
2270      * ones.
2271      *
2272      * @param mainType the main type of the filter.
2273      * @param subType the subtype of the filter.
2274      * @param bufferSize the buffer size of the filter to be opened in bytes. The buffer holds the
2275      * data output from the filter.
2276      * @param executor the executor on which callback will be invoked. The default event handler
2277      * executor is used if it's {@code null}.
2278      * @param cb the callback to receive notifications from filter.
2279      * @return the opened filter. {@code null} if the operation failed.
2280      */
2281     @Nullable
openFilter(@ype int mainType, @Subtype int subType, @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor, @Nullable FilterCallback cb)2282     public Filter openFilter(@Type int mainType, @Subtype int subType,
2283             @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor,
2284             @Nullable FilterCallback cb) {
2285         mDemuxLock.lock();
2286         try {
2287             int tunerMajorVersion = TunerVersionChecker.getMajorVersion(sTunerVersion);
2288             if (sTunerVersion >= TunerVersionChecker.TUNER_VERSION_3_0) {
2289                 DemuxInfo demuxInfo = new DemuxInfo(mainType);
2290                 int res = configureDemuxInternal(demuxInfo, false /* reduceDesiredFilterTypes */);
2291                 if (res != RESULT_SUCCESS) {
2292                     Log.e(TAG, "openFilter called for unsupported mainType: " + mainType);
2293                     return null;
2294                 }
2295             }
2296             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
2297                 return null;
2298             }
2299             Filter filter = nativeOpenFilter(
2300                     mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
2301             if (filter != null) {
2302                 filter.setType(mainType, subType);
2303                 filter.setCallback(cb, executor);
2304                 if (mHandler == null) {
2305                     mHandler = createEventHandler();
2306                 }
2307                 synchronized (mFilters) {
2308                     WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter);
2309                     mFilters.add(weakFilter);
2310                     if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) {
2311                         Iterator<WeakReference<Filter>> iterator = mFilters.iterator();
2312                         while (iterator.hasNext()) {
2313                             WeakReference<Filter> wFilter = iterator.next();
2314                             if (wFilter.get() == null) {
2315                                 iterator.remove();
2316                             }
2317                         }
2318                     }
2319                 }
2320             }
2321             return filter;
2322         } finally {
2323             mDemuxLock.unlock();
2324         }
2325     }
2326 
2327     /**
2328      * Opens an LNB (low-noise block downconverter) object.
2329      *
2330      * <p>If there is an existing Lnb object, it will be replace by the newly opened one.
2331      *
2332      * @param executor the executor on which callback will be invoked. The default event handler
2333      * executor is used if it's {@code null}.
2334      * @param cb the callback to receive notifications from LNB.
2335      * @return the opened LNB object. {@code null} if the operation failed.
2336      */
2337     @Nullable
openLnb(@allbackExecutor @onNull Executor executor, @NonNull LnbCallback cb)2338     public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
2339         mLnbLock.lock();
2340         try {
2341             Objects.requireNonNull(executor, "executor must not be null");
2342             Objects.requireNonNull(cb, "LnbCallback must not be null");
2343             if (mLnb != null) {
2344                 mLnb.setCallbackAndOwner(this, executor, cb);
2345                 return mLnb;
2346             }
2347             if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, mLnbLock)
2348                     && mLnb != null) {
2349                 mLnb.setCallbackAndOwner(this, executor, cb);
2350                 if (mFrontendHandle != null && mFrontend != null) {
2351                     setLnb(mLnb);
2352                 }
2353                 return mLnb;
2354             }
2355             return null;
2356         } finally {
2357             mLnbLock.unlock();
2358         }
2359     }
2360 
2361     /**
2362      * Opens an LNB (low-noise block downconverter) object specified by the give name.
2363      *
2364      * @param name the LNB name.
2365      * @param executor the executor on which callback will be invoked. The default event handler
2366      * executor is used if it's {@code null}.
2367      * @param cb the callback to receive notifications from LNB.
2368      * @return the opened LNB object. {@code null} if the operation failed.
2369      */
2370     @Nullable
openLnbByName(@onNull String name, @CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb)2371     public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor,
2372             @NonNull LnbCallback cb) {
2373         acquireTRMSLock("openLnbByName");
2374         mLnbLock.lock();
2375         try {
2376             Objects.requireNonNull(name, "LNB name must not be null");
2377             Objects.requireNonNull(executor, "executor must not be null");
2378             Objects.requireNonNull(cb, "LnbCallback must not be null");
2379             Lnb newLnb = nativeOpenLnbByName(name);
2380             if (newLnb != null) {
2381                 if (mLnb != null) {
2382                     mLnb.closeInternal();
2383                     mLnbHandle = null;
2384                 }
2385                 mLnb = newLnb;
2386                 mLnb.setCallbackAndOwner(this, executor, cb);
2387                 if (mFrontendHandle != null && mFrontend != null) {
2388                     setLnb(mLnb);
2389                 }
2390             }
2391             return mLnb;
2392         } finally {
2393             releaseTRMSLock();
2394             mLnbLock.unlock();
2395         }
2396     }
2397 
requestLnb()2398     private boolean requestLnb() {
2399         long[] lnbHandle = new long[1];
2400         TunerLnbRequest request = new TunerLnbRequest();
2401         request.clientId = mClientId;
2402         boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle);
2403         if (granted) {
2404             mLnbHandle = lnbHandle[0];
2405             mLnb = nativeOpenLnbByHandle(mLnbHandle);
2406         }
2407         return granted;
2408     }
2409 
2410     /**
2411      * Open a time filter object.
2412      *
2413      * @return the opened time filter object. {@code null} if the operation failed.
2414      */
2415     @Nullable
openTimeFilter()2416     public TimeFilter openTimeFilter() {
2417         mDemuxLock.lock();
2418         try {
2419             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
2420                 return null;
2421             }
2422             return nativeOpenTimeFilter();
2423         } finally {
2424             mDemuxLock.unlock();
2425         }
2426     }
2427 
2428     /**
2429      * Opens a Descrambler in tuner.
2430      *
2431      * @return a {@link Descrambler} object.
2432      */
2433     @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
2434     @Nullable
openDescrambler()2435     public Descrambler openDescrambler() {
2436         acquireTRMSLock("openDescrambler()");
2437         mDemuxLock.lock();
2438         try {
2439             // no need to unlock mDemuxLock (so pass null instead) as TRMS lock is already acquired
2440             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, null)) {
2441                 return null;
2442             }
2443             return requestDescrambler();
2444         } finally {
2445             releaseTRMSLock();
2446             mDemuxLock.unlock();
2447         }
2448     }
2449 
2450     /**
2451      * Open a DVR (Digital Video Record) recorder instance.
2452      *
2453      * @param bufferSize the buffer size of the output in bytes. It's used to hold output data of
2454      * the attached filters.
2455      * @param executor the executor on which callback will be invoked. The default event handler
2456      * executor is used if it's {@code null}.
2457      * @param l the listener to receive notifications from DVR recorder.
2458      * @return the opened DVR recorder object. {@code null} if the operation failed.
2459      */
2460     @Nullable
openDvrRecorder( @ytesLong long bufferSize, @CallbackExecutor @NonNull Executor executor, @NonNull OnRecordStatusChangedListener l)2461     public DvrRecorder openDvrRecorder(
2462             @BytesLong long bufferSize,
2463             @CallbackExecutor @NonNull Executor executor,
2464             @NonNull OnRecordStatusChangedListener l) {
2465         mDemuxLock.lock();
2466         try {
2467             Objects.requireNonNull(executor, "executor must not be null");
2468             Objects.requireNonNull(l, "OnRecordStatusChangedListener must not be null");
2469             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
2470                 return null;
2471             }
2472             DvrRecorder dvr = nativeOpenDvrRecorder(bufferSize);
2473             dvr.setListener(executor, l);
2474             return dvr;
2475         } finally {
2476             mDemuxLock.unlock();
2477         }
2478     }
2479 
2480     /**
2481      * Open a DVR (Digital Video Record) playback instance.
2482      *
2483      * @param bufferSize the buffer size of the output in bytes. It's used to hold output data of
2484      * the attached filters.
2485      * @param executor the executor on which callback will be invoked. The default event handler
2486      * executor is used if it's {@code null}.
2487      * @param l the listener to receive notifications from DVR recorder.
2488      * @return the opened DVR playback object. {@code null} if the operation failed.
2489      */
2490     @Nullable
openDvrPlayback( @ytesLong long bufferSize, @CallbackExecutor @NonNull Executor executor, @NonNull OnPlaybackStatusChangedListener l)2491     public DvrPlayback openDvrPlayback(
2492             @BytesLong long bufferSize,
2493             @CallbackExecutor @NonNull Executor executor,
2494             @NonNull OnPlaybackStatusChangedListener l) {
2495         mDemuxLock.lock();
2496         try {
2497             Objects.requireNonNull(executor, "executor must not be null");
2498             Objects.requireNonNull(l, "OnPlaybackStatusChangedListener must not be null");
2499             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
2500                 return null;
2501             }
2502             DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
2503             dvr.setListener(executor, l);
2504             return dvr;
2505         } finally {
2506             mDemuxLock.unlock();
2507         }
2508     }
2509 
2510     /**
2511      * Request a frontend by frontend info.
2512      *
2513      * <p> This API is used if the applications want to select a desired frontend before
2514      * {@link tune} to use a specific satellite or sending SatCR DiSEqC command for {@link tune}.
2515      *
2516      * @param desiredFrontendInfo the FrontendInfo of the desired fronted. It can be retrieved by
2517      * {@link getAvailableFrontendInfos}
2518      *
2519      * @return result status of open operation.
2520      * @throws SecurityException if the caller does not have appropriate permissions.
2521      */
2522     @Result
applyFrontend(@onNull FrontendInfo desiredFrontendInfo)2523     public int applyFrontend(@NonNull FrontendInfo desiredFrontendInfo) {
2524         Objects.requireNonNull(desiredFrontendInfo, "desiredFrontendInfo must not be null");
2525         mFrontendLock.lock();
2526         try {
2527             if (mFeOwnerTuner != null) {
2528                 Log.e(TAG, "Operation connot be done by sharee of tuner");
2529                 return RESULT_INVALID_STATE;
2530             }
2531             if (mFrontendHandle != null) {
2532                 Log.e(TAG, "A frontend has been opened before");
2533                 return RESULT_INVALID_STATE;
2534             }
2535             mFrontendType = desiredFrontendInfo.getType();
2536             mDesiredFrontendId = desiredFrontendInfo.getId();
2537             if (DEBUG) {
2538                 Log.d(TAG, "Applying frontend with type " + mFrontendType + ", id "
2539                         + mDesiredFrontendId);
2540             }
2541             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
2542                 return RESULT_UNAVAILABLE;
2543             }
2544             return RESULT_SUCCESS;
2545         } finally {
2546             mFrontendLock.unlock();
2547         }
2548     }
2549 
2550     /**
2551      * Request a frontend by frontend type.
2552      *
2553      * <p> This API is used (before {@link #tune(FrontendSettings)}) if the applications want to
2554      * select a frontend of a particular type for {@link #tune(FrontendSettings)} when there are
2555      * multiple frontends of the same type present, allowing the system to select which one is
2556      * applied. The applied frontend will be one of the not-in-use frontends. If all frontends are
2557      * in-use, this API will reclaim and apply the frontend owned by the lowest priority client if
2558      * current client has higher priority. Otherwise, this API will not apply any frontend and
2559      * return {@link #RESULT_UNAVAILABLE}.
2560      *
2561      * @param desiredFrontendType the Type of the desired fronted. Should be one of
2562      *                            {@link android.media.tv.tuner.frontend.FrontendSettings.Type}
2563      * @return result status of open operation.
2564      * @see #applyFrontend(FrontendInfo)
2565      * @see #tune(FrontendSettings)
2566      */
2567     @Result
2568     @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
2569     @RequiresPermission(
2570         allOf = {"android.permission.TUNER_RESOURCE_ACCESS", "android.permission.ACCESS_TV_TUNER"})
applyFrontendByType(@rontendSettings.Type int desiredFrontendType)2571     public int applyFrontendByType(@FrontendSettings.Type int desiredFrontendType) {
2572         mFrontendLock.lock();
2573         try {
2574             if (mFeOwnerTuner != null) {
2575                 Log.e(TAG, "Operation connot be done by sharee of tuner");
2576                 return RESULT_INVALID_STATE;
2577             }
2578             if (mFrontendHandle != null) {
2579                 Log.e(TAG, "A frontend has been opened before");
2580                 return RESULT_INVALID_STATE;
2581             }
2582 
2583             mDesiredFrontendId = null;
2584             mFrontendType = desiredFrontendType;
2585             if (DEBUG) {
2586                 Log.d(TAG, "Applying frontend with type " + mFrontendType);
2587             }
2588             if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
2589                 return RESULT_UNAVAILABLE;
2590             }
2591             return RESULT_SUCCESS;
2592         } finally {
2593             mFrontendLock.unlock();
2594         }
2595     }
2596 
2597     /**
2598      * Open a shared filter instance.
2599      *
2600      * @param context the context of the caller.
2601      * @param sharedFilterToken the token of the shared filter being opened.
2602      * @param executor the executor on which callback will be invoked.
2603      * @param cb the listener to receive notifications from shared filter.
2604      * @return the opened shared filter object. {@code null} if the operation failed.
2605      */
2606     @RequiresPermission(android.Manifest.permission.ACCESS_TV_SHARED_FILTER)
2607     @Nullable
openSharedFilter(@onNull Context context, @NonNull String sharedFilterToken, @CallbackExecutor @NonNull Executor executor, @NonNull SharedFilterCallback cb)2608     static public SharedFilter openSharedFilter(@NonNull Context context,
2609             @NonNull String sharedFilterToken, @CallbackExecutor @NonNull Executor executor,
2610             @NonNull SharedFilterCallback cb) {
2611         // TODO: check what happenes when onReclaimResources() is called and see if
2612         // this needs to be protected with TRMS lock
2613         Objects.requireNonNull(sharedFilterToken, "sharedFilterToken must not be null");
2614         Objects.requireNonNull(executor, "executor must not be null");
2615         Objects.requireNonNull(cb, "SharedFilterCallback must not be null");
2616 
2617         if (context.checkCallingOrSelfPermission(
2618                     android.Manifest.permission.ACCESS_TV_SHARED_FILTER)
2619                 != PackageManager.PERMISSION_GRANTED) {
2620             throw new SecurityException("Caller must have ACCESS_TV_SHAREDFILTER permission.");
2621         }
2622 
2623         SharedFilter filter = nativeOpenSharedFilter(sharedFilterToken);
2624         if (filter != null) {
2625             filter.setCallback(cb, executor);
2626         }
2627         return filter;
2628     }
2629 
2630     /**
2631      * Configures the desired {@link DemuxInfo}
2632      *
2633      * <p>The already held demux and filters will be released when desiredDemuxInfo is null or the
2634      * desireDemuxInfo.getFilterTypes() is not supported by the already held demux.
2635      *
2636      * @param desiredDemuxInfo the desired {@link DemuxInfo}, which includes information such as
2637      *                         filterTypes ({@link DemuxFilterMainType}).
2638      * @return result status of configure demux operation. {@link #RESULT_UNAVAILABLE} is returned
2639      *                when a) the desired capabilities are not supported by the system,
2640      *                b) this API is called on unsupported version, or
2641      *                c) either getDemuxCapabilities or getFilterTypeCapabilityList()
2642      *                returns an empty array
2643      */
2644     @Result
configureDemux(@ullable DemuxInfo desiredDemuxInfo)2645     public int configureDemux(@Nullable DemuxInfo desiredDemuxInfo) {
2646         int tunerMajorVersion = TunerVersionChecker.getMajorVersion(sTunerVersion);
2647         if (sTunerVersion < TunerVersionChecker.TUNER_VERSION_3_0) {
2648             Log.e(TAG, "configureDemux() is not supported for tuner version:"
2649                     + TunerVersionChecker.getMajorVersion(sTunerVersion) + "."
2650                     + TunerVersionChecker.getMinorVersion(sTunerVersion) + ".");
2651             return RESULT_UNAVAILABLE;
2652         }
2653 
2654         synchronized (mDemuxLock) {
2655             return configureDemuxInternal(desiredDemuxInfo, true /* reduceDesiredFilterTypes */);
2656         }
2657     }
2658 
configureDemuxInternal(@ullable DemuxInfo desiredDemuxInfo, boolean reduceDesiredFilterTypes)2659     private int configureDemuxInternal(@Nullable DemuxInfo desiredDemuxInfo,
2660             boolean reduceDesiredFilterTypes) {
2661         // release the currently held demux if the desired demux info is null
2662         if (desiredDemuxInfo == null) {
2663             if (mDemuxHandle != null) {
2664                 releaseFilters();
2665                 releaseDemux();
2666             }
2667             return RESULT_SUCCESS;
2668         }
2669 
2670         int desiredFilterTypes = desiredDemuxInfo.getFilterTypes();
2671 
2672         // just update and return success if the desiredFilterTypes is equal to or a subset of
2673         // a previously configured value
2674         if ((mDesiredDemuxInfo.getFilterTypes() & desiredFilterTypes)
2675                 == desiredFilterTypes) {
2676             if (reduceDesiredFilterTypes) {
2677                 mDesiredDemuxInfo.setFilterTypes(desiredFilterTypes);
2678             }
2679             return RESULT_SUCCESS;
2680         }
2681 
2682         // check if the desire capability is supported
2683         DemuxCapabilities caps = nativeGetDemuxCapabilities();
2684         if (caps == null) {
2685             Log.e(TAG, "configureDemuxInternal:failed to get DemuxCapabilities");
2686             return RESULT_UNAVAILABLE;
2687         }
2688 
2689         int[] filterCapsList = caps.getFilterTypeCapabilityList();
2690         if (filterCapsList.length <= 0) {
2691             Log.e(TAG, "configureDemuxInternal: getFilterTypeCapabilityList()"
2692                     + " returned an empty array");
2693             return RESULT_UNAVAILABLE;
2694         }
2695 
2696         boolean supported = false;
2697         for (int filterCaps : filterCapsList) {
2698             if ((desiredFilterTypes & filterCaps) == desiredFilterTypes) {
2699                 supported = true;
2700                 break;
2701             }
2702         }
2703         if (!supported) {
2704             Log.e(TAG, "configureDemuxInternal: requested caps:" + desiredFilterTypes
2705                     + " is not supported by the system");
2706             return RESULT_UNAVAILABLE;
2707         }
2708 
2709         // close demux if not compatible
2710         if (mDemuxHandle != null) {
2711             if (desiredFilterTypes != Filter.TYPE_UNDEFINED) {
2712                 // Release the existing demux only if
2713                 // the desired caps is not supported
2714                 DemuxInfo currentDemuxInfo = nativeGetDemuxInfo(mDemuxHandle);
2715                 if (currentDemuxInfo != null) {
2716                     if ((desiredFilterTypes & currentDemuxInfo.getFilterTypes())
2717                             != desiredFilterTypes) {
2718                         releaseFilters();
2719                         releaseDemux();
2720                     }
2721                 }
2722             }
2723         }
2724         mDesiredDemuxInfo.setFilterTypes(desiredFilterTypes);
2725         return RESULT_SUCCESS;
2726     }
2727 
requestDemux()2728     private boolean requestDemux() {
2729         long[] demuxHandle = new long[1];
2730         TunerDemuxRequest request = new TunerDemuxRequest();
2731         request.clientId = mClientId;
2732         request.desiredFilterTypes = mDesiredDemuxInfo.getFilterTypes();
2733         boolean granted = mTunerResourceManager.requestDemux(request, demuxHandle);
2734         if (granted) {
2735             mDemuxHandle = demuxHandle[0];
2736             nativeOpenDemuxByhandle(mDemuxHandle);
2737         }
2738         return granted;
2739     }
2740 
requestDescrambler()2741     private Descrambler requestDescrambler() {
2742         long[] descramblerHandle = new long[1];
2743         TunerDescramblerRequest request = new TunerDescramblerRequest();
2744         request.clientId = mClientId;
2745         boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle);
2746         if (!granted) {
2747             return null;
2748         }
2749         long handle = descramblerHandle[0];
2750         Descrambler descrambler = nativeOpenDescramblerByHandle(handle);
2751         if (descrambler != null) {
2752             synchronized (mDescramblers) {
2753                 WeakReference weakDescrambler = new WeakReference<Descrambler>(descrambler);
2754                 mDescramblers.put(handle, weakDescrambler);
2755             }
2756         } else {
2757             mTunerResourceManager.releaseDescrambler(handle, mClientId);
2758         }
2759         return descrambler;
2760     }
2761 
requestFrontendCiCam(int ciCamId)2762     private boolean requestFrontendCiCam(int ciCamId) {
2763         long[] ciCamHandle = new long[1];
2764         TunerCiCamRequest request = new TunerCiCamRequest();
2765         request.clientId = mClientId;
2766         request.ciCamId = ciCamId;
2767         boolean granted = mTunerResourceManager.requestCiCam(request, ciCamHandle);
2768         if (granted) {
2769             mFrontendCiCamHandle = ciCamHandle[0];
2770             mFrontendCiCamId = ciCamId;
2771         }
2772         return granted;
2773     }
2774 
checkResource(int resourceType, ReentrantLock localLock)2775     private boolean checkResource(int resourceType, ReentrantLock localLock) {
2776         switch (resourceType) {
2777             case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
2778                 if (mFrontendHandle == null && !requestResource(resourceType, localLock)) {
2779                     return false;
2780                 }
2781                 break;
2782             }
2783             case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: {
2784                 if (mLnb == null && !requestResource(resourceType, localLock)) {
2785                     return false;
2786                 }
2787                 break;
2788             }
2789             case TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX: {
2790                 if (mDemuxHandle == null && !requestResource(resourceType, localLock)) {
2791                     return false;
2792                 }
2793                 break;
2794             }
2795             case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM: {
2796                 if (mFrontendCiCamHandle == null && !requestResource(resourceType, localLock)) {
2797                     return false;
2798                 }
2799                 break;
2800             }
2801             default:
2802                 return false;
2803         }
2804         return true;
2805     }
2806 
2807     // Expected flow of how to use this function is:
2808     // 1) lock the localLock and check if the resource is already held
2809     // 2) if yes, no need to call this function and continue with the handle with the lock held
2810     // 3) if no, then first release the held lock and grab the TRMS lock to avoid deadlock
2811     // 4) grab the local lock again and release the TRMS lock
2812     // If localLock is null, we'll assume the caller does not want the lock related operations
requestResource(int resourceType, ReentrantLock localLock)2813     private boolean requestResource(int resourceType, ReentrantLock localLock) {
2814         boolean enableLockOperations = localLock != null;
2815 
2816         // release the local lock first to avoid deadlock
2817         if (enableLockOperations) {
2818             if (localLock.isLocked()) {
2819                 localLock.unlock();
2820             } else {
2821                 throw new IllegalStateException("local lock must be locked beforehand");
2822             }
2823         }
2824 
2825         // now safe to grab TRMS lock
2826         if (enableLockOperations) {
2827             acquireTRMSLock("requestResource:" + resourceType);
2828         }
2829 
2830         try {
2831             // lock the local lock
2832             if (enableLockOperations) {
2833                 localLock.lock();
2834             }
2835             switch (resourceType) {
2836                 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
2837                     return requestFrontend();
2838                 }
2839                 case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: {
2840                     return requestLnb();
2841                 }
2842                 case TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX: {
2843                     return requestDemux();
2844                 }
2845                 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM: {
2846                     return requestFrontendCiCam(mRequestedCiCamId);
2847                 }
2848                 default:
2849                     return false;
2850             }
2851         } finally {
2852             if (enableLockOperations) {
2853                 releaseTRMSLock();
2854             }
2855         }
2856     }
2857 
2858     // Must be called while TRMS lock is being held
releaseLnb()2859     /* package */ void releaseLnb() {
2860         mLnbLock.lock();
2861         try {
2862             if (mLnbHandle != null) {
2863                 // LNB handle can be null if it's opened by name.
2864                 if (DEBUG) {
2865                     Log.d(TAG, "releasing Lnb");
2866                 }
2867                 mTunerResourceManager.releaseLnb(mLnbHandle, mClientId);
2868                 mLnbHandle = null;
2869             } else {
2870                 if (DEBUG) {
2871                     Log.d(TAG, "NOT releasing Lnb because mLnbHandle is null");
2872                 }
2873             }
2874             mLnb = null;
2875         } finally {
2876             mLnbLock.unlock();
2877         }
2878     }
2879 
2880     /** @hide */
getClientId()2881     public int getClientId() {
2882         return mClientId;
2883     }
2884 
getTunerResourceManager()2885     /* package */ TunerResourceManager getTunerResourceManager() {
2886         return mTunerResourceManager;
2887     }
2888 
acquireTRMSLock(String functionNameForLog)2889     private void acquireTRMSLock(String functionNameForLog) {
2890         if (DEBUG) {
2891             Log.d(TAG, "ATTEMPT:acquireLock() in " + functionNameForLog
2892                     + "for clientId:" + mClientId);
2893         }
2894         if (!mTunerResourceManager.acquireLock(mClientId)) {
2895             Log.e(TAG, "FAILED:acquireLock() in " + functionNameForLog
2896                     + " for clientId:" + mClientId + " - this can cause deadlock between"
2897                     + " Tuner API calls and onReclaimResources()");
2898         }
2899     }
2900 
releaseTRMSLock()2901     private void releaseTRMSLock() {
2902         mTunerResourceManager.releaseLock(mClientId);
2903     }
2904 }
2905