Enable capability based demux management

Adding the following 3 new SystemAPIs along with new class (DemuxInfo),
which holds the capabilities information such as DemuxFilterMainType
 - API to configue desired demux capabilities such as
   DemuxFilterMainType
 - API to get the info of currently held demux
 - API to get the list of infos of all the demux in the system

Also, with this change Demux resource will now be managed under
TunerResourceManager.

Bug: 239211919
Bug: 239227510
Test: atest TunerTest TunerResourceManagerServiceTest VtsHalTvTunerTargetTest

Change-Id: Ifa3a30bbd6ea4ad1ded5eedca60eb240887c0303
This commit is contained in:
Kensuke Miyagi
2022-11-07 10:35:41 -08:00
parent 613dc26088
commit 318fe68496
16 changed files with 896 additions and 46 deletions

View File

@@ -7461,6 +7461,7 @@ package android.media.tv.tuner {
method public int getAudioFilterCount();
method public int getDemuxCount();
method public int getFilterCapabilities();
method @NonNull public int[] getFilterTypeCapabilityList();
method @NonNull @Size(5) public int[] getLinkCapabilities();
method public int getPcrFilterCount();
method public int getPesFilterCount();
@@ -7473,6 +7474,12 @@ package android.media.tv.tuner {
method public boolean isTimeFilterSupported();
}
public class DemuxInfo {
ctor public DemuxInfo(int);
method public int getFilterTypes();
method public void setFilterTypes(int);
}
public class Descrambler implements java.lang.AutoCloseable {
method public int addPid(int, int, @Nullable android.media.tv.tuner.filter.Filter);
method public void close();
@@ -7525,6 +7532,7 @@ package android.media.tv.tuner {
method public void clearResourceLostListener();
method public void close();
method public void closeFrontend();
method public int configureDemux(@Nullable android.media.tv.tuner.DemuxInfo);
method public int connectCiCam(int);
method public int connectFrontendToCiCam(int);
method public int disconnectCiCam();
@@ -7532,6 +7540,7 @@ package android.media.tv.tuner {
method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
method public long getAvSyncTime(int);
method @Nullable public java.util.List<android.media.tv.tuner.frontend.FrontendInfo> getAvailableFrontendInfos();
method @Nullable public android.media.tv.tuner.DemuxInfo getCurrentDemuxInfo();
method @Nullable public String getCurrentFrontendHardwareInfo();
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();

View File

@@ -36,8 +36,14 @@ import java.lang.annotation.RetentionPolicy;
public class DemuxCapabilities {
/** @hide */
@IntDef(value = {Filter.TYPE_TS, Filter.TYPE_MMTP, Filter.TYPE_IP, Filter.TYPE_TLV,
Filter.TYPE_ALP})
@IntDef(flag = true, prefix = { "TYPE_" }, value = {
Filter.TYPE_UNDEFINED,
Filter.TYPE_TS,
Filter.TYPE_MMTP,
Filter.TYPE_IP,
Filter.TYPE_TLV,
Filter.TYPE_ALP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface FilterCapabilities {}
@@ -51,14 +57,16 @@ public class DemuxCapabilities {
private final int mPesFilterCount;
private final int mPcrFilterCount;
private final long mSectionFilterLength;
private final int mFilterCaps;
private final @FilterCapabilities int mFilterCaps;
private final @FilterCapabilities int[] mFilterCapsList;
private final int[] mLinkCaps;
private final boolean mSupportTimeFilter;
// Used by JNI
private DemuxCapabilities(int demuxCount, int recordCount, int playbackCount, int tsFilterCount,
int sectionFilterCount, int audioFilterCount, int videoFilterCount, int pesFilterCount,
int pcrFilterCount, long sectionFilterLength, int filterCaps, int[] linkCaps,
int pcrFilterCount, long sectionFilterLength, int filterCaps,
@FilterCapabilities int[] filterCapsList, @FilterCapabilities int[] linkCaps,
boolean timeFilter) {
mDemuxCount = demuxCount;
mRecordCount = recordCount;
@@ -71,6 +79,7 @@ public class DemuxCapabilities {
mPcrFilterCount = pcrFilterCount;
mSectionFilterLength = sectionFilterLength;
mFilterCaps = filterCaps;
mFilterCapsList = filterCapsList;
mLinkCaps = linkCaps;
mSupportTimeFilter = timeFilter;
}
@@ -147,6 +156,24 @@ public class DemuxCapabilities {
return mFilterCaps;
}
/**
* Gets the list of filter main type capabilities in bit field.
*
* <p>Each element in the returned array represents the supported filter main types
* represented as bitwise OR of the types in {@link FilterConfiguration}.
* <p>Whereas getFilterCapabilities() returns the bitwise OR value of all the supported filter
* types in the system, this API returns a list of supported filter types in the system with
* each entry representing the supported filter types per demux resource.
*
* @return an array of supported filter main types for the demux resources in the system
* an empty array should be returned for devices with Tuner HAL version 2 and below
*/
@FilterCapabilities
@NonNull
public int[] getFilterTypeCapabilityList() {
return mFilterCapsList;
}
/**
* Gets link capabilities.
*

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2022 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.tuner;
import android.annotation.SystemApi;
import android.media.tv.tuner.DemuxCapabilities.FilterCapabilities;
/**
* This class is used to specify information of a demux.
*
* @hide
*/
@SystemApi
public class DemuxInfo {
// Bitwise OR of filter types
private int mFilterTypes;
public DemuxInfo(@FilterCapabilities int filterTypes) {
setFilterTypes(filterTypes);
}
/**
* Gets the filter types
*
* @return the filter types
*/
@FilterCapabilities
public int getFilterTypes() {
return mFilterTypes;
}
/**
* Sets the filter types
*
* @param filterTypes the filter types to set
*/
public void setFilterTypes(@FilterCapabilities int filterTypes) {
mFilterTypes = filterTypes;
}
}

View File

@@ -281,6 +281,7 @@ public class Tuner implements AutoCloseable {
private final TunerResourceManager mTunerResourceManager;
private final int mClientId;
private static int sTunerVersion = TunerVersionChecker.TUNER_VERSION_UNKNOWN;
private DemuxInfo mDesiredDemuxInfo = new DemuxInfo(Filter.TYPE_UNDEFINED);
private Frontend mFrontend;
private EventHandler mHandler;
@@ -895,12 +896,7 @@ public class Tuner implements AutoCloseable {
}
}
private void releaseAll() {
// release CiCam before frontend because frontend handle is needed to unlink CiCam
releaseCiCam();
releaseFrontend();
private void closeLnb() {
mLnbLock.lock();
try {
// mLnb will be non-null only for owner tuner
@@ -917,8 +913,23 @@ public class Tuner implements AutoCloseable {
} finally {
mLnbLock.unlock();
}
}
private void releaseFilters() {
synchronized (mFilters) {
if (!mFilters.isEmpty()) {
for (WeakReference<Filter> weakFilter : mFilters) {
Filter filter = weakFilter.get();
if (filter != null) {
filter.close();
}
}
mFilters.clear();
}
}
}
private void releaseDescramblers() {
synchronized (mDescramblers) {
if (!mDescramblers.isEmpty()) {
for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) {
@@ -931,19 +942,9 @@ public class Tuner implements AutoCloseable {
mDescramblers.clear();
}
}
}
synchronized (mFilters) {
if (!mFilters.isEmpty()) {
for (WeakReference<Filter> weakFilter : mFilters) {
Filter filter = weakFilter.get();
if (filter != null) {
filter.close();
}
}
mFilters.clear();
}
}
private void releaseDemux() {
mDemuxLock.lock();
try {
if (mDemuxHandle != null) {
@@ -957,9 +958,17 @@ public class Tuner implements AutoCloseable {
} finally {
mDemuxLock.unlock();
}
}
private void releaseAll() {
// release CiCam before frontend because frontend handle is needed to unlink CiCam
releaseCiCam();
releaseFrontend();
closeLnb();
releaseDescramblers();
releaseFilters();
releaseDemux();
mTunerResourceManager.unregisterClientProfile(mClientId);
}
/**
@@ -1025,6 +1034,7 @@ public class Tuner implements AutoCloseable {
private native DvrPlayback nativeOpenDvrPlayback(long bufferSize);
private native DemuxCapabilities nativeGetDemuxCapabilities();
private native DemuxInfo nativeGetDemuxInfo(int demuxHandle);
private native int nativeCloseDemux(int handle);
private native int nativeCloseFrontend(int handle);
@@ -1865,6 +1875,30 @@ public class Tuner implements AutoCloseable {
}
}
/**
* Gets DemuxInfo of the currently held demux
*
* @return A {@link DemuxInfo} of currently held demux resource.
* Returns null if no demux resource is held.
*/
@Nullable
public DemuxInfo getCurrentDemuxInfo() {
mDemuxLock.lock();
try {
if (mDemuxHandle == null) {
return null;
}
return nativeGetDemuxInfo(mDemuxHandle);
} finally {
mDemuxLock.unlock();
}
}
/** @hide */
public DemuxInfo getDesiredDemuxInfo() {
return mDesiredDemuxInfo;
}
private void onFrontendEvent(int eventType) {
Log.d(TAG, "Got event from tuning. Event type: " + eventType + " for " + this);
synchronized (mOnTuneEventLock) {
@@ -2173,6 +2207,11 @@ public class Tuner implements AutoCloseable {
/**
* Opens a filter object based on the given types and buffer size.
*
* <p>For TUNER_VERSION_3_0 and above, configureDemuxInternal() will be called with mainType.
* However, unlike when configureDemux() is called directly, the desired filter types will not
* be changed when previously set desired filter types are the superset of the newly desired
* ones.
*
* @param mainType the main type of the filter.
* @param subType the subtype of the filter.
* @param bufferSize the buffer size of the filter to be opened in bytes. The buffer holds the
@@ -2188,6 +2227,15 @@ public class Tuner implements AutoCloseable {
@Nullable FilterCallback cb) {
mDemuxLock.lock();
try {
int tunerMajorVersion = TunerVersionChecker.getMajorVersion(sTunerVersion);
if (sTunerVersion >= TunerVersionChecker.TUNER_VERSION_3_0) {
DemuxInfo demuxInfo = new DemuxInfo(mainType);
int res = configureDemuxInternal(demuxInfo, false /* reduceDesiredFilterTypes */);
if (res != RESULT_SUCCESS) {
Log.e(TAG, "openFilter called for unsupported mainType: " + mainType);
return null;
}
}
if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
return null;
}
@@ -2470,10 +2518,109 @@ public class Tuner implements AutoCloseable {
return filter;
}
/**
* Configures the desired {@link DemuxInfo}
*
* <p>The already held demux and filters will be released when desiredDemuxInfo is null or the
* desireDemuxInfo.getFilterTypes() is not supported by the already held demux.
*
* @param desiredDemuxInfo the desired {@link DemuxInfo}, which includes information such as
* filterTypes ({@link DemuxFilterMainType}).
* @return result status of configure demux operation. {@link #RESULT_UNAVAILABLE} is returned
* when a) the desired capabilities are not supported by the system,
* b) this API is called on unsupported version, or
* c) either getDemuxCapabilities or getFilterTypeCapabilityList()
* returns an empty array
*/
@Result
public int configureDemux(@Nullable DemuxInfo desiredDemuxInfo) {
int tunerMajorVersion = TunerVersionChecker.getMajorVersion(sTunerVersion);
if (sTunerVersion < TunerVersionChecker.TUNER_VERSION_3_0) {
Log.e(TAG, "configureDemux() is not supported for tuner version:"
+ TunerVersionChecker.getMajorVersion(sTunerVersion) + "."
+ TunerVersionChecker.getMinorVersion(sTunerVersion) + ".");
return RESULT_UNAVAILABLE;
}
synchronized (mDemuxLock) {
return configureDemuxInternal(desiredDemuxInfo, true /* reduceDesiredFilterTypes */);
}
}
private int configureDemuxInternal(@Nullable DemuxInfo desiredDemuxInfo,
boolean reduceDesiredFilterTypes) {
// release the currently held demux if the desired demux info is null
if (desiredDemuxInfo == null) {
if (mDemuxHandle != null) {
releaseFilters();
releaseDemux();
}
return RESULT_SUCCESS;
}
int desiredFilterTypes = desiredDemuxInfo.getFilterTypes();
// just update and return success if the desiredFilterTypes is equal to or a subset of
// a previously configured value
if ((mDesiredDemuxInfo.getFilterTypes() & desiredFilterTypes)
== desiredFilterTypes) {
if (reduceDesiredFilterTypes) {
mDesiredDemuxInfo.setFilterTypes(desiredFilterTypes);
}
return RESULT_SUCCESS;
}
// check if the desire capability is supported
DemuxCapabilities caps = nativeGetDemuxCapabilities();
if (caps == null) {
Log.e(TAG, "configureDemuxInternal:failed to get DemuxCapabilities");
return RESULT_UNAVAILABLE;
}
int[] filterCapsList = caps.getFilterTypeCapabilityList();
if (filterCapsList.length <= 0) {
Log.e(TAG, "configureDemuxInternal: getFilterTypeCapabilityList()"
+ " returned an empty array");
return RESULT_UNAVAILABLE;
}
boolean supported = false;
for (int filterCaps : filterCapsList) {
if ((desiredFilterTypes & filterCaps) == desiredFilterTypes) {
supported = true;
break;
}
}
if (!supported) {
Log.e(TAG, "configureDemuxInternal: requested caps:" + desiredFilterTypes
+ " is not supported by the system");
return RESULT_UNAVAILABLE;
}
// close demux if not compatible
if (mDemuxHandle != null) {
if (desiredFilterTypes != Filter.TYPE_UNDEFINED) {
// Release the existing demux only if
// the desired caps is not supported
DemuxInfo currentDemuxInfo = nativeGetDemuxInfo(mDemuxHandle);
if (currentDemuxInfo != null) {
if ((desiredFilterTypes & currentDemuxInfo.getFilterTypes())
!= desiredFilterTypes) {
releaseFilters();
releaseDemux();
}
}
}
}
mDesiredDemuxInfo.setFilterTypes(desiredFilterTypes);
return RESULT_SUCCESS;
}
private boolean requestDemux() {
int[] demuxHandle = new int[1];
TunerDemuxRequest request = new TunerDemuxRequest();
request.clientId = mClientId;
request.desiredFilterTypes = mDesiredDemuxInfo.getFilterTypes();
boolean granted = mTunerResourceManager.requestDemux(request, demuxHandle);
if (granted) {
mDemuxHandle = demuxHandle[0];

View File

@@ -288,6 +288,23 @@ public class TunerResourceManager {
}
}
/**
* Updates the current TRM of the TunerHAL Demux information.
*
* <p><strong>Note:</strong> 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.
*

View File

@@ -20,6 +20,7 @@ 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.TunerDemuxInfo;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
@@ -129,6 +130,17 @@ interface ITunerResourceManager {
*/
void updateCasInfo(in int casSystemId, in int maxSessionNum);
/*
* Updates the available Demux resources information on the current device.
*
* <p><strong>Note:</strong> This update must happen before the first
* {@link #requestDemux(TunerDemux,int[])} and {@link #releaseDemux(int, int)}
* call.
*
* @param infos an array of the available {@link TunerDemux} information.
*/
void setDemuxInfoList(in TunerDemuxInfo[] infos);
/*
* Updates the available Lnb resource information on the current device.
*

View File

@@ -0,0 +1,35 @@
/**
* Copyright 2022, 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;
/**
* TunerDemuxInfo interface that carries tuner demux information.
*
* This is used to update the TunerResourceManager demux resources.
* @hide
*/
parcelable TunerDemuxInfo {
/**
* Demux handle
*/
int handle;
/**
* Supported filter types (defined in {@link android.media.tv.tuner.filter.Filter})
*/
int filterTypes;
}

View File

@@ -23,4 +23,9 @@ package android.media.tv.tunerresourcemanager;
*/
parcelable TunerDemuxRequest {
int clientId;
}
/**
* Desired filter types (defined in {@link android.media.tv.tuner.filter.Filter})
*/
int desiredFilterTypes;
}

View File

@@ -47,6 +47,7 @@
#include <aidl/android/hardware/tv/tuner/DemuxFilterSubType.h>
#include <aidl/android/hardware/tv/tuner/DemuxFilterTemiEvent.h>
#include <aidl/android/hardware/tv/tuner/DemuxFilterTsRecordEvent.h>
#include <aidl/android/hardware/tv/tuner/DemuxInfo.h>
#include <aidl/android/hardware/tv/tuner/DemuxIpAddress.h>
#include <aidl/android/hardware/tv/tuner/DemuxIpFilterSettings.h>
#include <aidl/android/hardware/tv/tuner/DemuxIpFilterType.h>
@@ -192,6 +193,7 @@ using ::aidl::android::hardware::tv::tuner::DemuxFilterSettings;
using ::aidl::android::hardware::tv::tuner::DemuxFilterSubType;
using ::aidl::android::hardware::tv::tuner::DemuxFilterTemiEvent;
using ::aidl::android::hardware::tv::tuner::DemuxFilterTsRecordEvent;
using ::aidl::android::hardware::tv::tuner::DemuxInfo;
using ::aidl::android::hardware::tv::tuner::DemuxIpAddress;
using ::aidl::android::hardware::tv::tuner::DemuxIpAddressIpAddress;
using ::aidl::android::hardware::tv::tuner::DemuxIpFilterSettings;
@@ -2068,7 +2070,7 @@ jobject JTuner::getDemuxCaps() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[IZ)V");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[I[IZ)V");
jint numDemux = caps->numDemux;
jint numRecord = caps->numRecord;
@@ -2080,16 +2082,49 @@ jobject JTuner::getDemuxCaps() {
jint numPesFilter = caps->numPesFilter;
jint numPcrFilter = caps->numPcrFilter;
jlong numBytesInSectionFilter = caps->numBytesInSectionFilter;
jint filterCaps = caps->filterCaps;
jboolean bTimeFilter = caps->bTimeFilter;
jint filterCaps = caps->filterCaps;
jintArray filterCapsList = nullptr;
vector<DemuxInfo> demuxInfoList;
sTunerClient->getDemuxInfoList(&demuxInfoList);
if (demuxInfoList.size() > 0) {
vector<int32_t> demuxFilterTypesList;
for (int i = 0; i < demuxInfoList.size(); i++) {
demuxFilterTypesList.push_back(demuxInfoList[i].filterTypes);
}
filterCapsList = env->NewIntArray(demuxFilterTypesList.size());
env->SetIntArrayRegion(filterCapsList, 0, demuxFilterTypesList.size(),
reinterpret_cast<jint *>(&demuxFilterTypesList[0]));
} else {
filterCapsList = env->NewIntArray(0);
}
jintArray linkCaps = env->NewIntArray(caps->linkCaps.size());
env->SetIntArrayRegion(linkCaps, 0, caps->linkCaps.size(),
reinterpret_cast<jint *>(&caps->linkCaps[0]));
return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter,
numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter,
numBytesInSectionFilter, filterCaps, linkCaps, bTimeFilter);
numBytesInSectionFilter, filterCaps, filterCapsList, linkCaps, bTimeFilter);
}
jobject JTuner::getDemuxInfo(int handle) {
if (sTunerClient == nullptr) {
ALOGE("tuner is not initialized");
return nullptr;
}
shared_ptr<DemuxInfo> demuxInfo = sTunerClient->getDemuxInfo(handle);
if (demuxInfo == nullptr) {
return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/DemuxInfo");
jmethodID infoInit = env->GetMethodID(clazz, "<init>", "(I)V");
jint filterTypes = demuxInfo->filterTypes;
return env->NewObject(clazz, infoInit, filterTypes);
}
jobject JTuner::getFrontendStatus(jintArray types) {
@@ -4469,6 +4504,11 @@ static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz)
return tuner->getDemuxCaps();
}
static jobject android_media_tv_Tuner_get_demux_info(JNIEnv* env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->getDemuxInfo(handle);
}
static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
return (jint)tuner->openDemux(handle);
@@ -4860,6 +4900,8 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_open_dvr_playback },
{ "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
(void *)android_media_tv_Tuner_get_demux_caps },
{ "nativeGetDemuxInfo", "(I)Landroid/media/tv/tuner/DemuxInfo;",
(void *)android_media_tv_Tuner_get_demux_info },
{ "nativeOpenDemuxByhandle", "(I)I", (void *)android_media_tv_Tuner_open_demux },
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
{ "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend },

View File

@@ -198,6 +198,7 @@ struct JTuner : public RefBase {
jobject openDescrambler();
jobject openDvr(DvrType type, jlong bufferSize);
jobject getDemuxCaps();
jobject getDemuxInfo(int handle);
jobject getFrontendStatus(jintArray types);
Result openDemux(int handle);
jint close();

View File

@@ -22,7 +22,6 @@
#include "TunerClient.h"
using ::aidl::android::hardware::tv::tuner::FrontendStatusType;
using ::aidl::android::hardware::tv::tuner::FrontendType;
namespace android {
@@ -108,6 +107,27 @@ sp<DemuxClient> TunerClient::openDemux(int32_t demuxHandle) {
return nullptr;
}
shared_ptr<DemuxInfo> TunerClient::getDemuxInfo(int32_t demuxHandle) {
if (mTunerService != nullptr) {
DemuxInfo aidlDemuxInfo;
Status s = mTunerService->getDemuxInfo(demuxHandle, &aidlDemuxInfo);
if (!s.isOk()) {
return nullptr;
}
return make_shared<DemuxInfo>(aidlDemuxInfo);
}
return nullptr;
}
void TunerClient::getDemuxInfoList(vector<DemuxInfo>* demuxInfoList) {
if (mTunerService != nullptr) {
Status s = mTunerService->getDemuxInfoList(demuxInfoList);
if (!s.isOk()) {
demuxInfoList->clear();
}
}
}
shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
if (mTunerService != nullptr) {
DemuxCapabilities aidlCaps;

View File

@@ -31,6 +31,7 @@
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::hardware::tv::tuner::DemuxCapabilities;
using ::aidl::android::hardware::tv::tuner::DemuxInfo;
using ::aidl::android::hardware::tv::tuner::FrontendInfo;
using ::aidl::android::hardware::tv::tuner::FrontendType;
using ::aidl::android::hardware::tv::tuner::Result;
@@ -82,6 +83,21 @@ public:
*/
sp<DemuxClient> openDemux(int32_t demuxHandle);
/**
* Retrieve the DemuxInfo of a specific demux
*
* @param demuxHandle the handle of the demux to query demux info for
* @return the demux info
*/
shared_ptr<DemuxInfo> getDemuxInfo(int32_t demuxHandle);
/**
* Retrieve a list of demux info
*
* @return a list of DemuxInfo
*/
void getDemuxInfoList(vector<DemuxInfo>* demuxInfoList);
/**
* Retrieve the Demux capabilities.
*

View File

@@ -79,6 +79,8 @@ public final class ClientProfile {
*/
private Set<Integer> mShareFeClientIds = new HashSet<>();
private Set<Integer> mUsingDemuxHandles = new HashSet<>();
/**
* List of the Lnb handles that are used by the current client.
*/
@@ -232,6 +234,31 @@ public final class ClientProfile {
mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
}
/**
* Set when the client starts to use a Demux.
*
* @param demuxHandle the demux being used.
*/
public void useDemux(int demuxHandle) {
mUsingDemuxHandles.add(demuxHandle);
}
/**
* Get the set of demux handles in use.
*/
public Set<Integer> getInUseDemuxHandles() {
return mUsingDemuxHandles;
}
/**
* Called when the client released a Demux.
*
* @param demuxHandle the demux handl being released.
*/
public void releaseDemux(int demuxHandle) {
mUsingDemuxHandles.remove(demuxHandle);
}
/**
* Set when the client starts to use an Lnb.
*

View File

@@ -0,0 +1,97 @@
/*
* Copyright 2022 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 demux resource object used by the Tuner Resource Manager to record the tuner Demux
* information.
*
* @hide
*/
public final class DemuxResource extends TunerResourceBasic {
private final int mFilterTypes;
private DemuxResource(Builder builder) {
super(builder);
this.mFilterTypes = builder.mFilterTypes;
}
public int getFilterTypes() {
return mFilterTypes;
}
@Override
public String toString() {
return "DemuxResource[handle=" + this.mHandle + ", filterTypes="
+ this.mFilterTypes + ", isInUse=" + this.mIsInUse
+ ", ownerClientId=" + this.mOwnerClientId + "]";
}
/**
* Returns true if the desired {@link DemuxFilterMainTypes} is supported.
*/
public boolean hasSufficientCaps(int desiredCaps) {
return desiredCaps == (desiredCaps & mFilterTypes);
}
/**
* Returns the number of supported {@link DemuxFilterMainTypes}.
*/
public int getNumOfCaps() {
int mask = 1;
int numOfCaps = 0;
for (int i = 0; i < Integer.SIZE; i++) {
if ((mFilterTypes & mask) == mask) {
numOfCaps = numOfCaps + 1;
}
mask = mask << 1;
}
return numOfCaps;
}
/**
* Builder class for {@link DemuxResource}.
*/
public static class Builder extends TunerResourceBasic.Builder {
private int mFilterTypes;
Builder(int handle) {
super(handle);
}
/**
* Builder for {@link DemuxResource}.
*
* @param filterTypes the supported {@link DemuxFilterMainTypes}
*/
public Builder filterTypes(int filterTypes) {
this.mFilterTypes = filterTypes;
return this;
}
/**
* Build a {@link DemuxResource}.
*
* @return {@link DemuxResource}.
*/
@Override
public DemuxResource build() {
DemuxResource demux = new DemuxResource(this);
return demux;
}
}
}

View File

@@ -22,6 +22,7 @@ import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.tv.tuner.DemuxFilterMainType;
import android.media.IResourceManagerService;
import android.media.tv.TvInputManager;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
@@ -29,6 +30,7 @@ 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.TunerDemuxInfo;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
@@ -96,6 +98,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
private SparseIntArray mFrontendUsedNumsBackup = new SparseIntArray();
private SparseIntArray mFrontendExistingNumsBackup = new SparseIntArray();
// Map of the current available demux resources
private Map<Integer, DemuxResource> mDemuxResources = new HashMap<>();
// Map of the current available lnb resources
private Map<Integer, LnbResource> mLnbResources = new HashMap<>();
// Map of the current available Cas resources
@@ -248,6 +252,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
}
@Override
public void setDemuxInfoList(@NonNull TunerDemuxInfo[] infos) throws RemoteException {
enforceTrmAccessPermission("setDemuxInfoList");
if (infos == null) {
throw new RemoteException("TunerDemuxInfo can't be null");
}
synchronized (mLock) {
setDemuxInfoListInternal(infos);
}
}
@Override
public void updateCasInfo(int casSystemId, int maxSessionNum) {
enforceTrmAccessPermission("updateCasInfo");
@@ -294,8 +309,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
@Override
public boolean setMaxNumberOfFrontends(int frontendType, int maxUsableNum) {
enforceTunerAccessPermission("requestFrontend");
enforceTrmAccessPermission("requestFrontend");
enforceTunerAccessPermission("setMaxNumberOfFrontends");
enforceTrmAccessPermission("setMaxNumberOfFrontends");
if (maxUsableNum < 0) {
Slog.w(TAG, "setMaxNumberOfFrontends failed with maxUsableNum:" + maxUsableNum
+ " frontendType:" + frontendType);
@@ -308,8 +323,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
@Override
public int getMaxNumberOfFrontends(int frontendType) {
enforceTunerAccessPermission("requestFrontend");
enforceTrmAccessPermission("requestFrontend");
enforceTunerAccessPermission("getMaxNumberOfFrontends");
enforceTrmAccessPermission("getMaxNumberOfFrontends");
synchronized (mLock) {
return getMaxNumberOfFrontendsInternal(frontendType);
}
@@ -466,11 +481,33 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@Override
public void releaseDemux(int demuxHandle, int clientId) {
public void releaseDemux(int demuxHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseDemux");
enforceTrmAccessPermission("releaseDemux");
if (DEBUG) {
Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")");
Slog.e(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")");
}
synchronized (mLock) {
// For Tuner 2.0 and below or any HW constraint devices that are unable to support
// ITuner.openDemuxById(), demux resources are not really managed under TRM and
// mDemuxResources.size() will be zero
if (mDemuxResources.size() == 0) {
return;
}
if (!checkClientExists(clientId)) {
throw new RemoteException("Release demux for unregistered client:" + clientId);
}
DemuxResource demux = getDemuxResource(demuxHandle);
if (demux == null) {
throw new RemoteException("Releasing demux does not exist.");
}
if (demux.getOwnerClientId() != clientId) {
throw new RemoteException("Client is not the current owner "
+ "of the releasing demux.");
}
releaseDemuxInternal(demux);
}
}
@@ -629,6 +666,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
dumpSIA(mFrontendExistingNumsBackup, "FrontendExistingNumsBackup:", ", ", pw);
dumpSIA(mFrontendUsedNumsBackup, "FrontendUsedNumsBackup:", ", ", pw);
dumpSIA(mFrontendMaxUsableNumsBackup, "FrontendUsedNumsBackup:", ", ", pw);
dumpMap(mDemuxResources, "DemuxResource:", "\n", pw);
dumpMap(mLnbResources, "LnbResource:", "\n", pw);
dumpMap(mCasResources, "CasResource:", "\n", pw);
dumpMap(mCiCamResources, "CiCamResource:", "\n", pw);
@@ -858,6 +896,41 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
}
@VisibleForTesting
protected void setDemuxInfoListInternal(TunerDemuxInfo[] infos) {
if (DEBUG) {
Slog.d(TAG, "updateDemuxInfo:");
for (int i = 0; i < infos.length; i++) {
Slog.d(TAG, infos[i].toString());
}
}
// A set to record the demuxes pending on updating. Ids will be removed
// from this set once its updating finished. Any demux left in this set when all
// the updates are done will be removed from mDemuxResources.
Set<Integer> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet());
// Update demuxResources map and other mappings accordingly
for (int i = 0; i < infos.length; i++) {
if (getDemuxResource(infos[i].handle) != null) {
if (DEBUG) {
Slog.d(TAG, "Demux handle=" + infos[i].handle + "exists.");
}
updatingDemuxHandles.remove(infos[i].handle);
} else {
// Add a new demux resource
DemuxResource newDemux = new DemuxResource.Builder(infos[i].handle)
.filterTypes(infos[i].filterTypes)
.build();
addDemuxResource(newDemux);
}
}
for (int removingHandle : updatingDemuxHandles) {
// update the exclusive group id member list
removeDemuxResource(removingHandle);
}
}
@VisibleForTesting
protected void setLnbInfoListInternal(int[] lnbHandles) {
if (DEBUG) {
@@ -1291,6 +1364,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde
clearFrontendAndClientMapping(getClientProfile(clientId));
}
@VisibleForTesting
protected void releaseDemuxInternal(DemuxResource demux) {
if (DEBUG) {
Slog.d(TAG, "releaseDemux(DemuxHandle=" + demux.getHandle() + ")");
}
updateDemuxClientMappingOnRelease(demux);
}
@VisibleForTesting
protected void releaseLnbInternal(LnbResource lnb) {
if (DEBUG) {
@@ -1320,9 +1401,91 @@ public class TunerResourceManagerService extends SystemService implements IBinde
if (DEBUG) {
Slog.d(TAG, "requestDemux(request=" + request + ")");
}
// There are enough Demux resources, so we don't manage Demux in R.
demuxHandle[0] = generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
return true;
// For Tuner 2.0 and below or any HW constraint devices that are unable to support
// ITuner.openDemuxById(), demux resources are not really managed under TRM and
// mDemuxResources.size() will be zero
if (mDemuxResources.size() == 0) {
// There are enough Demux resources, so we don't manage Demux in R.
demuxHandle[0] =
generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
return true;
}
demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
ClientProfile requestClient = getClientProfile(request.clientId);
if (requestClient == null) {
return false;
}
clientPriorityUpdateOnRequest(requestClient);
int grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
int inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
// Priority max value is 1000
int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
// If the desired demux id was specified, we only need to check the demux.
boolean hasDesiredDemuxCap = request.desiredFilterTypes
!= DemuxFilterMainType.UNDEFINED;
int smallestNumOfSupportedCaps = Integer.SIZE + 1;
for (DemuxResource dr : getDemuxResources().values()) {
if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
if (!dr.isInUse()) {
int numOfSupportedCaps = dr.getNumOfCaps();
// look for the demux with minimum caps supporting the desired caps
if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
smallestNumOfSupportedCaps = numOfSupportedCaps;
grantingDemuxHandle = dr.getHandle();
}
} else if (grantingDemuxHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
// Record the demux id with the lowest client priority among all the
// in use demuxes when no availabledemux has been found.
int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId());
if (currentLowestPriority >= priority) {
int numOfSupportedCaps = dr.getNumOfCaps();
boolean shouldUpdate = false;
// update lowest priority
if (currentLowestPriority > priority) {
currentLowestPriority = priority;
shouldUpdate = true;
}
// update smallest caps
if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
smallestNumOfSupportedCaps = numOfSupportedCaps;
shouldUpdate = true;
}
if (shouldUpdate) {
inUseLowestPriorityDrHandle = dr.getHandle();
}
}
}
}
}
// Grant demux when there is unused resource.
if (grantingDemuxHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
demuxHandle[0] = grantingDemuxHandle;
updateDemuxClientMappingOnNewGrant(grantingDemuxHandle, request.clientId);
return true;
}
// When all the resources are occupied, grant the lowest priority resource if the
// request client has higher priority.
if (inUseLowestPriorityDrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
&& (requestClient.getPriority() > currentLowestPriority)) {
if (!reclaimResource(
getDemuxResource(inUseLowestPriorityDrHandle).getOwnerClientId(),
TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
return false;
}
demuxHandle[0] = inUseLowestPriorityDrHandle;
updateDemuxClientMappingOnNewGrant(
inUseLowestPriorityDrHandle, request.clientId);
return true;
}
return false;
}
@VisibleForTesting
@@ -1673,6 +1836,21 @@ public class TunerResourceManagerService extends SystemService implements IBinde
ownerProfile.setPrimaryFrontend(grantingHandle);
}
private void updateDemuxClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
DemuxResource grantingDemux = getDemuxResource(grantingHandle);
if (grantingDemux != null) {
ClientProfile ownerProfile = getClientProfile(ownerClientId);
grantingDemux.setOwner(ownerClientId);
ownerProfile.useDemux(grantingHandle);
}
}
private void updateDemuxClientMappingOnRelease(@NonNull DemuxResource releasingDemux) {
ClientProfile ownerProfile = getClientProfile(releasingDemux.getOwnerClientId());
releasingDemux.removeOwner();
ownerProfile.releaseDemux(releasingDemux.getHandle());
}
private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
LnbResource grantingLnb = getLnbResource(grantingHandle);
ClientProfile ownerProfile = getClientProfile(ownerClientId);
@@ -1764,6 +1942,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
return mFrontendResources;
}
@VisibleForTesting
@Nullable
protected DemuxResource getDemuxResource(int demuxHandle) {
return mDemuxResources.get(demuxHandle);
}
@VisibleForTesting
protected Map<Integer, DemuxResource> getDemuxResources() {
return mDemuxResources;
}
private boolean setMaxNumberOfFrontendsInternal(int frontendType, int maxUsableNum) {
int usedNum = mFrontendUsedNums.get(frontendType, INVALID_FE_COUNT);
if (usedNum == INVALID_FE_COUNT || usedNum <= maxUsableNum) {
@@ -1887,6 +2076,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
private void addDemuxResource(DemuxResource newDemux) {
mDemuxResources.put(newDemux.getHandle(), newDemux);
}
private void removeFrontendResource(int removingHandle) {
FrontendResource fe = getFrontendResource(removingHandle);
if (fe == null) {
@@ -1907,6 +2100,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
mFrontendResources.remove(removingHandle);
}
private void removeDemuxResource(int removingHandle) {
DemuxResource demux = getDemuxResource(removingHandle);
if (demux == null) {
return;
}
if (demux.isInUse()) {
releaseDemuxInternal(demux);
}
mDemuxResources.remove(removingHandle);
}
@VisibleForTesting
@Nullable
protected LnbResource getLnbResource(int lnbHandle) {
@@ -2062,6 +2266,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde
if (profile.getInUseCiCamId() != ClientProfile.INVALID_RESOURCE_ID) {
getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId());
}
// Clear Demux
for (Integer demuxHandle : profile.getInUseDemuxHandles()) {
getDemuxResource(demuxHandle).removeOwner();
}
// Clear Frontend
clearFrontendAndClientMapping(profile);
profile.reclaimAllResources();

View File

@@ -25,11 +25,13 @@ import android.content.ContextWrapper;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputService;
import android.media.tv.tuner.filter.Filter;
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.TunerDemuxInfo;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
@@ -806,20 +808,137 @@ public class TunerResourceManagerServiceTest {
@Test
public void requestDemuxTest() {
// Register client
ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId = new int[1];
ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] clientId0 = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
profile, null /*listener*/, clientId);
assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
profile0, null /*listener*/, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
int[] demuxHandle = new int[1];
TunerDemuxRequest request = new TunerDemuxRequest();
request.clientId = clientId[0];
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle))
TunerDemuxInfo[] infos = new TunerDemuxInfo[3];
infos[0] = tunerDemuxInfo(0 /* handle */, Filter.TYPE_TS | Filter.TYPE_IP);
infos[1] = tunerDemuxInfo(1 /* handle */, Filter.TYPE_TLV);
infos[2] = tunerDemuxInfo(2 /* handle */, Filter.TYPE_TS);
mTunerResourceManagerService.setDemuxInfoListInternal(infos);
int[] demuxHandle0 = new int[1];
// first with undefined type (should be the first one with least # of caps)
TunerDemuxRequest request = tunerDemuxRequest(clientId0[0], Filter.TYPE_UNDEFINED);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle[0]))
assertThat(demuxHandle0[0]).isEqualTo(1);
DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
// now with non-supported type (ALP)
request.desiredFilterTypes = Filter.TYPE_ALP;
demuxHandle0[0] = -1;
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isFalse();
assertThat(demuxHandle0[0]).isEqualTo(-1);
// now with TS (should be the one with least # of caps that supports TS)
request.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isTrue();
assertThat(demuxHandle0[0]).isEqualTo(2);
// request for another TS
int[] clientId1 = new int[1];
mTunerResourceManagerService.registerClientProfileInternal(
profile1, null /*listener*/, clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
int[] demuxHandle1 = new int[1];
TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_TS);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1))
.isTrue();
assertThat(demuxHandle1[0]).isEqualTo(0);
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle1[0]))
.isEqualTo(0);
// release demuxes
dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
dr = mTunerResourceManagerService.getDemuxResource(demuxHandle1[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
}
@Test
public void requestDemuxTest_ResourceReclaim() {
// Register clients
ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
ResourceClientProfile profile2 = resourceClientProfile("2" /*sessionId*/,
TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
int[] clientId0 = new int[1];
int[] clientId1 = new int[1];
int[] clientId2 = new int[1];
TestResourcesReclaimListener listener0 = new TestResourcesReclaimListener();
TestResourcesReclaimListener listener1 = new TestResourcesReclaimListener();
TestResourcesReclaimListener listener2 = new TestResourcesReclaimListener();
mTunerResourceManagerService.registerClientProfileInternal(
profile0, listener0, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
mTunerResourceManagerService.registerClientProfileInternal(
profile1, listener1, clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
mTunerResourceManagerService.registerClientProfileInternal(
profile2, listener2, clientId1);
assertThat(clientId2[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
// Init demux resources.
TunerDemuxInfo[] infos = new TunerDemuxInfo[2];
infos[0] = tunerDemuxInfo(0 /*handle*/, Filter.TYPE_TS | Filter.TYPE_IP);
infos[1] = tunerDemuxInfo(1 /*handle*/, Filter.TYPE_TS);
mTunerResourceManagerService.setDemuxInfoListInternal(infos);
// let clientId0(prio:100) request for IP - should succeed
TunerDemuxRequest request0 = tunerDemuxRequest(clientId0[0], Filter.TYPE_IP);
int[] demuxHandle0 = new int[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request0, demuxHandle0)).isTrue();
assertThat(demuxHandle0[0]).isEqualTo(0);
// let clientId1(prio:50) request for IP - should fail
TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_IP);
int[] demuxHandle1 = new int[1];
demuxHandle1[0] = -1;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request1, demuxHandle1)).isFalse();
assertThat(listener0.isReclaimed()).isFalse();
assertThat(demuxHandle1[0]).isEqualTo(-1);
// let clientId1(prio:50) request for TS - should succeed
request1.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request1, demuxHandle1)).isTrue();
assertThat(demuxHandle1[0]).isEqualTo(1);
assertThat(listener0.isReclaimed()).isFalse();
// now release demux for the clientId0 (higher priority) and request demux
DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
// let clientId2(prio:50) request for TS - should succeed
TunerDemuxRequest request2 = tunerDemuxRequest(clientId2[0], Filter.TYPE_TS);
int[] demuxHandle2 = new int[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request2, demuxHandle2)).isTrue();
assertThat(demuxHandle2[0]).isEqualTo(0);
assertThat(listener1.isReclaimed()).isFalse();
// let clientId0(prio:100) request for TS - should reclaim from clientId2
// , who has the smaller caps
request0.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request0, demuxHandle0)).isTrue();
assertThat(listener1.isReclaimed()).isFalse();
assertThat(listener2.isReclaimed()).isTrue();
}
@Test
@@ -1188,4 +1307,18 @@ public class TunerResourceManagerServiceTest {
request.ciCamId = ciCamId;
return request;
}
private TunerDemuxInfo tunerDemuxInfo(int handle, int supportedFilterTypes) {
TunerDemuxInfo info = new TunerDemuxInfo();
info.handle = handle;
info.filterTypes = supportedFilterTypes;
return info;
}
private TunerDemuxRequest tunerDemuxRequest(int clientId, int desiredFilterTypes) {
TunerDemuxRequest request = new TunerDemuxRequest();
request.clientId = clientId;
request.desiredFilterTypes = desiredFilterTypes;
return request;
}
}