diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 1bd0e2cd77712..46b29f5bc90a4 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -44,6 +44,7 @@ import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType; import android.media.tv.tuner.frontend.OnTuneEventListener; import android.media.tv.tuner.frontend.ScanCallback; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; @@ -297,6 +298,8 @@ public class Tuner implements AutoCloseable { private Executor mOnResourceLostListenerExecutor; private Integer mDemuxHandle; + private Integer mFrontendCiCamHandle; + private Integer mFrontendCiCamId; private Map> mDescramblers = new HashMap<>(); private List> mFilters = new ArrayList>(); @@ -469,6 +472,14 @@ public class Tuner implements AutoCloseable { if (mLnb != null) { mLnb.close(); } + if (mFrontendCiCamHandle != null) { + int result = nativeUnlinkCiCam(mFrontendCiCamId); + if (result == RESULT_SUCCESS) { + mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); + mFrontendCiCamId = null; + mFrontendCiCamHandle = null; + } + } synchronized (mDescramblers) { if (!mDescramblers.isEmpty()) { for (Map.Entry> d : mDescramblers.entrySet()) { @@ -917,7 +928,8 @@ public class Tuner implements AutoCloseable { public int connectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "linkFrontendToCiCam")) { - if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND) + && checkCiCamResource(ciCamId)) { return nativeLinkCiCam(ciCamId); } } @@ -936,7 +948,7 @@ public class Tuner implements AutoCloseable { */ @Result public int disconnectCiCam() { - if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + if (mDemuxHandle != null) { return nativeDisconnectCiCam(); } return RESULT_UNAVAILABLE; @@ -962,8 +974,14 @@ public class Tuner implements AutoCloseable { public int disconnectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "unlinkFrontendToCiCam")) { - if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { - return nativeUnlinkCiCam(ciCamId); + if (mFrontendCiCamHandle != null && mFrontendCiCamId == ciCamId) { + int result = nativeUnlinkCiCam(ciCamId); + if (result == RESULT_SUCCESS) { + mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); + mFrontendCiCamId = null; + mFrontendCiCamHandle = null; + } + return result; } } return RESULT_UNAVAILABLE; @@ -1360,6 +1378,18 @@ public class Tuner implements AutoCloseable { return descrambler; } + private boolean requestFrontendCiCam(int ciCamId) { + int[] ciCamHandle = new int[1]; + TunerCiCamRequest request = new TunerCiCamRequest(); + request.clientId = mClientId; + request.ciCamId = ciCamId; + boolean granted = mTunerResourceManager.requestCiCam(request, ciCamHandle); + if (granted) { + mFrontendCiCamHandle = ciCamHandle[0]; + } + return granted; + } + private boolean checkResource(int resourceType) { switch (resourceType) { case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: { @@ -1386,6 +1416,13 @@ public class Tuner implements AutoCloseable { return true; } + private boolean checkCiCamResource(int ciCamId) { + if (mFrontendCiCamHandle == null && !requestFrontendCiCam(ciCamId)) { + return false; + } + return true; + } + /* package */ void releaseLnb() { if (mLnbHandle != null) { // LNB handle can be null if it's opened by name. diff --git a/media/java/android/media/tv/tunerresourcemanager/Android.bp b/media/java/android/media/tv/tunerresourcemanager/Android.bp index cb2e19005aa38..02390bb7c31b3 100644 --- a/media/java/android/media/tv/tunerresourcemanager/Android.bp +++ b/media/java/android/media/tv/tunerresourcemanager/Android.bp @@ -4,6 +4,7 @@ filegroup { "aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl", "aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl", "aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl", + "aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl", "aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl", "aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl", "aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl", diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index be102d8acc10b..6f7adbc653182 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -74,6 +74,7 @@ public class TunerResourceManager { 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) @@ -84,7 +85,8 @@ public class TunerResourceManager { 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_MAX = 5; + 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; @@ -378,6 +380,38 @@ public class TunerResourceManager { return result; } + /** + * Requests a CiCam resource. + * + *

There are three possible scenarios: + *

    + *
  • If there is CiCam available, the API would send the id back. + * + *
  • If no CiCam is available but the current request info can show higher priority than + * other uses of the CiCam, the API will send + * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + *

    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. * @@ -481,6 +515,25 @@ public class TunerResourceManager { } } + /** + * 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. * diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 487b444eb627b..a1f6687a1b81d 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -19,6 +19,7 @@ package android.media.tv.tunerresourcemanager; import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; @@ -224,6 +225,31 @@ interface ITunerResourceManager { */ boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle); + /* + * This API is used by the Tuner framework to request an available CuCam. + * + *

    There are three possible scenarios: + *

      + *
    • If there is CiCam available, the API would send the handle back. + * + *
    • If no CiCma is available but the current request info can show higher priority than + * other uses of the ciCam, the API will send + * {@link ITunerResourceManagerCallback#onReclaimResources()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + *
    • If no CiCam can be granted, the API would return false. + *
        + * + *

        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. + * + * @return true if there is CiCam granted. + */ + boolean requestCiCam(in TunerCiCamRequest request, out int[] ciCamHandle); + /* * This API is used by the Tuner framework to request an available Lnb from the TunerHAL. * @@ -293,6 +319,19 @@ interface ITunerResourceManager { */ void releaseCasSession(in int casSessionHandle, int clientId); + /** + * 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. + */ + void releaseCiCam(in int ciCamHandle, int clientId); + /* * Notifies the TRM that the Lnb with the given handle was released. * diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl new file mode 100644 index 0000000000000..76f9f83ead82b --- /dev/null +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * A wrapper of a ciCam requests that contains all the request info of the client. + * + * @hide + */ +parcelable TunerCiCamRequest { + int clientId; + + int ciCamId; +} \ No newline at end of file diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java index 54ad1d268e560..4a81c95f0b8fa 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java @@ -25,7 +25,7 @@ import java.util.Set; * * @hide */ -public final class CasResource { +public class CasResource { private final int mSystemId; @@ -38,7 +38,7 @@ public final class CasResource { */ private Map mOwnerClientIdsToSessionNum = new HashMap<>(); - private CasResource(Builder builder) { + CasResource(Builder builder) { this.mSystemId = builder.mSystemId; this.mMaxSessionNum = builder.mMaxSessionNum; this.mAvailableSessionNum = builder.mMaxSessionNum; @@ -111,7 +111,7 @@ public final class CasResource { public static class Builder { private int mSystemId; - private int mMaxSessionNum; + protected int mMaxSessionNum; Builder(int systemId) { this.mSystemId = systemId; @@ -138,7 +138,7 @@ public final class CasResource { } } - private String ownersMapToString() { + protected String ownersMapToString() { StringBuilder string = new StringBuilder("{"); for (int clienId : mOwnerClientIdsToSessionNum.keySet()) { string.append(" clientId=") diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java new file mode 100644 index 0000000000000..31149f3590b8a --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java @@ -0,0 +1,70 @@ +/* + * 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; + +/** + * A CiCam resource object used by the Tuner Resource Manager to record the CiCam + * information. + * + * @hide + */ +public final class CiCamResource extends CasResource { + private CiCamResource(Builder builder) { + super(builder); + } + + @Override + public String toString() { + return "CiCamResource[systemId=" + this.getSystemId() + + ", isFullyUsed=" + (this.isFullyUsed()) + + ", maxSessionNum=" + this.getMaxSessionNum() + + ", ownerClients=" + ownersMapToString() + "]"; + } + + public int getCiCamId() { + return this.getSystemId(); + } + + /** + * Builder class for {@link CiCamResource}. + */ + public static class Builder extends CasResource.Builder { + Builder(int systemId) { + super(systemId); + } + + /** + * Builder for {@link CasResource}. + * + * @param maxSessionNum the max session num the current Cas has. + */ + public Builder maxSessionNum(int maxSessionNum) { + super.mMaxSessionNum = maxSessionNum; + return this; + } + + /** + * Build a {@link CiCamResource}. + * + * @return {@link CiCamResource}. + */ + @Override + public CiCamResource build() { + CiCamResource ciCam = new CiCamResource(this); + return ciCam; + } + } +} 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 cad8100c88dba..5723e1dcceb59 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -84,6 +84,11 @@ public final class ClientProfile { */ private int mUsingCasSystemId = INVALID_RESOURCE_ID; + /** + * CiCam id that is used by the client. + */ + private int mUsingCiCamId = INVALID_RESOURCE_ID; + /** * Optional arbitrary priority value given by the client. * @@ -237,6 +242,26 @@ public final class ClientProfile { mUsingCasSystemId = INVALID_RESOURCE_ID; } + /** + * Set when the client starts to connect to a CiCam. + * + * @param ciCamId ciCam being used. + */ + public void useCiCam(int ciCamId) { + mUsingCiCamId = ciCamId; + } + + public int getInUseCiCamId() { + return mUsingCiCamId; + } + + /** + * Called when the client disconnect to a CiCam. + */ + public void releaseCiCam() { + mUsingCiCamId = INVALID_RESOURCE_ID; + } + /** * Called to reclaim all the resources being used by the current client. */ @@ -245,6 +270,7 @@ public final class ClientProfile { mShareFeClientIds.clear(); mUsingLnbHandles.clear(); mUsingCasSystemId = INVALID_RESOURCE_ID; + mUsingCiCamId = 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 5f885c94fd022..072bdd2d15069 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -27,6 +27,7 @@ import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ITunerResourceManager; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; @@ -71,6 +72,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde private Map mLnbResources = new HashMap<>(); // Map of the current available Cas resources private Map mCasResources = new HashMap<>(); + // Map of the current available CiCam resources + private Map mCiCamResources = new HashMap<>(); @GuardedBy("mLock") private Map mListeners = new HashMap<>(); @@ -293,6 +296,22 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } + @Override + public boolean requestCiCam(@NonNull TunerCiCamRequest request, + @NonNull int[] ciCamHandle) throws RemoteException { + enforceTrmAccessPermission("requestCiCam"); + if (ciCamHandle == null) { + throw new RemoteException("ciCamHandle can't be null"); + } + synchronized (mLock) { + if (!checkClientExists(request.clientId)) { + throw new RemoteException("Request ciCam from unregistered client:" + + request.clientId); + } + return requestCiCamInternal(request, ciCamHandle); + } + } + @Override public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) throws RemoteException { @@ -377,6 +396,34 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } + @Override + public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException { + enforceTrmAccessPermission("releaseCiCam"); + if (!validateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) { + throw new RemoteException("ciCamHandle can't be invalid"); + } + synchronized (mLock) { + if (!checkClientExists(clientId)) { + throw new RemoteException("Release ciCam from unregistered client:" + clientId); + } + int ciCamId = getClientProfile(clientId).getInUseCiCamId(); + if (ciCamId != getResourceIdFromHandle(ciCamHandle)) { + throw new RemoteException("The client " + clientId + " is not the owner of " + + "the releasing ciCam."); + } + CiCamResource ciCam = getCiCamResource(ciCamId); + if (ciCam == null) { + throw new RemoteException("Releasing ciCam does not exist."); + } + if (!ciCam.getOwnerClientIds().contains(clientId)) { + throw new RemoteException( + "Client is not the current owner of the releasing ciCam."); + } + releaseCiCamInternal(ciCam, clientId); + } + } + @Override public void releaseLnb(int lnbHandle, int clientId) throws RemoteException { enforceTunerAccessPermission("releaseLnb"); @@ -586,24 +633,33 @@ public class TunerResourceManagerService extends SystemService implements IBinde // If maxSessionNum is 0, removing the Cas Resource. if (maxSessionNum == 0) { removeCasResource(casSystemId); + removeCiCamResource(casSystemId); return; } // If the Cas exists, updates the Cas Resource accordingly. CasResource cas = getCasResource(casSystemId); + CiCamResource ciCam = getCiCamResource(casSystemId); if (cas != null) { if (cas.getUsedSessionNum() > maxSessionNum) { // Sort and release the short number of Cas resources. int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum; - releaseLowerPriorityClientCasResources(releasingCasResourceNum); + // TODO: handle CiCam session update. } cas.updateMaxSessionNum(maxSessionNum); + if (ciCam != null) { + ciCam.updateMaxSessionNum(maxSessionNum); + } return; } // Add the new Cas Resource. cas = new CasResource.Builder(casSystemId) .maxSessionNum(maxSessionNum) .build(); + ciCam = new CiCamResource.Builder(casSystemId) + .maxSessionNum(maxSessionNum) + .build(); addCasResource(cas); + addCiCamResource(ciCam); } @VisibleForTesting @@ -784,6 +840,55 @@ public class TunerResourceManagerService extends SystemService implements IBinde return false; } + @VisibleForTesting + protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) { + if (DEBUG) { + Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")"); + } + CiCamResource ciCam = getCiCamResource(request.ciCamId); + // Unregistered Cas System is treated as having unlimited sessions. + if (ciCam == null) { + ciCam = new CiCamResource.Builder(request.ciCamId) + .maxSessionNum(Integer.MAX_VALUE) + .build(); + addCiCamResource(ciCam); + } + ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); + int lowestPriorityOwnerId = -1; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + if (!ciCam.isFullyUsed()) { + ciCamHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); + updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); + return true; + } + for (int ownerId : ciCam.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 CiCam 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_FRONTEND_CICAM)) { + return false; + } + ciCamHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); + updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); + return true; + } + return false; + } + @VisibleForTesting protected boolean isHigherPriorityInternal(ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) { @@ -842,6 +947,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde updateCasClientMappingOnRelease(cas, ownerClientId); } + @VisibleForTesting + protected void releaseCiCamInternal(CiCamResource ciCam, int ownerClientId) { + if (DEBUG) { + Slog.d(TAG, "releaseCiCamInternal(ciCamId=" + ciCam.getCiCamId() + ")"); + } + updateCiCamClientMappingOnRelease(ciCam, ownerClientId); + } + @VisibleForTesting protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { if (DEBUG) { @@ -1019,6 +1132,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde ownerProfile.useCas(grantingId); } + private void updateCiCamClientMappingOnNewGrant(int grantingId, int ownerClientId) { + CiCamResource grantingCiCam = getCiCamResource(grantingId); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + grantingCiCam.setOwner(ownerClientId); + ownerProfile.useCiCam(grantingId); + } + private void updateCasClientMappingOnRelease( @NonNull CasResource releasingCas, int ownerClientId) { ClientProfile ownerProfile = getClientProfile(ownerClientId); @@ -1026,6 +1146,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde ownerProfile.releaseCas(); } + private void updateCiCamClientMappingOnRelease( + @NonNull CiCamResource releasingCiCam, int ownerClientId) { + ClientProfile ownerProfile = getClientProfile(ownerClientId); + releasingCiCam.removeOwner(ownerClientId); + ownerProfile.releaseCiCam(); + } + /** * Get the owner client's priority. * @@ -1117,16 +1244,32 @@ public class TunerResourceManagerService extends SystemService implements IBinde return mCasResources.get(systemId); } + @VisibleForTesting + @Nullable + protected CiCamResource getCiCamResource(int ciCamId) { + return mCiCamResources.get(ciCamId); + } + @VisibleForTesting protected Map getCasResources() { return mCasResources; } + @VisibleForTesting + protected Map getCiCamResources() { + return mCiCamResources; + } + private void addCasResource(CasResource newCas) { // Update resource list and available id list mCasResources.put(newCas.getSystemId(), newCas); } + private void addCiCamResource(CiCamResource newCiCam) { + // Update resource list and available id list + mCiCamResources.put(newCiCam.getCiCamId(), newCiCam); + } + private void removeCasResource(int removingId) { CasResource cas = getCasResource(removingId); if (cas == null) { @@ -1138,6 +1281,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde mCasResources.remove(removingId); } + private void removeCiCamResource(int removingId) { + CiCamResource ciCam = getCiCamResource(removingId); + if (ciCam == null) { + return; + } + for (int ownerId : ciCam.getOwnerClientIds()) { + getClientProfile(ownerId).releaseCiCam(); + } + mCiCamResources.remove(removingId); + } + private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) { // TODO: Sort with a treemap @@ -1186,6 +1340,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) { getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId()); } + // Clear CiCam + if (profile.getInUseCiCamId() != ClientProfile.INVALID_RESOURCE_ID) { + getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId()); + } // Clear Frontend clearFrontendAndClientMapping(profile); 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 e8479a8f2790e..385837009b2f6 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 @@ -29,6 +29,7 @@ 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.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; @@ -551,6 +552,62 @@ public class TunerResourceManagerServiceTest { assertThat(listener.isReclaimed()).isTrue(); } + @Test + public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = resourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = 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 cicam/cas resources. + mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); + + TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/); + int[] ciCamHandle = new int[1]; + // Request for 2 ciCam sessions. + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCiCamId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet(Arrays.asList(clientId0[0]))); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue(); + + request = tunerCiCamRequest(clientId1[0], 1); + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0]) + .getInUseCiCamId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet(Arrays.asList(clientId1[0]))); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + assertThat(listener.isReclaimed()).isTrue(); + } + @Test public void releaseCasTest() { // Register clients @@ -588,6 +645,43 @@ public class TunerResourceManagerServiceTest { .getOwnerClientIds()).isEmpty(); } + @Test + public void releaseCiCamTest() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[1]; + profiles[0] = 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*/); + + TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/); + int[] ciCamHandle = new int[1]; + // Request for 1 ciCam sessions. + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCiCamId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet(Arrays.asList(clientId[0]))); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + + // Release ciCam + mTunerResourceManagerService.releaseCiCamInternal(mTunerResourceManagerService + .getCiCamResource(1), clientId[0]); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEmpty(); + } + @Test public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() { // Register clients @@ -1087,4 +1181,11 @@ public class TunerResourceManagerServiceTest { request.casSystemId = casSystemId; return request; } + + private TunerCiCamRequest tunerCiCamRequest(int clientId, int ciCamId) { + TunerCiCamRequest request = new TunerCiCamRequest(); + request.clientId = clientId; + request.ciCamId = ciCamId; + return request; + } }