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