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