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