audio: Add StreamDescriptor.frameSizeBytes

This field indicates the frame size and is filled by
the HAL module, so that the client does not have
to calculate it.

As a side effect, in the VTS code, a StreamContext can
now be created solely from a StreamDescriptor.

Added unit tests for the functions from Utils.

Bug: 205884982
Test: atest libaudioaidlcommon_test
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Ief836b8b2d35bacb1f9778e2462d540554149d7f
diff --git a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
index e7ed502..db1ac22 100644
--- a/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/aidl_api/android.hardware.audio.core/current/android/hardware/audio/core/StreamDescriptor.aidl
@@ -36,6 +36,7 @@
 parcelable StreamDescriptor {
   android.hardware.common.fmq.MQDescriptor<android.hardware.audio.core.StreamDescriptor.Command,android.hardware.common.fmq.SynchronizedReadWrite> command;
   android.hardware.common.fmq.MQDescriptor<android.hardware.audio.core.StreamDescriptor.Reply,android.hardware.common.fmq.SynchronizedReadWrite> reply;
+  int frameSizeBytes;
   long bufferSizeFrames;
   android.hardware.audio.core.StreamDescriptor.AudioBuffer audio;
   const int COMMAND_BURST = 1;
diff --git a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
index 74090d7..2b1ed8c 100644
--- a/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
+++ b/audio/aidl/android/hardware/audio/core/StreamDescriptor.aidl
@@ -145,6 +145,14 @@
     MQDescriptor<Reply, SynchronizedReadWrite> reply;
 
     /**
+     * The size of one frame of audio data in bytes. For PCM formats this is
+     * usually equal to the size of a sample multiplied by the number of
+     * channels used. For encoded bitstreams encapsulated into PCM the sample
+     * size of the underlying PCM stream is used. For encoded bitstreams that
+     * are passed without encapsulation, the frame size is usually 1 byte.
+     */
+    int frameSizeBytes;
+    /**
      * Total buffer size in frames. This applies both to the size of the 'audio.fmq'
      * queue and to the size of the shared memory buffer for MMap No IRQ streams.
      * Note that this size may end up being slightly larger than the size requested
diff --git a/audio/aidl/common/Android.bp b/audio/aidl/common/Android.bp
index f2d8fc2..a3f7f0b 100644
--- a/audio/aidl/common/Android.bp
+++ b/audio/aidl/common/Android.bp
@@ -46,6 +46,7 @@
     host_supported: true,
     vendor_available: true,
     static_libs: [
+        "android.media.audio.common.types-V1-ndk",
         "libaudioaidlcommon",
     ],
     shared_libs: [
@@ -59,6 +60,7 @@
     ],
     srcs: [
         "tests/streamworker_tests.cpp",
+        "tests/utils_tests.cpp",
     ],
     test_suites: [
         "general-tests",
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index 1a87882..74549d4 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -62,12 +62,20 @@
 constexpr size_t getFrameSizeInBytes(
         const ::aidl::android::media::audio::common::AudioFormatDescription& format,
         const ::aidl::android::media::audio::common::AudioChannelLayout& layout) {
+    if (format == ::aidl::android::media::audio::common::AudioFormatDescription{}) {
+        // Unspecified format.
+        return 0;
+    }
     using ::aidl::android::media::audio::common::AudioFormatType;
     if (format.type == AudioFormatType::PCM) {
         return getPcmSampleSizeInBytes(format.pcm) * getChannelCount(layout);
+    } else if (format.type == AudioFormatType::NON_PCM) {
+        // For non-PCM formats always use the underlying PCM size. The default value for
+        // PCM is "UINT_8_BIT", thus non-encapsulated streams have the frame size of 1.
+        return getPcmSampleSizeInBytes(format.pcm);
     }
-    // For non-PCM formats always use frame size of 1.
-    return 1;
+    // Something unexpected.
+    return 0;
 }
 
 }  // namespace android::hardware::audio::common
diff --git a/audio/aidl/common/tests/utils_tests.cpp b/audio/aidl/common/tests/utils_tests.cpp
new file mode 100644
index 0000000..d7f1a5d
--- /dev/null
+++ b/audio/aidl/common/tests/utils_tests.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <Utils.h>
+
+#include <gtest/gtest.h>
+#define LOG_TAG "Utils_Test"
+#include <log/log.h>
+
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
+using aidl::android::media::audio::common::PcmType;
+using android::hardware::audio::common::getChannelCount;
+using android::hardware::audio::common::getFrameSizeInBytes;
+using android::hardware::audio::common::getPcmSampleSizeInBytes;
+
+TEST(UtilsTest, ChannelCountOddCases) {
+    using Tag = AudioChannelLayout::Tag;
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout{}));
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::invalid>(0)));
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::invalid>(-1)));
+}
+
+TEST(UtilsTest, ChannelCountForIndexMask) {
+    using Tag = AudioChannelLayout::Tag;
+    EXPECT_EQ(0UL, getChannelCount(AudioChannelLayout::make<Tag::indexMask>(0)));
+#define VERIFY_INDEX_MASK(N)                                                                  \
+    {                                                                                         \
+        const auto l =                                                                        \
+                AudioChannelLayout::make<Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##N); \
+        EXPECT_EQ(N##UL, getChannelCount(l)) << l.toString();                                 \
+    }
+    VERIFY_INDEX_MASK(1);
+    VERIFY_INDEX_MASK(2);
+    VERIFY_INDEX_MASK(3);
+    VERIFY_INDEX_MASK(4);
+    VERIFY_INDEX_MASK(5);
+    VERIFY_INDEX_MASK(6);
+    VERIFY_INDEX_MASK(7);
+    VERIFY_INDEX_MASK(8);
+    VERIFY_INDEX_MASK(9);
+    VERIFY_INDEX_MASK(10);
+    VERIFY_INDEX_MASK(11);
+    VERIFY_INDEX_MASK(12);
+    VERIFY_INDEX_MASK(13);
+    VERIFY_INDEX_MASK(14);
+    VERIFY_INDEX_MASK(15);
+    VERIFY_INDEX_MASK(16);
+    VERIFY_INDEX_MASK(17);
+    VERIFY_INDEX_MASK(18);
+    VERIFY_INDEX_MASK(19);
+    VERIFY_INDEX_MASK(20);
+    VERIFY_INDEX_MASK(21);
+    VERIFY_INDEX_MASK(22);
+    VERIFY_INDEX_MASK(23);
+    VERIFY_INDEX_MASK(24);
+#undef VERIFY_INDEX_MASK
+}
+
+TEST(UtilsTest, ChannelCountForLayoutMask) {
+    using Tag = AudioChannelLayout::Tag;
+    const std::vector<std::pair<size_t, int32_t>> kTestLayouts = {
+            std::make_pair(0UL, 0),
+            std::make_pair(1UL, AudioChannelLayout::LAYOUT_MONO),
+            std::make_pair(2UL, AudioChannelLayout::LAYOUT_STEREO),
+            std::make_pair(6UL, AudioChannelLayout::LAYOUT_5POINT1),
+            std::make_pair(8UL, AudioChannelLayout::LAYOUT_7POINT1),
+            std::make_pair(16UL, AudioChannelLayout::LAYOUT_9POINT1POINT6),
+            std::make_pair(13UL, AudioChannelLayout::LAYOUT_13POINT_360RA),
+            std::make_pair(24UL, AudioChannelLayout::LAYOUT_22POINT2),
+            std::make_pair(3UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_A),
+            std::make_pair(4UL, AudioChannelLayout::LAYOUT_STEREO_HAPTIC_AB)};
+    for (const auto& [expected_count, layout] : kTestLayouts) {
+        const auto l = AudioChannelLayout::make<Tag::layoutMask>(layout);
+        EXPECT_EQ(expected_count, getChannelCount(l)) << l.toString();
+    }
+}
+
+TEST(UtilsTest, ChannelCountForVoiceMask) {
+    using Tag = AudioChannelLayout::Tag;
+    // clang-format off
+    const std::vector<std::pair<size_t, int32_t>> kTestLayouts = {
+            std::make_pair(0UL, 0),
+            std::make_pair(1UL, AudioChannelLayout::VOICE_UPLINK_MONO),
+            std::make_pair(1UL, AudioChannelLayout::VOICE_DNLINK_MONO),
+            std::make_pair(2UL, AudioChannelLayout::VOICE_CALL_MONO)};
+    // clang-format on
+    for (const auto& [expected_count, layout] : kTestLayouts) {
+        const auto l = AudioChannelLayout::make<Tag::voiceMask>(layout);
+        EXPECT_EQ(expected_count, getChannelCount(l)) << l.toString();
+    }
+}
+
+namespace {
+
+AudioChannelLayout make_AudioChannelLayout_Mono() {
+    return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+            AudioChannelLayout::LAYOUT_MONO);
+}
+
+AudioChannelLayout make_AudioChannelLayout_Stereo() {
+    return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+            AudioChannelLayout::LAYOUT_STEREO);
+}
+
+AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
+    AudioFormatDescription result;
+    result.type = type;
+    return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
+    auto result = make_AudioFormatDescription(AudioFormatType::PCM);
+    result.pcm = pcm;
+    return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(const std::string& encoding) {
+    AudioFormatDescription result;
+    result.encoding = encoding;
+    return result;
+}
+
+AudioFormatDescription make_AudioFormatDescription(PcmType transport, const std::string& encoding) {
+    auto result = make_AudioFormatDescription(encoding);
+    result.pcm = transport;
+    return result;
+}
+
+}  // namespace
+
+TEST(UtilsTest, FrameSize) {
+    EXPECT_EQ(0UL, getFrameSizeInBytes(AudioFormatDescription{}, AudioChannelLayout{}));
+    EXPECT_EQ(sizeof(int16_t), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT),
+                                                   make_AudioChannelLayout_Mono()));
+    EXPECT_EQ(2 * sizeof(int16_t),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT),
+                                  make_AudioChannelLayout_Stereo()));
+    EXPECT_EQ(sizeof(int32_t), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_32_BIT),
+                                                   make_AudioChannelLayout_Mono()));
+    EXPECT_EQ(2 * sizeof(int32_t),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_32_BIT),
+                                  make_AudioChannelLayout_Stereo()));
+    EXPECT_EQ(sizeof(float), getFrameSizeInBytes(make_AudioFormatDescription(PcmType::FLOAT_32_BIT),
+                                                 make_AudioChannelLayout_Mono()));
+    EXPECT_EQ(2 * sizeof(float),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::FLOAT_32_BIT),
+                                  make_AudioChannelLayout_Stereo()));
+    EXPECT_EQ(sizeof(uint8_t),
+              getFrameSizeInBytes(make_AudioFormatDescription("bitstream"), AudioChannelLayout{}));
+    EXPECT_EQ(sizeof(int16_t),
+              getFrameSizeInBytes(make_AudioFormatDescription(PcmType::INT_16_BIT, "encapsulated"),
+                                  AudioChannelLayout{}));
+}
+
+TEST(UtilsTest, PcmSampleSize) {
+    EXPECT_EQ(1UL, getPcmSampleSizeInBytes(PcmType{}));
+    EXPECT_EQ(sizeof(uint8_t), getPcmSampleSizeInBytes(PcmType::UINT_8_BIT));
+    EXPECT_EQ(sizeof(int16_t), getPcmSampleSizeInBytes(PcmType::INT_16_BIT));
+    EXPECT_EQ(sizeof(int32_t), getPcmSampleSizeInBytes(PcmType::INT_32_BIT));
+    EXPECT_EQ(sizeof(int32_t), getPcmSampleSizeInBytes(PcmType::FIXED_Q_8_24));
+    EXPECT_EQ(sizeof(float), getPcmSampleSizeInBytes(PcmType::FLOAT_32_BIT));
+    EXPECT_EQ(3UL, getPcmSampleSizeInBytes(PcmType::INT_24_BIT));
+    EXPECT_EQ(0UL, getPcmSampleSizeInBytes(PcmType(-1)));
+    using PcmTypeUnderlyingType = std::underlying_type_t<PcmType>;
+    EXPECT_EQ(0UL,
+              getPcmSampleSizeInBytes(PcmType(std::numeric_limits<PcmTypeUnderlyingType>::min())));
+    EXPECT_EQ(0UL,
+              getPcmSampleSizeInBytes(PcmType(std::numeric_limits<PcmTypeUnderlyingType>::max())));
+}
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 24e46db..312df72 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -35,6 +35,7 @@
         desc->reply = mReplyMQ->dupeDesc();
     }
     if (mDataMQ) {
+        desc->frameSizeBytes = mFrameSize;
         desc->bufferSizeFrames =
                 mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / mFrameSize;
         desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
index ac41ac3..290f5f0 100644
--- a/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTest.cpp
@@ -28,7 +28,6 @@
 #include <android-base/logging.h>
 
 #include <StreamWorker.h>
-#include <Utils.h>
 #include <aidl/Gtest.h>
 #include <aidl/Vintf.h>
 #include <aidl/android/hardware/audio/core/IConfig.h>
@@ -55,6 +54,7 @@
 using aidl::android::hardware::audio::core::IStreamOut;
 using aidl::android::hardware::audio::core::ModuleDebug;
 using aidl::android::hardware::audio::core::StreamDescriptor;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
 using aidl::android::media::audio::common::AudioContentType;
 using aidl::android::media::audio::common::AudioDevice;
 using aidl::android::media::audio::common::AudioDeviceAddress;
@@ -68,7 +68,6 @@
 using aidl::android::media::audio::common::AudioPortExt;
 using aidl::android::media::audio::common::AudioSource;
 using aidl::android::media::audio::common::AudioUsage;
-using android::hardware::audio::common::getFrameSizeInBytes;
 using android::hardware::audio::common::StreamLogic;
 using android::hardware::audio::common::StreamWorker;
 using ndk::ScopedAStatus;
@@ -368,18 +367,12 @@
 
 class StreamContext {
   public:
-    typedef AidlMessageQueue<StreamDescriptor::Command,
-                             ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
-            CommandMQ;
-    typedef AidlMessageQueue<StreamDescriptor::Reply,
-                             ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
-            ReplyMQ;
-    typedef AidlMessageQueue<int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
-            DataMQ;
+    typedef AidlMessageQueue<StreamDescriptor::Command, SynchronizedReadWrite> CommandMQ;
+    typedef AidlMessageQueue<StreamDescriptor::Reply, SynchronizedReadWrite> ReplyMQ;
+    typedef AidlMessageQueue<int8_t, SynchronizedReadWrite> DataMQ;
 
-    StreamContext(const AudioPortConfig& portConfig, const StreamDescriptor& descriptor)
-        : mFrameSizeBytes(
-                  getFrameSizeInBytes(portConfig.format.value(), portConfig.channelMask.value())),
+    explicit StreamContext(const StreamDescriptor& descriptor)
+        : mFrameSizeBytes(descriptor.frameSizeBytes),
           mCommandMQ(new CommandMQ(descriptor.command)),
           mReplyMQ(new ReplyMQ(descriptor.reply)),
           mBufferSizeFrames(descriptor.bufferSizeFrames),
@@ -392,6 +385,10 @@
         EXPECT_TRUE(mReplyMQ->isValid());
         if (mDataMQ != nullptr) {
             EXPECT_TRUE(mDataMQ->isValid());
+            EXPECT_GE(mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize(),
+                      mFrameSizeBytes * mBufferSizeFrames)
+                    << "Data MQ actual buffer size is "
+                       "less than the buffer size as specified by the descriptor";
         }
     }
     size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; }
@@ -605,7 +602,7 @@
         ASSERT_NE(nullptr, mStream) << "; port config id " << getPortId();
         EXPECT_GE(mDescriptor.bufferSizeFrames, bufferSizeFrames)
                 << "actual buffer size must be no less than requested";
-        mContext.emplace(mPortConfig.get(), mDescriptor);
+        mContext.emplace(mDescriptor);
         ASSERT_NO_FATAL_FAILURE(mContext.value().checkIsValid());
     }
     Stream* get() const { return mStream.get(); }