1 /* 2 * Copyright 2020 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 com.android.server.tv.tunerresourcemanager; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.ActivityManager; 22 import android.app.ActivityManager.RunningAppProcessInfo; 23 import android.content.Context; 24 import android.content.pm.PackageManager; 25 import android.hardware.tv.tuner.DemuxFilterMainType; 26 import android.media.IResourceManagerService; 27 import android.media.tv.TvInputManager; 28 import android.media.tv.tunerresourcemanager.CasSessionRequest; 29 import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; 30 import android.media.tv.tunerresourcemanager.ITunerResourceManager; 31 import android.media.tv.tunerresourcemanager.ResourceClientProfile; 32 import android.media.tv.tunerresourcemanager.TunerCiCamRequest; 33 import android.media.tv.tunerresourcemanager.TunerDemuxInfo; 34 import android.media.tv.tunerresourcemanager.TunerDemuxRequest; 35 import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; 36 import android.media.tv.tunerresourcemanager.TunerFrontendInfo; 37 import android.media.tv.tunerresourcemanager.TunerFrontendRequest; 38 import android.media.tv.tunerresourcemanager.TunerLnbRequest; 39 import android.media.tv.tunerresourcemanager.TunerResourceManager; 40 import android.os.Binder; 41 import android.os.IBinder; 42 import android.os.RemoteException; 43 import android.os.SystemClock; 44 import android.os.SystemProperties; 45 import android.util.IndentingPrintWriter; 46 import android.util.Log; 47 import android.util.Slog; 48 import android.util.SparseIntArray; 49 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.server.SystemService; 53 54 import java.io.FileDescriptor; 55 import java.io.PrintWriter; 56 import java.util.HashMap; 57 import java.util.HashSet; 58 import java.util.List; 59 import java.util.Map; 60 import java.util.Set; 61 import java.util.concurrent.TimeUnit; 62 import java.util.concurrent.locks.Condition; 63 import java.util.concurrent.locks.ReentrantLock; 64 65 /** 66 * This class provides a system service that manages the TV tuner resources. 67 * 68 * @hide 69 */ 70 public class TunerResourceManagerService extends SystemService implements IBinder.DeathRecipient { 71 private static final String TAG = "TunerResourceManagerService"; 72 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 73 74 public static final int INVALID_CLIENT_ID = -1; 75 private static final int MAX_CLIENT_PRIORITY = 1000; 76 private static final long INVALID_THREAD_ID = -1; 77 private static final long TRMS_LOCK_TIMEOUT = 500; 78 79 private static final int INVALID_FE_COUNT = -1; 80 81 // Map of the registered client profiles 82 private Map<Integer, ClientProfile> mClientProfiles = new HashMap<>(); 83 private int mNextUnusedClientId = 0; 84 85 // Map of the current available frontend resources 86 private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>(); 87 // SparseIntArray of the max usable number for each frontend resource type 88 private SparseIntArray mFrontendMaxUsableNums = new SparseIntArray(); 89 // SparseIntArray of the currently used number for each frontend resource type 90 private SparseIntArray mFrontendUsedNums = new SparseIntArray(); 91 // SparseIntArray of the existing number for each frontend resource type 92 private SparseIntArray mFrontendExistingNums = new SparseIntArray(); 93 94 // Backups for the frontend resource maps for enabling testing with custom resource maps 95 // such as TunerTest.testHasUnusedFrontend1() 96 private Map<Integer, FrontendResource> mFrontendResourcesBackup = new HashMap<>(); 97 private SparseIntArray mFrontendMaxUsableNumsBackup = new SparseIntArray(); 98 private SparseIntArray mFrontendUsedNumsBackup = new SparseIntArray(); 99 private SparseIntArray mFrontendExistingNumsBackup = new SparseIntArray(); 100 101 // Map of the current available demux resources 102 private Map<Integer, DemuxResource> mDemuxResources = new HashMap<>(); 103 // Map of the current available lnb resources 104 private Map<Integer, LnbResource> mLnbResources = new HashMap<>(); 105 // Map of the current available Cas resources 106 private Map<Integer, CasResource> mCasResources = new HashMap<>(); 107 // Map of the current available CiCam resources 108 private Map<Integer, CiCamResource> mCiCamResources = new HashMap<>(); 109 110 @GuardedBy("mLock") 111 private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>(); 112 113 private TvInputManager mTvInputManager; 114 private ActivityManager mActivityManager; 115 private IResourceManagerService mMediaResourceManager; 116 private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints(); 117 118 // An internal resource request count to help generate resource handle. 119 private int mResourceRequestCount = 0; 120 121 // Used to synchronize the access to the service. 122 private final Object mLock = new Object(); 123 124 private final ReentrantLock mLockForTRMSLock = new ReentrantLock(); 125 private final Condition mTunerApiLockReleasedCV = mLockForTRMSLock.newCondition(); 126 private int mTunerApiLockHolder = INVALID_CLIENT_ID; 127 private long mTunerApiLockHolderThreadId = INVALID_THREAD_ID; 128 private int mTunerApiLockNestedCount = 0; 129 TunerResourceManagerService(@ullable Context context)130 public TunerResourceManagerService(@Nullable Context context) { 131 super(context); 132 } 133 134 @Override onStart()135 public void onStart() { 136 onStart(false /*isForTesting*/); 137 } 138 139 @VisibleForTesting onStart(boolean isForTesting)140 protected void onStart(boolean isForTesting) { 141 if (!isForTesting) { 142 publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); 143 } 144 mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); 145 mActivityManager = 146 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); 147 mPriorityCongfig.parse(); 148 149 // Call SystemProperties.set() in mock app will throw exception because of permission. 150 if (!isForTesting) { 151 final boolean lazyHal = SystemProperties.getBoolean("ro.tuner.lazyhal", false); 152 if (!lazyHal) { 153 // The HAL is not a lazy HAL, enable the tuner server. 154 SystemProperties.set("tuner.server.enable", "true"); 155 } 156 } 157 158 if (mMediaResourceManager == null) { 159 IBinder mediaResourceManagerBinder = getBinderService("media.resource_manager"); 160 if (mediaResourceManagerBinder == null) { 161 Slog.w(TAG, "Resource Manager Service not available."); 162 return; 163 } 164 try { 165 mediaResourceManagerBinder.linkToDeath(this, /*flags*/ 0); 166 } catch (RemoteException e) { 167 Slog.w(TAG, "Could not link to death of native resource manager service."); 168 return; 169 } 170 mMediaResourceManager = IResourceManagerService.Stub.asInterface( 171 mediaResourceManagerBinder); 172 } 173 } 174 175 private final class BinderService extends ITunerResourceManager.Stub { 176 @Override registerClientProfile(@onNull ResourceClientProfile profile, @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)177 public void registerClientProfile(@NonNull ResourceClientProfile profile, 178 @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId) 179 throws RemoteException { 180 enforceTrmAccessPermission("registerClientProfile"); 181 enforceTunerAccessPermission("registerClientProfile"); 182 if (profile == null) { 183 throw new RemoteException("ResourceClientProfile can't be null"); 184 } 185 186 if (clientId == null) { 187 throw new RemoteException("clientId can't be null!"); 188 } 189 190 if (listener == null) { 191 throw new RemoteException("IResourcesReclaimListener can't be null!"); 192 } 193 194 if (!mPriorityCongfig.isDefinedUseCase(profile.useCase)) { 195 throw new RemoteException("Use undefined client use case:" + profile.useCase); 196 } 197 198 synchronized (mLock) { 199 registerClientProfileInternal(profile, listener, clientId); 200 } 201 } 202 203 @Override unregisterClientProfile(int clientId)204 public void unregisterClientProfile(int clientId) throws RemoteException { 205 enforceTrmAccessPermission("unregisterClientProfile"); 206 synchronized (mLock) { 207 if (!checkClientExists(clientId)) { 208 Slog.e(TAG, "Unregistering non exists client:" + clientId); 209 return; 210 } 211 unregisterClientProfileInternal(clientId); 212 } 213 } 214 215 @Override updateClientPriority(int clientId, int priority, int niceValue)216 public boolean updateClientPriority(int clientId, int priority, int niceValue) { 217 enforceTrmAccessPermission("updateClientPriority"); 218 synchronized (mLock) { 219 return updateClientPriorityInternal(clientId, priority, niceValue); 220 } 221 } 222 223 @Override hasUnusedFrontend(int frontendType)224 public boolean hasUnusedFrontend(int frontendType) { 225 enforceTrmAccessPermission("hasUnusedFrontend"); 226 synchronized (mLock) { 227 return hasUnusedFrontendInternal(frontendType); 228 } 229 } 230 231 @Override isLowestPriority(int clientId, int frontendType)232 public boolean isLowestPriority(int clientId, int frontendType) 233 throws RemoteException { 234 enforceTrmAccessPermission("isLowestPriority"); 235 synchronized (mLock) { 236 if (!checkClientExists(clientId)) { 237 throw new RemoteException("isLowestPriority called from unregistered client: " 238 + clientId); 239 } 240 return isLowestPriorityInternal(clientId, frontendType); 241 } 242 } 243 244 @Override setFrontendInfoList(@onNull TunerFrontendInfo[] infos)245 public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException { 246 enforceTrmAccessPermission("setFrontendInfoList"); 247 if (infos == null) { 248 throw new RemoteException("TunerFrontendInfo can't be null"); 249 } 250 synchronized (mLock) { 251 setFrontendInfoListInternal(infos); 252 } 253 } 254 255 @Override setDemuxInfoList(@onNull TunerDemuxInfo[] infos)256 public void setDemuxInfoList(@NonNull TunerDemuxInfo[] infos) throws RemoteException { 257 enforceTrmAccessPermission("setDemuxInfoList"); 258 if (infos == null) { 259 throw new RemoteException("TunerDemuxInfo can't be null"); 260 } 261 synchronized (mLock) { 262 setDemuxInfoListInternal(infos); 263 } 264 } 265 266 @Override updateCasInfo(int casSystemId, int maxSessionNum)267 public void updateCasInfo(int casSystemId, int maxSessionNum) { 268 enforceTrmAccessPermission("updateCasInfo"); 269 synchronized (mLock) { 270 updateCasInfoInternal(casSystemId, maxSessionNum); 271 } 272 } 273 274 @Override setLnbInfoList(int[] lnbHandles)275 public void setLnbInfoList(int[] lnbHandles) throws RemoteException { 276 enforceTrmAccessPermission("setLnbInfoList"); 277 if (lnbHandles == null) { 278 throw new RemoteException("Lnb handle list can't be null"); 279 } 280 synchronized (mLock) { 281 setLnbInfoListInternal(lnbHandles); 282 } 283 } 284 285 @Override requestFrontend(@onNull TunerFrontendRequest request, @NonNull int[] frontendHandle)286 public boolean requestFrontend(@NonNull TunerFrontendRequest request, 287 @NonNull int[] frontendHandle) { 288 enforceTunerAccessPermission("requestFrontend"); 289 enforceTrmAccessPermission("requestFrontend"); 290 if (frontendHandle == null) { 291 Slog.e(TAG, "frontendHandle can't be null"); 292 return false; 293 } 294 synchronized (mLock) { 295 if (!checkClientExists(request.clientId)) { 296 Slog.e(TAG, "Request frontend from unregistered client: " 297 + request.clientId); 298 return false; 299 } 300 // If the request client is holding or sharing a frontend, throw an exception. 301 if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) { 302 Slog.e(TAG, "Release frontend before requesting another one. Client id: " 303 + request.clientId); 304 return false; 305 } 306 return requestFrontendInternal(request, frontendHandle); 307 } 308 } 309 310 @Override setMaxNumberOfFrontends(int frontendType, int maxUsableNum)311 public boolean setMaxNumberOfFrontends(int frontendType, int maxUsableNum) { 312 enforceTunerAccessPermission("setMaxNumberOfFrontends"); 313 enforceTrmAccessPermission("setMaxNumberOfFrontends"); 314 if (maxUsableNum < 0) { 315 Slog.w(TAG, "setMaxNumberOfFrontends failed with maxUsableNum:" + maxUsableNum 316 + " frontendType:" + frontendType); 317 return false; 318 } 319 synchronized (mLock) { 320 return setMaxNumberOfFrontendsInternal(frontendType, maxUsableNum); 321 } 322 } 323 324 @Override getMaxNumberOfFrontends(int frontendType)325 public int getMaxNumberOfFrontends(int frontendType) { 326 enforceTunerAccessPermission("getMaxNumberOfFrontends"); 327 enforceTrmAccessPermission("getMaxNumberOfFrontends"); 328 synchronized (mLock) { 329 return getMaxNumberOfFrontendsInternal(frontendType); 330 } 331 } 332 333 @Override shareFrontend(int selfClientId, int targetClientId)334 public void shareFrontend(int selfClientId, int targetClientId) throws RemoteException { 335 enforceTunerAccessPermission("shareFrontend"); 336 enforceTrmAccessPermission("shareFrontend"); 337 synchronized (mLock) { 338 if (!checkClientExists(selfClientId)) { 339 throw new RemoteException("Share frontend request from an unregistered client:" 340 + selfClientId); 341 } 342 if (!checkClientExists(targetClientId)) { 343 throw new RemoteException("Request to share frontend with an unregistered " 344 + "client:" + targetClientId); 345 } 346 if (getClientProfile(targetClientId).getInUseFrontendHandles().isEmpty()) { 347 throw new RemoteException("Request to share frontend with a client that has no " 348 + "frontend resources. Target client id:" + targetClientId); 349 } 350 shareFrontendInternal(selfClientId, targetClientId); 351 } 352 } 353 354 @Override transferOwner(int resourceType, int currentOwnerId, int newOwnerId)355 public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) { 356 enforceTunerAccessPermission("transferOwner"); 357 enforceTrmAccessPermission("transferOwner"); 358 synchronized (mLock) { 359 if (!checkClientExists(currentOwnerId)) { 360 Slog.e(TAG, "currentOwnerId:" + currentOwnerId + " does not exit"); 361 return false; 362 } 363 if (!checkClientExists(newOwnerId)) { 364 Slog.e(TAG, "newOwnerId:" + newOwnerId + " does not exit"); 365 return false; 366 } 367 return transferOwnerInternal(resourceType, currentOwnerId, newOwnerId); 368 } 369 } 370 371 @Override requestDemux(@onNull TunerDemuxRequest request, @NonNull int[] demuxHandle)372 public boolean requestDemux(@NonNull TunerDemuxRequest request, 373 @NonNull int[] demuxHandle) throws RemoteException { 374 enforceTunerAccessPermission("requestDemux"); 375 enforceTrmAccessPermission("requestDemux"); 376 if (demuxHandle == null) { 377 throw new RemoteException("demuxHandle can't be null"); 378 } 379 synchronized (mLock) { 380 if (!checkClientExists(request.clientId)) { 381 throw new RemoteException("Request demux from unregistered client:" 382 + request.clientId); 383 } 384 return requestDemuxInternal(request, demuxHandle); 385 } 386 } 387 388 @Override requestDescrambler(@onNull TunerDescramblerRequest request, @NonNull int[] descramblerHandle)389 public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, 390 @NonNull int[] descramblerHandle) throws RemoteException { 391 enforceDescramblerAccessPermission("requestDescrambler"); 392 enforceTrmAccessPermission("requestDescrambler"); 393 if (descramblerHandle == null) { 394 throw new RemoteException("descramblerHandle can't be null"); 395 } 396 synchronized (mLock) { 397 if (!checkClientExists(request.clientId)) { 398 throw new RemoteException("Request descrambler from unregistered client:" 399 + request.clientId); 400 } 401 return requestDescramblerInternal(request, descramblerHandle); 402 } 403 } 404 405 @Override requestCasSession(@onNull CasSessionRequest request, @NonNull int[] casSessionHandle)406 public boolean requestCasSession(@NonNull CasSessionRequest request, 407 @NonNull int[] casSessionHandle) throws RemoteException { 408 enforceTrmAccessPermission("requestCasSession"); 409 if (casSessionHandle == null) { 410 throw new RemoteException("casSessionHandle can't be null"); 411 } 412 synchronized (mLock) { 413 if (!checkClientExists(request.clientId)) { 414 throw new RemoteException("Request cas from unregistered client:" 415 + request.clientId); 416 } 417 return requestCasSessionInternal(request, casSessionHandle); 418 } 419 } 420 421 @Override requestCiCam(@onNull TunerCiCamRequest request, @NonNull int[] ciCamHandle)422 public boolean requestCiCam(@NonNull TunerCiCamRequest request, 423 @NonNull int[] ciCamHandle) throws RemoteException { 424 enforceTrmAccessPermission("requestCiCam"); 425 if (ciCamHandle == null) { 426 throw new RemoteException("ciCamHandle can't be null"); 427 } 428 synchronized (mLock) { 429 if (!checkClientExists(request.clientId)) { 430 throw new RemoteException("Request ciCam from unregistered client:" 431 + request.clientId); 432 } 433 return requestCiCamInternal(request, ciCamHandle); 434 } 435 } 436 437 @Override requestLnb(@onNull TunerLnbRequest request, @NonNull int[] lnbHandle)438 public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) 439 throws RemoteException { 440 enforceTunerAccessPermission("requestLnb"); 441 enforceTrmAccessPermission("requestLnb"); 442 if (lnbHandle == null) { 443 throw new RemoteException("lnbHandle can't be null"); 444 } 445 synchronized (mLock) { 446 if (!checkClientExists(request.clientId)) { 447 throw new RemoteException("Request lnb from unregistered client:" 448 + request.clientId); 449 } 450 return requestLnbInternal(request, lnbHandle); 451 } 452 } 453 454 @Override releaseFrontend(int frontendHandle, int clientId)455 public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException { 456 enforceTunerAccessPermission("releaseFrontend"); 457 enforceTrmAccessPermission("releaseFrontend"); 458 if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, 459 frontendHandle)) { 460 throw new RemoteException("frontendHandle can't be invalid"); 461 } 462 synchronized (mLock) { 463 if (!checkClientExists(clientId)) { 464 throw new RemoteException("Release frontend from unregistered client:" 465 + clientId); 466 } 467 FrontendResource fe = getFrontendResource(frontendHandle); 468 if (fe == null) { 469 throw new RemoteException("Releasing frontend does not exist."); 470 } 471 int ownerClientId = fe.getOwnerClientId(); 472 ClientProfile ownerProfile = getClientProfile(ownerClientId); 473 if (ownerClientId != clientId 474 && (ownerProfile != null 475 && !ownerProfile.getShareFeClientIds().contains(clientId))) { 476 throw new RemoteException( 477 "Client is not the current owner of the releasing fe."); 478 } 479 releaseFrontendInternal(fe, clientId); 480 } 481 } 482 483 @Override releaseDemux(int demuxHandle, int clientId)484 public void releaseDemux(int demuxHandle, int clientId) throws RemoteException { 485 enforceTunerAccessPermission("releaseDemux"); 486 enforceTrmAccessPermission("releaseDemux"); 487 if (DEBUG) { 488 Slog.e(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")"); 489 } 490 491 synchronized (mLock) { 492 // For Tuner 2.0 and below or any HW constraint devices that are unable to support 493 // ITuner.openDemuxById(), demux resources are not really managed under TRM and 494 // mDemuxResources.size() will be zero 495 if (mDemuxResources.size() == 0) { 496 return; 497 } 498 499 if (!checkClientExists(clientId)) { 500 throw new RemoteException("Release demux for unregistered client:" + clientId); 501 } 502 DemuxResource demux = getDemuxResource(demuxHandle); 503 if (demux == null) { 504 throw new RemoteException("Releasing demux does not exist."); 505 } 506 if (demux.getOwnerClientId() != clientId) { 507 throw new RemoteException("Client is not the current owner " 508 + "of the releasing demux."); 509 } 510 releaseDemuxInternal(demux); 511 } 512 } 513 514 @Override releaseDescrambler(int descramblerHandle, int clientId)515 public void releaseDescrambler(int descramblerHandle, int clientId) { 516 enforceTunerAccessPermission("releaseDescrambler"); 517 enforceTrmAccessPermission("releaseDescrambler"); 518 if (DEBUG) { 519 Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")"); 520 } 521 } 522 523 @Override releaseCasSession(int casSessionHandle, int clientId)524 public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException { 525 enforceTrmAccessPermission("releaseCasSession"); 526 if (!validateResourceHandle( 527 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) { 528 throw new RemoteException("casSessionHandle can't be invalid"); 529 } 530 synchronized (mLock) { 531 if (!checkClientExists(clientId)) { 532 throw new RemoteException("Release cas from unregistered client:" + clientId); 533 } 534 int casSystemId = getClientProfile(clientId).getInUseCasSystemId(); 535 CasResource cas = getCasResource(casSystemId); 536 if (cas == null) { 537 throw new RemoteException("Releasing cas does not exist."); 538 } 539 if (!cas.getOwnerClientIds().contains(clientId)) { 540 throw new RemoteException( 541 "Client is not the current owner of the releasing cas."); 542 } 543 releaseCasSessionInternal(cas, clientId); 544 } 545 } 546 547 @Override releaseCiCam(int ciCamHandle, int clientId)548 public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException { 549 enforceTrmAccessPermission("releaseCiCam"); 550 if (!validateResourceHandle( 551 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) { 552 throw new RemoteException("ciCamHandle can't be invalid"); 553 } 554 synchronized (mLock) { 555 if (!checkClientExists(clientId)) { 556 throw new RemoteException("Release ciCam from unregistered client:" + clientId); 557 } 558 int ciCamId = getClientProfile(clientId).getInUseCiCamId(); 559 if (ciCamId != getResourceIdFromHandle(ciCamHandle)) { 560 throw new RemoteException("The client " + clientId + " is not the owner of " 561 + "the releasing ciCam."); 562 } 563 CiCamResource ciCam = getCiCamResource(ciCamId); 564 if (ciCam == null) { 565 throw new RemoteException("Releasing ciCam does not exist."); 566 } 567 if (!ciCam.getOwnerClientIds().contains(clientId)) { 568 throw new RemoteException( 569 "Client is not the current owner of the releasing ciCam."); 570 } 571 releaseCiCamInternal(ciCam, clientId); 572 } 573 } 574 575 @Override releaseLnb(int lnbHandle, int clientId)576 public void releaseLnb(int lnbHandle, int clientId) throws RemoteException { 577 enforceTunerAccessPermission("releaseLnb"); 578 enforceTrmAccessPermission("releaseLnb"); 579 if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) { 580 throw new RemoteException("lnbHandle can't be invalid"); 581 } 582 synchronized (mLock) { 583 if (!checkClientExists(clientId)) { 584 throw new RemoteException("Release lnb from unregistered client:" + clientId); 585 } 586 LnbResource lnb = getLnbResource(lnbHandle); 587 if (lnb == null) { 588 throw new RemoteException("Releasing lnb does not exist."); 589 } 590 if (lnb.getOwnerClientId() != clientId) { 591 throw new RemoteException("Client is not the current owner " 592 + "of the releasing lnb."); 593 } 594 releaseLnbInternal(lnb); 595 } 596 } 597 598 @Override isHigherPriority( ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile)599 public boolean isHigherPriority( 600 ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) 601 throws RemoteException { 602 enforceTrmAccessPermission("isHigherPriority"); 603 if (challengerProfile == null || holderProfile == null) { 604 throw new RemoteException("Client profiles can't be null."); 605 } 606 synchronized (mLock) { 607 return isHigherPriorityInternal(challengerProfile, holderProfile); 608 } 609 } 610 611 @Override storeResourceMap(int resourceType)612 public void storeResourceMap(int resourceType) { 613 enforceTrmAccessPermission("storeResourceMap"); 614 synchronized (mLock) { 615 storeResourceMapInternal(resourceType); 616 } 617 } 618 619 @Override clearResourceMap(int resourceType)620 public void clearResourceMap(int resourceType) { 621 enforceTrmAccessPermission("clearResourceMap"); 622 synchronized (mLock) { 623 clearResourceMapInternal(resourceType); 624 } 625 } 626 627 @Override restoreResourceMap(int resourceType)628 public void restoreResourceMap(int resourceType) { 629 enforceTrmAccessPermission("restoreResourceMap"); 630 synchronized (mLock) { 631 restoreResourceMapInternal(resourceType); 632 } 633 } 634 635 @Override acquireLock(int clientId, long clientThreadId)636 public boolean acquireLock(int clientId, long clientThreadId) { 637 enforceTrmAccessPermission("acquireLock"); 638 // this must not be locked with mLock 639 return acquireLockInternal(clientId, clientThreadId, TRMS_LOCK_TIMEOUT); 640 } 641 642 @Override releaseLock(int clientId)643 public boolean releaseLock(int clientId) { 644 enforceTrmAccessPermission("releaseLock"); 645 // this must not be locked with mLock 646 return releaseLockInternal(clientId, TRMS_LOCK_TIMEOUT, false, false); 647 } 648 649 @Override dump(FileDescriptor fd, final PrintWriter writer, String[] args)650 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 651 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 652 653 if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 654 != PackageManager.PERMISSION_GRANTED) { 655 pw.println("Permission Denial: can't dump!"); 656 return; 657 } 658 659 synchronized (mLock) { 660 dumpMap(mClientProfiles, "ClientProfiles:", "\n", pw); 661 dumpMap(mFrontendResources, "FrontendResources:", "\n", pw); 662 dumpSIA(mFrontendExistingNums, "FrontendExistingNums:", ", ", pw); 663 dumpSIA(mFrontendUsedNums, "FrontendUsedNums:", ", ", pw); 664 dumpSIA(mFrontendMaxUsableNums, "FrontendMaxUsableNums:", ", ", pw); 665 dumpMap(mFrontendResourcesBackup, "FrontendResourcesBackUp:", "\n", pw); 666 dumpSIA(mFrontendExistingNumsBackup, "FrontendExistingNumsBackup:", ", ", pw); 667 dumpSIA(mFrontendUsedNumsBackup, "FrontendUsedNumsBackup:", ", ", pw); 668 dumpSIA(mFrontendMaxUsableNumsBackup, "FrontendUsedNumsBackup:", ", ", pw); 669 dumpMap(mDemuxResources, "DemuxResource:", "\n", pw); 670 dumpMap(mLnbResources, "LnbResource:", "\n", pw); 671 dumpMap(mCasResources, "CasResource:", "\n", pw); 672 dumpMap(mCiCamResources, "CiCamResource:", "\n", pw); 673 dumpMap(mListeners, "Listners:", "\n", pw); 674 } 675 } 676 677 @Override getClientPriority(int useCase, int pid)678 public int getClientPriority(int useCase, int pid) throws RemoteException { 679 enforceTrmAccessPermission("getClientPriority"); 680 synchronized (mLock) { 681 return TunerResourceManagerService.this.getClientPriority( 682 useCase, checkIsForeground(pid)); 683 } 684 } 685 @Override getConfigPriority(int useCase, boolean isForeground)686 public int getConfigPriority(int useCase, boolean isForeground) throws RemoteException { 687 enforceTrmAccessPermission("getConfigPriority"); 688 synchronized (mLock) { 689 return TunerResourceManagerService.this.getClientPriority(useCase, isForeground); 690 } 691 } 692 } 693 694 /** 695 * Handle the death of the native resource manager service 696 */ 697 @Override binderDied()698 public void binderDied() { 699 if (DEBUG) { 700 Slog.w(TAG, "Native media resource manager service has died"); 701 } 702 synchronized (mLock) { 703 mMediaResourceManager = null; 704 } 705 } 706 707 @VisibleForTesting registerClientProfileInternal(ResourceClientProfile profile, IResourcesReclaimListener listener, int[] clientId)708 protected void registerClientProfileInternal(ResourceClientProfile profile, 709 IResourcesReclaimListener listener, int[] clientId) { 710 if (DEBUG) { 711 Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); 712 } 713 714 clientId[0] = INVALID_CLIENT_ID; 715 if (mTvInputManager == null) { 716 Slog.e(TAG, "TvInputManager is null. Can't register client profile."); 717 return; 718 } 719 // TODO tell if the client already exists 720 clientId[0] = mNextUnusedClientId++; 721 722 int pid = profile.tvInputSessionId == null 723 ? Binder.getCallingPid() /*callingPid*/ 724 : mTvInputManager.getClientPid(profile.tvInputSessionId); /*tvAppId*/ 725 726 // Update Media Resource Manager with the tvAppId 727 if (profile.tvInputSessionId != null && mMediaResourceManager != null) { 728 try { 729 mMediaResourceManager.overridePid(Binder.getCallingPid(), pid); 730 } catch (RemoteException e) { 731 Slog.e(TAG, "Could not overridePid in resourceManagerSercice," 732 + " remote exception: " + e); 733 } 734 } 735 736 ClientProfile clientProfile = new ClientProfile.Builder(clientId[0]) 737 .tvInputSessionId(profile.tvInputSessionId) 738 .useCase(profile.useCase) 739 .processId(pid) 740 .build(); 741 clientProfile.setPriority( 742 getClientPriority(profile.useCase, checkIsForeground(pid))); 743 744 addClientProfile(clientId[0], clientProfile, listener); 745 } 746 747 @VisibleForTesting unregisterClientProfileInternal(int clientId)748 protected void unregisterClientProfileInternal(int clientId) { 749 if (DEBUG) { 750 Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); 751 } 752 removeClientProfile(clientId); 753 // Remove the Media Resource Manager callingPid to tvAppId mapping 754 if (mMediaResourceManager != null) { 755 try { 756 mMediaResourceManager.overridePid(Binder.getCallingPid(), -1); 757 } catch (RemoteException e) { 758 Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister," 759 + " remote exception: " + e); 760 } 761 } 762 } 763 764 @VisibleForTesting updateClientPriorityInternal(int clientId, int priority, int niceValue)765 protected boolean updateClientPriorityInternal(int clientId, int priority, int niceValue) { 766 if (DEBUG) { 767 Slog.d(TAG, 768 "updateClientPriority(clientId=" + clientId + ", priority=" + priority 769 + ", niceValue=" + niceValue + ")"); 770 } 771 772 ClientProfile profile = getClientProfile(clientId); 773 if (profile == null) { 774 Slog.e(TAG, 775 "Can not find client profile with id " + clientId 776 + " when trying to update the client priority."); 777 return false; 778 } 779 780 profile.overwritePriority(priority); 781 profile.setNiceValue(niceValue); 782 783 return true; 784 } 785 786 hasUnusedFrontendInternal(int frontendType)787 protected boolean hasUnusedFrontendInternal(int frontendType) { 788 for (FrontendResource fr : getFrontendResources().values()) { 789 if (fr.getType() == frontendType && !fr.isInUse()) { 790 return true; 791 } 792 } 793 return false; 794 } 795 isLowestPriorityInternal(int clientId, int frontendType)796 protected boolean isLowestPriorityInternal(int clientId, int frontendType) 797 throws RemoteException { 798 // Update the client priority 799 ClientProfile requestClient = getClientProfile(clientId); 800 if (requestClient == null) { 801 return true; 802 } 803 clientPriorityUpdateOnRequest(requestClient); 804 int clientPriority = requestClient.getPriority(); 805 806 // Check if there is another holder with lower priority 807 for (FrontendResource fr : getFrontendResources().values()) { 808 if (fr.getType() == frontendType && fr.isInUse()) { 809 int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId()); 810 // Returns false only when the clientPriority is strictly greater 811 // because false means that there is another reclaimable resource 812 if (clientPriority > priority) { 813 return false; 814 } 815 } 816 } 817 return true; 818 } 819 storeResourceMapInternal(int resourceType)820 protected void storeResourceMapInternal(int resourceType) { 821 switch (resourceType) { 822 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: 823 replaceFeResourceMap(mFrontendResources, mFrontendResourcesBackup); 824 replaceFeCounts(mFrontendExistingNums, mFrontendExistingNumsBackup); 825 replaceFeCounts(mFrontendUsedNums, mFrontendUsedNumsBackup); 826 replaceFeCounts(mFrontendMaxUsableNums, mFrontendMaxUsableNumsBackup); 827 break; 828 // TODO: implement for other resource type when needed 829 default: 830 break; 831 } 832 } 833 clearResourceMapInternal(int resourceType)834 protected void clearResourceMapInternal(int resourceType) { 835 switch (resourceType) { 836 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: 837 replaceFeResourceMap(null, mFrontendResources); 838 replaceFeCounts(null, mFrontendExistingNums); 839 replaceFeCounts(null, mFrontendUsedNums); 840 replaceFeCounts(null, mFrontendMaxUsableNums); 841 break; 842 // TODO: implement for other resource type when needed 843 default: 844 break; 845 } 846 } 847 restoreResourceMapInternal(int resourceType)848 protected void restoreResourceMapInternal(int resourceType) { 849 switch (resourceType) { 850 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: 851 replaceFeResourceMap(mFrontendResourcesBackup, mFrontendResources); 852 replaceFeCounts(mFrontendExistingNumsBackup, mFrontendExistingNums); 853 replaceFeCounts(mFrontendUsedNumsBackup, mFrontendUsedNums); 854 replaceFeCounts(mFrontendMaxUsableNumsBackup, mFrontendMaxUsableNums); 855 break; 856 // TODO: implement for other resource type when needed 857 default: 858 break; 859 } 860 } 861 862 @VisibleForTesting setFrontendInfoListInternal(TunerFrontendInfo[] infos)863 protected void setFrontendInfoListInternal(TunerFrontendInfo[] infos) { 864 if (DEBUG) { 865 Slog.d(TAG, "updateFrontendInfo:"); 866 for (int i = 0; i < infos.length; i++) { 867 Slog.d(TAG, infos[i].toString()); 868 } 869 } 870 871 // A set to record the frontends pending on updating. Ids will be removed 872 // from this set once its updating finished. Any frontend left in this set when all 873 // the updates are done will be removed from mFrontendResources. 874 Set<Integer> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet()); 875 876 // Update frontendResources map and other mappings accordingly 877 for (int i = 0; i < infos.length; i++) { 878 if (getFrontendResource(infos[i].handle) != null) { 879 if (DEBUG) { 880 Slog.d(TAG, "Frontend handle=" + infos[i].handle + "exists."); 881 } 882 updatingFrontendHandles.remove(infos[i].handle); 883 } else { 884 // Add a new fe resource 885 FrontendResource newFe = new FrontendResource.Builder(infos[i].handle) 886 .type(infos[i].type) 887 .exclusiveGroupId(infos[i].exclusiveGroupId) 888 .build(); 889 addFrontendResource(newFe); 890 } 891 } 892 893 for (int removingHandle : updatingFrontendHandles) { 894 // update the exclusive group id member list 895 removeFrontendResource(removingHandle); 896 } 897 } 898 899 @VisibleForTesting setDemuxInfoListInternal(TunerDemuxInfo[] infos)900 protected void setDemuxInfoListInternal(TunerDemuxInfo[] infos) { 901 if (DEBUG) { 902 Slog.d(TAG, "updateDemuxInfo:"); 903 for (int i = 0; i < infos.length; i++) { 904 Slog.d(TAG, infos[i].toString()); 905 } 906 } 907 908 // A set to record the demuxes pending on updating. Ids will be removed 909 // from this set once its updating finished. Any demux left in this set when all 910 // the updates are done will be removed from mDemuxResources. 911 Set<Integer> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet()); 912 913 // Update demuxResources map and other mappings accordingly 914 for (int i = 0; i < infos.length; i++) { 915 if (getDemuxResource(infos[i].handle) != null) { 916 if (DEBUG) { 917 Slog.d(TAG, "Demux handle=" + infos[i].handle + "exists."); 918 } 919 updatingDemuxHandles.remove(infos[i].handle); 920 } else { 921 // Add a new demux resource 922 DemuxResource newDemux = new DemuxResource.Builder(infos[i].handle) 923 .filterTypes(infos[i].filterTypes) 924 .build(); 925 addDemuxResource(newDemux); 926 } 927 } 928 929 for (int removingHandle : updatingDemuxHandles) { 930 // update the exclusive group id member list 931 removeDemuxResource(removingHandle); 932 } 933 } 934 @VisibleForTesting setLnbInfoListInternal(int[] lnbHandles)935 protected void setLnbInfoListInternal(int[] lnbHandles) { 936 if (DEBUG) { 937 for (int i = 0; i < lnbHandles.length; i++) { 938 Slog.d(TAG, "updateLnbInfo(lnbHanle=" + lnbHandles[i] + ")"); 939 } 940 } 941 942 // A set to record the Lnbs pending on updating. Handles will be removed 943 // from this set once its updating finished. Any lnb left in this set when all 944 // the updates are done will be removed from mLnbResources. 945 Set<Integer> updatingLnbHandles = new HashSet<>(getLnbResources().keySet()); 946 947 // Update lnbResources map and other mappings accordingly 948 for (int i = 0; i < lnbHandles.length; i++) { 949 if (getLnbResource(lnbHandles[i]) != null) { 950 if (DEBUG) { 951 Slog.d(TAG, "Lnb handle=" + lnbHandles[i] + "exists."); 952 } 953 updatingLnbHandles.remove(lnbHandles[i]); 954 } else { 955 // Add a new lnb resource 956 LnbResource newLnb = new LnbResource.Builder(lnbHandles[i]).build(); 957 addLnbResource(newLnb); 958 } 959 } 960 961 for (int removingHandle : updatingLnbHandles) { 962 removeLnbResource(removingHandle); 963 } 964 } 965 966 @VisibleForTesting updateCasInfoInternal(int casSystemId, int maxSessionNum)967 protected void updateCasInfoInternal(int casSystemId, int maxSessionNum) { 968 if (DEBUG) { 969 Slog.d(TAG, 970 "updateCasInfo(casSystemId=" + casSystemId 971 + ", maxSessionNum=" + maxSessionNum + ")"); 972 } 973 // If maxSessionNum is 0, removing the Cas Resource. 974 if (maxSessionNum == 0) { 975 removeCasResource(casSystemId); 976 removeCiCamResource(casSystemId); 977 return; 978 } 979 // If the Cas exists, updates the Cas Resource accordingly. 980 CasResource cas = getCasResource(casSystemId); 981 CiCamResource ciCam = getCiCamResource(casSystemId); 982 if (cas != null) { 983 if (cas.getUsedSessionNum() > maxSessionNum) { 984 // Sort and release the short number of Cas resources. 985 int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum; 986 // TODO: handle CiCam session update. 987 } 988 cas.updateMaxSessionNum(maxSessionNum); 989 if (ciCam != null) { 990 ciCam.updateMaxSessionNum(maxSessionNum); 991 } 992 return; 993 } 994 // Add the new Cas Resource. 995 cas = new CasResource.Builder(casSystemId) 996 .maxSessionNum(maxSessionNum) 997 .build(); 998 ciCam = new CiCamResource.Builder(casSystemId) 999 .maxSessionNum(maxSessionNum) 1000 .build(); 1001 addCasResource(cas); 1002 addCiCamResource(ciCam); 1003 } 1004 1005 @VisibleForTesting requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle)1006 protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) { 1007 if (DEBUG) { 1008 Slog.d(TAG, "requestFrontend(request=" + request + ")"); 1009 } 1010 1011 frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1012 ClientProfile requestClient = getClientProfile(request.clientId); 1013 // TODO: check if this is really needed 1014 if (requestClient == null) { 1015 return false; 1016 } 1017 clientPriorityUpdateOnRequest(requestClient); 1018 int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1019 int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1020 // Priority max value is 1000 1021 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 1022 boolean isRequestFromSameProcess = false; 1023 // If the desired frontend id was specified, we only need to check the frontend. 1024 boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest.DEFAULT_DESIRED_ID; 1025 for (FrontendResource fr : getFrontendResources().values()) { 1026 int frontendId = getResourceIdFromHandle(fr.getHandle()); 1027 if (fr.getType() == request.frontendType 1028 && (!hasDesiredFrontend || frontendId == request.desiredId)) { 1029 if (!fr.isInUse()) { 1030 // Unused resource cannot be acquired if the max is already reached, but 1031 // TRM still has to look for the reclaim candidate 1032 if (isFrontendMaxNumUseReached(request.frontendType)) { 1033 continue; 1034 } 1035 // Grant unused frontend with no exclusive group members first. 1036 if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) { 1037 grantingFrontendHandle = fr.getHandle(); 1038 break; 1039 } else if (grantingFrontendHandle 1040 == TunerResourceManager.INVALID_RESOURCE_HANDLE) { 1041 // Grant the unused frontend with lower id first if all the unused 1042 // frontends have exclusive group members. 1043 grantingFrontendHandle = fr.getHandle(); 1044 } 1045 } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { 1046 // Record the frontend id with the lowest client priority among all the 1047 // in use frontends when no available frontend has been found. 1048 int priority = getFrontendHighestClientPriority(fr.getOwnerClientId()); 1049 if (currentLowestPriority > priority) { 1050 inUseLowestPriorityFrHandle = fr.getHandle(); 1051 currentLowestPriority = priority; 1052 isRequestFromSameProcess = (requestClient.getProcessId() 1053 == (getClientProfile(fr.getOwnerClientId())).getProcessId()); 1054 } 1055 } 1056 } 1057 } 1058 1059 // Grant frontend when there is unused resource. 1060 if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { 1061 frontendHandle[0] = grantingFrontendHandle; 1062 updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId); 1063 return true; 1064 } 1065 1066 // When all the resources are occupied, grant the lowest priority resource if the 1067 // request client has higher priority. 1068 if (inUseLowestPriorityFrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE 1069 && ((requestClient.getPriority() > currentLowestPriority) || ( 1070 (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { 1071 if (!reclaimResource( 1072 getFrontendResource(inUseLowestPriorityFrHandle).getOwnerClientId(), 1073 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { 1074 return false; 1075 } 1076 frontendHandle[0] = inUseLowestPriorityFrHandle; 1077 updateFrontendClientMappingOnNewGrant( 1078 inUseLowestPriorityFrHandle, request.clientId); 1079 return true; 1080 } 1081 1082 return false; 1083 } 1084 1085 @VisibleForTesting shareFrontendInternal(int selfClientId, int targetClientId)1086 protected void shareFrontendInternal(int selfClientId, int targetClientId) { 1087 if (DEBUG) { 1088 Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId); 1089 } 1090 for (int feId : getClientProfile(targetClientId).getInUseFrontendHandles()) { 1091 getClientProfile(selfClientId).useFrontend(feId); 1092 } 1093 getClientProfile(targetClientId).shareFrontend(selfClientId); 1094 } 1095 transferFeOwner(int currentOwnerId, int newOwnerId)1096 private boolean transferFeOwner(int currentOwnerId, int newOwnerId) { 1097 ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId); 1098 ClientProfile newOwnerProfile = getClientProfile(newOwnerId); 1099 // change the owner of all the inUse frontend 1100 newOwnerProfile.shareFrontend(currentOwnerId); 1101 currentOwnerProfile.stopSharingFrontend(newOwnerId); 1102 for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) { 1103 getFrontendResource(inUseHandle).setOwner(newOwnerId); 1104 } 1105 // change the primary frontend 1106 newOwnerProfile.setPrimaryFrontend(currentOwnerProfile.getPrimaryFrontend()); 1107 currentOwnerProfile.setPrimaryFrontend(TunerResourceManager.INVALID_RESOURCE_HANDLE); 1108 // double check there is no other resources tied to the previous owner 1109 for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) { 1110 int ownerId = getFrontendResource(inUseHandle).getOwnerClientId(); 1111 if (ownerId != newOwnerId) { 1112 Slog.e(TAG, "something is wrong in transferFeOwner:" + inUseHandle 1113 + ", " + ownerId + ", " + newOwnerId); 1114 return false; 1115 } 1116 } 1117 return true; 1118 } 1119 transferFeCiCamOwner(int currentOwnerId, int newOwnerId)1120 private boolean transferFeCiCamOwner(int currentOwnerId, int newOwnerId) { 1121 ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId); 1122 ClientProfile newOwnerProfile = getClientProfile(newOwnerId); 1123 1124 // link ciCamId to the new profile 1125 int ciCamId = currentOwnerProfile.getInUseCiCamId(); 1126 newOwnerProfile.useCiCam(ciCamId); 1127 1128 // set the new owner Id 1129 CiCamResource ciCam = getCiCamResource(ciCamId); 1130 ciCam.setOwner(newOwnerId); 1131 1132 // unlink cicam resource from the original owner profile 1133 currentOwnerProfile.releaseCiCam(); 1134 return true; 1135 } 1136 transferLnbOwner(int currentOwnerId, int newOwnerId)1137 private boolean transferLnbOwner(int currentOwnerId, int newOwnerId) { 1138 ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId); 1139 ClientProfile newOwnerProfile = getClientProfile(newOwnerId); 1140 1141 Set<Integer> inUseLnbHandles = new HashSet<>(); 1142 for (Integer lnbHandle : currentOwnerProfile.getInUseLnbHandles()) { 1143 // link lnb handle to the new profile 1144 newOwnerProfile.useLnb(lnbHandle); 1145 1146 // set new owner Id 1147 LnbResource lnb = getLnbResource(lnbHandle); 1148 lnb.setOwner(newOwnerId); 1149 1150 inUseLnbHandles.add(lnbHandle); 1151 } 1152 1153 // unlink lnb handles from the original owner 1154 for (Integer lnbHandle : inUseLnbHandles) { 1155 currentOwnerProfile.releaseLnb(lnbHandle); 1156 } 1157 1158 return true; 1159 } 1160 1161 @VisibleForTesting transferOwnerInternal(int resourceType, int currentOwnerId, int newOwnerId)1162 protected boolean transferOwnerInternal(int resourceType, int currentOwnerId, int newOwnerId) { 1163 switch (resourceType) { 1164 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: 1165 return transferFeOwner(currentOwnerId, newOwnerId); 1166 case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM: 1167 return transferFeCiCamOwner(currentOwnerId, newOwnerId); 1168 case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: 1169 return transferLnbOwner(currentOwnerId, newOwnerId); 1170 default: 1171 Slog.e(TAG, "transferOwnerInternal. unsupported resourceType: " + resourceType); 1172 return false; 1173 } 1174 } 1175 1176 @VisibleForTesting requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)1177 protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) { 1178 if (DEBUG) { 1179 Slog.d(TAG, "requestLnb(request=" + request + ")"); 1180 } 1181 1182 lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1183 ClientProfile requestClient = getClientProfile(request.clientId); 1184 clientPriorityUpdateOnRequest(requestClient); 1185 int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1186 int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1187 // Priority max value is 1000 1188 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 1189 boolean isRequestFromSameProcess = false; 1190 for (LnbResource lnb : getLnbResources().values()) { 1191 if (!lnb.isInUse()) { 1192 // Grant the unused lnb with lower handle first 1193 grantingLnbHandle = lnb.getHandle(); 1194 break; 1195 } else { 1196 // Record the lnb id with the lowest client priority among all the 1197 // in use lnb when no available lnb has been found. 1198 int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId()); 1199 if (currentLowestPriority > priority) { 1200 inUseLowestPriorityLnbHandle = lnb.getHandle(); 1201 currentLowestPriority = priority; 1202 isRequestFromSameProcess = (requestClient.getProcessId() 1203 == (getClientProfile(lnb.getOwnerClientId())).getProcessId()); 1204 } 1205 } 1206 } 1207 1208 // Grant Lnb when there is unused resource. 1209 if (grantingLnbHandle > -1) { 1210 lnbHandle[0] = grantingLnbHandle; 1211 updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId); 1212 return true; 1213 } 1214 1215 // When all the resources are occupied, grant the lowest priority resource if the 1216 // request client has higher priority. 1217 if (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE 1218 && ((requestClient.getPriority() > currentLowestPriority) || ( 1219 (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { 1220 if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(), 1221 TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) { 1222 return false; 1223 } 1224 lnbHandle[0] = inUseLowestPriorityLnbHandle; 1225 updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId); 1226 return true; 1227 } 1228 1229 return false; 1230 } 1231 1232 @VisibleForTesting requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle)1233 protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) { 1234 if (DEBUG) { 1235 Slog.d(TAG, "requestCasSession(request=" + request + ")"); 1236 } 1237 CasResource cas = getCasResource(request.casSystemId); 1238 // Unregistered Cas System is treated as having unlimited sessions. 1239 if (cas == null) { 1240 cas = new CasResource.Builder(request.casSystemId) 1241 .maxSessionNum(Integer.MAX_VALUE) 1242 .build(); 1243 addCasResource(cas); 1244 } 1245 casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1246 ClientProfile requestClient = getClientProfile(request.clientId); 1247 clientPriorityUpdateOnRequest(requestClient); 1248 int lowestPriorityOwnerId = -1; 1249 // Priority max value is 1000 1250 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 1251 boolean isRequestFromSameProcess = false; 1252 if (!cas.isFullyUsed()) { 1253 casSessionHandle[0] = generateResourceHandle( 1254 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); 1255 updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); 1256 return true; 1257 } 1258 for (int ownerId : cas.getOwnerClientIds()) { 1259 // Record the client id with lowest priority that is using the current Cas system. 1260 int priority = updateAndGetOwnerClientPriority(ownerId); 1261 if (currentLowestPriority > priority) { 1262 lowestPriorityOwnerId = ownerId; 1263 currentLowestPriority = priority; 1264 isRequestFromSameProcess = (requestClient.getProcessId() 1265 == (getClientProfile(ownerId)).getProcessId()); 1266 } 1267 } 1268 1269 // When all the Cas sessions are occupied, reclaim the lowest priority client if the 1270 // request client has higher priority. 1271 if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority) 1272 || ((requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { 1273 if (!reclaimResource(lowestPriorityOwnerId, 1274 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { 1275 return false; 1276 } 1277 casSessionHandle[0] = generateResourceHandle( 1278 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); 1279 updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); 1280 return true; 1281 } 1282 return false; 1283 } 1284 1285 @VisibleForTesting requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle)1286 protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) { 1287 if (DEBUG) { 1288 Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")"); 1289 } 1290 CiCamResource ciCam = getCiCamResource(request.ciCamId); 1291 // Unregistered Cas System is treated as having unlimited sessions. 1292 if (ciCam == null) { 1293 ciCam = new CiCamResource.Builder(request.ciCamId) 1294 .maxSessionNum(Integer.MAX_VALUE) 1295 .build(); 1296 addCiCamResource(ciCam); 1297 } 1298 ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1299 ClientProfile requestClient = getClientProfile(request.clientId); 1300 clientPriorityUpdateOnRequest(requestClient); 1301 int lowestPriorityOwnerId = -1; 1302 // Priority max value is 1000 1303 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 1304 boolean isRequestFromSameProcess = false; 1305 if (!ciCam.isFullyUsed()) { 1306 ciCamHandle[0] = generateResourceHandle( 1307 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); 1308 updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); 1309 return true; 1310 } 1311 for (int ownerId : ciCam.getOwnerClientIds()) { 1312 // Record the client id with lowest priority that is using the current Cas system. 1313 int priority = updateAndGetOwnerClientPriority(ownerId); 1314 if (currentLowestPriority > priority) { 1315 lowestPriorityOwnerId = ownerId; 1316 currentLowestPriority = priority; 1317 isRequestFromSameProcess = (requestClient.getProcessId() 1318 == (getClientProfile(ownerId)).getProcessId()); 1319 } 1320 } 1321 1322 // When all the CiCam sessions are occupied, reclaim the lowest priority client if the 1323 // request client has higher priority. 1324 if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority) 1325 || ((requestClient.getPriority() == currentLowestPriority) 1326 && isRequestFromSameProcess))) { 1327 if (!reclaimResource(lowestPriorityOwnerId, 1328 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) { 1329 return false; 1330 } 1331 ciCamHandle[0] = generateResourceHandle( 1332 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); 1333 updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); 1334 return true; 1335 } 1336 return false; 1337 } 1338 1339 @VisibleForTesting isHigherPriorityInternal(ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile)1340 protected boolean isHigherPriorityInternal(ResourceClientProfile challengerProfile, 1341 ResourceClientProfile holderProfile) { 1342 if (DEBUG) { 1343 Slog.d(TAG, 1344 "isHigherPriority(challengerProfile=" + challengerProfile 1345 + ", holderProfile=" + challengerProfile + ")"); 1346 } 1347 if (mTvInputManager == null) { 1348 Slog.e(TAG, "TvInputManager is null. Can't compare the priority."); 1349 // Allow the client to acquire the hardware interface 1350 // when the TRM is not able to compare the priority. 1351 return true; 1352 } 1353 1354 int challengerPid = challengerProfile.tvInputSessionId == null 1355 ? Binder.getCallingPid() /*callingPid*/ 1356 : mTvInputManager.getClientPid(challengerProfile.tvInputSessionId); /*tvAppId*/ 1357 int holderPid = holderProfile.tvInputSessionId == null 1358 ? Binder.getCallingPid() /*callingPid*/ 1359 : mTvInputManager.getClientPid(holderProfile.tvInputSessionId); /*tvAppId*/ 1360 1361 int challengerPriority = getClientPriority( 1362 challengerProfile.useCase, checkIsForeground(challengerPid)); 1363 int holderPriority = getClientPriority(holderProfile.useCase, checkIsForeground(holderPid)); 1364 return challengerPriority > holderPriority; 1365 } 1366 1367 @VisibleForTesting releaseFrontendInternal(FrontendResource fe, int clientId)1368 protected void releaseFrontendInternal(FrontendResource fe, int clientId) { 1369 if (DEBUG) { 1370 Slog.d(TAG, "releaseFrontend(id=" + fe.getHandle() + ", clientId=" + clientId + " )"); 1371 } 1372 if (clientId == fe.getOwnerClientId()) { 1373 ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId()); 1374 if (ownerClient != null) { 1375 for (int shareOwnerId : ownerClient.getShareFeClientIds()) { 1376 reclaimResource(shareOwnerId, 1377 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND); 1378 } 1379 } 1380 } 1381 clearFrontendAndClientMapping(getClientProfile(clientId)); 1382 } 1383 1384 @VisibleForTesting releaseDemuxInternal(DemuxResource demux)1385 protected void releaseDemuxInternal(DemuxResource demux) { 1386 if (DEBUG) { 1387 Slog.d(TAG, "releaseDemux(DemuxHandle=" + demux.getHandle() + ")"); 1388 } 1389 updateDemuxClientMappingOnRelease(demux); 1390 } 1391 1392 @VisibleForTesting releaseLnbInternal(LnbResource lnb)1393 protected void releaseLnbInternal(LnbResource lnb) { 1394 if (DEBUG) { 1395 Slog.d(TAG, "releaseLnb(lnbHandle=" + lnb.getHandle() + ")"); 1396 } 1397 updateLnbClientMappingOnRelease(lnb); 1398 } 1399 1400 @VisibleForTesting releaseCasSessionInternal(CasResource cas, int ownerClientId)1401 protected void releaseCasSessionInternal(CasResource cas, int ownerClientId) { 1402 if (DEBUG) { 1403 Slog.d(TAG, "releaseCasSession(sessionResourceId=" + cas.getSystemId() + ")"); 1404 } 1405 updateCasClientMappingOnRelease(cas, ownerClientId); 1406 } 1407 1408 @VisibleForTesting releaseCiCamInternal(CiCamResource ciCam, int ownerClientId)1409 protected void releaseCiCamInternal(CiCamResource ciCam, int ownerClientId) { 1410 if (DEBUG) { 1411 Slog.d(TAG, "releaseCiCamInternal(ciCamId=" + ciCam.getCiCamId() + ")"); 1412 } 1413 updateCiCamClientMappingOnRelease(ciCam, ownerClientId); 1414 } 1415 1416 @VisibleForTesting requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle)1417 protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { 1418 if (DEBUG) { 1419 Slog.d(TAG, "requestDemux(request=" + request + ")"); 1420 } 1421 1422 // For Tuner 2.0 and below or any HW constraint devices that are unable to support 1423 // ITuner.openDemuxById(), demux resources are not really managed under TRM and 1424 // mDemuxResources.size() will be zero 1425 if (mDemuxResources.size() == 0) { 1426 // There are enough Demux resources, so we don't manage Demux in R. 1427 demuxHandle[0] = 1428 generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0); 1429 return true; 1430 } 1431 1432 demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1433 ClientProfile requestClient = getClientProfile(request.clientId); 1434 1435 if (requestClient == null) { 1436 return false; 1437 } 1438 1439 clientPriorityUpdateOnRequest(requestClient); 1440 int grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1441 int inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 1442 // Priority max value is 1000 1443 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 1444 boolean isRequestFromSameProcess = false; 1445 // If the desired demux id was specified, we only need to check the demux. 1446 boolean hasDesiredDemuxCap = request.desiredFilterTypes 1447 != DemuxFilterMainType.UNDEFINED; 1448 int smallestNumOfSupportedCaps = Integer.SIZE + 1; 1449 for (DemuxResource dr : getDemuxResources().values()) { 1450 if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) { 1451 if (!dr.isInUse()) { 1452 int numOfSupportedCaps = dr.getNumOfCaps(); 1453 1454 // look for the demux with minimum caps supporting the desired caps 1455 if (smallestNumOfSupportedCaps > numOfSupportedCaps) { 1456 smallestNumOfSupportedCaps = numOfSupportedCaps; 1457 grantingDemuxHandle = dr.getHandle(); 1458 } 1459 } else if (grantingDemuxHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { 1460 // Record the demux id with the lowest client priority among all the 1461 // in use demuxes when no availabledemux has been found. 1462 int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId()); 1463 if (currentLowestPriority >= priority) { 1464 int numOfSupportedCaps = dr.getNumOfCaps(); 1465 boolean shouldUpdate = false; 1466 // update lowest priority 1467 if (currentLowestPriority > priority) { 1468 currentLowestPriority = priority; 1469 isRequestFromSameProcess = (requestClient.getProcessId() 1470 == (getClientProfile(dr.getOwnerClientId())).getProcessId()); 1471 shouldUpdate = true; 1472 } 1473 // update smallest caps 1474 if (smallestNumOfSupportedCaps > numOfSupportedCaps) { 1475 smallestNumOfSupportedCaps = numOfSupportedCaps; 1476 shouldUpdate = true; 1477 } 1478 if (shouldUpdate) { 1479 inUseLowestPriorityDrHandle = dr.getHandle(); 1480 } 1481 } 1482 } 1483 } 1484 } 1485 1486 // Grant demux when there is unused resource. 1487 if (grantingDemuxHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { 1488 demuxHandle[0] = grantingDemuxHandle; 1489 updateDemuxClientMappingOnNewGrant(grantingDemuxHandle, request.clientId); 1490 return true; 1491 } 1492 1493 // When all the resources are occupied, grant the lowest priority resource if the 1494 // request client has higher priority. 1495 if (inUseLowestPriorityDrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE 1496 && ((requestClient.getPriority() > currentLowestPriority) || ( 1497 (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { 1498 if (!reclaimResource( 1499 getDemuxResource(inUseLowestPriorityDrHandle).getOwnerClientId(), 1500 TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { 1501 return false; 1502 } 1503 demuxHandle[0] = inUseLowestPriorityDrHandle; 1504 updateDemuxClientMappingOnNewGrant( 1505 inUseLowestPriorityDrHandle, request.clientId); 1506 return true; 1507 } 1508 1509 return false; 1510 } 1511 1512 @VisibleForTesting 1513 // This mothod is to sync up the request/holder client's foreground/background status and update 1514 // the client priority accordingly whenever a new resource request comes in. clientPriorityUpdateOnRequest(ClientProfile profile)1515 protected void clientPriorityUpdateOnRequest(ClientProfile profile) { 1516 if (profile.isPriorityOverwritten()) { 1517 // To avoid overriding the priority set through updateClientPriority API. 1518 return; 1519 } 1520 int pid = profile.getProcessId(); 1521 boolean currentIsForeground = checkIsForeground(pid); 1522 profile.setPriority( 1523 getClientPriority(profile.getUseCase(), currentIsForeground)); 1524 } 1525 1526 @VisibleForTesting requestDescramblerInternal( TunerDescramblerRequest request, int[] descramblerHandle)1527 protected boolean requestDescramblerInternal( 1528 TunerDescramblerRequest request, int[] descramblerHandle) { 1529 if (DEBUG) { 1530 Slog.d(TAG, "requestDescrambler(request=" + request + ")"); 1531 } 1532 // There are enough Descrambler resources, so we don't manage Descrambler in R. 1533 descramblerHandle[0] = 1534 generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER, 0); 1535 return true; 1536 } 1537 1538 // Return value is guaranteed to be positive getElapsedTime(long begin)1539 private long getElapsedTime(long begin) { 1540 long now = SystemClock.uptimeMillis(); 1541 long elapsed; 1542 if (now >= begin) { 1543 elapsed = now - begin; 1544 } else { 1545 elapsed = Long.MAX_VALUE - begin + now; 1546 if (elapsed < 0) { 1547 elapsed = Long.MAX_VALUE; 1548 } 1549 } 1550 return elapsed; 1551 } 1552 lockForTunerApiLock(int clientId, long timeoutMS, String callerFunction)1553 private boolean lockForTunerApiLock(int clientId, long timeoutMS, String callerFunction) { 1554 try { 1555 if (mLockForTRMSLock.tryLock(timeoutMS, TimeUnit.MILLISECONDS)) { 1556 return true; 1557 } else { 1558 Slog.e(TAG, "FAILED to lock mLockForTRMSLock in " + callerFunction 1559 + ", clientId:" + clientId + ", timeoutMS:" + timeoutMS 1560 + ", mTunerApiLockHolder:" + mTunerApiLockHolder); 1561 return false; 1562 } 1563 } catch (InterruptedException ie) { 1564 Slog.e(TAG, "exception thrown in " + callerFunction + ":" + ie); 1565 if (mLockForTRMSLock.isHeldByCurrentThread()) { 1566 mLockForTRMSLock.unlock(); 1567 } 1568 return false; 1569 } 1570 } 1571 acquireLockInternal(int clientId, long clientThreadId, long timeoutMS)1572 private boolean acquireLockInternal(int clientId, long clientThreadId, long timeoutMS) { 1573 long begin = SystemClock.uptimeMillis(); 1574 1575 // Grab lock 1576 if (!lockForTunerApiLock(clientId, timeoutMS, "acquireLockInternal()")) { 1577 return false; 1578 } 1579 1580 try { 1581 boolean available = mTunerApiLockHolder == INVALID_CLIENT_ID; 1582 boolean nestedSelf = (clientId == mTunerApiLockHolder) 1583 && (clientThreadId == mTunerApiLockHolderThreadId); 1584 boolean recovery = false; 1585 1586 // Allow same thread to grab the lock multiple times 1587 while (!available && !nestedSelf) { 1588 // calculate how much time is left before timeout 1589 long leftOverMS = timeoutMS - getElapsedTime(begin); 1590 if (leftOverMS <= 0) { 1591 Slog.e(TAG, "FAILED:acquireLockInternal(" + clientId + ", " + clientThreadId 1592 + ", " + timeoutMS + ") - timed out, but will grant the lock to " 1593 + "the callee by stealing it from the current holder:" 1594 + mTunerApiLockHolder + "(" + mTunerApiLockHolderThreadId + "), " 1595 + "who likely failed to call releaseLock(), " 1596 + "to prevent this from becoming an unrecoverable error"); 1597 // This should not normally happen, but there sometimes are cases where 1598 // in-flight tuner API execution gets scheduled even after binderDied(), 1599 // which can leave the in-flight execution dissappear/stopped in between 1600 // acquireLock and releaseLock 1601 recovery = true; 1602 break; 1603 } 1604 1605 // Cond wait for left over time 1606 mTunerApiLockReleasedCV.await(leftOverMS, TimeUnit.MILLISECONDS); 1607 1608 // Check the availability for "spurious wakeup" 1609 // The case that was confirmed is that someone else can acquire this in between 1610 // signal() and wakup from the above await() 1611 available = mTunerApiLockHolder == INVALID_CLIENT_ID; 1612 1613 if (!available) { 1614 Slog.w(TAG, "acquireLockInternal(" + clientId + ", " + clientThreadId + ", " 1615 + timeoutMS + ") - woken up from cond wait, but " + mTunerApiLockHolder 1616 + "(" + mTunerApiLockHolderThreadId + ") is already holding the lock. " 1617 + "Going to wait again if timeout hasn't reached yet"); 1618 } 1619 } 1620 1621 // Will always grant unless exception is thrown (or lock is already held) 1622 if (available || recovery) { 1623 if (DEBUG) { 1624 Slog.d(TAG, "SUCCESS:acquireLockInternal(" + clientId + ", " + clientThreadId 1625 + ", " + timeoutMS + ")"); 1626 } 1627 1628 if (mTunerApiLockNestedCount != 0) { 1629 Slog.w(TAG, "Something is wrong as nestedCount(" + mTunerApiLockNestedCount 1630 + ") is not zero. Will overriding it to 1 anyways"); 1631 } 1632 1633 // set the caller to be the holder 1634 mTunerApiLockHolder = clientId; 1635 mTunerApiLockHolderThreadId = clientThreadId; 1636 mTunerApiLockNestedCount = 1; 1637 } else if (nestedSelf) { 1638 // Increment the nested count so releaseLockInternal won't signal prematuredly 1639 mTunerApiLockNestedCount++; 1640 if (DEBUG) { 1641 Slog.d(TAG, "acquireLockInternal(" + clientId + ", " + clientThreadId 1642 + ", " + timeoutMS + ") - nested count incremented to " 1643 + mTunerApiLockNestedCount); 1644 } 1645 } else { 1646 Slog.e(TAG, "acquireLockInternal(" + clientId + ", " + clientThreadId 1647 + ", " + timeoutMS + ") - should not reach here"); 1648 } 1649 // return true in "recovery" so callee knows that the deadlock is possible 1650 // only when the return value is false 1651 return (available || nestedSelf || recovery); 1652 } catch (InterruptedException ie) { 1653 Slog.e(TAG, "exception thrown in acquireLockInternal(" + clientId + ", " 1654 + clientThreadId + ", " + timeoutMS + "):" + ie); 1655 return false; 1656 } finally { 1657 if (mLockForTRMSLock.isHeldByCurrentThread()) { 1658 mLockForTRMSLock.unlock(); 1659 } 1660 } 1661 } 1662 releaseLockInternal(int clientId, long timeoutMS, boolean ignoreNestedCount, boolean suppressError)1663 private boolean releaseLockInternal(int clientId, long timeoutMS, 1664 boolean ignoreNestedCount, boolean suppressError) { 1665 // Grab lock first 1666 if (!lockForTunerApiLock(clientId, timeoutMS, "releaseLockInternal()")) { 1667 return false; 1668 } 1669 1670 try { 1671 if (mTunerApiLockHolder == clientId) { 1672 // Should always reach here unless called from binderDied() 1673 mTunerApiLockNestedCount--; 1674 if (ignoreNestedCount || mTunerApiLockNestedCount <= 0) { 1675 if (DEBUG) { 1676 Slog.d(TAG, "SUCCESS:releaseLockInternal(" + clientId + ", " + timeoutMS 1677 + ", " + ignoreNestedCount + ", " + suppressError 1678 + ") - signaling!"); 1679 } 1680 // Reset the current holder and signal 1681 mTunerApiLockHolder = INVALID_CLIENT_ID; 1682 mTunerApiLockHolderThreadId = INVALID_THREAD_ID; 1683 mTunerApiLockNestedCount = 0; 1684 mTunerApiLockReleasedCV.signal(); 1685 } else { 1686 if (DEBUG) { 1687 Slog.d(TAG, "releaseLockInternal(" + clientId + ", " + timeoutMS 1688 + ", " + ignoreNestedCount + ", " + suppressError 1689 + ") - NOT signaling because nested count is not zero (" 1690 + mTunerApiLockNestedCount + ")"); 1691 } 1692 } 1693 return true; 1694 } else if (mTunerApiLockHolder == INVALID_CLIENT_ID) { 1695 if (!suppressError) { 1696 Slog.w(TAG, "releaseLockInternal(" + clientId + ", " + timeoutMS 1697 + ") - called while there is no current holder"); 1698 } 1699 // No need to do anything. 1700 // Shouldn't reach here unless called from binderDied() 1701 return false; 1702 } else { 1703 if (!suppressError) { 1704 Slog.e(TAG, "releaseLockInternal(" + clientId + ", " + timeoutMS 1705 + ") - called while someone else:" + mTunerApiLockHolder 1706 + "is the current holder"); 1707 } 1708 // Cannot reset the holder Id because it reaches here when called 1709 // from binderDied() 1710 return false; 1711 } 1712 } finally { 1713 if (mLockForTRMSLock.isHeldByCurrentThread()) { 1714 mLockForTRMSLock.unlock(); 1715 } 1716 } 1717 } 1718 1719 @VisibleForTesting 1720 protected class ResourcesReclaimListenerRecord implements IBinder.DeathRecipient { 1721 private final IResourcesReclaimListener mListener; 1722 private final int mClientId; 1723 ResourcesReclaimListenerRecord(IResourcesReclaimListener listener, int clientId)1724 public ResourcesReclaimListenerRecord(IResourcesReclaimListener listener, int clientId) { 1725 mListener = listener; 1726 mClientId = clientId; 1727 } 1728 1729 @Override binderDied()1730 public void binderDied() { 1731 try { 1732 synchronized (mLock) { 1733 if (checkClientExists(mClientId)) { 1734 removeClientProfile(mClientId); 1735 } 1736 } 1737 } finally { 1738 // reset the tuner API lock 1739 releaseLockInternal(mClientId, TRMS_LOCK_TIMEOUT, true, true); 1740 } 1741 } 1742 getId()1743 public int getId() { 1744 return mClientId; 1745 } 1746 getListener()1747 public IResourcesReclaimListener getListener() { 1748 return mListener; 1749 } 1750 } 1751 addResourcesReclaimListener(int clientId, IResourcesReclaimListener listener)1752 private void addResourcesReclaimListener(int clientId, IResourcesReclaimListener listener) { 1753 if (listener == null) { 1754 if (DEBUG) { 1755 Slog.w(TAG, "Listener is null when client " + clientId + " registered!"); 1756 } 1757 return; 1758 } 1759 1760 ResourcesReclaimListenerRecord record = 1761 new ResourcesReclaimListenerRecord(listener, clientId); 1762 1763 try { 1764 listener.asBinder().linkToDeath(record, 0); 1765 } catch (RemoteException e) { 1766 Slog.w(TAG, "Listener already died."); 1767 return; 1768 } 1769 1770 mListeners.put(clientId, record); 1771 } 1772 1773 @VisibleForTesting reclaimResource(int reclaimingClientId, @TunerResourceManager.TunerResourceType int resourceType)1774 protected boolean reclaimResource(int reclaimingClientId, 1775 @TunerResourceManager.TunerResourceType int resourceType) { 1776 1777 // Allowing this because: 1778 // 1) serialization of resource reclaim is required in the current design 1779 // 2) the outgoing transaction is handled by the system app (with 1780 // android.Manifest.permission.TUNER_RESOURCE_ACCESS), which goes through full 1781 // Google certification 1782 Binder.allowBlockingForCurrentThread(); 1783 1784 // Reclaim all the resources of the share owners of the frontend that is used by the current 1785 // resource reclaimed client. 1786 ClientProfile profile = getClientProfile(reclaimingClientId); 1787 // TODO: check if this check is really needed. 1788 if (profile == null) { 1789 return true; 1790 } 1791 Set<Integer> shareFeClientIds = profile.getShareFeClientIds(); 1792 for (int clientId : shareFeClientIds) { 1793 try { 1794 mListeners.get(clientId).getListener().onReclaimResources(); 1795 } catch (RemoteException e) { 1796 Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e); 1797 return false; 1798 } 1799 clearAllResourcesAndClientMapping(getClientProfile(clientId)); 1800 } 1801 1802 if (DEBUG) { 1803 Slog.d(TAG, "Reclaiming resources because higher priority client request resource type " 1804 + resourceType + ", clientId:" + reclaimingClientId); 1805 } 1806 try { 1807 mListeners.get(reclaimingClientId).getListener().onReclaimResources(); 1808 } catch (RemoteException e) { 1809 Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e); 1810 return false; 1811 } 1812 clearAllResourcesAndClientMapping(profile); 1813 return true; 1814 } 1815 1816 @VisibleForTesting getClientPriority(int useCase, boolean isForeground)1817 protected int getClientPriority(int useCase, boolean isForeground) { 1818 if (DEBUG) { 1819 Slog.d(TAG, "getClientPriority useCase=" + useCase 1820 + ", isForeground=" + isForeground + ")"); 1821 } 1822 1823 if (isForeground) { 1824 return mPriorityCongfig.getForegroundPriority(useCase); 1825 } 1826 return mPriorityCongfig.getBackgroundPriority(useCase); 1827 } 1828 1829 @VisibleForTesting checkIsForeground(int pid)1830 protected boolean checkIsForeground(int pid) { 1831 if (mActivityManager == null) { 1832 return false; 1833 } 1834 List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses(); 1835 if (appProcesses == null) { 1836 return false; 1837 } 1838 for (RunningAppProcessInfo appProcess : appProcesses) { 1839 if (appProcess.pid == pid 1840 && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { 1841 return true; 1842 } 1843 } 1844 return false; 1845 } 1846 updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId)1847 private void updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { 1848 FrontendResource grantingFrontend = getFrontendResource(grantingHandle); 1849 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1850 grantingFrontend.setOwner(ownerClientId); 1851 increFrontendNum(mFrontendUsedNums, grantingFrontend.getType()); 1852 ownerProfile.useFrontend(grantingHandle); 1853 for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) { 1854 getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId); 1855 ownerProfile.useFrontend(exclusiveGroupMember); 1856 } 1857 ownerProfile.setPrimaryFrontend(grantingHandle); 1858 } 1859 updateDemuxClientMappingOnNewGrant(int grantingHandle, int ownerClientId)1860 private void updateDemuxClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { 1861 DemuxResource grantingDemux = getDemuxResource(grantingHandle); 1862 if (grantingDemux != null) { 1863 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1864 grantingDemux.setOwner(ownerClientId); 1865 ownerProfile.useDemux(grantingHandle); 1866 } 1867 } 1868 updateDemuxClientMappingOnRelease(@onNull DemuxResource releasingDemux)1869 private void updateDemuxClientMappingOnRelease(@NonNull DemuxResource releasingDemux) { 1870 ClientProfile ownerProfile = getClientProfile(releasingDemux.getOwnerClientId()); 1871 releasingDemux.removeOwner(); 1872 ownerProfile.releaseDemux(releasingDemux.getHandle()); 1873 } 1874 updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId)1875 private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { 1876 LnbResource grantingLnb = getLnbResource(grantingHandle); 1877 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1878 grantingLnb.setOwner(ownerClientId); 1879 ownerProfile.useLnb(grantingHandle); 1880 } 1881 updateLnbClientMappingOnRelease(@onNull LnbResource releasingLnb)1882 private void updateLnbClientMappingOnRelease(@NonNull LnbResource releasingLnb) { 1883 ClientProfile ownerProfile = getClientProfile(releasingLnb.getOwnerClientId()); 1884 releasingLnb.removeOwner(); 1885 ownerProfile.releaseLnb(releasingLnb.getHandle()); 1886 } 1887 updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId)1888 private void updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId) { 1889 CasResource grantingCas = getCasResource(grantingId); 1890 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1891 grantingCas.setOwner(ownerClientId); 1892 ownerProfile.useCas(grantingId); 1893 } 1894 updateCiCamClientMappingOnNewGrant(int grantingId, int ownerClientId)1895 private void updateCiCamClientMappingOnNewGrant(int grantingId, int ownerClientId) { 1896 CiCamResource grantingCiCam = getCiCamResource(grantingId); 1897 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1898 grantingCiCam.setOwner(ownerClientId); 1899 ownerProfile.useCiCam(grantingId); 1900 } 1901 updateCasClientMappingOnRelease( @onNull CasResource releasingCas, int ownerClientId)1902 private void updateCasClientMappingOnRelease( 1903 @NonNull CasResource releasingCas, int ownerClientId) { 1904 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1905 releasingCas.removeOwner(ownerClientId); 1906 ownerProfile.releaseCas(); 1907 } 1908 updateCiCamClientMappingOnRelease( @onNull CiCamResource releasingCiCam, int ownerClientId)1909 private void updateCiCamClientMappingOnRelease( 1910 @NonNull CiCamResource releasingCiCam, int ownerClientId) { 1911 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1912 releasingCiCam.removeOwner(ownerClientId); 1913 ownerProfile.releaseCiCam(); 1914 } 1915 1916 /** 1917 * Update and get the owner client's priority. 1918 * 1919 * @param clientId the owner client id. 1920 * @return the priority of the owner client. 1921 */ updateAndGetOwnerClientPriority(int clientId)1922 private int updateAndGetOwnerClientPriority(int clientId) { 1923 ClientProfile profile = getClientProfile(clientId); 1924 clientPriorityUpdateOnRequest(profile); 1925 return profile.getPriority(); 1926 } 1927 1928 /** 1929 * Update the owner and sharee clients' priority and get the highest priority 1930 * for frontend resource 1931 * 1932 * @param clientId the owner client id. 1933 * @return the highest priority among all the clients holding the same frontend resource. 1934 */ getFrontendHighestClientPriority(int clientId)1935 private int getFrontendHighestClientPriority(int clientId) { 1936 // Check if the owner profile exists 1937 ClientProfile ownerClient = getClientProfile(clientId); 1938 if (ownerClient == null) { 1939 return 0; 1940 } 1941 1942 // Update and get the priority of the owner client 1943 int highestPriority = updateAndGetOwnerClientPriority(clientId); 1944 1945 // Update and get all the client IDs of frontend resource holders 1946 for (int shareeId : ownerClient.getShareFeClientIds()) { 1947 int priority = updateAndGetOwnerClientPriority(shareeId); 1948 if (priority > highestPriority) { 1949 highestPriority = priority; 1950 } 1951 } 1952 return highestPriority; 1953 } 1954 1955 @VisibleForTesting 1956 @Nullable getFrontendResource(int frontendHandle)1957 protected FrontendResource getFrontendResource(int frontendHandle) { 1958 return mFrontendResources.get(frontendHandle); 1959 } 1960 1961 @VisibleForTesting getFrontendResources()1962 protected Map<Integer, FrontendResource> getFrontendResources() { 1963 return mFrontendResources; 1964 } 1965 1966 @VisibleForTesting 1967 @Nullable getDemuxResource(int demuxHandle)1968 protected DemuxResource getDemuxResource(int demuxHandle) { 1969 return mDemuxResources.get(demuxHandle); 1970 } 1971 1972 @VisibleForTesting getDemuxResources()1973 protected Map<Integer, DemuxResource> getDemuxResources() { 1974 return mDemuxResources; 1975 } 1976 setMaxNumberOfFrontendsInternal(int frontendType, int maxUsableNum)1977 private boolean setMaxNumberOfFrontendsInternal(int frontendType, int maxUsableNum) { 1978 int usedNum = mFrontendUsedNums.get(frontendType, INVALID_FE_COUNT); 1979 if (usedNum == INVALID_FE_COUNT || usedNum <= maxUsableNum) { 1980 mFrontendMaxUsableNums.put(frontendType, maxUsableNum); 1981 return true; 1982 } else { 1983 Slog.e(TAG, "max number of frontend for frontendType: " + frontendType 1984 + " cannot be set to a value lower than the current usage count." 1985 + " (requested max num = " + maxUsableNum + ", current usage = " + usedNum); 1986 return false; 1987 } 1988 } 1989 getMaxNumberOfFrontendsInternal(int frontendType)1990 private int getMaxNumberOfFrontendsInternal(int frontendType) { 1991 int existingNum = mFrontendExistingNums.get(frontendType, INVALID_FE_COUNT); 1992 if (existingNum == INVALID_FE_COUNT) { 1993 Log.e(TAG, "existingNum is -1 for " + frontendType); 1994 return -1; 1995 } 1996 int maxUsableNum = mFrontendMaxUsableNums.get(frontendType, INVALID_FE_COUNT); 1997 if (maxUsableNum == INVALID_FE_COUNT) { 1998 return existingNum; 1999 } else { 2000 return maxUsableNum; 2001 } 2002 } 2003 isFrontendMaxNumUseReached(int frontendType)2004 private boolean isFrontendMaxNumUseReached(int frontendType) { 2005 int maxUsableNum = mFrontendMaxUsableNums.get(frontendType, INVALID_FE_COUNT); 2006 if (maxUsableNum == INVALID_FE_COUNT) { 2007 return false; 2008 } 2009 int useNum = mFrontendUsedNums.get(frontendType, INVALID_FE_COUNT); 2010 if (useNum == INVALID_FE_COUNT) { 2011 useNum = 0; 2012 } 2013 return useNum >= maxUsableNum; 2014 } 2015 increFrontendNum(SparseIntArray targetNums, int frontendType)2016 private void increFrontendNum(SparseIntArray targetNums, int frontendType) { 2017 int num = targetNums.get(frontendType, INVALID_FE_COUNT); 2018 if (num == INVALID_FE_COUNT) { 2019 targetNums.put(frontendType, 1); 2020 } else { 2021 targetNums.put(frontendType, num + 1); 2022 } 2023 } 2024 decreFrontendNum(SparseIntArray targetNums, int frontendType)2025 private void decreFrontendNum(SparseIntArray targetNums, int frontendType) { 2026 int num = targetNums.get(frontendType, INVALID_FE_COUNT); 2027 if (num != INVALID_FE_COUNT) { 2028 targetNums.put(frontendType, num - 1); 2029 } 2030 } 2031 replaceFeResourceMap(Map<Integer, FrontendResource> srcMap, Map<Integer, FrontendResource> dstMap)2032 private void replaceFeResourceMap(Map<Integer, FrontendResource> srcMap, Map<Integer, 2033 FrontendResource> dstMap) { 2034 if (dstMap != null) { 2035 dstMap.clear(); 2036 if (srcMap != null && srcMap.size() > 0) { 2037 dstMap.putAll(srcMap); 2038 } 2039 } 2040 } 2041 replaceFeCounts(SparseIntArray srcCounts, SparseIntArray dstCounts)2042 private void replaceFeCounts(SparseIntArray srcCounts, SparseIntArray dstCounts) { 2043 if (dstCounts != null) { 2044 dstCounts.clear(); 2045 if (srcCounts != null) { 2046 for (int i = 0; i < srcCounts.size(); i++) { 2047 dstCounts.put(srcCounts.keyAt(i), srcCounts.valueAt(i)); 2048 } 2049 } 2050 } 2051 } dumpMap(Map<?, ?> targetMap, String headline, String delimiter, IndentingPrintWriter pw)2052 private void dumpMap(Map<?, ?> targetMap, String headline, String delimiter, 2053 IndentingPrintWriter pw) { 2054 if (targetMap != null) { 2055 pw.println(headline); 2056 pw.increaseIndent(); 2057 for (Map.Entry<?, ?> entry : targetMap.entrySet()) { 2058 pw.print(entry.getKey() + " : " + entry.getValue()); 2059 pw.print(delimiter); 2060 } 2061 pw.println(); 2062 pw.decreaseIndent(); 2063 } 2064 } 2065 dumpSIA(SparseIntArray array, String headline, String delimiter, IndentingPrintWriter pw)2066 private void dumpSIA(SparseIntArray array, String headline, String delimiter, 2067 IndentingPrintWriter pw) { 2068 if (array != null) { 2069 pw.println(headline); 2070 pw.increaseIndent(); 2071 for (int i = 0; i < array.size(); i++) { 2072 pw.print(array.keyAt(i) + " : " + array.valueAt(i)); 2073 pw.print(delimiter); 2074 } 2075 pw.println(); 2076 pw.decreaseIndent(); 2077 } 2078 } 2079 addFrontendResource(FrontendResource newFe)2080 private void addFrontendResource(FrontendResource newFe) { 2081 // Update the exclusive group member list in all the existing Frontend resource 2082 for (FrontendResource fe : getFrontendResources().values()) { 2083 if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) { 2084 newFe.addExclusiveGroupMemberFeHandle(fe.getHandle()); 2085 newFe.addExclusiveGroupMemberFeHandles(fe.getExclusiveGroupMemberFeHandles()); 2086 for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { 2087 getFrontendResource(excGroupmemberFeHandle) 2088 .addExclusiveGroupMemberFeHandle(newFe.getHandle()); 2089 } 2090 fe.addExclusiveGroupMemberFeHandle(newFe.getHandle()); 2091 break; 2092 } 2093 } 2094 // Update resource list and available id list 2095 mFrontendResources.put(newFe.getHandle(), newFe); 2096 increFrontendNum(mFrontendExistingNums, newFe.getType()); 2097 2098 } 2099 addDemuxResource(DemuxResource newDemux)2100 private void addDemuxResource(DemuxResource newDemux) { 2101 mDemuxResources.put(newDemux.getHandle(), newDemux); 2102 } 2103 removeFrontendResource(int removingHandle)2104 private void removeFrontendResource(int removingHandle) { 2105 FrontendResource fe = getFrontendResource(removingHandle); 2106 if (fe == null) { 2107 return; 2108 } 2109 if (fe.isInUse()) { 2110 ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId()); 2111 for (int shareOwnerId : ownerClient.getShareFeClientIds()) { 2112 clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); 2113 } 2114 clearFrontendAndClientMapping(ownerClient); 2115 } 2116 for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { 2117 getFrontendResource(excGroupmemberFeHandle) 2118 .removeExclusiveGroupMemberFeId(fe.getHandle()); 2119 } 2120 decreFrontendNum(mFrontendExistingNums, fe.getType()); 2121 mFrontendResources.remove(removingHandle); 2122 } 2123 removeDemuxResource(int removingHandle)2124 private void removeDemuxResource(int removingHandle) { 2125 DemuxResource demux = getDemuxResource(removingHandle); 2126 if (demux == null) { 2127 return; 2128 } 2129 if (demux.isInUse()) { 2130 releaseDemuxInternal(demux); 2131 } 2132 mDemuxResources.remove(removingHandle); 2133 } 2134 2135 @VisibleForTesting 2136 @Nullable getLnbResource(int lnbHandle)2137 protected LnbResource getLnbResource(int lnbHandle) { 2138 return mLnbResources.get(lnbHandle); 2139 } 2140 2141 @VisibleForTesting getLnbResources()2142 protected Map<Integer, LnbResource> getLnbResources() { 2143 return mLnbResources; 2144 } 2145 addLnbResource(LnbResource newLnb)2146 private void addLnbResource(LnbResource newLnb) { 2147 // Update resource list and available id list 2148 mLnbResources.put(newLnb.getHandle(), newLnb); 2149 } 2150 removeLnbResource(int removingHandle)2151 private void removeLnbResource(int removingHandle) { 2152 LnbResource lnb = getLnbResource(removingHandle); 2153 if (lnb == null) { 2154 return; 2155 } 2156 if (lnb.isInUse()) { 2157 releaseLnbInternal(lnb); 2158 } 2159 mLnbResources.remove(removingHandle); 2160 } 2161 2162 @VisibleForTesting 2163 @Nullable getCasResource(int systemId)2164 protected CasResource getCasResource(int systemId) { 2165 return mCasResources.get(systemId); 2166 } 2167 2168 @VisibleForTesting 2169 @Nullable getCiCamResource(int ciCamId)2170 protected CiCamResource getCiCamResource(int ciCamId) { 2171 return mCiCamResources.get(ciCamId); 2172 } 2173 2174 @VisibleForTesting getCasResources()2175 protected Map<Integer, CasResource> getCasResources() { 2176 return mCasResources; 2177 } 2178 2179 @VisibleForTesting getCiCamResources()2180 protected Map<Integer, CiCamResource> getCiCamResources() { 2181 return mCiCamResources; 2182 } 2183 addCasResource(CasResource newCas)2184 private void addCasResource(CasResource newCas) { 2185 // Update resource list and available id list 2186 mCasResources.put(newCas.getSystemId(), newCas); 2187 } 2188 addCiCamResource(CiCamResource newCiCam)2189 private void addCiCamResource(CiCamResource newCiCam) { 2190 // Update resource list and available id list 2191 mCiCamResources.put(newCiCam.getCiCamId(), newCiCam); 2192 } 2193 removeCasResource(int removingId)2194 private void removeCasResource(int removingId) { 2195 CasResource cas = getCasResource(removingId); 2196 if (cas == null) { 2197 return; 2198 } 2199 for (int ownerId : cas.getOwnerClientIds()) { 2200 getClientProfile(ownerId).releaseCas(); 2201 } 2202 mCasResources.remove(removingId); 2203 } 2204 removeCiCamResource(int removingId)2205 private void removeCiCamResource(int removingId) { 2206 CiCamResource ciCam = getCiCamResource(removingId); 2207 if (ciCam == null) { 2208 return; 2209 } 2210 for (int ownerId : ciCam.getOwnerClientIds()) { 2211 getClientProfile(ownerId).releaseCiCam(); 2212 } 2213 mCiCamResources.remove(removingId); 2214 } 2215 releaseLowerPriorityClientCasResources(int releasingCasResourceNum)2216 private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) { 2217 // TODO: Sort with a treemap 2218 2219 // select the first num client to release 2220 } 2221 2222 @VisibleForTesting 2223 @Nullable getClientProfile(int clientId)2224 protected ClientProfile getClientProfile(int clientId) { 2225 return mClientProfiles.get(clientId); 2226 } 2227 addClientProfile(int clientId, ClientProfile profile, IResourcesReclaimListener listener)2228 private void addClientProfile(int clientId, ClientProfile profile, 2229 IResourcesReclaimListener listener) { 2230 mClientProfiles.put(clientId, profile); 2231 addResourcesReclaimListener(clientId, listener); 2232 } 2233 removeClientProfile(int clientId)2234 private void removeClientProfile(int clientId) { 2235 for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) { 2236 clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); 2237 } 2238 clearAllResourcesAndClientMapping(getClientProfile(clientId)); 2239 mClientProfiles.remove(clientId); 2240 2241 // it may be called by unregisterClientProfileInternal under test 2242 synchronized (mLock) { 2243 ResourcesReclaimListenerRecord record = mListeners.remove(clientId); 2244 if (record != null) { 2245 record.getListener().asBinder().unlinkToDeath(record, 0); 2246 } 2247 } 2248 } 2249 clearFrontendAndClientMapping(ClientProfile profile)2250 private void clearFrontendAndClientMapping(ClientProfile profile) { 2251 // TODO: check if this check is really needed 2252 if (profile == null) { 2253 return; 2254 } 2255 for (Integer feId : profile.getInUseFrontendHandles()) { 2256 FrontendResource fe = getFrontendResource(feId); 2257 int ownerClientId = fe.getOwnerClientId(); 2258 if (ownerClientId == profile.getId()) { 2259 fe.removeOwner(); 2260 continue; 2261 } 2262 ClientProfile ownerClientProfile = getClientProfile(ownerClientId); 2263 if (ownerClientProfile != null) { 2264 ownerClientProfile.stopSharingFrontend(profile.getId()); 2265 } 2266 2267 } 2268 2269 int primaryFeId = profile.getPrimaryFrontend(); 2270 if (primaryFeId != TunerResourceManager.INVALID_RESOURCE_HANDLE) { 2271 FrontendResource primaryFe = getFrontendResource(primaryFeId); 2272 if (primaryFe != null) { 2273 decreFrontendNum(mFrontendUsedNums, primaryFe.getType()); 2274 } 2275 } 2276 2277 profile.releaseFrontend(); 2278 } 2279 clearAllResourcesAndClientMapping(ClientProfile profile)2280 private void clearAllResourcesAndClientMapping(ClientProfile profile) { 2281 // TODO: check if this check is really needed. Maybe needed for reclaimResource path. 2282 if (profile == null) { 2283 return; 2284 } 2285 // Clear Lnb 2286 for (Integer lnbHandle : profile.getInUseLnbHandles()) { 2287 getLnbResource(lnbHandle).removeOwner(); 2288 } 2289 // Clear Cas 2290 if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) { 2291 getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId()); 2292 } 2293 // Clear CiCam 2294 if (profile.getInUseCiCamId() != ClientProfile.INVALID_RESOURCE_ID) { 2295 getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId()); 2296 } 2297 // Clear Demux 2298 for (Integer demuxHandle : profile.getInUseDemuxHandles()) { 2299 getDemuxResource(demuxHandle).removeOwner(); 2300 } 2301 // Clear Frontend 2302 clearFrontendAndClientMapping(profile); 2303 profile.reclaimAllResources(); 2304 } 2305 2306 @VisibleForTesting checkClientExists(int clientId)2307 protected boolean checkClientExists(int clientId) { 2308 return mClientProfiles.keySet().contains(clientId); 2309 } 2310 generateResourceHandle( @unerResourceManager.TunerResourceType int resourceType, int resourceId)2311 private int generateResourceHandle( 2312 @TunerResourceManager.TunerResourceType int resourceType, int resourceId) { 2313 return (resourceType & 0x000000ff) << 24 2314 | (resourceId << 16) 2315 | (mResourceRequestCount++ & 0xffff); 2316 } 2317 2318 @VisibleForTesting getResourceIdFromHandle(int resourceHandle)2319 protected int getResourceIdFromHandle(int resourceHandle) { 2320 if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { 2321 return resourceHandle; 2322 } 2323 return (resourceHandle & 0x00ff0000) >> 16; 2324 } 2325 validateResourceHandle(int resourceType, int resourceHandle)2326 private boolean validateResourceHandle(int resourceType, int resourceHandle) { 2327 if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE 2328 || ((resourceHandle & 0xff000000) >> 24) != resourceType) { 2329 return false; 2330 } 2331 return true; 2332 } 2333 enforceTrmAccessPermission(String apiName)2334 private void enforceTrmAccessPermission(String apiName) { 2335 getContext().enforceCallingOrSelfPermission("android.permission.TUNER_RESOURCE_ACCESS", 2336 TAG + ": " + apiName); 2337 } 2338 enforceTunerAccessPermission(String apiName)2339 private void enforceTunerAccessPermission(String apiName) { 2340 getContext().enforceCallingPermission("android.permission.ACCESS_TV_TUNER", 2341 TAG + ": " + apiName); 2342 } 2343 enforceDescramblerAccessPermission(String apiName)2344 private void enforceDescramblerAccessPermission(String apiName) { 2345 getContext().enforceCallingPermission("android.permission.ACCESS_TV_DESCRAMBLER", 2346 TAG + ": " + apiName); 2347 } 2348 } 2349