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