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.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.RequiresPermission; 25 import android.annotation.SuppressLint; 26 import android.annotation.SystemApi; 27 import android.content.Context; 28 import android.hardware.tv.tuner.V1_0.Constants; 29 import android.media.tv.TvInputService; 30 import android.media.tv.tuner.dvr.DvrPlayback; 31 import android.media.tv.tuner.dvr.DvrRecorder; 32 import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener; 33 import android.media.tv.tuner.dvr.OnRecordStatusChangedListener; 34 import android.media.tv.tuner.filter.Filter; 35 import android.media.tv.tuner.filter.Filter.Subtype; 36 import android.media.tv.tuner.filter.Filter.Type; 37 import android.media.tv.tuner.filter.FilterCallback; 38 import android.media.tv.tuner.filter.TimeFilter; 39 import android.media.tv.tuner.frontend.Atsc3PlpInfo; 40 import android.media.tv.tuner.frontend.FrontendInfo; 41 import android.media.tv.tuner.frontend.FrontendSettings; 42 import android.media.tv.tuner.frontend.FrontendStatus; 43 import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType; 44 import android.media.tv.tuner.frontend.OnTuneEventListener; 45 import android.media.tv.tuner.frontend.ScanCallback; 46 import android.media.tv.tunerresourcemanager.ResourceClientProfile; 47 import android.media.tv.tunerresourcemanager.TunerCiCamRequest; 48 import android.media.tv.tunerresourcemanager.TunerDemuxRequest; 49 import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; 50 import android.media.tv.tunerresourcemanager.TunerFrontendRequest; 51 import android.media.tv.tunerresourcemanager.TunerLnbRequest; 52 import android.media.tv.tunerresourcemanager.TunerResourceManager; 53 import android.os.Handler; 54 import android.os.HandlerExecutor; 55 import android.os.Looper; 56 import android.os.Message; 57 import android.os.Process; 58 import android.util.Log; 59 60 import com.android.internal.util.FrameworkStatsLog; 61 62 import java.lang.annotation.Retention; 63 import java.lang.annotation.RetentionPolicy; 64 import java.lang.ref.WeakReference; 65 import java.util.ArrayList; 66 import java.util.Arrays; 67 import java.util.HashMap; 68 import java.util.Iterator; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.Objects; 72 import java.util.concurrent.Executor; 73 74 /** 75 * This class is used to interact with hardware tuners devices. 76 * 77 * <p> Each TvInputService Session should create one instance of this class. 78 * 79 * <p> This class controls the TIS interaction with Tuner HAL. 80 * 81 * @hide 82 */ 83 @SystemApi 84 public class Tuner implements AutoCloseable { 85 /** 86 * Invalid TS packet ID. 87 */ 88 public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID; 89 /** 90 * Invalid stream ID. 91 */ 92 public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID; 93 /** 94 * Invalid filter ID. 95 */ 96 public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID; 97 /** 98 * Invalid AV Sync ID. 99 */ 100 public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID; 101 /** 102 * Invalid timestamp. 103 * 104 * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()}, 105 * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, 106 * {@link Tuner#getAvSyncTime(int)} or {@link TsRecordEvent#getPts()} and 107 * {@link MmtpRecordEvent#getPts()} when the requested timestamp is not available. 108 * 109 * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime() 110 * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp() 111 * @see Tuner#getAvSyncTime(int) 112 * @see android.media.tv.tuner.filter.TsRecordEvent#getPts() 113 * @see android.media.tv.tuner.filter.MmtpRecordEvent#getPts() 114 */ 115 public static final long INVALID_TIMESTAMP = 116 android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_PRESENTATION_TIME_STAMP; 117 /** 118 * Invalid mpu sequence number in MmtpRecordEvent. 119 * 120 * <p>Returned by {@link MmtpRecordEvent#getMpuSequenceNumber()} when the requested sequence 121 * number is not available. 122 * 123 * @see android.media.tv.tuner.filter.MmtpRecordEvent#getMpuSequenceNumber() 124 */ 125 public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = 126 android.hardware.tv.tuner.V1_1.Constants.Constant 127 .INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM; 128 /** 129 * Invalid first macroblock address in MmtpRecordEvent and TsRecordEvent. 130 * 131 * <p>Returned by {@link MmtpRecordEvent#getMbInSlice()} and 132 * {@link TsRecordEvent#getMbInSlice()} when the requested sequence number is not available. 133 * 134 * @see android.media.tv.tuner.filter.MmtpRecordEvent#getMbInSlice() 135 * @see android.media.tv.tuner.filter.TsRecordEvent#getMbInSlice() 136 */ 137 public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE = 138 android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FIRST_MACROBLOCK_IN_SLICE; 139 /** 140 * Invalid local transport stream id. 141 * 142 * <p>Returned by {@link #linkFrontendToCiCam(int)} when the requested failed 143 * or the hal implementation does not support the operation. 144 * 145 * @see #linkFrontendToCiCam(int) 146 */ 147 public static final int INVALID_LTS_ID = 148 android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_LTS_ID; 149 /** 150 * Invalid 64-bit filter ID. 151 */ 152 public static final long INVALID_FILTER_ID_LONG = 153 android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_FILTER_ID_64BIT; 154 /** 155 * Invalid frequency that is used as the default frontend frequency setting. 156 */ 157 public static final int INVALID_FRONTEND_SETTING_FREQUENCY = 158 android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FRONTEND_SETTING_FREQUENCY; 159 /** 160 * Invalid frontend id. 161 */ 162 public static final int INVALID_FRONTEND_ID = 163 android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FRONTEND_ID; 164 /** 165 * Invalid LNB id. 166 * 167 * @hide 168 */ 169 public static final int INVALID_LNB_ID = 170 android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_LNB_ID; 171 /** 172 * A void key token. It is used to remove the current key from descrambler. 173 * 174 * <p>If the current keyToken comes from a MediaCas session, App is recommended to 175 * to use this constant to remove current key before closing MediaCas session. 176 */ 177 @NonNull 178 public static final byte[] VOID_KEYTOKEN = 179 {android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_KEYTOKEN}; 180 181 /** @hide */ 182 @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND}) 183 @Retention(RetentionPolicy.SOURCE) 184 public @interface ScanType {} 185 /** 186 * Scan type undefined. 187 */ 188 public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED; 189 /** 190 * Scan type auto. 191 * 192 * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked} 193 */ 194 public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO; 195 /** 196 * Blind scan. 197 * 198 * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an 199 * implementation specific range. 200 */ 201 public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND; 202 203 204 /** @hide */ 205 @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE, 206 RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR}) 207 @Retention(RetentionPolicy.SOURCE) 208 public @interface Result {} 209 210 /** 211 * Operation succeeded. 212 */ 213 public static final int RESULT_SUCCESS = Constants.Result.SUCCESS; 214 /** 215 * Operation failed because the corresponding resources are not available. 216 */ 217 public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE; 218 /** 219 * Operation failed because the corresponding resources are not initialized. 220 */ 221 public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED; 222 /** 223 * Operation failed because it's not in a valid state. 224 */ 225 public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE; 226 /** 227 * Operation failed because there are invalid arguments. 228 */ 229 public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT; 230 /** 231 * Memory allocation failed. 232 */ 233 public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY; 234 /** 235 * Operation failed due to unknown errors. 236 */ 237 public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR; 238 239 240 241 private static final String TAG = "MediaTvTuner"; 242 private static final boolean DEBUG = false; 243 244 private static final int MSG_RESOURCE_LOST = 1; 245 private static final int MSG_ON_FILTER_EVENT = 2; 246 private static final int MSG_ON_FILTER_STATUS = 3; 247 private static final int MSG_ON_LNB_EVENT = 4; 248 249 private static final int FILTER_CLEANUP_THRESHOLD = 256; 250 251 /** @hide */ 252 @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK}) 253 @Retention(RetentionPolicy.SOURCE) 254 public @interface DvrType {} 255 256 /** 257 * DVR for recording. 258 * @hide 259 */ 260 public static final int DVR_TYPE_RECORD = Constants.DvrType.RECORD; 261 /** 262 * DVR for playback of recorded programs. 263 * @hide 264 */ 265 public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK; 266 267 static { 268 try { 269 System.loadLibrary("media_tv_tuner"); nativeInit()270 nativeInit(); 271 } catch (UnsatisfiedLinkError e) { 272 Log.d(TAG, "tuner JNI library not found!"); 273 } 274 } 275 276 private final Context mContext; 277 private final TunerResourceManager mTunerResourceManager; 278 private final int mClientId; 279 private static int sTunerVersion = TunerVersionChecker.TUNER_VERSION_UNKNOWN; 280 281 private Frontend mFrontend; 282 private EventHandler mHandler; 283 @Nullable 284 private FrontendInfo mFrontendInfo; 285 private Integer mFrontendHandle; 286 private Boolean mIsSharedFrontend = false; 287 private int mFrontendType = FrontendSettings.TYPE_UNDEFINED; 288 private int mUserId; 289 private Lnb mLnb; 290 private Integer mLnbHandle; 291 @Nullable 292 private OnTuneEventListener mOnTuneEventListener; 293 @Nullable 294 private Executor mOnTuneEventExecutor; 295 @Nullable 296 private ScanCallback mScanCallback; 297 @Nullable 298 private Executor mScanCallbackExecutor; 299 @Nullable 300 private OnResourceLostListener mOnResourceLostListener; 301 @Nullable 302 private Executor mOnResourceLostListenerExecutor; 303 304 private final Object mOnTuneEventLock = new Object(); 305 private final Object mScanCallbackLock = new Object(); 306 private final Object mOnResourceLostListenerLock = new Object(); 307 308 private Integer mDemuxHandle; 309 private Integer mFrontendCiCamHandle; 310 private Integer mFrontendCiCamId; 311 private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>(); 312 private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>(); 313 314 private final TunerResourceManager.ResourcesReclaimListener mResourceListener = 315 new TunerResourceManager.ResourcesReclaimListener() { 316 @Override 317 public void onReclaimResources() { 318 if (mFrontend != null) { 319 FrameworkStatsLog 320 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 321 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); 322 } 323 releaseAll(); 324 mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST)); 325 } 326 }; 327 328 /** 329 * Constructs a Tuner instance. 330 * 331 * @param context the context of the caller. 332 * @param tvInputSessionId the session ID of the TV input. 333 * @param useCase the use case of this Tuner instance. 334 */ 335 @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) Tuner(@onNull Context context, @Nullable String tvInputSessionId, @TvInputService.PriorityHintUseCaseType int useCase)336 public Tuner(@NonNull Context context, @Nullable String tvInputSessionId, 337 @TvInputService.PriorityHintUseCaseType int useCase) { 338 nativeSetup(); 339 sTunerVersion = nativeGetTunerVersion(); 340 if (sTunerVersion == TunerVersionChecker.TUNER_VERSION_UNKNOWN) { 341 Log.e(TAG, "Unknown Tuner version!"); 342 } else { 343 Log.d(TAG, "Current Tuner version is " 344 + TunerVersionChecker.getMajorVersion(sTunerVersion) + "." 345 + TunerVersionChecker.getMinorVersion(sTunerVersion) + "."); 346 } 347 mContext = context; 348 mTunerResourceManager = (TunerResourceManager) 349 context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); 350 if (mHandler == null) { 351 mHandler = createEventHandler(); 352 } 353 354 int[] clientId = new int[1]; 355 ResourceClientProfile profile = new ResourceClientProfile(); 356 profile.tvInputSessionId = tvInputSessionId; 357 profile.useCase = useCase; 358 mTunerResourceManager.registerClientProfile( 359 profile, new HandlerExecutor(mHandler), mResourceListener, clientId); 360 mClientId = clientId[0]; 361 362 mUserId = Process.myUid(); 363 } 364 365 /** 366 * Get frontend info list from native and build them into a {@link FrontendInfo} list. Any 367 * {@code null} FrontendInfo element would be removed. 368 */ getFrontendInfoListInternal()369 private FrontendInfo[] getFrontendInfoListInternal() { 370 List<Integer> ids = getFrontendIds(); 371 if (ids == null) { 372 return null; 373 } 374 FrontendInfo[] infos = new FrontendInfo[ids.size()]; 375 for (int i = 0; i < ids.size(); i++) { 376 int id = ids.get(i); 377 FrontendInfo frontendInfo = getFrontendInfoById(id); 378 if (frontendInfo == null) { 379 Log.e(TAG, "Failed to get a FrontendInfo on frontend id:" + id + "!"); 380 continue; 381 } 382 infos[i] = frontendInfo; 383 } 384 return Arrays.stream(infos).filter(Objects::nonNull).toArray(FrontendInfo[]::new); 385 } 386 387 /** @hide */ getTunerVersion()388 public static int getTunerVersion() { 389 return sTunerVersion; 390 } 391 392 /** @hide */ getFrontendIds()393 public List<Integer> getFrontendIds() { 394 return nativeGetFrontendIds(); 395 } 396 397 /** 398 * Sets the listener for resource lost. 399 * 400 * @param executor the executor on which the listener should be invoked. 401 * @param listener the listener that will be run. 402 */ setResourceLostListener(@onNull @allbackExecutor Executor executor, @NonNull OnResourceLostListener listener)403 public void setResourceLostListener(@NonNull @CallbackExecutor Executor executor, 404 @NonNull OnResourceLostListener listener) { 405 synchronized (mOnResourceLostListenerLock) { 406 Objects.requireNonNull(executor, "OnResourceLostListener must not be null"); 407 Objects.requireNonNull(listener, "executor must not be null"); 408 mOnResourceLostListener = listener; 409 mOnResourceLostListenerExecutor = executor; 410 } 411 } 412 413 /** 414 * Removes the listener for resource lost. 415 */ clearResourceLostListener()416 public void clearResourceLostListener() { 417 synchronized (mOnResourceLostListenerLock) { 418 mOnResourceLostListener = null; 419 mOnResourceLostListenerExecutor = null; 420 } 421 } 422 423 /** 424 * Shares the frontend resource with another Tuner instance 425 * 426 * @param tuner the Tuner instance to share frontend resource with. 427 */ shareFrontendFromTuner(@onNull Tuner tuner)428 public void shareFrontendFromTuner(@NonNull Tuner tuner) { 429 mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId); 430 synchronized (mIsSharedFrontend) { 431 mFrontendHandle = tuner.mFrontendHandle; 432 mFrontend = tuner.mFrontend; 433 mIsSharedFrontend = true; 434 } 435 } 436 437 /** 438 * Updates client priority with an arbitrary value along with a nice value. 439 * 440 * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able 441 * to reclaim insufficient resources from another client. 442 * 443 * <p>The nice value represents how much the client intends to give up the resource when an 444 * insufficient resource situation happens. 445 * 446 * @param priority the new priority. Any negative value would cause no-op on priority setting 447 * and the API would only process nice value setting in that case. 448 * @param niceValue the nice value. 449 */ updateResourcePriority(int priority, int niceValue)450 public void updateResourcePriority(int priority, int niceValue) { 451 mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue); 452 } 453 454 private long mNativeContext; // used by native jMediaTuner 455 456 /** 457 * Releases the Tuner instance. 458 */ 459 @Override close()460 public void close() { 461 releaseAll(); 462 TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner"); 463 } 464 releaseAll()465 private void releaseAll() { 466 if (mFrontendHandle != null) { 467 synchronized (mIsSharedFrontend) { 468 if (!mIsSharedFrontend) { 469 int res = nativeCloseFrontend(mFrontendHandle); 470 if (res != Tuner.RESULT_SUCCESS) { 471 TunerUtils.throwExceptionForResult(res, "failed to close frontend"); 472 } 473 } 474 mIsSharedFrontend = false; 475 } 476 mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId); 477 FrameworkStatsLog 478 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 479 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); 480 mFrontendHandle = null; 481 mFrontend = null; 482 } 483 if (mLnb != null) { 484 mLnb.close(); 485 } 486 if (mFrontendCiCamHandle != null) { 487 int result = nativeUnlinkCiCam(mFrontendCiCamId); 488 if (result == RESULT_SUCCESS) { 489 mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); 490 mFrontendCiCamId = null; 491 mFrontendCiCamHandle = null; 492 } 493 } 494 synchronized (mDescramblers) { 495 if (!mDescramblers.isEmpty()) { 496 for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) { 497 Descrambler descrambler = d.getValue().get(); 498 if (descrambler != null) { 499 descrambler.close(); 500 } 501 mTunerResourceManager.releaseDescrambler(d.getKey(), mClientId); 502 } 503 mDescramblers.clear(); 504 } 505 } 506 synchronized (mFilters) { 507 if (!mFilters.isEmpty()) { 508 for (WeakReference<Filter> weakFilter : mFilters) { 509 Filter filter = weakFilter.get(); 510 if (filter != null) { 511 filter.close(); 512 } 513 } 514 mFilters.clear(); 515 } 516 } 517 if (mDemuxHandle != null) { 518 int res = nativeCloseDemux(mDemuxHandle); 519 if (res != Tuner.RESULT_SUCCESS) { 520 TunerUtils.throwExceptionForResult(res, "failed to close demux"); 521 } 522 mTunerResourceManager.releaseDemux(mDemuxHandle, mClientId); 523 mDemuxHandle = null; 524 } 525 526 mTunerResourceManager.unregisterClientProfile(mClientId); 527 528 } 529 530 /** 531 * Native Initialization. 532 */ nativeInit()533 private static native void nativeInit(); 534 535 /** 536 * Native setup. 537 */ nativeSetup()538 private native void nativeSetup(); 539 540 /** 541 * Native method to get all frontend IDs. 542 */ nativeGetTunerVersion()543 private native int nativeGetTunerVersion(); 544 545 /** 546 * Native method to get all frontend IDs. 547 */ nativeGetFrontendIds()548 private native List<Integer> nativeGetFrontendIds(); 549 550 /** 551 * Native method to open frontend of the given ID. 552 */ nativeOpenFrontendByHandle(int handle)553 private native Frontend nativeOpenFrontendByHandle(int handle); 554 @Result nativeTune(int type, FrontendSettings settings)555 private native int nativeTune(int type, FrontendSettings settings); nativeStopTune()556 private native int nativeStopTune(); nativeScan(int settingsType, FrontendSettings settings, int scanType)557 private native int nativeScan(int settingsType, FrontendSettings settings, int scanType); nativeStopScan()558 private native int nativeStopScan(); nativeSetLnb(Lnb lnb)559 private native int nativeSetLnb(Lnb lnb); nativeSetLna(boolean enable)560 private native int nativeSetLna(boolean enable); nativeGetFrontendStatus(int[] statusTypes)561 private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes); nativeGetAvSyncHwId(Filter filter)562 private native Integer nativeGetAvSyncHwId(Filter filter); nativeGetAvSyncTime(int avSyncId)563 private native Long nativeGetAvSyncTime(int avSyncId); nativeConnectCiCam(int ciCamId)564 private native int nativeConnectCiCam(int ciCamId); nativeLinkCiCam(int ciCamId)565 private native int nativeLinkCiCam(int ciCamId); nativeDisconnectCiCam()566 private native int nativeDisconnectCiCam(); nativeUnlinkCiCam(int ciCamId)567 private native int nativeUnlinkCiCam(int ciCamId); nativeGetFrontendInfo(int id)568 private native FrontendInfo nativeGetFrontendInfo(int id); nativeOpenFilter(int type, int subType, long bufferSize)569 private native Filter nativeOpenFilter(int type, int subType, long bufferSize); nativeOpenTimeFilter()570 private native TimeFilter nativeOpenTimeFilter(); 571 nativeOpenLnbByHandle(int handle)572 private native Lnb nativeOpenLnbByHandle(int handle); nativeOpenLnbByName(String name)573 private native Lnb nativeOpenLnbByName(String name); 574 nativeOpenDescramblerByHandle(int handle)575 private native Descrambler nativeOpenDescramblerByHandle(int handle); nativeOpenDemuxByhandle(int handle)576 private native int nativeOpenDemuxByhandle(int handle); 577 nativeOpenDvrRecorder(long bufferSize)578 private native DvrRecorder nativeOpenDvrRecorder(long bufferSize); nativeOpenDvrPlayback(long bufferSize)579 private native DvrPlayback nativeOpenDvrPlayback(long bufferSize); 580 nativeGetDemuxCapabilities()581 private native DemuxCapabilities nativeGetDemuxCapabilities(); 582 nativeCloseDemux(int handle)583 private native int nativeCloseDemux(int handle); nativeCloseFrontend(int handle)584 private native int nativeCloseFrontend(int handle); nativeClose()585 private native int nativeClose(); 586 587 588 /** 589 * Listener for resource lost. 590 * 591 * <p>Insufficient resources are reclaimed by higher priority clients. 592 */ 593 public interface OnResourceLostListener { 594 /** 595 * Invoked when resource lost. 596 * 597 * @param tuner the tuner instance whose resource is being reclaimed. 598 */ onResourceLost(@onNull Tuner tuner)599 void onResourceLost(@NonNull Tuner tuner); 600 } 601 602 @Nullable createEventHandler()603 private EventHandler createEventHandler() { 604 Looper looper; 605 if ((looper = Looper.myLooper()) != null) { 606 return new EventHandler(looper); 607 } else if ((looper = Looper.getMainLooper()) != null) { 608 return new EventHandler(looper); 609 } 610 return null; 611 } 612 613 private class EventHandler extends Handler { EventHandler(Looper looper)614 private EventHandler(Looper looper) { 615 super(looper); 616 } 617 618 @Override handleMessage(Message msg)619 public void handleMessage(Message msg) { 620 switch (msg.what) { 621 case MSG_ON_FILTER_STATUS: { 622 Filter filter = (Filter) msg.obj; 623 if (filter.getCallback() != null) { 624 filter.getCallback().onFilterStatusChanged(filter, msg.arg1); 625 } 626 break; 627 } 628 case MSG_RESOURCE_LOST: { 629 synchronized (mOnResourceLostListenerLock) { 630 if (mOnResourceLostListener != null 631 && mOnResourceLostListenerExecutor != null) { 632 mOnResourceLostListenerExecutor.execute( 633 () -> mOnResourceLostListener.onResourceLost(Tuner.this)); 634 } 635 } 636 break; 637 } 638 default: 639 // fall through 640 } 641 } 642 } 643 644 private class Frontend { 645 private int mId; 646 Frontend(int id)647 private Frontend(int id) { 648 mId = id; 649 } 650 } 651 652 /** 653 * Listens for tune events. 654 * 655 * <p> 656 * Tuner events are started when {@link #tune(FrontendSettings)} is called and end when {@link 657 * #cancelTuning()} is called. 658 * 659 * @param eventListener receives tune events. 660 * @throws SecurityException if the caller does not have appropriate permissions. 661 * @see #tune(FrontendSettings) 662 */ setOnTuneEventListener(@onNull @allbackExecutor Executor executor, @NonNull OnTuneEventListener eventListener)663 public void setOnTuneEventListener(@NonNull @CallbackExecutor Executor executor, 664 @NonNull OnTuneEventListener eventListener) { 665 synchronized (mOnTuneEventLock) { 666 mOnTuneEventListener = eventListener; 667 mOnTuneEventExecutor = executor; 668 } 669 } 670 671 /** 672 * Clears the {@link OnTuneEventListener} and its associated {@link Executor}. 673 * 674 * @throws SecurityException if the caller does not have appropriate permissions. 675 * @see #setOnTuneEventListener(Executor, OnTuneEventListener) 676 */ clearOnTuneEventListener()677 public void clearOnTuneEventListener() { 678 synchronized (mOnTuneEventLock) { 679 mOnTuneEventListener = null; 680 mOnTuneEventExecutor = null; 681 } 682 } 683 684 /** 685 * Tunes the frontend to using the settings given. 686 * 687 * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able 688 * to get frontend resource. If the client can't get the resource, this call returns {@link 689 * #RESULT_UNAVAILABLE}. 690 * 691 * <p> 692 * This locks the frontend to a frequency by providing signal 693 * delivery information. If previous tuning isn't completed, this stop the previous tuning, and 694 * start a new tuning. 695 * 696 * <p> 697 * Tune is an async call, with {@link OnTuneEventListener#SIGNAL_LOCKED} and {@link 698 * OnTuneEventListener#SIGNAL_NO_SIGNAL} events sent to the {@link OnTuneEventListener} 699 * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}. 700 * 701 * <p>Tuning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only 702 * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link 703 * TunerVersionChecker#getTunerVersion()} to get the version information. 704 * 705 * @param settings Signal delivery information the frontend uses to 706 * search and lock the signal. 707 * @return result status of tune operation. 708 * @throws SecurityException if the caller does not have appropriate permissions. 709 * @see #setOnTuneEventListener(Executor, OnTuneEventListener) 710 */ 711 @Result tune(@onNull FrontendSettings settings)712 public int tune(@NonNull FrontendSettings settings) { 713 Log.d(TAG, "Tune to " + settings.getFrequency()); 714 mFrontendType = settings.getType(); 715 if (mFrontendType == FrontendSettings.TYPE_DTMB) { 716 if (!TunerVersionChecker.checkHigherOrEqualVersionTo( 717 TunerVersionChecker.TUNER_VERSION_1_1, "Tuner with DTMB Frontend")) { 718 return RESULT_UNAVAILABLE; 719 } 720 } 721 if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { 722 mFrontendInfo = null; 723 Log.d(TAG, "Write Stats Log for tuning."); 724 FrameworkStatsLog 725 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 726 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__TUNING); 727 return nativeTune(settings.getType(), settings); 728 } 729 return RESULT_UNAVAILABLE; 730 } 731 732 /** 733 * Stops a previous tuning. 734 * 735 * <p>If the method completes successfully, the frontend is no longer tuned and no data 736 * will be sent to attached filters. 737 * 738 * @return result status of the operation. 739 */ 740 @Result cancelTuning()741 public int cancelTuning() { 742 return nativeStopTune(); 743 } 744 745 /** 746 * Scan for channels. 747 * 748 * <p>Details for channels found are returned via {@link ScanCallback}. 749 * 750 * <p>Scanning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only 751 * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link 752 * TunerVersionChecker#getTunerVersion()} to get the version information. 753 * 754 * @param settings A {@link FrontendSettings} to configure the frontend. 755 * @param scanType The scan type. 756 * @throws SecurityException if the caller does not have appropriate permissions. 757 * @throws IllegalStateException if {@code scan} is called again before 758 * {@link #cancelScanning()} is called. 759 */ 760 @Result scan(@onNull FrontendSettings settings, @ScanType int scanType, @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback)761 public int scan(@NonNull FrontendSettings settings, @ScanType int scanType, 762 @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) { 763 synchronized (mScanCallbackLock) { 764 // Scan can be called again for blink scan if scanCallback and executor are same as 765 //before. 766 if (((mScanCallback != null) && (mScanCallback != scanCallback)) 767 || ((mScanCallbackExecutor != null) && (mScanCallbackExecutor != executor))) { 768 throw new IllegalStateException( 769 "Different Scan session already in progress. stopScan must be called " 770 + "before a new scan session can be " + "started."); 771 } 772 mFrontendType = settings.getType(); 773 if (mFrontendType == FrontendSettings.TYPE_DTMB) { 774 if (!TunerVersionChecker.checkHigherOrEqualVersionTo( 775 TunerVersionChecker.TUNER_VERSION_1_1, 776 "Scan with DTMB Frontend")) { 777 return RESULT_UNAVAILABLE; 778 } 779 } 780 if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { 781 mScanCallback = scanCallback; 782 mScanCallbackExecutor = executor; 783 mFrontendInfo = null; 784 FrameworkStatsLog 785 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 786 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCANNING); 787 return nativeScan(settings.getType(), settings, scanType); 788 } 789 return RESULT_UNAVAILABLE; 790 } 791 } 792 793 /** 794 * Stops a previous scanning. 795 * 796 * <p> 797 * The {@link ScanCallback} and it's {@link Executor} will be removed. 798 * 799 * <p> 800 * If the method completes successfully, the frontend stopped previous scanning. 801 * 802 * @throws SecurityException if the caller does not have appropriate permissions. 803 */ 804 @Result cancelScanning()805 public int cancelScanning() { 806 synchronized (mScanCallbackLock) { 807 FrameworkStatsLog.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 808 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCAN_STOPPED); 809 810 int retVal = nativeStopScan(); 811 mScanCallback = null; 812 mScanCallbackExecutor = null; 813 return retVal; 814 } 815 } 816 requestFrontend()817 private boolean requestFrontend() { 818 int[] feHandle = new int[1]; 819 TunerFrontendRequest request = new TunerFrontendRequest(); 820 request.clientId = mClientId; 821 request.frontendType = mFrontendType; 822 boolean granted = mTunerResourceManager.requestFrontend(request, feHandle); 823 if (granted) { 824 mFrontendHandle = feHandle[0]; 825 mFrontend = nativeOpenFrontendByHandle(mFrontendHandle); 826 } 827 return granted; 828 } 829 830 /** 831 * Sets Low-Noise Block downconverter (LNB) for satellite frontend. 832 * 833 * <p>This assigns a hardware LNB resource to the satellite tuner. It can be 834 * called multiple times to update LNB assignment. 835 * 836 * @param lnb the LNB instance. 837 * 838 * @return result status of the operation. 839 */ 840 @Result setLnb(@onNull Lnb lnb)841 private int setLnb(@NonNull Lnb lnb) { 842 return nativeSetLnb(lnb); 843 } 844 845 /** 846 * Enable or Disable Low Noise Amplifier (LNA). 847 * 848 * @param enable {@code true} to activate LNA module; {@code false} to deactivate LNA. 849 * 850 * @return result status of the operation. 851 */ 852 @Result setLnaEnabled(boolean enable)853 public int setLnaEnabled(boolean enable) { 854 return nativeSetLna(enable); 855 } 856 857 /** 858 * Gets the statuses of the frontend. 859 * 860 * <p>This retrieve the statuses of the frontend for given status types. 861 * 862 * @param statusTypes an array of status types which the caller requests. Any types that are not 863 * in {@link FrontendInfo#getStatusCapabilities()} would be ignored. 864 * @return statuses which response the caller's requests. {@code null} if the operation failed. 865 */ 866 @Nullable getFrontendStatus(@onNull @rontendStatusType int[] statusTypes)867 public FrontendStatus getFrontendStatus(@NonNull @FrontendStatusType int[] statusTypes) { 868 if (mFrontend == null) { 869 throw new IllegalStateException("frontend is not initialized"); 870 } 871 return nativeGetFrontendStatus(statusTypes); 872 } 873 874 /** 875 * Gets hardware sync ID for audio and video. 876 * 877 * @param filter the filter instance for the hardware sync ID. 878 * @return the id of hardware A/V sync. 879 */ getAvSyncHwId(@onNull Filter filter)880 public int getAvSyncHwId(@NonNull Filter filter) { 881 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 882 return INVALID_AV_SYNC_ID; 883 } 884 Integer id = nativeGetAvSyncHwId(filter); 885 return id == null ? INVALID_AV_SYNC_ID : id; 886 } 887 888 /** 889 * Gets the current timestamp for Audio/Video sync 890 * 891 * <p>The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is 892 * the same as PTS (Presentation Time Stamp). 893 * 894 * @param avSyncHwId the hardware id of A/V sync. 895 * @return the current timestamp of hardware A/V sync. 896 */ getAvSyncTime(int avSyncHwId)897 public long getAvSyncTime(int avSyncHwId) { 898 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 899 return INVALID_TIMESTAMP; 900 } 901 Long time = nativeGetAvSyncTime(avSyncHwId); 902 return time == null ? INVALID_TIMESTAMP : time; 903 } 904 905 /** 906 * Connects Conditional Access Modules (CAM) through Common Interface (CI). 907 * 908 * <p>The demux uses the output from the frontend as the input by default, and must change to 909 * use the output from CI-CAM as the input after this call. 910 * 911 * <p> Note that this API is used to connect the CI-CAM to the Demux module while 912 * {@link #connectFrontendToCiCam(int)} is used to connect CI-CAM to the Frontend module. 913 * 914 * @param ciCamId specify CI-CAM Id to connect. 915 * @return result status of the operation. 916 */ 917 @Result connectCiCam(int ciCamId)918 public int connectCiCam(int ciCamId) { 919 if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 920 return nativeConnectCiCam(ciCamId); 921 } 922 return RESULT_UNAVAILABLE; 923 } 924 925 /** 926 * Connect Conditional Access Modules (CAM) Frontend to support Common Interface (CI) 927 * by-pass mode. 928 * 929 * <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that 930 * the CICAM also receives the TS concurrently from the frontend when the Demux is receiving 931 * the TS directly from the frontend. 932 * 933 * <p> Note that this API is used to connect the CI-CAM to the Frontend module while 934 * {@link #connectCiCam(int)} is used to connect CI-CAM to the Demux module. 935 * 936 * <p>Use {@link #disconnectFrontendToCiCam(int)} to disconnect. 937 * 938 * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause 939 * no-op and return {@link #INVALID_LTS_ID}. Use {@link TunerVersionChecker#getTunerVersion()} 940 * to check the version. 941 * 942 * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM) 943 * Common Interface (CI), to link. 944 * @return Local transport stream id when connection is successfully established. Failed 945 * operation returns {@link #INVALID_LTS_ID} while unsupported version also returns 946 * {@link #INVALID_LTS_ID}. Check the current HAL version using 947 * {@link TunerVersionChecker#getTunerVersion()}. 948 */ connectFrontendToCiCam(int ciCamId)949 public int connectFrontendToCiCam(int ciCamId) { 950 if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, 951 "linkFrontendToCiCam")) { 952 if (checkCiCamResource(ciCamId) 953 && checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { 954 return nativeLinkCiCam(ciCamId); 955 } 956 } 957 return INVALID_LTS_ID; 958 } 959 960 /** 961 * Disconnects Conditional Access Modules (CAM). 962 * 963 * <p>The demux will use the output from the frontend as the input after this call. 964 * 965 * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while 966 * {@link #disconnectFrontendToCiCam(int)} is used to disconnect CI-CAM to the Frontend module. 967 * 968 * @return result status of the operation. 969 */ 970 @Result disconnectCiCam()971 public int disconnectCiCam() { 972 if (mDemuxHandle != null) { 973 return nativeDisconnectCiCam(); 974 } 975 return RESULT_UNAVAILABLE; 976 } 977 978 /** 979 * Disconnect Conditional Access Modules (CAM) Frontend. 980 * 981 * <p>It is used by the client to unlink CI-CAM to a Frontend. 982 * 983 * <p> Note that this API is used to disconnect the CI-CAM to the Demux module while 984 * {@link #disconnectCiCam(int)} is used to disconnect CI-CAM to the Frontend module. 985 * 986 * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause 987 * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version. 988 * 989 * @param ciCamId specify CI-CAM Id, which is the id of the Conditional Access Modules (CAM) 990 * Common Interface (CI), to disconnect. 991 * @return result status of the operation. Unsupported version would return 992 * {@link #RESULT_UNAVAILABLE} 993 */ 994 @Result disconnectFrontendToCiCam(int ciCamId)995 public int disconnectFrontendToCiCam(int ciCamId) { 996 if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, 997 "unlinkFrontendToCiCam")) { 998 if (mFrontendCiCamHandle != null && mFrontendCiCamId != null 999 && mFrontendCiCamId == ciCamId) { 1000 int result = nativeUnlinkCiCam(ciCamId); 1001 if (result == RESULT_SUCCESS) { 1002 mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); 1003 mFrontendCiCamId = null; 1004 mFrontendCiCamHandle = null; 1005 } 1006 return result; 1007 } 1008 } 1009 return RESULT_UNAVAILABLE; 1010 } 1011 1012 /** 1013 * Gets the currently initialized and activated frontend information. To get all the available 1014 * frontend info on the device, use {@link getAvailableFrontendInfos()}. 1015 * 1016 * @return The active frontend information. {@code null} if the operation failed. 1017 * @throws IllegalStateException if there is no active frontend currently. 1018 */ 1019 @Nullable getFrontendInfo()1020 public FrontendInfo getFrontendInfo() { 1021 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { 1022 return null; 1023 } 1024 if (mFrontend == null) { 1025 throw new IllegalStateException("frontend is not initialized"); 1026 } 1027 if (mFrontendInfo == null) { 1028 mFrontendInfo = getFrontendInfoById(mFrontend.mId); 1029 } 1030 return mFrontendInfo; 1031 } 1032 1033 /** 1034 * Gets a list of all the available frontend information on the device. To get the information 1035 * of the currently active frontend, use {@link getFrontendInfo()}. The active frontend 1036 * information is also included in the list of the available frontend information. 1037 * 1038 * @return The list of all the available frontend information. {@code null} if the operation 1039 * failed. 1040 */ 1041 @Nullable 1042 @SuppressLint("NullableCollection") getAvailableFrontendInfos()1043 public List<FrontendInfo> getAvailableFrontendInfos() { 1044 FrontendInfo[] feInfoList = getFrontendInfoListInternal(); 1045 if (feInfoList == null) { 1046 return null; 1047 } 1048 return Arrays.asList(feInfoList); 1049 } 1050 1051 /** @hide */ getFrontendInfoById(int id)1052 public FrontendInfo getFrontendInfoById(int id) { 1053 return nativeGetFrontendInfo(id); 1054 } 1055 1056 /** 1057 * Gets Demux capabilities. 1058 * 1059 * @return A {@link DemuxCapabilities} instance that represents the demux capabilities. 1060 * {@code null} if the operation failed. 1061 */ 1062 @Nullable getDemuxCapabilities()1063 public DemuxCapabilities getDemuxCapabilities() { 1064 return nativeGetDemuxCapabilities(); 1065 } 1066 onFrontendEvent(int eventType)1067 private void onFrontendEvent(int eventType) { 1068 Log.d(TAG, "Got event from tuning. Event type: " + eventType); 1069 synchronized (mOnTuneEventLock) { 1070 if (mOnTuneEventExecutor != null && mOnTuneEventListener != null) { 1071 mOnTuneEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType)); 1072 } 1073 } 1074 1075 Log.d(TAG, "Wrote Stats Log for the events from tuning."); 1076 if (eventType == OnTuneEventListener.SIGNAL_LOCKED) { 1077 FrameworkStatsLog 1078 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 1079 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED); 1080 } else if (eventType == OnTuneEventListener.SIGNAL_NO_SIGNAL) { 1081 FrameworkStatsLog 1082 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 1083 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__NOT_LOCKED); 1084 } else if (eventType == OnTuneEventListener.SIGNAL_LOST_LOCK) { 1085 FrameworkStatsLog 1086 .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 1087 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SIGNAL_LOST); 1088 } 1089 } 1090 onLocked()1091 private void onLocked() { 1092 Log.d(TAG, "Wrote Stats Log for locked event from scanning."); 1093 FrameworkStatsLog.write( 1094 FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, 1095 FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED); 1096 1097 synchronized (mScanCallbackLock) { 1098 if (mScanCallbackExecutor != null && mScanCallback != null) { 1099 mScanCallbackExecutor.execute(() -> mScanCallback.onLocked()); 1100 } 1101 } 1102 } 1103 onScanStopped()1104 private void onScanStopped() { 1105 synchronized (mScanCallbackLock) { 1106 if (mScanCallbackExecutor != null && mScanCallback != null) { 1107 mScanCallbackExecutor.execute(() -> mScanCallback.onScanStopped()); 1108 } 1109 } 1110 } 1111 onProgress(int percent)1112 private void onProgress(int percent) { 1113 synchronized (mScanCallbackLock) { 1114 if (mScanCallbackExecutor != null && mScanCallback != null) { 1115 mScanCallbackExecutor.execute(() -> mScanCallback.onProgress(percent)); 1116 } 1117 } 1118 } 1119 onFrequenciesReport(int[] frequency)1120 private void onFrequenciesReport(int[] frequency) { 1121 synchronized (mScanCallbackLock) { 1122 if (mScanCallbackExecutor != null && mScanCallback != null) { 1123 mScanCallbackExecutor.execute(() -> mScanCallback.onFrequenciesReported(frequency)); 1124 } 1125 } 1126 } 1127 onSymbolRates(int[] rate)1128 private void onSymbolRates(int[] rate) { 1129 synchronized (mScanCallbackLock) { 1130 if (mScanCallbackExecutor != null && mScanCallback != null) { 1131 mScanCallbackExecutor.execute(() -> mScanCallback.onSymbolRatesReported(rate)); 1132 } 1133 } 1134 } 1135 onHierarchy(int hierarchy)1136 private void onHierarchy(int hierarchy) { 1137 synchronized (mScanCallbackLock) { 1138 if (mScanCallbackExecutor != null && mScanCallback != null) { 1139 mScanCallbackExecutor.execute(() -> mScanCallback.onHierarchyReported(hierarchy)); 1140 } 1141 } 1142 } 1143 onSignalType(int signalType)1144 private void onSignalType(int signalType) { 1145 synchronized (mScanCallbackLock) { 1146 if (mScanCallbackExecutor != null && mScanCallback != null) { 1147 mScanCallbackExecutor.execute(() -> mScanCallback.onSignalTypeReported(signalType)); 1148 } 1149 } 1150 } 1151 onPlpIds(int[] plpIds)1152 private void onPlpIds(int[] plpIds) { 1153 synchronized (mScanCallbackLock) { 1154 if (mScanCallbackExecutor != null && mScanCallback != null) { 1155 mScanCallbackExecutor.execute(() -> mScanCallback.onPlpIdsReported(plpIds)); 1156 } 1157 } 1158 } 1159 onGroupIds(int[] groupIds)1160 private void onGroupIds(int[] groupIds) { 1161 synchronized (mScanCallbackLock) { 1162 if (mScanCallbackExecutor != null && mScanCallback != null) { 1163 mScanCallbackExecutor.execute(() -> mScanCallback.onGroupIdsReported(groupIds)); 1164 } 1165 } 1166 } 1167 onInputStreamIds(int[] inputStreamIds)1168 private void onInputStreamIds(int[] inputStreamIds) { 1169 synchronized (mScanCallbackLock) { 1170 if (mScanCallbackExecutor != null && mScanCallback != null) { 1171 mScanCallbackExecutor.execute( 1172 () -> mScanCallback.onInputStreamIdsReported(inputStreamIds)); 1173 } 1174 } 1175 } 1176 onDvbsStandard(int dvbsStandandard)1177 private void onDvbsStandard(int dvbsStandandard) { 1178 synchronized (mScanCallbackLock) { 1179 if (mScanCallbackExecutor != null && mScanCallback != null) { 1180 mScanCallbackExecutor.execute( 1181 () -> mScanCallback.onDvbsStandardReported(dvbsStandandard)); 1182 } 1183 } 1184 } 1185 onDvbtStandard(int dvbtStandard)1186 private void onDvbtStandard(int dvbtStandard) { 1187 synchronized (mScanCallbackLock) { 1188 if (mScanCallbackExecutor != null && mScanCallback != null) { 1189 mScanCallbackExecutor.execute( 1190 () -> mScanCallback.onDvbtStandardReported(dvbtStandard)); 1191 } 1192 } 1193 } 1194 onAnalogSifStandard(int sif)1195 private void onAnalogSifStandard(int sif) { 1196 synchronized (mScanCallbackLock) { 1197 if (mScanCallbackExecutor != null && mScanCallback != null) { 1198 mScanCallbackExecutor.execute(() -> mScanCallback.onAnalogSifStandardReported(sif)); 1199 } 1200 } 1201 } 1202 onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos)1203 private void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos) { 1204 synchronized (mScanCallbackLock) { 1205 if (mScanCallbackExecutor != null && mScanCallback != null) { 1206 mScanCallbackExecutor.execute( 1207 () -> mScanCallback.onAtsc3PlpInfosReported(atsc3PlpInfos)); 1208 } 1209 } 1210 } 1211 onModulationReported(int modulation)1212 private void onModulationReported(int modulation) { 1213 synchronized (mScanCallbackLock) { 1214 if (mScanCallbackExecutor != null && mScanCallback != null) { 1215 mScanCallbackExecutor.execute( 1216 () -> mScanCallback.onModulationReported(modulation)); 1217 } 1218 } 1219 } 1220 onPriorityReported(boolean isHighPriority)1221 private void onPriorityReported(boolean isHighPriority) { 1222 synchronized (mScanCallbackLock) { 1223 if (mScanCallbackExecutor != null && mScanCallback != null) { 1224 mScanCallbackExecutor.execute( 1225 () -> mScanCallback.onPriorityReported(isHighPriority)); 1226 } 1227 } 1228 } 1229 onDvbcAnnexReported(int dvbcAnnex)1230 private void onDvbcAnnexReported(int dvbcAnnex) { 1231 synchronized (mScanCallbackLock) { 1232 if (mScanCallbackExecutor != null && mScanCallback != null) { 1233 mScanCallbackExecutor.execute( 1234 () -> mScanCallback.onDvbcAnnexReported(dvbcAnnex)); 1235 } 1236 } 1237 } 1238 1239 /** 1240 * Opens a filter object based on the given types and buffer size. 1241 * 1242 * @param mainType the main type of the filter. 1243 * @param subType the subtype of the filter. 1244 * @param bufferSize the buffer size of the filter to be opened in bytes. The buffer holds the 1245 * data output from the filter. 1246 * @param executor the executor on which callback will be invoked. The default event handler 1247 * executor is used if it's {@code null}. 1248 * @param cb the callback to receive notifications from filter. 1249 * @return the opened filter. {@code null} if the operation failed. 1250 */ 1251 @Nullable openFilter(@ype int mainType, @Subtype int subType, @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor, @Nullable FilterCallback cb)1252 public Filter openFilter(@Type int mainType, @Subtype int subType, 1253 @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor, 1254 @Nullable FilterCallback cb) { 1255 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 1256 return null; 1257 } 1258 Filter filter = nativeOpenFilter( 1259 mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize); 1260 if (filter != null) { 1261 filter.setType(mainType, subType); 1262 filter.setCallback(cb, executor); 1263 if (mHandler == null) { 1264 mHandler = createEventHandler(); 1265 } 1266 synchronized (mFilters) { 1267 WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter); 1268 mFilters.add(weakFilter); 1269 if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) { 1270 Iterator<WeakReference<Filter>> iterator = mFilters.iterator(); 1271 while (iterator.hasNext()) { 1272 WeakReference<Filter> wFilter = iterator.next(); 1273 if (wFilter.get() == null) { 1274 iterator.remove(); 1275 } 1276 } 1277 } 1278 } 1279 } 1280 return filter; 1281 } 1282 1283 /** 1284 * Opens an LNB (low-noise block downconverter) object. 1285 * 1286 * <p>If there is an existing Lnb object, it will be replace by the newly opened one. 1287 * 1288 * @param executor the executor on which callback will be invoked. The default event handler 1289 * executor is used if it's {@code null}. 1290 * @param cb the callback to receive notifications from LNB. 1291 * @return the opened LNB object. {@code null} if the operation failed. 1292 */ 1293 @Nullable openLnb(@allbackExecutor @onNull Executor executor, @NonNull LnbCallback cb)1294 public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) { 1295 Objects.requireNonNull(executor, "executor must not be null"); 1296 Objects.requireNonNull(cb, "LnbCallback must not be null"); 1297 if (mLnb != null) { 1298 mLnb.setCallback(executor, cb, this); 1299 return mLnb; 1300 } 1301 if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB) && mLnb != null) { 1302 mLnb.setCallback(executor, cb, this); 1303 setLnb(mLnb); 1304 return mLnb; 1305 } 1306 return null; 1307 } 1308 1309 /** 1310 * Opens an LNB (low-noise block downconverter) object specified by the give name. 1311 * 1312 * @param name the LNB name. 1313 * @param executor the executor on which callback will be invoked. The default event handler 1314 * executor is used if it's {@code null}. 1315 * @param cb the callback to receive notifications from LNB. 1316 * @return the opened LNB object. {@code null} if the operation failed. 1317 */ 1318 @Nullable openLnbByName(@onNull String name, @CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb)1319 public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor, 1320 @NonNull LnbCallback cb) { 1321 Objects.requireNonNull(name, "LNB name must not be null"); 1322 Objects.requireNonNull(executor, "executor must not be null"); 1323 Objects.requireNonNull(cb, "LnbCallback must not be null"); 1324 Lnb newLnb = nativeOpenLnbByName(name); 1325 if (newLnb != null) { 1326 if (mLnb != null) { 1327 mLnb.close(); 1328 mLnbHandle = null; 1329 } 1330 mLnb = newLnb; 1331 mLnb.setCallback(executor, cb, this); 1332 setLnb(mLnb); 1333 } 1334 return mLnb; 1335 } 1336 requestLnb()1337 private boolean requestLnb() { 1338 int[] lnbHandle = new int[1]; 1339 TunerLnbRequest request = new TunerLnbRequest(); 1340 request.clientId = mClientId; 1341 boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle); 1342 if (granted) { 1343 mLnbHandle = lnbHandle[0]; 1344 mLnb = nativeOpenLnbByHandle(mLnbHandle); 1345 } 1346 return granted; 1347 } 1348 1349 /** 1350 * Open a time filter object. 1351 * 1352 * @return the opened time filter object. {@code null} if the operation failed. 1353 */ 1354 @Nullable openTimeFilter()1355 public TimeFilter openTimeFilter() { 1356 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 1357 return null; 1358 } 1359 return nativeOpenTimeFilter(); 1360 } 1361 1362 /** 1363 * Opens a Descrambler in tuner. 1364 * 1365 * @return a {@link Descrambler} object. 1366 */ 1367 @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) 1368 @Nullable openDescrambler()1369 public Descrambler openDescrambler() { 1370 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 1371 return null; 1372 } 1373 return requestDescrambler(); 1374 } 1375 1376 /** 1377 * Open a DVR (Digital Video Record) recorder instance. 1378 * 1379 * @param bufferSize the buffer size of the output in bytes. It's used to hold output data of 1380 * the attached filters. 1381 * @param executor the executor on which callback will be invoked. The default event handler 1382 * executor is used if it's {@code null}. 1383 * @param l the listener to receive notifications from DVR recorder. 1384 * @return the opened DVR recorder object. {@code null} if the operation failed. 1385 */ 1386 @Nullable openDvrRecorder( @ytesLong long bufferSize, @CallbackExecutor @NonNull Executor executor, @NonNull OnRecordStatusChangedListener l)1387 public DvrRecorder openDvrRecorder( 1388 @BytesLong long bufferSize, 1389 @CallbackExecutor @NonNull Executor executor, 1390 @NonNull OnRecordStatusChangedListener l) { 1391 Objects.requireNonNull(executor, "executor must not be null"); 1392 Objects.requireNonNull(l, "OnRecordStatusChangedListener must not be null"); 1393 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 1394 return null; 1395 } 1396 DvrRecorder dvr = nativeOpenDvrRecorder(bufferSize); 1397 dvr.setListener(executor, l); 1398 return dvr; 1399 } 1400 1401 /** 1402 * Open a DVR (Digital Video Record) playback instance. 1403 * 1404 * @param bufferSize the buffer size of the output in bytes. It's used to hold output data of 1405 * the attached filters. 1406 * @param executor the executor on which callback will be invoked. The default event handler 1407 * executor is used if it's {@code null}. 1408 * @param l the listener to receive notifications from DVR recorder. 1409 * @return the opened DVR playback object. {@code null} if the operation failed. 1410 */ 1411 @Nullable openDvrPlayback( @ytesLong long bufferSize, @CallbackExecutor @NonNull Executor executor, @NonNull OnPlaybackStatusChangedListener l)1412 public DvrPlayback openDvrPlayback( 1413 @BytesLong long bufferSize, 1414 @CallbackExecutor @NonNull Executor executor, 1415 @NonNull OnPlaybackStatusChangedListener l) { 1416 Objects.requireNonNull(executor, "executor must not be null"); 1417 Objects.requireNonNull(l, "OnPlaybackStatusChangedListener must not be null"); 1418 if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 1419 return null; 1420 } 1421 DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize); 1422 dvr.setListener(executor, l); 1423 return dvr; 1424 } 1425 requestDemux()1426 private boolean requestDemux() { 1427 int[] demuxHandle = new int[1]; 1428 TunerDemuxRequest request = new TunerDemuxRequest(); 1429 request.clientId = mClientId; 1430 boolean granted = mTunerResourceManager.requestDemux(request, demuxHandle); 1431 if (granted) { 1432 mDemuxHandle = demuxHandle[0]; 1433 nativeOpenDemuxByhandle(mDemuxHandle); 1434 } 1435 return granted; 1436 } 1437 requestDescrambler()1438 private Descrambler requestDescrambler() { 1439 int[] descramblerHandle = new int[1]; 1440 TunerDescramblerRequest request = new TunerDescramblerRequest(); 1441 request.clientId = mClientId; 1442 boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle); 1443 if (!granted) { 1444 return null; 1445 } 1446 int handle = descramblerHandle[0]; 1447 Descrambler descrambler = nativeOpenDescramblerByHandle(handle); 1448 if (descrambler != null) { 1449 synchronized (mDescramblers) { 1450 WeakReference weakDescrambler = new WeakReference<Descrambler>(descrambler); 1451 mDescramblers.put(handle, weakDescrambler); 1452 } 1453 } else { 1454 mTunerResourceManager.releaseDescrambler(handle, mClientId); 1455 } 1456 return descrambler; 1457 } 1458 requestFrontendCiCam(int ciCamId)1459 private boolean requestFrontendCiCam(int ciCamId) { 1460 int[] ciCamHandle = new int[1]; 1461 TunerCiCamRequest request = new TunerCiCamRequest(); 1462 request.clientId = mClientId; 1463 request.ciCamId = ciCamId; 1464 boolean granted = mTunerResourceManager.requestCiCam(request, ciCamHandle); 1465 if (granted) { 1466 mFrontendCiCamHandle = ciCamHandle[0]; 1467 mFrontendCiCamId = ciCamId; 1468 } 1469 return granted; 1470 } 1471 checkResource(int resourceType)1472 private boolean checkResource(int resourceType) { 1473 switch (resourceType) { 1474 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: { 1475 if (mFrontendHandle == null && !requestFrontend()) { 1476 return false; 1477 } 1478 break; 1479 } 1480 case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: { 1481 if (mLnb == null && !requestLnb()) { 1482 return false; 1483 } 1484 break; 1485 } 1486 case TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX: { 1487 if (mDemuxHandle == null && !requestDemux()) { 1488 return false; 1489 } 1490 break; 1491 } 1492 default: 1493 return false; 1494 } 1495 return true; 1496 } 1497 checkCiCamResource(int ciCamId)1498 private boolean checkCiCamResource(int ciCamId) { 1499 if (mFrontendCiCamHandle == null && !requestFrontendCiCam(ciCamId)) { 1500 return false; 1501 } 1502 return true; 1503 } 1504 releaseLnb()1505 /* package */ void releaseLnb() { 1506 if (mLnbHandle != null) { 1507 // LNB handle can be null if it's opened by name. 1508 mTunerResourceManager.releaseLnb(mLnbHandle, mClientId); 1509 mLnbHandle = null; 1510 } 1511 mLnb = null; 1512 } 1513 } 1514