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