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.media.IResourceManagerService; 25 import android.media.tv.TvInputManager; 26 import android.media.tv.tuner.TunerFrontendInfo; 27 import android.media.tv.tunerresourcemanager.CasSessionRequest; 28 import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; 29 import android.media.tv.tunerresourcemanager.ITunerResourceManager; 30 import android.media.tv.tunerresourcemanager.ResourceClientProfile; 31 import android.media.tv.tunerresourcemanager.TunerCiCamRequest; 32 import android.media.tv.tunerresourcemanager.TunerDemuxRequest; 33 import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; 34 import android.media.tv.tunerresourcemanager.TunerFrontendRequest; 35 import android.media.tv.tunerresourcemanager.TunerLnbRequest; 36 import android.media.tv.tunerresourcemanager.TunerResourceManager; 37 import android.os.Binder; 38 import android.os.IBinder; 39 import android.os.RemoteException; 40 import android.util.Log; 41 import android.util.Slog; 42 43 import com.android.internal.annotations.GuardedBy; 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.server.SystemService; 46 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Set; 52 53 /** 54 * This class provides a system service that manages the TV tuner resources. 55 * 56 * @hide 57 */ 58 public class TunerResourceManagerService extends SystemService implements IBinder.DeathRecipient { 59 private static final String TAG = "TunerResourceManagerService"; 60 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 61 62 public static final int INVALID_CLIENT_ID = -1; 63 private static final int MAX_CLIENT_PRIORITY = 1000; 64 65 // Map of the registered client profiles 66 private Map<Integer, ClientProfile> mClientProfiles = new HashMap<>(); 67 private int mNextUnusedClientId = 0; 68 69 // Map of the current available frontend resources 70 private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>(); 71 // Map of the current available lnb resources 72 private Map<Integer, LnbResource> mLnbResources = new HashMap<>(); 73 // Map of the current available Cas resources 74 private Map<Integer, CasResource> mCasResources = new HashMap<>(); 75 // Map of the current available CiCam resources 76 private Map<Integer, CiCamResource> mCiCamResources = new HashMap<>(); 77 78 @GuardedBy("mLock") 79 private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>(); 80 81 private TvInputManager mTvInputManager; 82 private ActivityManager mActivityManager; 83 private IResourceManagerService mMediaResourceManager; 84 private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints(); 85 86 // An internal resource request count to help generate resource handle. 87 private int mResourceRequestCount = 0; 88 89 // Used to synchronize the access to the service. 90 private final Object mLock = new Object(); 91 TunerResourceManagerService(@ullable Context context)92 public TunerResourceManagerService(@Nullable Context context) { 93 super(context); 94 } 95 96 @Override onStart()97 public void onStart() { 98 onStart(false /*isForTesting*/); 99 } 100 101 @VisibleForTesting onStart(boolean isForTesting)102 protected void onStart(boolean isForTesting) { 103 if (!isForTesting) { 104 publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); 105 } 106 mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); 107 mActivityManager = 108 (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); 109 mPriorityCongfig.parse(); 110 111 if (mMediaResourceManager == null) { 112 IBinder mediaResourceManagerBinder = getBinderService("media.resource_manager"); 113 if (mediaResourceManagerBinder == null) { 114 Slog.w(TAG, "Resource Manager Service not available."); 115 return; 116 } 117 try { 118 mediaResourceManagerBinder.linkToDeath(this, /*flags*/ 0); 119 } catch (RemoteException e) { 120 Slog.w(TAG, "Could not link to death of native resource manager service."); 121 return; 122 } 123 mMediaResourceManager = IResourceManagerService.Stub.asInterface( 124 mediaResourceManagerBinder); 125 } 126 } 127 128 private final class BinderService extends ITunerResourceManager.Stub { 129 @Override registerClientProfile(@onNull ResourceClientProfile profile, @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)130 public void registerClientProfile(@NonNull ResourceClientProfile profile, 131 @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId) 132 throws RemoteException { 133 enforceTrmAccessPermission("registerClientProfile"); 134 enforceTunerAccessPermission("registerClientProfile"); 135 if (profile == null) { 136 throw new RemoteException("ResourceClientProfile can't be null"); 137 } 138 139 if (clientId == null) { 140 throw new RemoteException("clientId can't be null!"); 141 } 142 143 if (listener == null) { 144 throw new RemoteException("IResourcesReclaimListener can't be null!"); 145 } 146 147 if (!mPriorityCongfig.isDefinedUseCase(profile.useCase)) { 148 throw new RemoteException("Use undefined client use case:" + profile.useCase); 149 } 150 151 synchronized (mLock) { 152 registerClientProfileInternal(profile, listener, clientId); 153 } 154 } 155 156 @Override unregisterClientProfile(int clientId)157 public void unregisterClientProfile(int clientId) throws RemoteException { 158 enforceTrmAccessPermission("unregisterClientProfile"); 159 synchronized (mLock) { 160 if (!checkClientExists(clientId)) { 161 Slog.e(TAG, "Unregistering non exists client:" + clientId); 162 return; 163 } 164 unregisterClientProfileInternal(clientId); 165 } 166 } 167 168 @Override updateClientPriority(int clientId, int priority, int niceValue)169 public boolean updateClientPriority(int clientId, int priority, int niceValue) { 170 enforceTrmAccessPermission("updateClientPriority"); 171 synchronized (mLock) { 172 return updateClientPriorityInternal(clientId, priority, niceValue); 173 } 174 } 175 176 @Override setFrontendInfoList(@onNull TunerFrontendInfo[] infos)177 public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException { 178 enforceTrmAccessPermission("setFrontendInfoList"); 179 if (infos == null) { 180 throw new RemoteException("TunerFrontendInfo can't be null"); 181 } 182 synchronized (mLock) { 183 setFrontendInfoListInternal(infos); 184 } 185 } 186 187 @Override updateCasInfo(int casSystemId, int maxSessionNum)188 public void updateCasInfo(int casSystemId, int maxSessionNum) { 189 enforceTrmAccessPermission("updateCasInfo"); 190 synchronized (mLock) { 191 updateCasInfoInternal(casSystemId, maxSessionNum); 192 } 193 } 194 195 @Override setLnbInfoList(int[] lnbHandles)196 public void setLnbInfoList(int[] lnbHandles) throws RemoteException { 197 enforceTrmAccessPermission("setLnbInfoList"); 198 if (lnbHandles == null) { 199 throw new RemoteException("Lnb handle list can't be null"); 200 } 201 synchronized (mLock) { 202 setLnbInfoListInternal(lnbHandles); 203 } 204 } 205 206 @Override requestFrontend(@onNull TunerFrontendRequest request, @NonNull int[] frontendHandle)207 public boolean requestFrontend(@NonNull TunerFrontendRequest request, 208 @NonNull int[] frontendHandle) throws RemoteException { 209 enforceTunerAccessPermission("requestFrontend"); 210 enforceTrmAccessPermission("requestFrontend"); 211 if (frontendHandle == null) { 212 throw new RemoteException("frontendHandle can't be null"); 213 } 214 synchronized (mLock) { 215 if (!checkClientExists(request.clientId)) { 216 throw new RemoteException("Request frontend from unregistered client: " 217 + request.clientId); 218 } 219 // If the request client is holding or sharing a frontend, throw an exception. 220 if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) { 221 throw new RemoteException("Release frontend before requesting another one. " 222 + "Client id: " + request.clientId); 223 } 224 return requestFrontendInternal(request, frontendHandle); 225 } 226 } 227 228 @Override shareFrontend(int selfClientId, int targetClientId)229 public void shareFrontend(int selfClientId, int targetClientId) throws RemoteException { 230 enforceTunerAccessPermission("shareFrontend"); 231 enforceTrmAccessPermission("shareFrontend"); 232 synchronized (mLock) { 233 if (!checkClientExists(selfClientId)) { 234 throw new RemoteException("Share frontend request from an unregistered client:" 235 + selfClientId); 236 } 237 if (!checkClientExists(targetClientId)) { 238 throw new RemoteException("Request to share frontend with an unregistered " 239 + "client:" + targetClientId); 240 } 241 if (getClientProfile(targetClientId).getInUseFrontendHandles().isEmpty()) { 242 throw new RemoteException("Request to share frontend with a client that has no " 243 + "frontend resources. Target client id:" + targetClientId); 244 } 245 shareFrontendInternal(selfClientId, targetClientId); 246 } 247 } 248 249 @Override requestDemux(@onNull TunerDemuxRequest request, @NonNull int[] demuxHandle)250 public boolean requestDemux(@NonNull TunerDemuxRequest request, 251 @NonNull int[] demuxHandle) throws RemoteException { 252 enforceTunerAccessPermission("requestDemux"); 253 enforceTrmAccessPermission("requestDemux"); 254 if (demuxHandle == null) { 255 throw new RemoteException("demuxHandle can't be null"); 256 } 257 synchronized (mLock) { 258 if (!checkClientExists(request.clientId)) { 259 throw new RemoteException("Request demux from unregistered client:" 260 + request.clientId); 261 } 262 return requestDemuxInternal(request, demuxHandle); 263 } 264 } 265 266 @Override requestDescrambler(@onNull TunerDescramblerRequest request, @NonNull int[] descramblerHandle)267 public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, 268 @NonNull int[] descramblerHandle) throws RemoteException { 269 enforceDescramblerAccessPermission("requestDescrambler"); 270 enforceTrmAccessPermission("requestDescrambler"); 271 if (descramblerHandle == null) { 272 throw new RemoteException("descramblerHandle can't be null"); 273 } 274 synchronized (mLock) { 275 if (!checkClientExists(request.clientId)) { 276 throw new RemoteException("Request descrambler from unregistered client:" 277 + request.clientId); 278 } 279 return requestDescramblerInternal(request, descramblerHandle); 280 } 281 } 282 283 @Override requestCasSession(@onNull CasSessionRequest request, @NonNull int[] casSessionHandle)284 public boolean requestCasSession(@NonNull CasSessionRequest request, 285 @NonNull int[] casSessionHandle) throws RemoteException { 286 enforceTrmAccessPermission("requestCasSession"); 287 if (casSessionHandle == null) { 288 throw new RemoteException("casSessionHandle can't be null"); 289 } 290 synchronized (mLock) { 291 if (!checkClientExists(request.clientId)) { 292 throw new RemoteException("Request cas from unregistered client:" 293 + request.clientId); 294 } 295 return requestCasSessionInternal(request, casSessionHandle); 296 } 297 } 298 299 @Override requestCiCam(@onNull TunerCiCamRequest request, @NonNull int[] ciCamHandle)300 public boolean requestCiCam(@NonNull TunerCiCamRequest request, 301 @NonNull int[] ciCamHandle) throws RemoteException { 302 enforceTrmAccessPermission("requestCiCam"); 303 if (ciCamHandle == null) { 304 throw new RemoteException("ciCamHandle can't be null"); 305 } 306 synchronized (mLock) { 307 if (!checkClientExists(request.clientId)) { 308 throw new RemoteException("Request ciCam from unregistered client:" 309 + request.clientId); 310 } 311 return requestCiCamInternal(request, ciCamHandle); 312 } 313 } 314 315 @Override requestLnb(@onNull TunerLnbRequest request, @NonNull int[] lnbHandle)316 public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) 317 throws RemoteException { 318 enforceTunerAccessPermission("requestLnb"); 319 enforceTrmAccessPermission("requestLnb"); 320 if (lnbHandle == null) { 321 throw new RemoteException("lnbHandle can't be null"); 322 } 323 synchronized (mLock) { 324 if (!checkClientExists(request.clientId)) { 325 throw new RemoteException("Request lnb from unregistered client:" 326 + request.clientId); 327 } 328 return requestLnbInternal(request, lnbHandle); 329 } 330 } 331 332 @Override releaseFrontend(int frontendHandle, int clientId)333 public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException { 334 enforceTunerAccessPermission("releaseFrontend"); 335 enforceTrmAccessPermission("releaseFrontend"); 336 if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, 337 frontendHandle)) { 338 throw new RemoteException("frontendHandle can't be invalid"); 339 } 340 synchronized (mLock) { 341 if (!checkClientExists(clientId)) { 342 throw new RemoteException("Release frontend from unregistered client:" 343 + clientId); 344 } 345 FrontendResource fe = getFrontendResource(frontendHandle); 346 if (fe == null) { 347 throw new RemoteException("Releasing frontend does not exist."); 348 } 349 if (fe.getOwnerClientId() != clientId) { 350 throw new RemoteException( 351 "Client is not the current owner of the releasing fe."); 352 } 353 releaseFrontendInternal(fe, clientId); 354 } 355 } 356 357 @Override releaseDemux(int demuxHandle, int clientId)358 public void releaseDemux(int demuxHandle, int clientId) { 359 enforceTunerAccessPermission("releaseDemux"); 360 enforceTrmAccessPermission("releaseDemux"); 361 if (DEBUG) { 362 Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")"); 363 } 364 } 365 366 @Override releaseDescrambler(int descramblerHandle, int clientId)367 public void releaseDescrambler(int descramblerHandle, int clientId) { 368 enforceTunerAccessPermission("releaseDescrambler"); 369 enforceTrmAccessPermission("releaseDescrambler"); 370 if (DEBUG) { 371 Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")"); 372 } 373 } 374 375 @Override releaseCasSession(int casSessionHandle, int clientId)376 public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException { 377 enforceTrmAccessPermission("releaseCasSession"); 378 if (!validateResourceHandle( 379 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) { 380 throw new RemoteException("casSessionHandle can't be invalid"); 381 } 382 synchronized (mLock) { 383 if (!checkClientExists(clientId)) { 384 throw new RemoteException("Release cas from unregistered client:" + clientId); 385 } 386 int casSystemId = getClientProfile(clientId).getInUseCasSystemId(); 387 CasResource cas = getCasResource(casSystemId); 388 if (cas == null) { 389 throw new RemoteException("Releasing cas does not exist."); 390 } 391 if (!cas.getOwnerClientIds().contains(clientId)) { 392 throw new RemoteException( 393 "Client is not the current owner of the releasing cas."); 394 } 395 releaseCasSessionInternal(cas, clientId); 396 } 397 } 398 399 @Override releaseCiCam(int ciCamHandle, int clientId)400 public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException { 401 enforceTrmAccessPermission("releaseCiCam"); 402 if (!validateResourceHandle( 403 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) { 404 throw new RemoteException("ciCamHandle can't be invalid"); 405 } 406 synchronized (mLock) { 407 if (!checkClientExists(clientId)) { 408 throw new RemoteException("Release ciCam from unregistered client:" + clientId); 409 } 410 int ciCamId = getClientProfile(clientId).getInUseCiCamId(); 411 if (ciCamId != getResourceIdFromHandle(ciCamHandle)) { 412 throw new RemoteException("The client " + clientId + " is not the owner of " 413 + "the releasing ciCam."); 414 } 415 CiCamResource ciCam = getCiCamResource(ciCamId); 416 if (ciCam == null) { 417 throw new RemoteException("Releasing ciCam does not exist."); 418 } 419 if (!ciCam.getOwnerClientIds().contains(clientId)) { 420 throw new RemoteException( 421 "Client is not the current owner of the releasing ciCam."); 422 } 423 releaseCiCamInternal(ciCam, clientId); 424 } 425 } 426 427 @Override releaseLnb(int lnbHandle, int clientId)428 public void releaseLnb(int lnbHandle, int clientId) throws RemoteException { 429 enforceTunerAccessPermission("releaseLnb"); 430 enforceTrmAccessPermission("releaseLnb"); 431 if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) { 432 throw new RemoteException("lnbHandle can't be invalid"); 433 } 434 if (!checkClientExists(clientId)) { 435 throw new RemoteException("Release lnb from unregistered client:" + clientId); 436 } 437 LnbResource lnb = getLnbResource(lnbHandle); 438 if (lnb == null) { 439 throw new RemoteException("Releasing lnb does not exist."); 440 } 441 if (lnb.getOwnerClientId() != clientId) { 442 throw new RemoteException("Client is not the current owner of the releasing lnb."); 443 } 444 synchronized (mLock) { 445 releaseLnbInternal(lnb); 446 } 447 } 448 449 @Override isHigherPriority( ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile)450 public boolean isHigherPriority( 451 ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) 452 throws RemoteException { 453 enforceTrmAccessPermission("isHigherPriority"); 454 if (challengerProfile == null || holderProfile == null) { 455 throw new RemoteException("Client profiles can't be null."); 456 } 457 synchronized (mLock) { 458 return isHigherPriorityInternal(challengerProfile, holderProfile); 459 } 460 } 461 } 462 463 /** 464 * Handle the death of the native resource manager service 465 */ 466 @Override binderDied()467 public void binderDied() { 468 if (DEBUG) { 469 Slog.w(TAG, "Native media resource manager service has died"); 470 } 471 synchronized (mLock) { 472 mMediaResourceManager = null; 473 } 474 } 475 476 @VisibleForTesting registerClientProfileInternal(ResourceClientProfile profile, IResourcesReclaimListener listener, int[] clientId)477 protected void registerClientProfileInternal(ResourceClientProfile profile, 478 IResourcesReclaimListener listener, int[] clientId) { 479 if (DEBUG) { 480 Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); 481 } 482 483 clientId[0] = INVALID_CLIENT_ID; 484 if (mTvInputManager == null) { 485 Slog.e(TAG, "TvInputManager is null. Can't register client profile."); 486 return; 487 } 488 // TODO tell if the client already exists 489 clientId[0] = mNextUnusedClientId++; 490 491 int pid = profile.tvInputSessionId == null 492 ? Binder.getCallingPid() /*callingPid*/ 493 : mTvInputManager.getClientPid(profile.tvInputSessionId); /*tvAppId*/ 494 495 // Update Media Resource Manager with the tvAppId 496 if (profile.tvInputSessionId != null && mMediaResourceManager != null) { 497 try { 498 mMediaResourceManager.overridePid(Binder.getCallingPid(), pid); 499 } catch (RemoteException e) { 500 Slog.e(TAG, "Could not overridePid in resourceManagerSercice," 501 + " remote exception: " + e); 502 } 503 } 504 505 ClientProfile clientProfile = new ClientProfile.Builder(clientId[0]) 506 .tvInputSessionId(profile.tvInputSessionId) 507 .useCase(profile.useCase) 508 .processId(pid) 509 .build(); 510 clientProfile.setForeground(checkIsForeground(pid)); 511 clientProfile.setPriority( 512 getClientPriority(profile.useCase, clientProfile.isForeground())); 513 514 addClientProfile(clientId[0], clientProfile, listener); 515 } 516 517 @VisibleForTesting unregisterClientProfileInternal(int clientId)518 protected void unregisterClientProfileInternal(int clientId) { 519 if (DEBUG) { 520 Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); 521 } 522 removeClientProfile(clientId); 523 // Remove the Media Resource Manager callingPid to tvAppId mapping 524 if (mMediaResourceManager != null) { 525 try { 526 mMediaResourceManager.overridePid(Binder.getCallingPid(), -1); 527 } catch (RemoteException e) { 528 Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister," 529 + " remote exception: " + e); 530 } 531 } 532 } 533 534 @VisibleForTesting updateClientPriorityInternal(int clientId, int priority, int niceValue)535 protected boolean updateClientPriorityInternal(int clientId, int priority, int niceValue) { 536 if (DEBUG) { 537 Slog.d(TAG, 538 "updateClientPriority(clientId=" + clientId + ", priority=" + priority 539 + ", niceValue=" + niceValue + ")"); 540 } 541 542 ClientProfile profile = getClientProfile(clientId); 543 if (profile == null) { 544 Slog.e(TAG, 545 "Can not find client profile with id " + clientId 546 + " when trying to update the client priority."); 547 return false; 548 } 549 550 profile.setForeground(checkIsForeground(profile.getProcessId())); 551 profile.setPriority(priority); 552 profile.setNiceValue(niceValue); 553 554 return true; 555 } 556 557 @VisibleForTesting setFrontendInfoListInternal(TunerFrontendInfo[] infos)558 protected void setFrontendInfoListInternal(TunerFrontendInfo[] infos) { 559 if (DEBUG) { 560 Slog.d(TAG, "updateFrontendInfo:"); 561 for (int i = 0; i < infos.length; i++) { 562 Slog.d(TAG, infos[i].toString()); 563 } 564 } 565 566 // A set to record the frontends pending on updating. Ids will be removed 567 // from this set once its updating finished. Any frontend left in this set when all 568 // the updates are done will be removed from mFrontendResources. 569 Set<Integer> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet()); 570 571 // Update frontendResources map and other mappings accordingly 572 for (int i = 0; i < infos.length; i++) { 573 if (getFrontendResource(infos[i].handle) != null) { 574 if (DEBUG) { 575 Slog.d(TAG, "Frontend handle=" + infos[i].handle + "exists."); 576 } 577 updatingFrontendHandles.remove(infos[i].handle); 578 } else { 579 // Add a new fe resource 580 FrontendResource newFe = new FrontendResource.Builder(infos[i].handle) 581 .type(infos[i].type) 582 .exclusiveGroupId(infos[i].exclusiveGroupId) 583 .build(); 584 addFrontendResource(newFe); 585 } 586 } 587 588 for (int removingHandle : updatingFrontendHandles) { 589 // update the exclusive group id member list 590 removeFrontendResource(removingHandle); 591 } 592 } 593 594 @VisibleForTesting setLnbInfoListInternal(int[] lnbHandles)595 protected void setLnbInfoListInternal(int[] lnbHandles) { 596 if (DEBUG) { 597 for (int i = 0; i < lnbHandles.length; i++) { 598 Slog.d(TAG, "updateLnbInfo(lnbHanle=" + lnbHandles[i] + ")"); 599 } 600 } 601 602 // A set to record the Lnbs pending on updating. Handles will be removed 603 // from this set once its updating finished. Any lnb left in this set when all 604 // the updates are done will be removed from mLnbResources. 605 Set<Integer> updatingLnbHandles = new HashSet<>(getLnbResources().keySet()); 606 607 // Update lnbResources map and other mappings accordingly 608 for (int i = 0; i < lnbHandles.length; i++) { 609 if (getLnbResource(lnbHandles[i]) != null) { 610 if (DEBUG) { 611 Slog.d(TAG, "Lnb handle=" + lnbHandles[i] + "exists."); 612 } 613 updatingLnbHandles.remove(lnbHandles[i]); 614 } else { 615 // Add a new lnb resource 616 LnbResource newLnb = new LnbResource.Builder(lnbHandles[i]).build(); 617 addLnbResource(newLnb); 618 } 619 } 620 621 for (int removingHandle : updatingLnbHandles) { 622 removeLnbResource(removingHandle); 623 } 624 } 625 626 @VisibleForTesting updateCasInfoInternal(int casSystemId, int maxSessionNum)627 protected void updateCasInfoInternal(int casSystemId, int maxSessionNum) { 628 if (DEBUG) { 629 Slog.d(TAG, 630 "updateCasInfo(casSystemId=" + casSystemId 631 + ", maxSessionNum=" + maxSessionNum + ")"); 632 } 633 // If maxSessionNum is 0, removing the Cas Resource. 634 if (maxSessionNum == 0) { 635 removeCasResource(casSystemId); 636 removeCiCamResource(casSystemId); 637 return; 638 } 639 // If the Cas exists, updates the Cas Resource accordingly. 640 CasResource cas = getCasResource(casSystemId); 641 CiCamResource ciCam = getCiCamResource(casSystemId); 642 if (cas != null) { 643 if (cas.getUsedSessionNum() > maxSessionNum) { 644 // Sort and release the short number of Cas resources. 645 int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum; 646 // TODO: handle CiCam session update. 647 } 648 cas.updateMaxSessionNum(maxSessionNum); 649 if (ciCam != null) { 650 ciCam.updateMaxSessionNum(maxSessionNum); 651 } 652 return; 653 } 654 // Add the new Cas Resource. 655 cas = new CasResource.Builder(casSystemId) 656 .maxSessionNum(maxSessionNum) 657 .build(); 658 ciCam = new CiCamResource.Builder(casSystemId) 659 .maxSessionNum(maxSessionNum) 660 .build(); 661 addCasResource(cas); 662 addCiCamResource(ciCam); 663 } 664 665 @VisibleForTesting requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle)666 protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) { 667 if (DEBUG) { 668 Slog.d(TAG, "requestFrontend(request=" + request + ")"); 669 } 670 671 frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 672 ClientProfile requestClient = getClientProfile(request.clientId); 673 if (requestClient == null) { 674 return false; 675 } 676 clientPriorityUpdateOnRequest(requestClient); 677 int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 678 int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 679 // Priority max value is 1000 680 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 681 for (FrontendResource fr : getFrontendResources().values()) { 682 if (fr.getType() == request.frontendType) { 683 if (!fr.isInUse()) { 684 // Grant unused frontend with no exclusive group members first. 685 if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) { 686 grantingFrontendHandle = fr.getHandle(); 687 break; 688 } else if (grantingFrontendHandle 689 == TunerResourceManager.INVALID_RESOURCE_HANDLE) { 690 // Grant the unused frontend with lower id first if all the unused 691 // frontends have exclusive group members. 692 grantingFrontendHandle = fr.getHandle(); 693 } 694 } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { 695 // Record the frontend id with the lowest client priority among all the 696 // in use frontends when no available frontend has been found. 697 int priority = getOwnerClientPriority(fr.getOwnerClientId()); 698 if (currentLowestPriority > priority) { 699 inUseLowestPriorityFrHandle = fr.getHandle(); 700 currentLowestPriority = priority; 701 } 702 } 703 } 704 } 705 706 // Grant frontend when there is unused resource. 707 if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { 708 frontendHandle[0] = grantingFrontendHandle; 709 updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId); 710 return true; 711 } 712 713 // When all the resources are occupied, grant the lowest priority resource if the 714 // request client has higher priority. 715 if (inUseLowestPriorityFrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE 716 && (requestClient.getPriority() > currentLowestPriority)) { 717 if (!reclaimResource( 718 getFrontendResource(inUseLowestPriorityFrHandle).getOwnerClientId(), 719 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { 720 return false; 721 } 722 frontendHandle[0] = inUseLowestPriorityFrHandle; 723 updateFrontendClientMappingOnNewGrant( 724 inUseLowestPriorityFrHandle, request.clientId); 725 return true; 726 } 727 728 return false; 729 } 730 731 @VisibleForTesting shareFrontendInternal(int selfClientId, int targetClientId)732 protected void shareFrontendInternal(int selfClientId, int targetClientId) { 733 if (DEBUG) { 734 Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId); 735 } 736 for (int feId : getClientProfile(targetClientId).getInUseFrontendHandles()) { 737 getClientProfile(selfClientId).useFrontend(feId); 738 } 739 getClientProfile(targetClientId).shareFrontend(selfClientId); 740 } 741 742 @VisibleForTesting requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)743 protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) { 744 if (DEBUG) { 745 Slog.d(TAG, "requestLnb(request=" + request + ")"); 746 } 747 748 lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 749 ClientProfile requestClient = getClientProfile(request.clientId); 750 clientPriorityUpdateOnRequest(requestClient); 751 int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 752 int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; 753 // Priority max value is 1000 754 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 755 for (LnbResource lnb : getLnbResources().values()) { 756 if (!lnb.isInUse()) { 757 // Grant the unused lnb with lower handle first 758 grantingLnbHandle = lnb.getHandle(); 759 break; 760 } else { 761 // Record the lnb id with the lowest client priority among all the 762 // in use lnb when no available lnb has been found. 763 int priority = getOwnerClientPriority(lnb.getOwnerClientId()); 764 if (currentLowestPriority > priority) { 765 inUseLowestPriorityLnbHandle = lnb.getHandle(); 766 currentLowestPriority = priority; 767 } 768 } 769 } 770 771 // Grant Lnb when there is unused resource. 772 if (grantingLnbHandle > -1) { 773 lnbHandle[0] = grantingLnbHandle; 774 updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId); 775 return true; 776 } 777 778 // When all the resources are occupied, grant the lowest priority resource if the 779 // request client has higher priority. 780 if (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE 781 && (requestClient.getPriority() > currentLowestPriority)) { 782 if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(), 783 TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) { 784 return false; 785 } 786 lnbHandle[0] = inUseLowestPriorityLnbHandle; 787 updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId); 788 return true; 789 } 790 791 return false; 792 } 793 794 @VisibleForTesting requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle)795 protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) { 796 if (DEBUG) { 797 Slog.d(TAG, "requestCasSession(request=" + request + ")"); 798 } 799 CasResource cas = getCasResource(request.casSystemId); 800 // Unregistered Cas System is treated as having unlimited sessions. 801 if (cas == null) { 802 cas = new CasResource.Builder(request.casSystemId) 803 .maxSessionNum(Integer.MAX_VALUE) 804 .build(); 805 addCasResource(cas); 806 } 807 casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 808 ClientProfile requestClient = getClientProfile(request.clientId); 809 clientPriorityUpdateOnRequest(requestClient); 810 int lowestPriorityOwnerId = -1; 811 // Priority max value is 1000 812 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 813 if (!cas.isFullyUsed()) { 814 casSessionHandle[0] = generateResourceHandle( 815 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); 816 updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); 817 return true; 818 } 819 for (int ownerId : cas.getOwnerClientIds()) { 820 // Record the client id with lowest priority that is using the current Cas system. 821 int priority = getOwnerClientPriority(ownerId); 822 if (currentLowestPriority > priority) { 823 lowestPriorityOwnerId = ownerId; 824 currentLowestPriority = priority; 825 } 826 } 827 828 // When all the Cas sessions are occupied, reclaim the lowest priority client if the 829 // request client has higher priority. 830 if (lowestPriorityOwnerId > -1 && (requestClient.getPriority() > currentLowestPriority)) { 831 if (!reclaimResource(lowestPriorityOwnerId, 832 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { 833 return false; 834 } 835 casSessionHandle[0] = generateResourceHandle( 836 TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); 837 updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); 838 return true; 839 } 840 return false; 841 } 842 843 @VisibleForTesting requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle)844 protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) { 845 if (DEBUG) { 846 Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")"); 847 } 848 CiCamResource ciCam = getCiCamResource(request.ciCamId); 849 // Unregistered Cas System is treated as having unlimited sessions. 850 if (ciCam == null) { 851 ciCam = new CiCamResource.Builder(request.ciCamId) 852 .maxSessionNum(Integer.MAX_VALUE) 853 .build(); 854 addCiCamResource(ciCam); 855 } 856 ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; 857 ClientProfile requestClient = getClientProfile(request.clientId); 858 clientPriorityUpdateOnRequest(requestClient); 859 int lowestPriorityOwnerId = -1; 860 // Priority max value is 1000 861 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; 862 if (!ciCam.isFullyUsed()) { 863 ciCamHandle[0] = generateResourceHandle( 864 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); 865 updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); 866 return true; 867 } 868 for (int ownerId : ciCam.getOwnerClientIds()) { 869 // Record the client id with lowest priority that is using the current Cas system. 870 int priority = getOwnerClientPriority(ownerId); 871 if (currentLowestPriority > priority) { 872 lowestPriorityOwnerId = ownerId; 873 currentLowestPriority = priority; 874 } 875 } 876 877 // When all the CiCam sessions are occupied, reclaim the lowest priority client if the 878 // request client has higher priority. 879 if (lowestPriorityOwnerId > -1 && (requestClient.getPriority() > currentLowestPriority)) { 880 if (!reclaimResource(lowestPriorityOwnerId, 881 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) { 882 return false; 883 } 884 ciCamHandle[0] = generateResourceHandle( 885 TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); 886 updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); 887 return true; 888 } 889 return false; 890 } 891 892 @VisibleForTesting isHigherPriorityInternal(ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile)893 protected boolean isHigherPriorityInternal(ResourceClientProfile challengerProfile, 894 ResourceClientProfile holderProfile) { 895 if (DEBUG) { 896 Slog.d(TAG, 897 "isHigherPriority(challengerProfile=" + challengerProfile 898 + ", holderProfile=" + challengerProfile + ")"); 899 } 900 if (mTvInputManager == null) { 901 Slog.e(TAG, "TvInputManager is null. Can't compare the priority."); 902 // Allow the client to acquire the hardware interface 903 // when the TRM is not able to compare the priority. 904 return true; 905 } 906 907 int challengerPid = challengerProfile.tvInputSessionId == null 908 ? Binder.getCallingPid() /*callingPid*/ 909 : mTvInputManager.getClientPid(challengerProfile.tvInputSessionId); /*tvAppId*/ 910 int holderPid = holderProfile.tvInputSessionId == null 911 ? Binder.getCallingPid() /*callingPid*/ 912 : mTvInputManager.getClientPid(holderProfile.tvInputSessionId); /*tvAppId*/ 913 914 int challengerPriority = getClientPriority( 915 challengerProfile.useCase, checkIsForeground(challengerPid)); 916 int holderPriority = getClientPriority(holderProfile.useCase, checkIsForeground(holderPid)); 917 return challengerPriority > holderPriority; 918 } 919 920 @VisibleForTesting releaseFrontendInternal(FrontendResource fe, int clientId)921 protected void releaseFrontendInternal(FrontendResource fe, int clientId) { 922 if (DEBUG) { 923 Slog.d(TAG, "releaseFrontend(id=" + fe.getHandle() + ", clientId=" + clientId + " )"); 924 } 925 if (clientId == fe.getOwnerClientId()) { 926 ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId()); 927 for (int shareOwnerId : ownerClient.getShareFeClientIds()) { 928 clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); 929 } 930 } 931 clearFrontendAndClientMapping(getClientProfile(clientId)); 932 } 933 934 @VisibleForTesting releaseLnbInternal(LnbResource lnb)935 protected void releaseLnbInternal(LnbResource lnb) { 936 if (DEBUG) { 937 Slog.d(TAG, "releaseLnb(lnbHandle=" + lnb.getHandle() + ")"); 938 } 939 updateLnbClientMappingOnRelease(lnb); 940 } 941 942 @VisibleForTesting releaseCasSessionInternal(CasResource cas, int ownerClientId)943 protected void releaseCasSessionInternal(CasResource cas, int ownerClientId) { 944 if (DEBUG) { 945 Slog.d(TAG, "releaseCasSession(sessionResourceId=" + cas.getSystemId() + ")"); 946 } 947 updateCasClientMappingOnRelease(cas, ownerClientId); 948 } 949 950 @VisibleForTesting releaseCiCamInternal(CiCamResource ciCam, int ownerClientId)951 protected void releaseCiCamInternal(CiCamResource ciCam, int ownerClientId) { 952 if (DEBUG) { 953 Slog.d(TAG, "releaseCiCamInternal(ciCamId=" + ciCam.getCiCamId() + ")"); 954 } 955 updateCiCamClientMappingOnRelease(ciCam, ownerClientId); 956 } 957 958 @VisibleForTesting requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle)959 protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { 960 if (DEBUG) { 961 Slog.d(TAG, "requestDemux(request=" + request + ")"); 962 } 963 // There are enough Demux resources, so we don't manage Demux in R. 964 demuxHandle[0] = generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0); 965 return true; 966 } 967 968 @VisibleForTesting 969 // This mothod is to sync up the request client's foreground/background status and update 970 // the client priority accordingly whenever new resource request comes in. clientPriorityUpdateOnRequest(ClientProfile requestProfile)971 protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) { 972 int pid = requestProfile.getProcessId(); 973 boolean currentIsForeground = checkIsForeground(pid); 974 if (requestProfile.isForeground() == currentIsForeground) { 975 // To avoid overriding the priority set through updateClientPriority API. 976 return; 977 } 978 requestProfile.setForeground(currentIsForeground); 979 requestProfile.setPriority( 980 getClientPriority(requestProfile.getUseCase(), currentIsForeground)); 981 } 982 983 @VisibleForTesting requestDescramblerInternal( TunerDescramblerRequest request, int[] descramblerHandle)984 protected boolean requestDescramblerInternal( 985 TunerDescramblerRequest request, int[] descramblerHandle) { 986 if (DEBUG) { 987 Slog.d(TAG, "requestDescrambler(request=" + request + ")"); 988 } 989 // There are enough Descrambler resources, so we don't manage Descrambler in R. 990 descramblerHandle[0] = 991 generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER, 0); 992 return true; 993 } 994 995 @VisibleForTesting 996 protected class ResourcesReclaimListenerRecord implements IBinder.DeathRecipient { 997 private final IResourcesReclaimListener mListener; 998 private final int mClientId; 999 ResourcesReclaimListenerRecord(IResourcesReclaimListener listener, int clientId)1000 public ResourcesReclaimListenerRecord(IResourcesReclaimListener listener, int clientId) { 1001 mListener = listener; 1002 mClientId = clientId; 1003 } 1004 1005 @Override binderDied()1006 public void binderDied() { 1007 synchronized (mLock) { 1008 removeClientProfile(mClientId); 1009 } 1010 } 1011 getId()1012 public int getId() { 1013 return mClientId; 1014 } 1015 getListener()1016 public IResourcesReclaimListener getListener() { 1017 return mListener; 1018 } 1019 } 1020 addResourcesReclaimListener(int clientId, IResourcesReclaimListener listener)1021 private void addResourcesReclaimListener(int clientId, IResourcesReclaimListener listener) { 1022 if (listener == null) { 1023 if (DEBUG) { 1024 Slog.w(TAG, "Listener is null when client " + clientId + " registered!"); 1025 } 1026 return; 1027 } 1028 1029 ResourcesReclaimListenerRecord record = 1030 new ResourcesReclaimListenerRecord(listener, clientId); 1031 1032 try { 1033 listener.asBinder().linkToDeath(record, 0); 1034 } catch (RemoteException e) { 1035 Slog.w(TAG, "Listener already died."); 1036 return; 1037 } 1038 1039 mListeners.put(clientId, record); 1040 } 1041 1042 @VisibleForTesting reclaimResource(int reclaimingClientId, @TunerResourceManager.TunerResourceType int resourceType)1043 protected boolean reclaimResource(int reclaimingClientId, 1044 @TunerResourceManager.TunerResourceType int resourceType) { 1045 if (DEBUG) { 1046 Slog.d(TAG, "Reclaiming resources because higher priority client request resource type " 1047 + resourceType); 1048 } 1049 try { 1050 mListeners.get(reclaimingClientId).getListener().onReclaimResources(); 1051 } catch (RemoteException e) { 1052 Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e); 1053 return false; 1054 } 1055 1056 // Reclaim all the resources of the share owners of the frontend that is used by the current 1057 // resource reclaimed client. 1058 ClientProfile profile = getClientProfile(reclaimingClientId); 1059 Set<Integer> shareFeClientIds = profile.getShareFeClientIds(); 1060 for (int clientId : shareFeClientIds) { 1061 try { 1062 mListeners.get(clientId).getListener().onReclaimResources(); 1063 } catch (RemoteException e) { 1064 Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e); 1065 return false; 1066 } 1067 clearAllResourcesAndClientMapping(getClientProfile(clientId)); 1068 } 1069 clearAllResourcesAndClientMapping(profile); 1070 return true; 1071 } 1072 1073 @VisibleForTesting getClientPriority(int useCase, boolean isForeground)1074 protected int getClientPriority(int useCase, boolean isForeground) { 1075 if (DEBUG) { 1076 Slog.d(TAG, "getClientPriority useCase=" + useCase 1077 + ", isForeground=" + isForeground + ")"); 1078 } 1079 1080 if (isForeground) { 1081 return mPriorityCongfig.getForegroundPriority(useCase); 1082 } 1083 return mPriorityCongfig.getBackgroundPriority(useCase); 1084 } 1085 1086 @VisibleForTesting checkIsForeground(int pid)1087 protected boolean checkIsForeground(int pid) { 1088 if (mActivityManager == null) { 1089 return false; 1090 } 1091 List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses(); 1092 if (appProcesses == null) { 1093 return false; 1094 } 1095 for (RunningAppProcessInfo appProcess : appProcesses) { 1096 if (appProcess.pid == pid 1097 && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { 1098 return true; 1099 } 1100 } 1101 return false; 1102 } 1103 updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId)1104 private void updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { 1105 FrontendResource grantingFrontend = getFrontendResource(grantingHandle); 1106 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1107 grantingFrontend.setOwner(ownerClientId); 1108 ownerProfile.useFrontend(grantingHandle); 1109 for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) { 1110 getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId); 1111 ownerProfile.useFrontend(exclusiveGroupMember); 1112 } 1113 } 1114 updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId)1115 private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { 1116 LnbResource grantingLnb = getLnbResource(grantingHandle); 1117 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1118 grantingLnb.setOwner(ownerClientId); 1119 ownerProfile.useLnb(grantingHandle); 1120 } 1121 updateLnbClientMappingOnRelease(@onNull LnbResource releasingLnb)1122 private void updateLnbClientMappingOnRelease(@NonNull LnbResource releasingLnb) { 1123 ClientProfile ownerProfile = getClientProfile(releasingLnb.getOwnerClientId()); 1124 releasingLnb.removeOwner(); 1125 ownerProfile.releaseLnb(releasingLnb.getHandle()); 1126 } 1127 updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId)1128 private void updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId) { 1129 CasResource grantingCas = getCasResource(grantingId); 1130 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1131 grantingCas.setOwner(ownerClientId); 1132 ownerProfile.useCas(grantingId); 1133 } 1134 updateCiCamClientMappingOnNewGrant(int grantingId, int ownerClientId)1135 private void updateCiCamClientMappingOnNewGrant(int grantingId, int ownerClientId) { 1136 CiCamResource grantingCiCam = getCiCamResource(grantingId); 1137 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1138 grantingCiCam.setOwner(ownerClientId); 1139 ownerProfile.useCiCam(grantingId); 1140 } 1141 updateCasClientMappingOnRelease( @onNull CasResource releasingCas, int ownerClientId)1142 private void updateCasClientMappingOnRelease( 1143 @NonNull CasResource releasingCas, int ownerClientId) { 1144 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1145 releasingCas.removeOwner(ownerClientId); 1146 ownerProfile.releaseCas(); 1147 } 1148 updateCiCamClientMappingOnRelease( @onNull CiCamResource releasingCiCam, int ownerClientId)1149 private void updateCiCamClientMappingOnRelease( 1150 @NonNull CiCamResource releasingCiCam, int ownerClientId) { 1151 ClientProfile ownerProfile = getClientProfile(ownerClientId); 1152 releasingCiCam.removeOwner(ownerClientId); 1153 ownerProfile.releaseCiCam(); 1154 } 1155 1156 /** 1157 * Get the owner client's priority. 1158 * 1159 * @param clientId the owner client id. 1160 * @return the priority of the owner client. 1161 */ getOwnerClientPriority(int clientId)1162 private int getOwnerClientPriority(int clientId) { 1163 return getClientProfile(clientId).getPriority(); 1164 } 1165 1166 @VisibleForTesting 1167 @Nullable getFrontendResource(int frontendHandle)1168 protected FrontendResource getFrontendResource(int frontendHandle) { 1169 return mFrontendResources.get(frontendHandle); 1170 } 1171 1172 @VisibleForTesting getFrontendResources()1173 protected Map<Integer, FrontendResource> getFrontendResources() { 1174 return mFrontendResources; 1175 } 1176 addFrontendResource(FrontendResource newFe)1177 private void addFrontendResource(FrontendResource newFe) { 1178 // Update the exclusive group member list in all the existing Frontend resource 1179 for (FrontendResource fe : getFrontendResources().values()) { 1180 if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) { 1181 newFe.addExclusiveGroupMemberFeHandle(fe.getHandle()); 1182 newFe.addExclusiveGroupMemberFeHandles(fe.getExclusiveGroupMemberFeHandles()); 1183 for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { 1184 getFrontendResource(excGroupmemberFeHandle) 1185 .addExclusiveGroupMemberFeHandle(newFe.getHandle()); 1186 } 1187 fe.addExclusiveGroupMemberFeHandle(newFe.getHandle()); 1188 break; 1189 } 1190 } 1191 // Update resource list and available id list 1192 mFrontendResources.put(newFe.getHandle(), newFe); 1193 } 1194 removeFrontendResource(int removingHandle)1195 private void removeFrontendResource(int removingHandle) { 1196 FrontendResource fe = getFrontendResource(removingHandle); 1197 if (fe == null) { 1198 return; 1199 } 1200 if (fe.isInUse()) { 1201 ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId()); 1202 for (int shareOwnerId : ownerClient.getShareFeClientIds()) { 1203 clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); 1204 } 1205 clearFrontendAndClientMapping(ownerClient); 1206 } 1207 for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { 1208 getFrontendResource(excGroupmemberFeHandle) 1209 .removeExclusiveGroupMemberFeId(fe.getHandle()); 1210 } 1211 mFrontendResources.remove(removingHandle); 1212 } 1213 1214 @VisibleForTesting 1215 @Nullable getLnbResource(int lnbHandle)1216 protected LnbResource getLnbResource(int lnbHandle) { 1217 return mLnbResources.get(lnbHandle); 1218 } 1219 1220 @VisibleForTesting getLnbResources()1221 protected Map<Integer, LnbResource> getLnbResources() { 1222 return mLnbResources; 1223 } 1224 addLnbResource(LnbResource newLnb)1225 private void addLnbResource(LnbResource newLnb) { 1226 // Update resource list and available id list 1227 mLnbResources.put(newLnb.getHandle(), newLnb); 1228 } 1229 removeLnbResource(int removingHandle)1230 private void removeLnbResource(int removingHandle) { 1231 LnbResource lnb = getLnbResource(removingHandle); 1232 if (lnb == null) { 1233 return; 1234 } 1235 if (lnb.isInUse()) { 1236 releaseLnbInternal(lnb); 1237 } 1238 mLnbResources.remove(removingHandle); 1239 } 1240 1241 @VisibleForTesting 1242 @Nullable getCasResource(int systemId)1243 protected CasResource getCasResource(int systemId) { 1244 return mCasResources.get(systemId); 1245 } 1246 1247 @VisibleForTesting 1248 @Nullable getCiCamResource(int ciCamId)1249 protected CiCamResource getCiCamResource(int ciCamId) { 1250 return mCiCamResources.get(ciCamId); 1251 } 1252 1253 @VisibleForTesting getCasResources()1254 protected Map<Integer, CasResource> getCasResources() { 1255 return mCasResources; 1256 } 1257 1258 @VisibleForTesting getCiCamResources()1259 protected Map<Integer, CiCamResource> getCiCamResources() { 1260 return mCiCamResources; 1261 } 1262 addCasResource(CasResource newCas)1263 private void addCasResource(CasResource newCas) { 1264 // Update resource list and available id list 1265 mCasResources.put(newCas.getSystemId(), newCas); 1266 } 1267 addCiCamResource(CiCamResource newCiCam)1268 private void addCiCamResource(CiCamResource newCiCam) { 1269 // Update resource list and available id list 1270 mCiCamResources.put(newCiCam.getCiCamId(), newCiCam); 1271 } 1272 removeCasResource(int removingId)1273 private void removeCasResource(int removingId) { 1274 CasResource cas = getCasResource(removingId); 1275 if (cas == null) { 1276 return; 1277 } 1278 for (int ownerId : cas.getOwnerClientIds()) { 1279 getClientProfile(ownerId).releaseCas(); 1280 } 1281 mCasResources.remove(removingId); 1282 } 1283 removeCiCamResource(int removingId)1284 private void removeCiCamResource(int removingId) { 1285 CiCamResource ciCam = getCiCamResource(removingId); 1286 if (ciCam == null) { 1287 return; 1288 } 1289 for (int ownerId : ciCam.getOwnerClientIds()) { 1290 getClientProfile(ownerId).releaseCiCam(); 1291 } 1292 mCiCamResources.remove(removingId); 1293 } 1294 releaseLowerPriorityClientCasResources(int releasingCasResourceNum)1295 private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) { 1296 // TODO: Sort with a treemap 1297 1298 // select the first num client to release 1299 } 1300 1301 @VisibleForTesting 1302 @Nullable getClientProfile(int clientId)1303 protected ClientProfile getClientProfile(int clientId) { 1304 return mClientProfiles.get(clientId); 1305 } 1306 addClientProfile(int clientId, ClientProfile profile, IResourcesReclaimListener listener)1307 private void addClientProfile(int clientId, ClientProfile profile, 1308 IResourcesReclaimListener listener) { 1309 mClientProfiles.put(clientId, profile); 1310 addResourcesReclaimListener(clientId, listener); 1311 } 1312 removeClientProfile(int clientId)1313 private void removeClientProfile(int clientId) { 1314 for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) { 1315 clearFrontendAndClientMapping(getClientProfile(shareOwnerId)); 1316 } 1317 clearAllResourcesAndClientMapping(getClientProfile(clientId)); 1318 mClientProfiles.remove(clientId); 1319 mListeners.remove(clientId); 1320 } 1321 clearFrontendAndClientMapping(ClientProfile profile)1322 private void clearFrontendAndClientMapping(ClientProfile profile) { 1323 for (Integer feId : profile.getInUseFrontendHandles()) { 1324 FrontendResource fe = getFrontendResource(feId); 1325 if (fe.getOwnerClientId() == profile.getId()) { 1326 fe.removeOwner(); 1327 continue; 1328 } 1329 getClientProfile(fe.getOwnerClientId()).stopSharingFrontend(profile.getId()); 1330 } 1331 profile.releaseFrontend(); 1332 } 1333 clearAllResourcesAndClientMapping(ClientProfile profile)1334 private void clearAllResourcesAndClientMapping(ClientProfile profile) { 1335 // Clear Lnb 1336 for (Integer lnbHandle : profile.getInUseLnbHandles()) { 1337 getLnbResource(lnbHandle).removeOwner(); 1338 } 1339 // Clear Cas 1340 if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) { 1341 getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId()); 1342 } 1343 // Clear CiCam 1344 if (profile.getInUseCiCamId() != ClientProfile.INVALID_RESOURCE_ID) { 1345 getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId()); 1346 } 1347 // Clear Frontend 1348 clearFrontendAndClientMapping(profile); 1349 profile.reclaimAllResources(); 1350 } 1351 1352 @VisibleForTesting checkClientExists(int clientId)1353 protected boolean checkClientExists(int clientId) { 1354 return mClientProfiles.keySet().contains(clientId); 1355 } 1356 generateResourceHandle( @unerResourceManager.TunerResourceType int resourceType, int resourceId)1357 private int generateResourceHandle( 1358 @TunerResourceManager.TunerResourceType int resourceType, int resourceId) { 1359 return (resourceType & 0x000000ff) << 24 1360 | (resourceId << 16) 1361 | (mResourceRequestCount++ & 0xffff); 1362 } 1363 1364 @VisibleForTesting getResourceIdFromHandle(int resourceHandle)1365 protected int getResourceIdFromHandle(int resourceHandle) { 1366 if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { 1367 return resourceHandle; 1368 } 1369 return (resourceHandle & 0x00ff0000) >> 16; 1370 } 1371 validateResourceHandle(int resourceType, int resourceHandle)1372 private boolean validateResourceHandle(int resourceType, int resourceHandle) { 1373 if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE 1374 || ((resourceHandle & 0xff000000) >> 24) != resourceType) { 1375 return false; 1376 } 1377 return true; 1378 } 1379 enforceTrmAccessPermission(String apiName)1380 private void enforceTrmAccessPermission(String apiName) { 1381 getContext().enforceCallingOrSelfPermission("android.permission.TUNER_RESOURCE_ACCESS", 1382 TAG + ": " + apiName); 1383 } 1384 enforceTunerAccessPermission(String apiName)1385 private void enforceTunerAccessPermission(String apiName) { 1386 getContext().enforceCallingPermission("android.permission.ACCESS_TV_TUNER", 1387 TAG + ": " + apiName); 1388 } 1389 enforceDescramblerAccessPermission(String apiName)1390 private void enforceDescramblerAccessPermission(String apiName) { 1391 getContext().enforceCallingPermission("android.permission.ACCESS_TV_DESCRAMBLER", 1392 TAG + ": " + apiName); 1393 } 1394 } 1395