Add Tuner framework AudioPresentation support

DVB MPEG-2 transport streams can include audio preselection descriptors
for next-generation audio (NGA) as specified in ETSI EN 300 468. The
tuner framework audio preselection is mapped to media framework audio
presentations and made available via MediaEvent.

Bug: 264812332
Test: CtsTvTestCases:TunerTest#testAudioFilterStreamTypeConfig
      on aosp_cf_x86_tv with android.hardware.tv.tuner-service.example
Change-Id: I57ff6fd29da0744f622aec64c1344abf4cfb0d91
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index a0304bb..ed1072c 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,6 +88,7 @@
         "android.hidl.memory@1.0",
         "android.hidl.token@1.0-utils",
         "android.hardware.drm-V1-ndk",
+        "android.hardware.tv.tuner-V2-ndk",
     ],
 
     header_libs: [
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
index a3adddd..0c83ddf 100644
--- a/media/jni/android_media_AudioPresentation.h
+++ b/media/jni/android_media_AudioPresentation.h
@@ -19,10 +19,14 @@
 
 #include "jni.h"
 
+#include <aidl/android/hardware/tv/tuner/AudioPresentation.h>
 #include <media/stagefright/foundation/ADebug.h>  // CHECK
 #include <media/stagefright/foundation/AudioPresentationInfo.h>
 #include <nativehelper/ScopedLocalRef.h>
 
+using ::aidl::android::hardware::tv::tuner::AudioPreselectionRenderingIndicationType;
+using TunerAudioPresentation = ::aidl::android::hardware::tv::tuner::AudioPresentation;
+
 namespace android {
 
 struct JAudioPresentationInfo {
@@ -97,6 +101,38 @@
     }
 
     static void addPresentations(JNIEnv *env, const fields_t& fields,
+            const std::vector<TunerAudioPresentation>& tunerAudioPresentations,
+            jobject presentationsJObj) {
+        AudioPresentationCollection apc = {};
+        static const std::map<AudioPreselectionRenderingIndicationType, MasteringIndication> mMap {
+            { AudioPreselectionRenderingIndicationType::NOT_INDICATED, MASTERING_NOT_INDICATED },
+            { AudioPreselectionRenderingIndicationType::STEREO, MASTERED_FOR_STEREO },
+            { AudioPreselectionRenderingIndicationType::TWO_DIMENSIONAL, MASTERED_FOR_SURROUND },
+            { AudioPreselectionRenderingIndicationType::THREE_DIMENSIONAL, MASTERED_FOR_3D },
+            { AudioPreselectionRenderingIndicationType::HEADPHONE, MASTERED_FOR_HEADPHONE },
+        };
+        for (const auto& tap : tunerAudioPresentations) {
+            AudioPresentationV1 ap;
+            ap.mPresentationId = tap.preselection.preselectionId;
+            ap.mProgramId = tap.ac4ShortProgramId;
+            for (const auto& md : tap.preselection.labels) {
+                ap.mLabels.insert(std::pair(md.language, md.text));
+            }
+            ap.mLanguage = tap.preselection.language;
+            ap.mMasteringIndication = MASTERING_NOT_INDICATED;
+            auto masteringSearch = mMap.find(tap.preselection.renderingIndication);
+            if (masteringSearch != mMap.end()) {
+                ap.mMasteringIndication = masteringSearch->second;
+            }
+            ap.mAudioDescriptionAvailable = tap.preselection.hasAudioDescription;
+            ap.mSpokenSubtitlesAvailable = tap.preselection.hasSpokenSubtitles;
+            ap.mDialogueEnhancementAvailable = tap.preselection.hasDialogueEnhancement;
+            apc.push_back(ap);
+        }
+        addPresentations(env, fields, apc, presentationsJObj);
+    }
+
+    static void addPresentations(JNIEnv *env, const fields_t& fields,
                     const AudioPresentationCollection& presentations, jobject presentationsJObj) {
         for (const auto& ap : presentations) {
             ScopedLocalRef<jobject> jLabelObject = convertLabelsToMap(env, fields, ap.mLabels);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index aacea3d..0147406 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -20,6 +20,7 @@
 #include "android_media_tv_Tuner.h"
 
 #include <aidl/android/hardware/tv/tuner/AudioExtraMetaData.h>
+#include <aidl/android/hardware/tv/tuner/AudioPresentation.h>
 #include <aidl/android/hardware/tv/tuner/AudioStreamType.h>
 #include <aidl/android/hardware/tv/tuner/AvStreamType.h>
 #include <aidl/android/hardware/tv/tuner/Constant.h>
@@ -155,12 +156,14 @@
 #include <nativehelper/ScopedLocalRef.h>
 #include <utils/Log.h>
 
+#include "android_media_AudioPresentation.h"
 #include "android_media_MediaCodecLinearBlock.h"
 #include "android_runtime/AndroidRuntime.h"
 
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 using ::aidl::android::hardware::tv::tuner::AudioExtraMetaData;
+using ::aidl::android::hardware::tv::tuner::AudioPreselection;
 using ::aidl::android::hardware::tv::tuner::AudioStreamType;
 using ::aidl::android::hardware::tv::tuner::AvStreamType;
 using ::aidl::android::hardware::tv::tuner::Constant;
@@ -350,6 +353,9 @@
 }
 
 namespace android {
+
+static JAudioPresentationInfo::fields_t gAudioPresentationFields;
+
 /////////////// LnbClientCallbackImpl ///////////////////////
 void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) {
     ALOGV("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType);
@@ -620,27 +626,45 @@
             eventClazz,
             "<init>",
             "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
-            "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;)V");
+            "ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;"
+            "Ljava/util/List;)V");
     jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
 
     const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
     jobject audioDescriptor = nullptr;
-    if (mediaEvent.extraMetaData.getTag() == DemuxFilterMediaEventExtraMetaData::Tag::audio) {
-        jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
-        jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
+    gAudioPresentationFields.init(env);
+    jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+    switch (mediaEvent.extraMetaData.getTag()) {
+        case DemuxFilterMediaEventExtraMetaData::Tag::audio: {
+            jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
+            jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
 
-        const AudioExtraMetaData &ad =
-                mediaEvent.extraMetaData.get<DemuxFilterMediaEventExtraMetaData::Tag::audio>();
-        jbyte adFade = ad.adFade;
-        jbyte adPan = ad.adPan;
-        jchar versionTextTag = ad.versionTextTag;
-        jbyte adGainCenter = ad.adGainCenter;
-        jbyte adGainFront = ad.adGainFront;
-        jbyte adGainSurround = ad.adGainSurround;
+            const AudioExtraMetaData &ad =
+                    mediaEvent.extraMetaData.get<DemuxFilterMediaEventExtraMetaData::Tag::audio>();
+            jbyte adFade = ad.adFade;
+            jbyte adPan = ad.adPan;
+            jchar versionTextTag = ad.versionTextTag;
+            jbyte adGainCenter = ad.adGainCenter;
+            jbyte adGainFront = ad.adGainFront;
+            jbyte adGainSurround = ad.adGainSurround;
 
-        audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag,
-                                         adGainCenter, adGainFront, adGainSurround);
-        env->DeleteLocalRef(adClazz);
+            audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag,
+                                             adGainCenter, adGainFront, adGainSurround);
+            env->DeleteLocalRef(adClazz);
+            break;
+        }
+        case DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations: {
+            JAudioPresentationInfo::addPresentations(
+                    env, gAudioPresentationFields,
+                    mediaEvent.extraMetaData
+                            .get<DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations>(),
+                    presentationsJObj);
+            break;
+        }
+        default: {
+            ALOGE("FilterClientCallbackImpl::getMediaEvent: unknown extraMetaData");
+            break;
+        }
     }
 
     jlong dataLength = mediaEvent.dataLength;
@@ -669,7 +693,8 @@
 
     jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
                                  dts, dataLength, offset, nullptr, isSecureMemory, avDataId,
-                                 mpuSequenceNumber, isPesPrivateData, sc, audioDescriptor);
+                                 mpuSequenceNumber, isPesPrivateData, sc, audioDescriptor,
+                                 presentationsJObj);
 
     uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
     if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
@@ -688,6 +713,7 @@
     }
     env->DeleteLocalRef(obj);
     env->DeleteLocalRef(eventClazz);
+    env->DeleteLocalRef(presentationsJObj);
 }
 
 void FilterClientCallbackImpl::getPesEvent(jobjectArray &arr, const int size,