diff options
| author | 2020-04-15 01:06:32 -0700 | |
|---|---|---|
| committer | 2020-04-18 01:19:34 +0000 | |
| commit | e1e65f18c0d8592efd61623804a08ec33379e58b (patch) | |
| tree | 474a8a479b8810400d2496906524682a87d56bf1 | |
| parent | d9b3f18c8b5ebb9ef43210d12351168c99e01dca (diff) | |
Complete the Cas System request/release/update implementation in TRM
Test: atest
Bug: 154076250
Change-Id: Ifaf80a3e91807e1b47e780d2b5fd699412e1e7cc
7 files changed, 552 insertions, 177 deletions
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index 97d32d88c7f2..c652628eb425 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -388,7 +388,7 @@ public final class MediaCas implements AutoCloseable { @Override public void onReclaimResources() { synchronized (mSessionMap) { - mSessionMap.forEach((casSession, sessionResourceId) -> casSession.close()); + mSessionMap.forEach((casSession, sessionResourceHandle) -> casSession.close()); } mEventHandler.sendMessage(mEventHandler.obtainMessage( EventHandler.MSG_CAS_RESOURCE_LOST)); @@ -868,7 +868,7 @@ public final class MediaCas implements AutoCloseable { } } - private int getSessionResourceId() throws MediaCasException { + private int getSessionResourceHandle() throws MediaCasException { validateInternalStates(); int[] sessionResourceHandle = new int[1]; @@ -881,14 +881,14 @@ public final class MediaCas implements AutoCloseable { "insufficient resource to Open Session"); } } - return sessionResourceHandle[0]; + return sessionResourceHandle[0]; } - private void addSessionToResourceMap(Session session, int sessionResourceId) { + private void addSessionToResourceMap(Session session, int sessionResourceHandle) { - if (sessionResourceId != -1) { + if (sessionResourceHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { synchronized (mSessionMap) { - mSessionMap.put(session, sessionResourceId); + mSessionMap.put(session, sessionResourceHandle); } } } @@ -918,13 +918,13 @@ public final class MediaCas implements AutoCloseable { * @throws MediaCasStateException for CAS-specific state exceptions. */ public Session openSession() throws MediaCasException { - int sessionResourceId = getSessionResourceId(); + int sessionResourceHandle = getSessionResourceHandle(); try { OpenSessionCallback cb = new OpenSessionCallback(); mICas.openSession(cb); MediaCasException.throwExceptionIfNeeded(cb.mStatus); - addSessionToResourceMap(cb.mSession, sessionResourceId); + addSessionToResourceMap(cb.mSession, sessionResourceHandle); return cb.mSession; } catch (RemoteException e) { cleanupAndRethrowIllegalState(); @@ -952,7 +952,7 @@ public final class MediaCas implements AutoCloseable { @Nullable public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode) throws MediaCasException { - int sessionResourceId = getSessionResourceId(); + int sessionResourceHandle = getSessionResourceHandle(); if (mICasV12 == null) { Log.d(TAG, "Open Session with scrambling mode is only supported by cas@1.2+ interface"); @@ -963,7 +963,7 @@ public final class MediaCas implements AutoCloseable { OpenSession_1_2_Callback cb = new OpenSession_1_2_Callback(); mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb); MediaCasException.throwExceptionIfNeeded(cb.mStatus); - addSessionToResourceMap(cb.mSession, sessionResourceId); + addSessionToResourceMap(cb.mSession, sessionResourceHandle); return cb.mSession; } catch (RemoteException e) { cleanupAndRethrowIllegalState(); diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 7077cd1b76a0..487b444eb627 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -218,11 +218,11 @@ interface ITunerResourceManager { * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this request. * * @param request {@link CasSessionRequest} information of the current request. - * @param sessionResourceId a one-element array to return the granted cas session id. + * @param casSessionHandle a one-element array to return the granted cas session handle. * * @return true if there is CAS session granted. */ - boolean requestCasSession(in CasSessionRequest request, out int[] sessionResourceId); + boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle); /* * This API is used by the Tuner framework to request an available Lnb from the TunerHAL. @@ -276,7 +276,7 @@ interface ITunerResourceManager { * * <p>Client must call this whenever it releases a descrambler. * - * @param demuxHandle the handle of the released Tuner Descrambler. + * @param descramblerHandle the handle of the released Tuner Descrambler. * @param clientId the id of the client that is releasing the descrambler. */ void releaseDescrambler(in int descramblerHandle, int clientId); @@ -288,10 +288,10 @@ interface ITunerResourceManager { * * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this release. * - * @param sessionResourceId the id of the released CAS session. + * @param casSessionHandle the handle of the released CAS session. * @param clientId the id of the client that is releasing the cas session. */ - void releaseCasSession(in int sessionResourceId, int clientId); + void releaseCasSession(in int casSessionHandle, int clientId); /* * Notifies the TRM that the Lnb with the given handle was released. diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index b4dcc5d8b3df..be102d8acc10 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -362,17 +362,16 @@ public class TunerResourceManager { * request. * * @param request {@link CasSessionRequest} information of the current request. - * @param sessionResourceId a one-element array to return the granted cas session id. - * If no CAS granted, this will return - * {@link #INVALID_CAS_SESSION_RESOURCE_ID}. + * @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[] sessionResourceId) { + @NonNull int[] casSessionHandle) { boolean result = false; try { - result = mService.requestCasSession(request, sessionResourceId); + result = mService.requestCasSession(request, casSessionHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -471,12 +470,12 @@ public class TunerResourceManager { * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this * release. * - * @param sessionResourceId the id of the released CAS session. + * @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 sessionResourceId, int clientId) { + public void releaseCasSession(int casSessionHandle, int clientId) { try { - mService.releaseCasSession(sessionResourceId, clientId); + mService.releaseCasSession(casSessionHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java new file mode 100644 index 000000000000..54ad1d268e56 --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java @@ -0,0 +1,152 @@ +/* + * 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 com.android.server.tv.tunerresourcemanager; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * A Cas resource object used by the Tuner Resource Manager to record the cas + * information. + * + * @hide + */ +public final class CasResource { + + private final int mSystemId; + + private int mMaxSessionNum; + + private int mAvailableSessionNum; + + /** + * The owner clients' ids when part of the Cas is occupied. + */ + private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>(); + + private CasResource(Builder builder) { + this.mSystemId = builder.mSystemId; + this.mMaxSessionNum = builder.mMaxSessionNum; + this.mAvailableSessionNum = builder.mMaxSessionNum; + } + + public int getSystemId() { + return mSystemId; + } + + public int getMaxSessionNum() { + return mMaxSessionNum; + } + + public int getUsedSessionNum() { + return (mMaxSessionNum - mAvailableSessionNum); + } + + public boolean isFullyUsed() { + return mAvailableSessionNum == 0; + } + + /** + * Update max session number. + * + * @param maxSessionNum the new max session num. + */ + public void updateMaxSessionNum(int maxSessionNum) { + mAvailableSessionNum = Math.max( + 0, mAvailableSessionNum + (maxSessionNum - mMaxSessionNum)); + mMaxSessionNum = maxSessionNum; + } + + /** + * Set an owner for the cas + * + * @param ownerId the client id of the owner. + */ + public void setOwner(int ownerId) { + int sessionNum = mOwnerClientIdsToSessionNum.get(ownerId) == null + ? 1 : (mOwnerClientIdsToSessionNum.get(ownerId) + 1); + mOwnerClientIdsToSessionNum.put(ownerId, sessionNum); + mAvailableSessionNum--; + } + + /** + * Remove an owner of the Cas. + * + * @param ownerId the removing client id of the owner. + */ + public void removeOwner(int ownerId) { + mAvailableSessionNum += mOwnerClientIdsToSessionNum.get(ownerId); + mOwnerClientIdsToSessionNum.remove(ownerId); + } + + public Set<Integer> getOwnerClientIds() { + return mOwnerClientIdsToSessionNum.keySet(); + } + + @Override + public String toString() { + return "CasResource[systemId=" + this.mSystemId + + ", isFullyUsed=" + (this.mAvailableSessionNum == 0) + + ", maxSessionNum=" + this.mMaxSessionNum + + ", ownerClients=" + ownersMapToString() + "]"; + } + + /** + * Builder class for {@link CasResource}. + */ + public static class Builder { + + private int mSystemId; + private int mMaxSessionNum; + + Builder(int systemId) { + this.mSystemId = systemId; + } + + /** + * Builder for {@link CasResource}. + * + * @param maxSessionNum the max session num the current Cas has. + */ + public Builder maxSessionNum(int maxSessionNum) { + this.mMaxSessionNum = maxSessionNum; + return this; + } + + /** + * Build a {@link CasResource}. + * + * @return {@link CasResource}. + */ + public CasResource build() { + CasResource cas = new CasResource(this); + return cas; + } + } + + private String ownersMapToString() { + StringBuilder string = new StringBuilder("{"); + for (int clienId : mOwnerClientIdsToSessionNum.keySet()) { + string.append(" clientId=") + .append(clienId) + .append(", owns session num=") + .append(mOwnerClientIdsToSessionNum.get(clienId)) + .append(","); + } + return string.append("}").toString(); + } +} diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index 4cdc17292ac4..2b0fe8a2602b 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -27,6 +27,7 @@ import java.util.Set; public final class ClientProfile { public static final int INVALID_GROUP_ID = -1; + public static final int INVALID_RESOURCE_ID = -1; /** * Client id sent to the client when registering with @@ -56,7 +57,6 @@ public final class ClientProfile { * also lose their resources. */ private int mGroupId = INVALID_GROUP_ID; - /** * Optional nice value for TRM to reduce client’s priority. */ @@ -73,6 +73,11 @@ public final class ClientProfile { private Set<Integer> mUsingLnbIds = new HashSet<>(); /** + * List of the Cas system ids that are used by the current client. + */ + private int mUsingCasSystemId = INVALID_RESOURCE_ID; + + /** * Optional arbitrary priority value given by the client. * * <p>This value can override the default priorotiy calculated from @@ -172,11 +177,32 @@ public final class ClientProfile { } /** + * Set when the client starts to use a Cas system. + * + * @param casSystemId cas being used. + */ + public void useCas(int casSystemId) { + mUsingCasSystemId = casSystemId; + } + + public int getInUseCasSystemId() { + return mUsingCasSystemId; + } + + /** + * Called when the client released a Cas System. + */ + public void releaseCas() { + mUsingCasSystemId = INVALID_RESOURCE_ID; + } + + /** * Called to reclaim all the resources being used by the current client. */ public void reclaimAllResources() { mUsingFrontendIds.clear(); mUsingLnbIds.clear(); + mUsingCasSystemId = INVALID_RESOURCE_ID; } @Override diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 7231813dd949..2f70840cfc8b 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -65,6 +65,8 @@ public class TunerResourceManagerService extends SystemService { private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>(); // Map of the current available lnb resources private Map<Integer, LnbResource> mLnbResources = new HashMap<>(); + // Map of the current available Cas resources + private Map<Integer, CasResource> mCasResources = new HashMap<>(); @GuardedBy("mLock") private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>(); @@ -158,10 +160,8 @@ public class TunerResourceManagerService extends SystemService { @Override public void updateCasInfo(int casSystemId, int maxSessionNum) { enforceTrmAccessPermission("updateCasInfo"); - if (DEBUG) { - Slog.d(TAG, - "updateCasInfo(casSystemId=" + casSystemId - + ", maxSessionNum=" + maxSessionNum + ")"); + synchronized (mLock) { + updateCasInfoInternal(casSystemId, maxSessionNum); } } @@ -185,11 +185,11 @@ public class TunerResourceManagerService extends SystemService { throw new RemoteException("frontendHandle can't be null"); } synchronized (mLock) { - try { - return requestFrontendInternal(request, frontendHandle); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request frontend from unregistered client:" + + request.getClientId()); } + return requestFrontendInternal(request, frontendHandle); } } @@ -211,32 +211,45 @@ public class TunerResourceManagerService extends SystemService { throw new RemoteException("demuxHandle can't be null"); } synchronized (mLock) { + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request demux from unregistered client:" + + request.getClientId()); + } return requestDemuxInternal(request, demuxHandle); } } @Override public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, - @NonNull int[] descrambleHandle) throws RemoteException { + @NonNull int[] descramblerHandle) throws RemoteException { enforceDescramblerAccessPermission("requestDescrambler"); enforceTrmAccessPermission("requestDescrambler"); - if (descrambleHandle == null) { - throw new RemoteException("descrambleHandle can't be null"); + if (descramblerHandle == null) { + throw new RemoteException("descramblerHandle can't be null"); } synchronized (mLock) { - return requestDescramblerInternal(request, descrambleHandle); + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request descrambler from unregistered client:" + + request.getClientId()); + } + return requestDescramblerInternal(request, descramblerHandle); } } @Override - public boolean requestCasSession( - @NonNull CasSessionRequest request, @NonNull int[] sessionResourceHandle) { + public boolean requestCasSession(@NonNull CasSessionRequest request, + @NonNull int[] casSessionHandle) throws RemoteException { enforceTrmAccessPermission("requestCasSession"); - if (DEBUG) { - Slog.d(TAG, "requestCasSession(request=" + request + ")"); + if (casSessionHandle == null) { + throw new RemoteException("casSessionHandle can't be null"); + } + synchronized (mLock) { + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request cas from unregistered client:" + + request.getClientId()); + } + return requestCasSessionInternal(request, casSessionHandle); } - - return true; } @Override @@ -248,11 +261,11 @@ public class TunerResourceManagerService extends SystemService { throw new RemoteException("lnbHandle can't be null"); } synchronized (mLock) { - try { - return requestLnbInternal(request, lnbHandle); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request lnb from unregistered client:" + + request.getClientId()); } + return requestLnbInternal(request, lnbHandle); } } @@ -264,15 +277,20 @@ public class TunerResourceManagerService extends SystemService { frontendHandle)) { throw new RemoteException("frontendHandle can't be invalid"); } - int frontendId = getResourceIdFromHandle(frontendHandle); - FrontendResource fe = getFrontendResource(frontendId); - if (fe == null) { - throw new RemoteException("Releasing frontend does not exist."); - } - if (fe.getOwnerClientId() != clientId) { - throw new RemoteException("Client is not the current owner of the releasing fe."); - } synchronized (mLock) { + if (!checkClientExists(clientId)) { + throw new RemoteException("Release frontend from unregistered client:" + + clientId); + } + int frontendId = getResourceIdFromHandle(frontendHandle); + FrontendResource fe = getFrontendResource(frontendId); + if (fe == null) { + throw new RemoteException("Releasing frontend does not exist."); + } + if (fe.getOwnerClientId() != clientId) { + throw new RemoteException( + "Client is not the current owner of the releasing fe."); + } releaseFrontendInternal(fe); } } @@ -296,10 +314,26 @@ public class TunerResourceManagerService extends SystemService { } @Override - public void releaseCasSession(int sessionResourceId, int clientId) { + public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException { enforceTrmAccessPermission("releaseCasSession"); - if (DEBUG) { - Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")"); + if (!validateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) { + throw new RemoteException("casSessionHandle can't be invalid"); + } + synchronized (mLock) { + if (!checkClientExists(clientId)) { + throw new RemoteException("Release cas from unregistered client:" + clientId); + } + int casSystemId = getClientProfile(clientId).getInUseCasSystemId(); + CasResource cas = getCasResource(casSystemId); + if (cas == null) { + throw new RemoteException("Releasing cas does not exist."); + } + if (!cas.getOwnerClientIds().contains(clientId)) { + throw new RemoteException( + "Client is not the current owner of the releasing cas."); + } + releaseCasSessionInternal(cas, clientId); } } @@ -310,6 +344,9 @@ public class TunerResourceManagerService extends SystemService { if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) { throw new RemoteException("lnbHandle can't be invalid"); } + if (!checkClientExists(clientId)) { + throw new RemoteException("Release lnb from unregistered client:" + clientId); + } int lnbId = getResourceIdFromHandle(lnbHandle); LnbResource lnb = getLnbResource(lnbId); if (lnb == null) { @@ -465,17 +502,42 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) - throws RemoteException { + protected void updateCasInfoInternal(int casSystemId, int maxSessionNum) { + if (DEBUG) { + Slog.d(TAG, + "updateCasInfo(casSystemId=" + casSystemId + + ", maxSessionNum=" + maxSessionNum + ")"); + } + // If maxSessionNum is 0, removing the Cas Resource. + if (maxSessionNum == 0) { + removeCasResource(casSystemId); + return; + } + // If the Cas exists, updates the Cas Resource accordingly. + CasResource cas = getCasResource(casSystemId); + if (cas != null) { + if (cas.getUsedSessionNum() > maxSessionNum) { + // Sort and release the short number of Cas resources. + int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum; + releaseLowerPriorityClientCasResources(releasingCasResourceNum); + } + cas.updateMaxSessionNum(maxSessionNum); + return; + } + // Add the new Cas Resource. + cas = new CasResource.Builder(casSystemId) + .maxSessionNum(maxSessionNum) + .build(); + addCasResource(cas); + } + + @VisibleForTesting + protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) { if (DEBUG) { Slog.d(TAG, "requestFrontend(request=" + request + ")"); } frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - if (!checkClientExists(request.getClientId())) { - Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId()); - return false; - } ClientProfile requestClient = getClientProfile(request.getClientId()); int grantingFrontendId = -1; int inUseLowestPriorityFrId = -1; @@ -496,7 +558,7 @@ public class TunerResourceManagerService extends SystemService { } else if (grantingFrontendId < 0) { // Record the frontend id with the lowest client priority among all the // in use frontends when no available frontend has been found. - int priority = getOwnerClientPriority(fr); + int priority = getOwnerClientPriority(fr.getOwnerClientId()); if (currentLowestPriority > priority) { inUseLowestPriorityFrId = fr.getId(); currentLowestPriority = priority; @@ -530,17 +592,12 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) - throws RemoteException { + protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) { if (DEBUG) { Slog.d(TAG, "requestLnb(request=" + request + ")"); } lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - if (!checkClientExists(request.getClientId())) { - Slog.e(TAG, "Request lnb from unregistered client:" + request.getClientId()); - return false; - } ClientProfile requestClient = getClientProfile(request.getClientId()); int grantingLnbId = -1; int inUseLowestPriorityLnbId = -1; @@ -554,7 +611,7 @@ public class TunerResourceManagerService extends SystemService { } else { // Record the lnb id with the lowest client priority among all the // in use lnb when no available lnb has been found. - int priority = getOwnerClientPriority(lnb); + int priority = getOwnerClientPriority(lnb.getOwnerClientId()); if (currentLowestPriority > priority) { inUseLowestPriorityLnbId = lnb.getId(); currentLowestPriority = priority; @@ -588,7 +645,55 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - void releaseFrontendInternal(FrontendResource fe) { + protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) { + if (DEBUG) { + Slog.d(TAG, "requestCasSession(request=" + request + ")"); + } + CasResource cas = getCasResource(request.getCasSystemId()); + // Unregistered Cas System is treated as having unlimited sessions. + if (cas == null) { + cas = new CasResource.Builder(request.getCasSystemId()) + .maxSessionNum(Integer.MAX_VALUE) + .build(); + addCasResource(cas); + } + casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + ClientProfile requestClient = getClientProfile(request.getClientId()); + int lowestPriorityOwnerId = -1; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + if (!cas.isFullyUsed()) { + casSessionHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); + updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId()); + return true; + } + for (int ownerId : cas.getOwnerClientIds()) { + // Record the client id with lowest priority that is using the current Cas system. + int priority = getOwnerClientPriority(ownerId); + if (currentLowestPriority > priority) { + lowestPriorityOwnerId = ownerId; + currentLowestPriority = priority; + } + } + + // When all the Cas sessions are occupied, reclaim the lowest priority client if the + // request client has higher priority. + if (lowestPriorityOwnerId > -1 && (requestClient.getPriority() > currentLowestPriority)) { + if (!reclaimResource(lowestPriorityOwnerId, + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { + return false; + } + casSessionHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); + updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId()); + return true; + } + return false; + } + + @VisibleForTesting + protected void releaseFrontendInternal(FrontendResource fe) { if (DEBUG) { Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ")"); } @@ -596,7 +701,7 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - void releaseLnbInternal(LnbResource lnb) { + protected void releaseLnbInternal(LnbResource lnb) { if (DEBUG) { Slog.d(TAG, "releaseLnb(lnbId=" + lnb.getId() + ")"); } @@ -604,7 +709,15 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { + protected void releaseCasSessionInternal(CasResource cas, int ownerClientId) { + if (DEBUG) { + Slog.d(TAG, "releaseCasSession(sessionResourceId=" + cas.getSystemId() + ")"); + } + updateCasClientMappingOnRelease(cas, ownerClientId); + } + + @VisibleForTesting + protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { if (DEBUG) { Slog.d(TAG, "requestDemux(request=" + request + ")"); } @@ -614,7 +727,8 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - boolean requestDescramblerInternal(TunerDescramblerRequest request, int[] descramblerHandle) { + protected boolean requestDescramblerInternal( + TunerDescramblerRequest request, int[] descramblerHandle) { if (DEBUG) { Slog.d(TAG, "requestDescrambler(request=" + request + ")"); } @@ -742,14 +856,28 @@ public class TunerResourceManagerService extends SystemService { ownerProfile.releaseLnb(releasingLnb.getId()); } + private void updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId) { + CasResource grantingCas = getCasResource(grantingId); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + grantingCas.setOwner(ownerClientId); + ownerProfile.useCas(grantingId); + } + + private void updateCasClientMappingOnRelease( + @NonNull CasResource releasingCas, int ownerClientId) { + ClientProfile ownerProfile = getClientProfile(ownerClientId); + releasingCas.removeOwner(ownerClientId); + ownerProfile.releaseCas(); + } + /** * Get the owner client's priority from the resource id. * - * @param resource a in use tuner resource. + * @param clientId the owner client id. * @return the priority of the owner client of the resource. */ - private int getOwnerClientPriority(TunerResourceBasic resource) { - return getClientProfile(resource.getOwnerClientId()).getPriority(); + private int getOwnerClientPriority(int clientId) { + return getClientProfile(clientId).getPriority(); } @VisibleForTesting @@ -783,6 +911,9 @@ public class TunerResourceManagerService extends SystemService { private void removeFrontendResource(int removingId) { FrontendResource fe = getFrontendResource(removingId); + if (fe == null) { + return; + } if (fe.isInUse()) { releaseFrontendInternal(fe); } @@ -811,6 +942,9 @@ public class TunerResourceManagerService extends SystemService { private void removeLnbResource(int removingId) { LnbResource lnb = getLnbResource(removingId); + if (lnb == null) { + return; + } if (lnb.isInUse()) { releaseLnbInternal(lnb); } @@ -819,6 +953,39 @@ public class TunerResourceManagerService extends SystemService { @VisibleForTesting @Nullable + protected CasResource getCasResource(int systemId) { + return mCasResources.get(systemId); + } + + @VisibleForTesting + protected Map<Integer, CasResource> getCasResources() { + return mCasResources; + } + + private void addCasResource(CasResource newCas) { + // Update resource list and available id list + mCasResources.put(newCas.getSystemId(), newCas); + } + + private void removeCasResource(int removingId) { + CasResource cas = getCasResource(removingId); + if (cas == null) { + return; + } + for (int ownerId : cas.getOwnerClientIds()) { + getClientProfile(ownerId).releaseCas(); + } + mCasResources.remove(removingId); + } + + private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) { + // TODO: Sort with a treemap + + // select the first num client to release + } + + @VisibleForTesting + @Nullable protected ClientProfile getClientProfile(int clientId) { return mClientProfiles.get(clientId); } @@ -830,12 +997,7 @@ public class TunerResourceManagerService extends SystemService { } private void removeClientProfile(int clientId) { - for (int id : getClientProfile(clientId).getInUseFrontendIds()) { - getFrontendResource(id).removeOwner(); - for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) { - getFrontendResource(groupMemberId).removeOwner(); - } - } + reclaimingResourcesFromClient(getClientProfile(clientId)); mClientProfiles.remove(clientId); mListeners.remove(clientId); } @@ -847,6 +1009,9 @@ public class TunerResourceManagerService extends SystemService { for (Integer lnbId : profile.getInUseLnbIds()) { getLnbResource(lnbId).removeOwner(); } + if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) { + getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId()); + } profile.reclaimAllResources(); } diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 965304f3c433..21af3563b869 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -26,6 +26,7 @@ import android.media.tv.ITvInputManager; import android.media.tv.TvInputManager; import android.media.tv.TvInputService; import android.media.tv.tuner.frontend.FrontendSettings; +import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ResourceClientProfile; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; @@ -34,7 +35,6 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; -import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -236,12 +236,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE); } @@ -264,12 +260,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE); } @@ -296,12 +288,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(0); } @@ -334,23 +322,15 @@ public class TunerResourceManagerServiceTest { int[] frontendHandle = new int[1]; TunerFrontendRequest request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[0].getId()); request = new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[1].getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) @@ -394,32 +374,20 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isRelaimed()).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); + assertThat(listener.isRelaimed()).isFalse(); request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isRelaimed()).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); + assertThat(listener.isRelaimed()).isFalse(); } @Test @@ -456,12 +424,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[0].getId()); assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) @@ -470,12 +434,8 @@ public class TunerResourceManagerServiceTest { request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[1].getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) @@ -511,12 +471,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); int frontendId = mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]); assertThat(frontendId).isEqualTo(infos[0].getId()); assertThat(mTunerResourceManagerService @@ -534,6 +490,99 @@ public class TunerResourceManagerServiceTest { } @Test + public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientPriorities = {100, 500}; + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[0], listener, clientId0); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfile(clientId0[0]) + .setPriority(clientPriorities[0]); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[1], new TestResourcesReclaimListener(), clientId1); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfile(clientId1[0]) + .setPriority(clientPriorities[1]); + + // Init cas resources. + mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); + + CasSessionRequest request = new CasSessionRequest(clientId0[0], 1 /*casSystemId*/); + int[] casSessionHandle = new int[1]; + // Request for 2 cas sessions. + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCasSystemId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0]))); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue(); + + request = new CasSessionRequest(clientId1[0], 1); + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0]) + .getInUseCasSystemId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0]))); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); + assertThat(listener.isRelaimed()).isTrue(); + } + + @Test + public void releaseCasTest() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[1]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); + mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init cas resources. + mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); + + CasSessionRequest request = new CasSessionRequest(clientId[0], 1 /*casSystemId*/); + int[] casSessionHandle = new int[1]; + // Request for 1 cas sessions. + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCasSystemId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0]))); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); + + // Release cas + mTunerResourceManagerService.releaseCasSessionInternal(mTunerResourceManagerService + .getCasResource(1), clientId[0]); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEmpty(); + } + + @Test public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[2]; @@ -562,24 +611,16 @@ public class TunerResourceManagerServiceTest { TunerLnbRequest request = new TunerLnbRequest(clientId0[0]); int[] lnbHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestLnbInternal(request, lnbHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0])) .isEqualTo(lnbIds[0]); assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) .getInUseLnbIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(lnbIds[0]))); request = new TunerLnbRequest(clientId1[0]); - try { - assertThat(mTunerResourceManagerService - .requestLnbInternal(request, lnbHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0])) .isEqualTo(lnbIds[0]); assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0]) @@ -608,12 +649,8 @@ public class TunerResourceManagerServiceTest { TunerLnbRequest request = new TunerLnbRequest(clientId[0]); int[] lnbHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestLnbInternal(request, lnbHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestLnbInternal(request, lnbHandle)).isTrue(); int lnbId = mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]); assertThat(lnbId).isEqualTo(lnbIds[0]); @@ -647,12 +684,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[0].getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) |