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 android.media.tv.tunerresourcemanager; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresFeature; 24 import android.annotation.SystemService; 25 import android.content.Context; 26 import android.content.pm.PackageManager; 27 import android.media.tv.tuner.TunerFrontendInfo; 28 import android.os.Binder; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.concurrent.Executor; 35 36 /** 37 * Interface of the Tuner Resource Manager(TRM). It manages resources used by TV Tuners. 38 * <p>Resources include: 39 * <ul> 40 * <li>TunerFrontend {@link android.media.tv.tuner.frontend}. 41 * <li>TunerLnb {@link android.media.tv.tuner.Lnb}. 42 * <li>MediaCas {@link android.media.MediaCas}. 43 * <ul> 44 * 45 * <p>Expected workflow is: 46 * <ul> 47 * <li>Tuner Java/MediaCas/TIF update resources of the current device with TRM. 48 * <li>Client registers its profile through {@link #registerClientProfile(ResourceClientProfile, 49 * Executor, ResourcesReclaimListener, int[])}. 50 * <li>Client requests resources through request APIs. 51 * <li>If the resource needs to be handed to a higher priority client from a lower priority 52 * one, TRM calls IResourcesReclaimListener registered by the lower priority client to release 53 * the resource. 54 * <ul> 55 * 56 * <p>TRM also exposes its priority comparison algorithm as a helping method to other services. 57 * {@see #isHigherPriority(ResourceClientProfile, ResourceClientProfile)}. 58 * 59 * @hide 60 */ 61 @RequiresFeature(PackageManager.FEATURE_LIVE_TV) 62 @SystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE) 63 public class TunerResourceManager { 64 private static final String TAG = "TunerResourceManager"; 65 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 66 67 public static final int INVALID_RESOURCE_HANDLE = -1; 68 public static final int INVALID_OWNER_ID = -1; 69 /** 70 * Tuner resource type to help generate resource handle 71 */ 72 @IntDef({ 73 TUNER_RESOURCE_TYPE_FRONTEND, 74 TUNER_RESOURCE_TYPE_DEMUX, 75 TUNER_RESOURCE_TYPE_DESCRAMBLER, 76 TUNER_RESOURCE_TYPE_LNB, 77 TUNER_RESOURCE_TYPE_CAS_SESSION, 78 TUNER_RESOURCE_TYPE_FRONTEND_CICAM, 79 TUNER_RESOURCE_TYPE_MAX, 80 }) 81 @Retention(RetentionPolicy.SOURCE) 82 public @interface TunerResourceType {} 83 84 public static final int TUNER_RESOURCE_TYPE_FRONTEND = 0; 85 public static final int TUNER_RESOURCE_TYPE_DEMUX = 1; 86 public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2; 87 public static final int TUNER_RESOURCE_TYPE_LNB = 3; 88 public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4; 89 public static final int TUNER_RESOURCE_TYPE_FRONTEND_CICAM = 5; 90 public static final int TUNER_RESOURCE_TYPE_MAX = 6; 91 92 private final ITunerResourceManager mService; 93 private final int mUserId; 94 95 /** 96 * @hide 97 */ TunerResourceManager(ITunerResourceManager service, int userId)98 public TunerResourceManager(ITunerResourceManager service, int userId) { 99 mService = service; 100 mUserId = userId; 101 } 102 103 /** 104 * This API is used by the client to register their profile with the Tuner Resource manager. 105 * 106 * <p>The profile contains information that can show the base priority score of the client. 107 * 108 * @param profile {@link ResourceClientProfile} profile of the current client. Undefined use 109 * case would cause IllegalArgumentException. 110 * @param executor the executor on which the listener would be invoked. 111 * @param listener {@link ResourcesReclaimListener} callback to reclaim clients' resources when 112 * needed. 113 * @param clientId returned a clientId from the resource manager when the 114 * the client registeres. 115 * @throws IllegalArgumentException when {@code profile} contains undefined use case. 116 */ registerClientProfile(@onNull ResourceClientProfile profile, @NonNull @CallbackExecutor Executor executor, @NonNull ResourcesReclaimListener listener, @NonNull int[] clientId)117 public void registerClientProfile(@NonNull ResourceClientProfile profile, 118 @NonNull @CallbackExecutor Executor executor, 119 @NonNull ResourcesReclaimListener listener, 120 @NonNull int[] clientId) { 121 // TODO: throw new IllegalArgumentException("Unknown client use case") 122 // when the use case is not defined. 123 try { 124 mService.registerClientProfile(profile, 125 new IResourcesReclaimListener.Stub() { 126 @Override 127 public void onReclaimResources() { 128 final long identity = Binder.clearCallingIdentity(); 129 try { 130 executor.execute(() -> listener.onReclaimResources()); 131 } finally { 132 Binder.restoreCallingIdentity(identity); 133 } 134 } 135 }, clientId); 136 } catch (RemoteException e) { 137 throw e.rethrowFromSystemServer(); 138 } 139 } 140 141 /** 142 * This API is used by the client to unregister their profile with the 143 * Tuner Resource manager. 144 * 145 * @param clientId the client id that needs to be unregistered. 146 */ unregisterClientProfile(int clientId)147 public void unregisterClientProfile(int clientId) { 148 try { 149 mService.unregisterClientProfile(clientId); 150 } catch (RemoteException e) { 151 throw e.rethrowFromSystemServer(); 152 } 153 } 154 155 /** 156 * This API is used by client to update its registered {@link ResourceClientProfile}. 157 * 158 * <p>We recommend creating a new tuner instance for different use cases instead of using this 159 * API since different use cases may need different resources. 160 * 161 * <p>If TIS updates use case, it needs to ensure underneath resources are exchangeable between 162 * two different use cases. 163 * 164 * <p>Only the arbitrary priority and niceValue are allowed to be updated. 165 * 166 * @param clientId the id of the client that is updating its profile. 167 * @param priority the priority that the client would like to update to. 168 * @param niceValue the nice value that the client would like to update to. 169 * 170 * @return true if the update is successful. 171 */ updateClientPriority(int clientId, int priority, int niceValue)172 public boolean updateClientPriority(int clientId, int priority, int niceValue) { 173 boolean result = false; 174 try { 175 result = mService.updateClientPriority(clientId, priority, niceValue); 176 } catch (RemoteException e) { 177 throw e.rethrowFromSystemServer(); 178 } 179 return result; 180 } 181 182 /** 183 * Updates the current TRM of the TunerHAL Frontend information. 184 * 185 * <p><strong>Note:</strong> This update must happen before the first 186 * {@link #requestFrontend(TunerFrontendRequest, int[])} and 187 * {@link #releaseFrontend(int, int)} call. 188 * 189 * @param infos an array of the available {@link TunerFrontendInfo} information. 190 */ setFrontendInfoList(@onNull TunerFrontendInfo[] infos)191 public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) { 192 try { 193 mService.setFrontendInfoList(infos); 194 } catch (RemoteException e) { 195 throw e.rethrowFromSystemServer(); 196 } 197 } 198 199 /** 200 * Updates the TRM of the current CAS information. 201 * 202 * <p><strong>Note:</strong> This update must happen before the first 203 * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int, int)} 204 * call. 205 * 206 * @param casSystemId id of the updating CAS system. 207 * @param maxSessionNum the max session number of the CAS system that is updated. 208 */ updateCasInfo(int casSystemId, int maxSessionNum)209 public void updateCasInfo(int casSystemId, int maxSessionNum) { 210 try { 211 mService.updateCasInfo(casSystemId, maxSessionNum); 212 } catch (RemoteException e) { 213 throw e.rethrowFromSystemServer(); 214 } 215 } 216 217 /** 218 * Updates the TRM of the current Lnb information. 219 * 220 * <p><strong>Note:</strong> This update must happen before the first 221 * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int, int)} call. 222 * 223 * @param lnbIds ids of the updating lnbs. 224 */ setLnbInfoList(int[] lnbIds)225 public void setLnbInfoList(int[] lnbIds) { 226 try { 227 mService.setLnbInfoList(lnbIds); 228 } catch (RemoteException e) { 229 throw e.rethrowFromSystemServer(); 230 } 231 } 232 233 /** 234 * Requests a frontend resource. 235 * 236 * <p>There are three possible scenarios: 237 * <ul> 238 * <li>If there is frontend available, the API would send the id back. 239 * 240 * <li>If no Frontend is available but the current request info can show higher priority than 241 * other uses of Frontend, the API will send 242 * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would 243 * handle the resource reclaim on the holder of lower priority and notify the holder of its 244 * resource loss. 245 * 246 * <li>If no frontend can be granted, the API would return false. 247 * <ul> 248 * 249 * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called 250 * before this request. 251 * 252 * @param request {@link TunerFrontendRequest} information of the current request. 253 * @param frontendHandle a one-element array to return the granted frontendHandle. If 254 * no frontend granted, this will return {@link #INVALID_RESOURCE_HANDLE}. 255 * 256 * @return true if there is frontend granted. 257 */ requestFrontend(@onNull TunerFrontendRequest request, @Nullable int[] frontendHandle)258 public boolean requestFrontend(@NonNull TunerFrontendRequest request, 259 @Nullable int[] frontendHandle) { 260 boolean result = false; 261 try { 262 result = mService.requestFrontend(request, frontendHandle); 263 } catch (RemoteException e) { 264 throw e.rethrowFromSystemServer(); 265 } 266 return result; 267 } 268 269 /** 270 * Requests from the client to share frontend with an existing client. 271 * 272 * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called 273 * before this request. 274 * 275 * @param selfClientId the id of the client that sends the request. 276 * @param targetClientId the id of the client to share the frontend with. 277 */ shareFrontend(int selfClientId, int targetClientId)278 public void shareFrontend(int selfClientId, int targetClientId) { 279 try { 280 mService.shareFrontend(selfClientId, targetClientId); 281 } catch (RemoteException e) { 282 throw e.rethrowFromSystemServer(); 283 } 284 } 285 286 /** 287 * Requests a Tuner Demux resource. 288 * 289 * <p>There are three possible scenarios: 290 * <ul> 291 * <li>If there is Demux available, the API would send the handle back. 292 * 293 * <li>If no Demux is available but the current request has a higher priority than other uses of 294 * demuxes, the API will send {@link IResourcesReclaimListener#onReclaimResources()} to the 295 * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and 296 * notify the holder of its resource loss. 297 * 298 * <li>If no Demux system can be granted, the API would return false. 299 * <ul> 300 * 301 * @param request {@link TunerDemuxRequest} information of the current request. 302 * @param demuxHandle a one-element array to return the granted Demux handle. 303 * If no Demux granted, this will return {@link #INVALID_RESOURCE_HANDLE}. 304 * 305 * @return true if there is Demux granted. 306 */ requestDemux(@onNull TunerDemuxRequest request, @NonNull int[] demuxHandle)307 public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) { 308 boolean result = false; 309 try { 310 result = mService.requestDemux(request, demuxHandle); 311 } catch (RemoteException e) { 312 throw e.rethrowFromSystemServer(); 313 } 314 return result; 315 } 316 317 /** 318 * Requests a Tuner Descrambler resource. 319 * 320 * <p>There are three possible scenarios: 321 * <ul> 322 * <li>If there is Descrambler available, the API would send the handle back. 323 * 324 * <li>If no Descrambler is available but the current request has a higher priority than other 325 * uses of descramblers, the API will send 326 * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would 327 * handle the resource reclaim on the holder of lower priority and notify the holder of its 328 * resource loss. 329 * 330 * <li>If no Descrambler system can be granted, the API would return false. 331 * <ul> 332 * 333 * @param request {@link TunerDescramblerRequest} information of the current request. 334 * @param descramblerHandle a one-element array to return the granted Descrambler handle. 335 * If no Descrambler granted, this will return 336 * {@link #INVALID_RESOURCE_HANDLE}. 337 * 338 * @return true if there is Descrambler granted. 339 */ requestDescrambler(@onNull TunerDescramblerRequest request, @NonNull int[] descramblerHandle)340 public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, 341 @NonNull int[] descramblerHandle) { 342 boolean result = false; 343 try { 344 result = mService.requestDescrambler(request, descramblerHandle); 345 } catch (RemoteException e) { 346 throw e.rethrowFromSystemServer(); 347 } 348 return result; 349 } 350 351 /** 352 * Requests a CAS session resource. 353 * 354 * <p>There are three possible scenarios: 355 * <ul> 356 * <li>If there is Cas session available, the API would send the id back. 357 * 358 * <li>If no Cas system is available but the current request info can show higher priority than 359 * other uses of the cas sessions under the requested cas system, the API will send 360 * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would 361 * handle the resource reclaim on the holder of lower priority and notify the holder of its 362 * resource loss. 363 * 364 * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this 365 * request. 366 * 367 * @param request {@link CasSessionRequest} information of the current request. 368 * @param casSessionHandle a one-element array to return the granted cas session handel. 369 * If no CAS granted, this will return {@link #INVALID_RESOURCE_HANDLE}. 370 * 371 * @return true if there is CAS session granted. 372 */ requestCasSession(@onNull CasSessionRequest request, @NonNull int[] casSessionHandle)373 public boolean requestCasSession(@NonNull CasSessionRequest request, 374 @NonNull int[] casSessionHandle) { 375 boolean result = false; 376 try { 377 result = mService.requestCasSession(request, casSessionHandle); 378 } catch (RemoteException e) { 379 throw e.rethrowFromSystemServer(); 380 } 381 return result; 382 } 383 384 /** 385 * Requests a CiCam resource. 386 * 387 * <p>There are three possible scenarios: 388 * <ul> 389 * <li>If there is CiCam available, the API would send the id back. 390 * 391 * <li>If no CiCam is available but the current request info can show higher priority than 392 * other uses of the CiCam, the API will send 393 * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would 394 * handle the resource reclaim on the holder of lower priority and notify the holder of its 395 * resource loss. 396 * 397 * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this 398 * request. 399 * 400 * @param request {@link TunerCiCamRequest} information of the current request. 401 * @param ciCamHandle a one-element array to return the granted ciCam handle. 402 * If no ciCam granted, this will return {@link #INVALID_RESOURCE_HANDLE}. 403 * 404 * @return true if there is ciCam granted. 405 */ requestCiCam(TunerCiCamRequest request, int[] ciCamHandle)406 public boolean requestCiCam(TunerCiCamRequest request, int[] ciCamHandle) { 407 boolean result = false; 408 try { 409 result = mService.requestCiCam(request, ciCamHandle); 410 } catch (RemoteException e) { 411 throw e.rethrowFromSystemServer(); 412 } 413 return result; 414 } 415 416 /** 417 * Requests a Tuner Lnb resource. 418 * 419 * <p>There are three possible scenarios: 420 * <ul> 421 * <li>If there is Lnb available, the API would send the id back. 422 * 423 * <li>If no Lnb is available but the current request has a higher priority than other uses of 424 * lnbs, the API will send {@link IResourcesReclaimListener#onReclaimResources()} to the 425 * {@link Tuner}. Tuner would handle the resource reclaim on the holder of lower priority and 426 * notify the holder of its resource loss. 427 * 428 * <li>If no Lnb system can be granted, the API would return false. 429 * <ul> 430 * 431 * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request. 432 * 433 * @param request {@link TunerLnbRequest} information of the current request. 434 * @param lnbHandle a one-element array to return the granted Lnb handle. 435 * If no Lnb granted, this will return {@link #INVALID_RESOURCE_HANDLE}. 436 * 437 * @return true if there is Lnb granted. 438 */ requestLnb(@onNull TunerLnbRequest request, @NonNull int[] lnbHandle)439 public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) { 440 boolean result = false; 441 try { 442 result = mService.requestLnb(request, lnbHandle); 443 } catch (RemoteException e) { 444 throw e.rethrowFromSystemServer(); 445 } 446 return result; 447 } 448 449 /** 450 * Notifies the TRM that the given frontend has been released. 451 * 452 * <p>Client must call this whenever it releases a Tuner frontend. 453 * 454 * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called 455 * before this release. 456 * 457 * @param frontendHandle the handle of the released frontend. 458 * @param clientId the id of the client that is releasing the frontend. 459 */ releaseFrontend(int frontendHandle, int clientId)460 public void releaseFrontend(int frontendHandle, int clientId) { 461 try { 462 mService.releaseFrontend(frontendHandle, clientId); 463 } catch (RemoteException e) { 464 throw e.rethrowFromSystemServer(); 465 } 466 } 467 468 /** 469 * Notifies the TRM that the Demux with the given handle has been released. 470 * 471 * <p>Client must call this whenever it releases an Demux. 472 * 473 * @param demuxHandle the handle of the released Tuner Demux. 474 * @param clientId the id of the client that is releasing the demux. 475 */ releaseDemux(int demuxHandle, int clientId)476 public void releaseDemux(int demuxHandle, int clientId) { 477 try { 478 mService.releaseDemux(demuxHandle, clientId); 479 } catch (RemoteException e) { 480 throw e.rethrowFromSystemServer(); 481 } 482 } 483 484 /** 485 * Notifies the TRM that the Descrambler with the given handle has been released. 486 * 487 * <p>Client must call this whenever it releases an Descrambler. 488 * 489 * @param descramblerHandle the handle of the released Tuner Descrambler. 490 * @param clientId the id of the client that is releasing the descrambler. 491 */ releaseDescrambler(int descramblerHandle, int clientId)492 public void releaseDescrambler(int descramblerHandle, int clientId) { 493 try { 494 mService.releaseDescrambler(descramblerHandle, clientId); 495 } catch (RemoteException e) { 496 throw e.rethrowFromSystemServer(); 497 } 498 } 499 500 /** 501 * Notifies the TRM that the given Cas session has been released. 502 * 503 * <p>Client must call this whenever it releases a Cas session. 504 * 505 * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this 506 * release. 507 * 508 * @param casSessionHandle the handle of the released CAS session. 509 * @param clientId the id of the client that is releasing the cas session. 510 */ releaseCasSession(int casSessionHandle, int clientId)511 public void releaseCasSession(int casSessionHandle, int clientId) { 512 try { 513 mService.releaseCasSession(casSessionHandle, clientId); 514 } catch (RemoteException e) { 515 throw e.rethrowFromSystemServer(); 516 } 517 } 518 519 /** 520 * Notifies the TRM that the given CiCam has been released. 521 * 522 * <p>Client must call this whenever it releases a CiCam. 523 * 524 * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this 525 * release. 526 * 527 * @param ciCamHandle the handle of the releasing CiCam. 528 * @param clientId the id of the client that is releasing the CiCam. 529 */ releaseCiCam(int ciCamHandle, int clientId)530 public void releaseCiCam(int ciCamHandle, int clientId) { 531 try { 532 mService.releaseCiCam(ciCamHandle, clientId); 533 } catch (RemoteException e) { 534 throw e.rethrowFromSystemServer(); 535 } 536 } 537 538 /** 539 * Notifies the TRM that the Lnb with the given id has been released. 540 * 541 * <p>Client must call this whenever it releases an Lnb. 542 * 543 * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release. 544 * 545 * @param lnbHandle the handle of the released Tuner Lnb. 546 * @param clientId the id of the client that is releasing the lnb. 547 */ releaseLnb(int lnbHandle, int clientId)548 public void releaseLnb(int lnbHandle, int clientId) { 549 try { 550 mService.releaseLnb(lnbHandle, clientId); 551 } catch (RemoteException e) { 552 throw e.rethrowFromSystemServer(); 553 } 554 } 555 556 /** 557 * Compare two clients' priority. 558 * 559 * @param challengerProfile the {@link ResourceClientProfile} of the challenger. 560 * @param holderProfile the {@link ResourceClientProfile} of the holder of the resource. 561 * 562 * @return true if the challenger has higher priority than the holder. 563 */ isHigherPriority(ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile)564 public boolean isHigherPriority(ResourceClientProfile challengerProfile, 565 ResourceClientProfile holderProfile) { 566 try { 567 return mService.isHigherPriority(challengerProfile, holderProfile); 568 } catch (RemoteException e) { 569 throw e.rethrowFromSystemServer(); 570 } 571 } 572 573 /** 574 * Interface used to receive events from TunerResourceManager. 575 */ 576 public abstract static class ResourcesReclaimListener { 577 /* 578 * To reclaim all the resources of the callack owner. 579 */ onReclaimResources()580 public abstract void onReclaimResources(); 581 } 582 } 583