audio: Fix VTS PCM Input tests for certain h/w platforms

Some h/w platforms require more metadata to be provided
for input streams. Otherwise, opening of a stream was
failing internally in the vendor HAL code and capture
position queries were not succeeding.

Bug: 183688436
Test: atest VtsHalAudioV7_0TargetTest
  on C1, C2, R3, S4, B5
Change-Id: Icb6d2cdb9150eb65281df2a4bea6dbe1fc6034c2
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
index 657b42d..0b3098b 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -580,12 +580,19 @@
         // Starting / resuming of streams is asynchronous at HAL level.
         // Sometimes HAL doesn't have enough information until the audio data actually gets
         // consumed by the hardware.
-        do {
+        bool timedOut = false;
+        res = Result::INVALID_STATE;
+        for (android::base::Timer elapsed;
+             res != Result::OK && !writer.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kWriteDurationUs);
             ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts)));
             ASSERT_RESULT(okOrInvalidState, res);
-        } while (res != Result::OK);
+        }
+        ASSERT_FALSE(writer.hasError());
+        ASSERT_FALSE(timedOut);
+
         uint64_t frames = framesInitial;
-        bool timedOut = false;
         for (android::base::Timer elapsed;
              frames <= framesInitial && !writer.hasError() &&
              !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
@@ -666,11 +673,18 @@
                 allParams.begin(), allParams.end(), std::back_inserter(pcmParams), [](auto cfg) {
                     const auto& flags = std::get<PARAM_FLAGS>(cfg);
                     return xsd::isLinearPcm(std::get<PARAM_CONFIG>(cfg).base.format)
-                           // MMAP NOIRQ profiles use different reading protocol.
+                           // MMAP NOIRQ profiles use different reading protocol,
+                           // reading h/w hotword might require Soundtrigger to be active.
                            &&
-                           std::find(flags.begin(), flags.end(),
-                                     toString(xsd::AudioInOutFlag::AUDIO_INPUT_FLAG_MMAP_NOIRQ)) ==
-                                   flags.end() &&
+                           std::find_if(
+                                   flags.begin(), flags.end(),
+                                   [](const auto& flag) {
+                                       return flag == toString(
+                                                              xsd::AudioInOutFlag::
+                                                                      AUDIO_INPUT_FLAG_MMAP_NOIRQ) ||
+                                              flag == toString(xsd::AudioInOutFlag::
+                                                                       AUDIO_INPUT_FLAG_HW_HOTWORD);
+                                   }) == flags.end() &&
                            !getCachedPolicyConfig()
                                     .getAttachedSourceDeviceForMixPort(
                                             std::get<PARAM_DEVICE_NAME>(
@@ -690,6 +704,15 @@
         InputStreamTest::TearDown();
     }
 
+    bool canQueryCapturePosition() const {
+        auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+                getDeviceName(), getMixPortName());
+        // Returning 'true' when no source is found so the test can fail later with a more clear
+        // problem description.
+        return !maybeSourceAddress.has_value() ||
+               !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType);
+    }
+
     void createPatchIfNeeded() {
         auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
                 getDeviceName(), getMixPortName());
@@ -714,6 +737,7 @@
             EXPECT_OK(stream->setDevices({maybeSourceAddress.value()}));
         }
     }
