Merge "Enable capability based demux management"
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4e0e4b9..e5b55b2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7503,6 +7503,7 @@
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();
@@ -7515,6 +7516,12 @@
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();
@@ -7567,6 +7574,7 @@
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();
@@ -7574,6 +7582,7 @@
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();
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index 14a9144..19cd023a 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -36,8 +36,14 @@
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 @@
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 @@
mPcrFilterCount = pcrFilterCount;
mSectionFilterLength = sectionFilterLength;
mFilterCaps = filterCaps;
+ mFilterCapsList = filterCapsList;
mLinkCaps = linkCaps;
mSupportTimeFilter = timeFilter;
}
@@ -148,6 +157,24 @@
}
/**
+ * 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.
*
* <p>The returned array contains the same elements as the number of types in
diff --git a/media/java/android/media/tv/tuner/DemuxInfo.java b/media/java/android/media/tv/tuner/DemuxInfo.java
new file mode 100644
index 0000000..de76165
--- /dev/null
+++ b/media/java/android/media/tv/tuner/DemuxInfo.java
@@ -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;
+ }
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 7039a3e..27c2a98 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -281,6 +281,7 @@
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 @@
}
}
- 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 @@
} 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 @@
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 @@
} 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 @@
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 @@
}
}
+ /**
+ * 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 @@
/**
* 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 @@
@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 @@
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];
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 15175a7..d268aeb 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -289,6 +289,23 @@
}
/**
+ * 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.
*
* <p><strong>Note:</strong> This update must happen before the first
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 e0af76d..5399697 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
@@ -20,6 +20,7 @@
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;
@@ -130,6 +131,17 @@
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.
*
* <p><strong>Note:</strong> This update must happen before the first
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
new file mode 100644
index 0000000..c14caf5
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl
@@ -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;
+}
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
index 457f90c..b24e273 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl
@@ -23,4 +23,9 @@
*/
parcelable TunerDemuxRequest {
int clientId;
-}
\ No newline at end of file
+
+ /**
+ * Desired filter types (defined in {@link android.media.tv.tuner.filter.Filter})
+ */
+ int desiredFilterTypes;
+}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index aacea3d..a73725ba 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -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::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;
@@ -2083,7 +2085,7 @@
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;
@@ -2095,16 +2097,49 @@
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) {
@@ -4487,6 +4522,11 @@
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);
@@ -4878,6 +4918,8 @@
(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 },
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 2b69e89..4069aaf 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -198,6 +198,7 @@
jobject openDescrambler();
jobject openDvr(DvrType type, jlong bufferSize);
jobject getDemuxCaps();
+ jobject getDemuxInfo(int handle);
jobject getFrontendStatus(jintArray types);
Result openDemux(int handle);
jint close();
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index ab28fb4..ea623d9 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -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 @@
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;
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 3f8b21c..6ab120b 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -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;
@@ -83,6 +84,21 @@
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.
*
* @return the demux’s capabilities.
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 fb9a4d4..301e612 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -79,6 +79,8 @@
*/
private Set<Integer> mShareFeClientIds = new HashSet<>();
+ private Set<Integer> mUsingDemuxHandles = new HashSet<>();
+
/**
* List of the Lnb handles that are used by the current client.
*/
@@ -233,6 +235,31 @@
}
/**
+ * 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.
*
* @param lnbHandle being used.
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
new file mode 100644
index 0000000..df73565
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java
@@ -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;
+ }
+ }
+}
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 ad1ff72..ed91775 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -22,6 +22,7 @@
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.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 @@
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
@@ -249,6 +253,17 @@
}
@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");
synchronized (mLock) {
@@ -294,8 +309,8 @@
@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 @@
@Override
public int getMaxNumberOfFrontends(int frontendType) {
- enforceTunerAccessPermission("requestFrontend");
- enforceTrmAccessPermission("requestFrontend");
+ enforceTunerAccessPermission("getMaxNumberOfFrontends");
+ enforceTrmAccessPermission("getMaxNumberOfFrontends");
synchronized (mLock) {
return getMaxNumberOfFrontendsInternal(frontendType);
}
@@ -466,11 +481,33 @@
}
@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 @@
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);
@@ -859,6 +897,41 @@
}
@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) {
for (int i = 0; i < lnbHandles.length; i++) {
@@ -1292,6 +1365,14 @@
}
@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) {
Slog.d(TAG, "releaseLnb(lnbHandle=" + lnb.getHandle() + ")");
@@ -1320,9 +1401,91 @@
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 @@
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 @@
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 @@
}
+ 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 @@
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 @@
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();
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 b8cb149..963b27e 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
@@ -25,11 +25,13 @@
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 @@
@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 @@
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;
+ }
}