audio: Add stub default MMAP implementation

Add support for MMAP simulation to the default
implementation. Since there is no support for MMAP audio I/O on
CVD, the implementation is a stub.

The implementation supports 'createMmapBuffer' operation via
vendor property 'aosp.createMmapBuffer'. VTS tests for MMAP
streams are enabled back for this case (and for upcoming API
V4). VTS calls 'createMmapBuffer' after the stream has
transitioned out from the 'STANDBY' state.

Also, VTS for MMAP updated to wait for the burst size duration
after each 'burst' command in order to simulate audio I/O flow
and ensure that the stream positions get eventually updated.

Note that even with 'createMmapBuffer', the HAL implementation
must still provide some valid shared memory FD when opening an MMAP
stream. This is a limitation due to the requirement of the HAL
API on the descriptor being "non-null". The VTS and the framework
will not try to 'mmap' that initial FD.

Bug: 274456992
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I6d5783b3108886ff5c1328ddc60c623d4290d3d3
diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp
index 2a1dba9..6cb9251 100644
--- a/audio/aidl/default/ModulePrimary.cpp
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -21,18 +21,23 @@
 #include <android-base/logging.h>
 
 #include "core-impl/ModulePrimary.h"
+#include "core-impl/StreamMmapStub.h"
 #include "core-impl/StreamOffloadStub.h"
 #include "core-impl/StreamPrimary.h"
 #include "core-impl/Telephony.h"
 
 using aidl::android::hardware::audio::common::areAllBitPositionFlagsSet;
+using aidl::android::hardware::audio::common::hasMmapFlag;
 using aidl::android::hardware::audio::common::SinkMetadata;
 using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::audio::core::StreamDescriptor;
+using aidl::android::media::audio::common::AudioInputFlags;
 using aidl::android::media::audio::common::AudioIoFlags;
 using aidl::android::media::audio::common::AudioOffloadInfo;
 using aidl::android::media::audio::common::AudioOutputFlags;
 using aidl::android::media::audio::common::AudioPort;
 using aidl::android::media::audio::common::AudioPortConfig;
+using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::MicrophoneInfo;
 
 namespace aidl::android::hardware::audio::core {
@@ -62,6 +67,11 @@
                                                     const SinkMetadata& sinkMetadata,
                                                     const std::vector<MicrophoneInfo>& microphones,
                                                     std::shared_ptr<StreamIn>* result) {
+    if (context.isMmap()) {
+        // "Stub" is used because there is no support for MMAP audio I/O on CVD.
+        return createStreamInstance<StreamInMmapStub>(result, std::move(context), sinkMetadata,
+                                                      microphones);
+    }
     return createStreamInstance<StreamInPrimary>(result, std::move(context), sinkMetadata,
                                                  microphones);
 }
@@ -69,26 +79,54 @@
 ndk::ScopedAStatus ModulePrimary::createOutputStream(
         StreamContext&& context, const SourceMetadata& sourceMetadata,
         const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
-    if (!areAllBitPositionFlagsSet(
-                context.getFlags().get<AudioIoFlags::output>(),
-                {AudioOutputFlags::COMPRESS_OFFLOAD, AudioOutputFlags::NON_BLOCKING})) {
-        return createStreamInstance<StreamOutPrimary>(result, std::move(context), sourceMetadata,
-                                                      offloadInfo);
-    } else {
+    if (context.isMmap()) {
+        // "Stub" is used because there is no support for MMAP audio I/O on CVD.
+        return createStreamInstance<StreamOutMmapStub>(result, std::move(context), sourceMetadata,
+                                                       offloadInfo);
+    } else if (areAllBitPositionFlagsSet(
+                       context.getFlags().get<AudioIoFlags::output>(),
+                       {AudioOutputFlags::COMPRESS_OFFLOAD, AudioOutputFlags::NON_BLOCKING})) {
         // "Stub" is used because there is no actual decoder. The stream just
         // extracts the clip duration from the media file header and simulates
         // playback over time.
         return createStreamInstance<StreamOutOffloadStub>(result, std::move(context),
                                                           sourceMetadata, offloadInfo);
     }
+    return createStreamInstance<StreamOutPrimary>(result, std::move(context), sourceMetadata,
+                                                  offloadInfo);
 }
 
-int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig&) {
+ndk::ScopedAStatus ModulePrimary::createMmapBuffer(const AudioPortConfig& portConfig,
+                                                   int32_t bufferSizeFrames, int32_t frameSizeBytes,
+                                                   MmapBufferDescriptor* desc) {
+    const size_t bufferSizeBytes = static_cast<size_t>(bufferSizeFrames) * frameSizeBytes;
+    // The actual mmap buffer for I/O is created after the stream exits standby, via
+    // 'IStreamCommon.createMmapBuffer'. But we must return a valid file descriptor here because
+    // 'MmapBufferDescriptor' can not contain a "null" fd.
+    const std::string regionName =
+            std::string("mmap-sim-o-") +
+            std::to_string(portConfig.ext.get<AudioPortExt::Tag::mix>().handle);
+    int fd = ashmem_create_region(regionName.c_str(), bufferSizeBytes);
+    if (fd < 0) {
+        PLOG(ERROR) << __func__ << ": failed to create shared memory region of " << bufferSizeBytes
+                    << " bytes";
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    desc->sharedMemory.fd = ndk::ScopedFileDescriptor(fd);
+    desc->sharedMemory.size = bufferSizeBytes;
+    desc->burstSizeFrames = bufferSizeFrames / 2;
+    desc->flags = 0;
+    LOG(DEBUG) << __func__ << ": " << desc->toString();
+    return ndk::ScopedAStatus::ok();
+}
+
+int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig& portConfig) {
+    static constexpr int32_t kLowLatencyMs = 5;
     // 85 ms is chosen considering 4096 frames @ 48 kHz. This is the value which allows
     // the virtual Android device implementation to pass CTS. Hardware implementations
     // should have significantly lower latency.
-    static constexpr int32_t kLatencyMs = 85;
-    return kLatencyMs;
+    static constexpr int32_t kStandardLatencyMs = 85;
+    return hasMmapFlag(portConfig.flags.value()) ? kLowLatencyMs : kStandardLatencyMs;
 }
 
 }  // namespace aidl::android::hardware::audio::core