diff options
16 files changed, 896 insertions, 46 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 6d42bb8676f6..82fd2fa93249 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -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(); diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java index 14a9144c9cdc..19cd023adbf7 100644 --- a/media/java/android/media/tv/tuner/DemuxCapabilities.java +++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java @@ -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; } @@ -148,6 +157,24 @@ public class DemuxCapabilities { } /** + * 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 000000000000..de76165e2657 --- /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 7039a3e01ac6..27c2a982a832 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -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]; diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index 15175a783924..d268aeba8011 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -289,6 +289,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. * * <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 e0af76d27a95..539969762a82 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.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; @@ -130,6 +131,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. * * <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 000000000000..c14caf50fa14 --- /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 457f90ce866d..b24e27301c60 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 @@ package android.media.tv.tunerresourcemanager; */ 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 58078cf39227..a7ebce82319f 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::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 }, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 2b69e89a0a2f..4069aafbf9bf 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -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(); diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index ab28fb4eca9a..ea623d97a394 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 @@ 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; diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h index 3f8b21cb8899..6ab120b56d97 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 @@ 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. * * @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 fb9a4d41ee90..301e612e4699 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 @@ 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. */ @@ -233,6 +235,31 @@ public final class ClientProfile { } /** + * 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 000000000000..df735659c0fe --- /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 ad1ff72f653c..ed9177546794 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; 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 @@ -249,6 +253,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"); synchronized (mLock) { @@ -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); @@ -859,6 +897,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) { for (int i = 0; i < lnbHandles.length; i++) { @@ -1292,6 +1365,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @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 @@ 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(); 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 b8cb1492ad52..963b27e010fa 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.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(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(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle[0])) + 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; + } } |