+
     void releasePatchIfNeeded() {
         if (areAudioPatchesSupported()) {
             if (mHasPatch) {
@@ -724,7 +748,42 @@
             EXPECT_OK(stream->setDevices({address}));
         }
     }
-    const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+
+    void waitForCapturePositionAdvance(StreamReader& reader, uint64_t* firstPosition = nullptr,
+                                       uint64_t* lastPosition = nullptr) {
+        static constexpr int kReadDurationUs = 50 * 1000;
+        static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
+        uint64_t framesInitial, ts;
+        // Starting / resuming of streams is asynchronous at HAL level.
+        // Sometimes HAL doesn't have enough information until the audio data actually has been
+        // produced by the hardware. Legacy HALs might return NOT_SUPPORTED when they actually
+        // mean INVALID_STATE.
+        bool timedOut = false;
+        res = Result::INVALID_STATE;
+        for (android::base::Timer elapsed;
+             res != Result::OK && !reader.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kReadDurationUs);
+            ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
+            ASSERT_RESULT(okOrInvalidStateOrNotSupported, res);
+        }
+        ASSERT_FALSE(reader.hasError());
+        ASSERT_FALSE(timedOut);
+
+        uint64_t frames = framesInitial;
+        for (android::base::Timer elapsed;
+             frames <= framesInitial && !reader.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kReadDurationUs);
+            ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
+            ASSERT_RESULT(Result::OK, res);
+        }
+        EXPECT_FALSE(timedOut);
+        EXPECT_FALSE(reader.hasError());
+        EXPECT_GT(frames, framesInitial);
+        if (firstPosition) *firstPosition = framesInitial;
+        if (lastPosition) *lastPosition = frames;
+    }
 
   private:
     AudioPatchHandle mPatchHandle = {};
@@ -740,47 +799,36 @@
 
 TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionAdvancesWithReads) {
     doc::test("Check that the capture position advances with reads");
+    if (!canQueryCapturePosition()) {
+        GTEST_SKIP() << "Capture position retrieval is not possible";
+    }
 
     ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
     StreamReader reader(stream.get(), stream->getBufferSize());
     ASSERT_TRUE(reader.start());
     EXPECT_TRUE(reader.waitForAtLeastOneCycle());
-
-    uint64_t framesInitial, ts;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
-    ASSERT_RESULT(Result::OK, res);
-
-    EXPECT_TRUE(reader.waitForAtLeastOneCycle());
-
-    uint64_t frames;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
-    ASSERT_RESULT(Result::OK, res);
-    EXPECT_GT(frames, framesInitial);
-
-    reader.stop();
-    releasePatchIfNeeded();
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader));
 }
 
 TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionPreservedOnStandby) {
     doc::test("Check that the capture position does not reset on standby");
+    if (!canQueryCapturePosition()) {
+        GTEST_SKIP() << "Capture position retrieval is not possible";
+    }
 
     ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
     StreamReader reader(stream.get(), stream->getBufferSize());
     ASSERT_TRUE(reader.start());
     EXPECT_TRUE(reader.waitForAtLeastOneCycle());
 
-    uint64_t framesInitial, ts;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
-    ASSERT_RESULT(Result::OK, res);
-
+    uint64_t framesInitial;
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, nullptr, &framesInitial));
     reader.pause();
     ASSERT_OK(stream->standby());
     reader.resume();
-    EXPECT_FALSE(reader.hasError());
 
     uint64_t frames;
-    ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
-    ASSERT_RESULT(Result::OK, res);
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, &frames, nullptr));
     EXPECT_GT(frames, framesInitial);
 
     reader.stop();
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index ae1467d..aa7fd8e 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -1194,7 +1194,17 @@
 #if MAJOR_VERSION <= 6
         address.device = AudioDevice::IN_DEFAULT;
 #elif MAJOR_VERSION >= 7
-        address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+        auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+                getDeviceName(), getMixPortName());
+        if (maybeSourceAddress.has_value() &&
+            !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType)) {
+            address = maybeSourceAddress.value();
+            auto& metadata = initMetadata.tracks[0];
+            metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED);
+            metadata.channelMask = getConfig().base.channelMask;
+        } else {
+            address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+        }
 #endif
         const AudioConfig& config = getConfig();
         auto flags = getInputFlags();
@@ -1212,7 +1222,8 @@
 #elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
      const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }};
 #elif MAJOR_VERSION >= 7
-     const SinkMetadata initMetadata = {
+     const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+     SinkMetadata initMetadata = {
              {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
                .gain = 1,
                .tags = {},