/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media.tv.tunerresourcemanager; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.SuppressLint; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; import android.media.tv.TvInputService; import android.os.Binder; import android.os.RemoteException; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; /** * Interface of the Tuner Resource Manager(TRM). It manages resources used by TV Tuners. *
Resources include: *
Expected workflow is: *
TRM also exposes its priority comparison algorithm as a helping method to other services. * {@see #isHigherPriority(ResourceClientProfile, ResourceClientProfile)}. * * @hide */ @RequiresFeature(PackageManager.FEATURE_LIVE_TV) @SystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE) public class TunerResourceManager { private static final String TAG = "TunerResourceManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final int INVALID_RESOURCE_HANDLE = -1; public static final int INVALID_OWNER_ID = -1; /** * Tuner resource type to help generate resource handle */ @IntDef({ TUNER_RESOURCE_TYPE_FRONTEND, TUNER_RESOURCE_TYPE_DEMUX, TUNER_RESOURCE_TYPE_DESCRAMBLER, TUNER_RESOURCE_TYPE_LNB, TUNER_RESOURCE_TYPE_CAS_SESSION, TUNER_RESOURCE_TYPE_FRONTEND_CICAM, TUNER_RESOURCE_TYPE_MAX, }) @Retention(RetentionPolicy.SOURCE) public @interface TunerResourceType {} public static final int TUNER_RESOURCE_TYPE_FRONTEND = 0; public static final int TUNER_RESOURCE_TYPE_DEMUX = 1; public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2; public static final int TUNER_RESOURCE_TYPE_LNB = 3; public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4; public static final int TUNER_RESOURCE_TYPE_FRONTEND_CICAM = 5; public static final int TUNER_RESOURCE_TYPE_MAX = 6; private final ITunerResourceManager mService; private final int mUserId; /** * @hide */ public TunerResourceManager(ITunerResourceManager service, int userId) { mService = service; mUserId = userId; } /** * This API is used by the client to register their profile with the Tuner Resource manager. * *
The profile contains information that can show the base priority score of the client. * * @param profile {@link ResourceClientProfile} profile of the current client. Undefined use * case would cause IllegalArgumentException. * @param executor the executor on which the listener would be invoked. * @param listener {@link ResourcesReclaimListener} callback to reclaim clients' resources when * needed. * @param clientId returned a clientId from the resource manager when the * the client registeres. * @throws IllegalArgumentException when {@code profile} contains undefined use case. */ public void registerClientProfile(@NonNull ResourceClientProfile profile, @NonNull @CallbackExecutor Executor executor, @NonNull ResourcesReclaimListener listener, @NonNull int[] clientId) { // TODO: throw new IllegalArgumentException("Unknown client use case") // when the use case is not defined. try { mService.registerClientProfile(profile, new IResourcesReclaimListener.Stub() { @Override public void onReclaimResources() { final long identity = Binder.clearCallingIdentity(); try { executor.execute(() -> listener.onReclaimResources()); } finally { Binder.restoreCallingIdentity(identity); } } }, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API is used by the client to unregister their profile with the * Tuner Resource manager. * * @param clientId the client id that needs to be unregistered. */ public void unregisterClientProfile(int clientId) { try { mService.unregisterClientProfile(clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * This API is used by client to update its registered {@link ResourceClientProfile}. * *
We recommend creating a new tuner instance for different use cases instead of using this * API since different use cases may need different resources. * *
If TIS updates use case, it needs to ensure underneath resources are exchangeable between * two different use cases. * *
Only the arbitrary priority and niceValue are allowed to be updated. * * @param clientId the id of the client that is updating its profile. * @param priority the priority that the client would like to update to. * @param niceValue the nice value that the client would like to update to. * * @return true if the update is successful. */ public boolean updateClientPriority(int clientId, int priority, int niceValue) { boolean result = false; try { result = mService.updateClientPriority(clientId, priority, niceValue); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Checks if there is an unused frontend resource available. * * @param frontendType The frontend type for the query to be done for. */ public boolean hasUnusedFrontend(int frontendType) { boolean result = false; try { result = mService.hasUnusedFrontend(frontendType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Checks if the client has the lowest priority among the clients that are holding * the frontend resource of the specified type. * *
When this function returns false, it means that there is at least one client with the * strictly lower priority (than clientId) that is reclaimable by the system. * * @param clientId The client ID to be checked the priority for. * @param frontendType The specific frontend type to be checked for. * * @return false if there is another client holding the frontend resource of the specified type * that can be reclaimed. Otherwise true. */ public boolean isLowestPriority(int clientId, int frontendType) { boolean result = false; try { result = mService.isLowestPriority(clientId, frontendType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Stores the frontend resource map if it was stored before. * *
This API is only for testing purpose and should be used in pair with * restoreResourceMap(), which allows testing of {@link Tuner} APIs * that behave differently based on different sets of resource map. * * @param resourceType The resource type to store the map for. */ public void storeResourceMap(int resourceType) { try { mService.storeResourceMap(resourceType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Clears the frontend resource map. * *
This API is only for testing purpose and should be called right after * storeResourceMap(), so TRMService#removeFrontendResource() does not * get called in TRMService#setFrontendInfoListInternal() for custom frontend * resource map creation. * * @param resourceType The resource type to clear the map for. */ public void clearResourceMap(int resourceType) { try { mService.clearResourceMap(resourceType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Restores Frontend resource map for the later restore. * *
This API is only for testing purpose and should be used in pair with * storeResourceMap(), which allows testing of {@link Tuner} APIs * that behave differently based on different sets of resource map. * * @param resourceType The resource type to restore the map for. */ public void restoreResourceMap(int resourceType) { try { mService.restoreResourceMap(resourceType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Updates the current TRM of the TunerHAL Frontend information. * *
Note: This update must happen before the first * {@link #requestFrontend(TunerFrontendRequest, int[])} and * {@link #releaseFrontend(int, int)} call. * * @param infos an array of the available {@link TunerFrontendInfo} information. */ public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) { try { mService.setFrontendInfoList(infos); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Updates the current TRM of the TunerHAL Demux information. * *
Note: This update must happen before the first * {@link #requestDemux(TunerDemuxRequest, int[])} and * {@link #releaseDemux(int, int)} call. * * @param infos an array of the available {@link TunerDemuxInfo} information. */ public void setDemuxInfoList(@NonNull TunerDemuxInfo[] infos) { try { mService.setDemuxInfoList(infos); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Updates the TRM of the current CAS information. * *
Note: This update must happen before the first * {@link #requestCasSession(CasSessionRequest, int[])} and {@link #releaseCasSession(int, int)} * call. * * @param casSystemId id of the updating CAS system. * @param maxSessionNum the max session number of the CAS system that is updated. */ public void updateCasInfo(int casSystemId, int maxSessionNum) { try { mService.updateCasInfo(casSystemId, maxSessionNum); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Updates the TRM of the current Lnb information. * *
Note: This update must happen before the first * {@link #requestLnb(TunerLnbRequest, int[])} and {@link #releaseLnb(int, int)} call. * * @param lnbIds ids of the updating lnbs. */ public void setLnbInfoList(int[] lnbIds) { try { mService.setLnbInfoList(lnbIds); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Grants the lock to the caller for public {@link Tuner} APIs * *
{@link Tuner} functions that call both [@link TunerResourceManager} APIs and * grabs lock that are also used in {@link IResourcesReclaimListener#onReclaimResources()} * must call this API before acquiring lock used in onReclaimResources(). * *
This API will block until it releases the lock or fails * * @param clientId The ID of the caller. * * @return true if the lock is granted. If false is returned, calling this API again is not * guaranteed to work and may be unrecoverrable. (This should not happen.) */ public boolean acquireLock(int clientId) { try { return mService.acquireLock(clientId, Thread.currentThread().getId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Releases the lock to the caller for public {@link Tuner} APIs * *
This API must be called in pair with {@link #acquireLock(int, int)} * *
This API will block until it releases the lock or fails * * @param clientId The ID of the caller. * * @return true if the lock is granted. If false is returned, calling this API again is not * guaranteed to work and may be unrecoverrable. (This should not happen.) */ public boolean releaseLock(int clientId) { try { return mService.releaseLock(clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Requests a frontend resource. * *
There are three possible scenarios: *
Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called * before this request. * * @param request {@link TunerFrontendRequest} information of the current request. * @param frontendHandle a one-element array to return the granted frontendHandle. If * no frontend granted, this will return {@link #INVALID_RESOURCE_HANDLE}. * * @return true if there is frontend granted. */ public boolean requestFrontend(@NonNull TunerFrontendRequest request, @Nullable int[] frontendHandle) { boolean result = false; try { result = mService.requestFrontend(request, frontendHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Sets the maximum usable frontends number of a given frontend type. It is used to enable or * disable frontends when cable connection status is changed by user. * * @param frontendType the frontendType which the maximum usable number will be set for. * @param maxNum the new maximum usable number. * * @return true if successful and false otherwise. */ public boolean setMaxNumberOfFrontends(int frontendType, int maxNum) { boolean result = false; try { result = mService.setMaxNumberOfFrontends(frontendType, maxNum); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Get the maximum usable frontends number of a given frontend type. * * @param frontendType the frontendType which the maximum usable number will be queried for. * * @return the maximum usable number of the queried frontend type. Returns -1 when the * frontendType is invalid */ public int getMaxNumberOfFrontends(int frontendType) { int result = -1; try { result = mService.getMaxNumberOfFrontends(frontendType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Requests from the client to share frontend with an existing client. * *
Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called * before this request. * * @param selfClientId the id of the client that sends the request. * @param targetClientId the id of the client to share the frontend with. */ public void shareFrontend(int selfClientId, int targetClientId) { try { mService.shareFrontend(selfClientId, targetClientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Transfers the ownership of shared resource. * *
Note: Only the existing frontend sharee can be the new owner. * * @param resourceType the type of the resource to transfer the ownership for. * @param currentOwnerId the id of the current owner client. * @param newOwnerId the id of the new owner client. * * @return true if successful and false otherwise. */ public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) { try { return mService.transferOwner(resourceType, currentOwnerId, newOwnerId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Requests a Tuner Demux resource. * *
There are three possible scenarios: *
There are three possible scenarios: *
There are three possible scenarios: *
Note: {@link #updateCasInfo(int, int)} must be called before this * request. * * @param request {@link CasSessionRequest} information of the current request. * @param casSessionHandle a one-element array to return the granted cas session handel. * If no CAS granted, this will return {@link #INVALID_RESOURCE_HANDLE}. * * @return true if there is CAS session granted. */ public boolean requestCasSession(@NonNull CasSessionRequest request, @NonNull int[] casSessionHandle) { boolean result = false; try { result = mService.requestCasSession(request, casSessionHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Requests a CiCam resource. * *
There are three possible scenarios: *
Note: {@link #updateCasInfo(int, int)} must be called before this * request. * * @param request {@link TunerCiCamRequest} information of the current request. * @param ciCamHandle a one-element array to return the granted ciCam handle. * If no ciCam granted, this will return {@link #INVALID_RESOURCE_HANDLE}. * * @return true if there is ciCam granted. */ public boolean requestCiCam(TunerCiCamRequest request, int[] ciCamHandle) { boolean result = false; try { result = mService.requestCiCam(request, ciCamHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Requests a Tuner Lnb resource. * *
There are three possible scenarios: *
Note: {@link #setLnbInfoList(int[])} must be called before this request. * * @param request {@link TunerLnbRequest} information of the current request. * @param lnbHandle a one-element array to return the granted Lnb handle. * If no Lnb granted, this will return {@link #INVALID_RESOURCE_HANDLE}. * * @return true if there is Lnb granted. */ public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) { boolean result = false; try { result = mService.requestLnb(request, lnbHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } return result; } /** * Notifies the TRM that the given frontend has been released. * *
Client must call this whenever it releases a Tuner frontend. * *
Note: {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called * before this release. * * @param frontendHandle the handle of the released frontend. * @param clientId the id of the client that is releasing the frontend. */ public void releaseFrontend(int frontendHandle, int clientId) { try { mService.releaseFrontend(frontendHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies the TRM that the Demux with the given handle has been released. * *
Client must call this whenever it releases an Demux. * * @param demuxHandle the handle of the released Tuner Demux. * @param clientId the id of the client that is releasing the demux. */ public void releaseDemux(int demuxHandle, int clientId) { try { mService.releaseDemux(demuxHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies the TRM that the Descrambler with the given handle has been released. * *
Client must call this whenever it releases an Descrambler. * * @param descramblerHandle the handle of the released Tuner Descrambler. * @param clientId the id of the client that is releasing the descrambler. */ public void releaseDescrambler(int descramblerHandle, int clientId) { try { mService.releaseDescrambler(descramblerHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies the TRM that the given Cas session has been released. * *
Client must call this whenever it releases a Cas session. * *
Note: {@link #updateCasInfo(int, int)} must be called before this * release. * * @param casSessionHandle the handle of the released CAS session. * @param clientId the id of the client that is releasing the cas session. */ public void releaseCasSession(int casSessionHandle, int clientId) { try { mService.releaseCasSession(casSessionHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies the TRM that the given CiCam has been released. * *
Client must call this whenever it releases a CiCam. * *
Note: {@link #updateCasInfo(int, int)} must be called before this * release. * * @param ciCamHandle the handle of the releasing CiCam. * @param clientId the id of the client that is releasing the CiCam. */ public void releaseCiCam(int ciCamHandle, int clientId) { try { mService.releaseCiCam(ciCamHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Notifies the TRM that the Lnb with the given id has been released. * *
Client must call this whenever it releases an Lnb. * *
Note: {@link #setLnbInfoList(int[])} must be called before this release. * * @param lnbHandle the handle of the released Tuner Lnb. * @param clientId the id of the client that is releasing the lnb. */ public void releaseLnb(int lnbHandle, int clientId) { try { mService.releaseLnb(lnbHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Compare two clients' priority. * * @param challengerProfile the {@link ResourceClientProfile} of the challenger. * @param holderProfile the {@link ResourceClientProfile} of the holder of the resource. * * @return true if the challenger has higher priority than the holder. */ public boolean isHigherPriority(ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) { try { return mService.isHigherPriority(challengerProfile, holderProfile); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a priority for the given use case type and the client's foreground or background * status. * * @param useCase the use case type of the client. When the given use case type is invalid, * the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}. * @param pid the pid of the client. When the pid is invalid, background status will be used as * a client's status. Otherwise, client's app corresponding to the given session id will * be used as a client. {@see TvInputService#onCreateSession(String, String)}. * * @return the client priority.. */ public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase, int pid) { try { return mService.getClientPriority(useCase, pid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Returns a config priority for the given use case type and the foreground or background * status. * * @param useCase the use case type of the client. When the given use case type is invalid, * the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}. * @param isForeground {@code true} if foreground, {@code false} otherwise. * * @return the config priority. * * @hide */ @TestApi @SuppressLint("ShowingMemberInHiddenClass") public int getConfigPriority(@TvInputService.PriorityHintUseCaseType int useCase, boolean isForeground) { try { return mService.getConfigPriority(useCase, isForeground); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Interface used to receive events from TunerResourceManager. */ public abstract static class ResourcesReclaimListener { /* * To reclaim all the resources of the callack owner. */ public abstract void onReclaimResources(); } }