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