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