Merge changes I26fd4fd2,Iaadf6e9b,I7d42f8a7,I0482376e into stage-aosp-tm-ts-dev am: 744c329227

Original change: https://googleplex-android-review.googlesource.com/c/platform/hardware/interfaces/+/19345935

Change-Id: I2b54fab89ae38c8060fbec12c080304830a467fc
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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 13068dc..505c54c 100644
--- a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -898,10 +898,49 @@
     }
 };
 
+class OffloadCallbacks : public IStreamOutCallback {
+  public:
+    Return<void> onDrainReady() override {
+        ALOGI("onDrainReady");
+        {
+            std::lock_guard lg(mLock);
+            mOnDrainReady = true;
+        }
+        mCondVar.notify_one();
+        return {};
+    }
+    Return<void> onWriteReady() override { return {}; }
+    Return<void> onError() override {
+        ALOGW("onError");
+        {
+            std::lock_guard lg(mLock);
+            mOnError = true;
+        }
+        mCondVar.notify_one();
+        return {};
+    }
+    bool waitForDrainReadyOrError() {
+        std::unique_lock l(mLock);
+        if (!mOnDrainReady && !mOnError) {
+            mCondVar.wait(l, [&]() { return mOnDrainReady || mOnError; });
+        }
+        const bool success = !mOnError;
+        mOnDrainReady = mOnError = false;
+        return success;
+    }
+
+  private:
+    std::mutex mLock;
+    bool mOnDrainReady = false;
+    bool mOnError = false;
+    std::condition_variable mCondVar;
+};
+
 TEST_P(CompressedOffloadOutputStreamTest, Mp3FormatGaplessOffload) {
     doc::test("Check that compressed offload mix ports for MP3 implement gapless offload");
     const auto& flags = getOutputFlags();
     const bool isNewDeviceLaunchingOnTPlus = property_get_int32("ro.vendor.api_level", 0) >= 33;
+    // See test instantiation, only offload MP3 mix ports are used.
     if (std::find_if(flags.begin(), flags.end(), [](const auto& flag) {
             return flag == toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD);
         }) == flags.end()) {
@@ -912,8 +951,7 @@
             GTEST_SKIP() << "Compressed offload mix port does not support gapless offload";
         }
     }
-    // FIXME: The presentation position is not updated if there is no zero padding in data.
-    std::vector<uint8_t> offloadData(stream->getBufferSize());
+    std::vector<uint8_t> offloadData;
     ASSERT_NO_FATAL_FAILURE(loadData("/data/local/tmp/sine882hz3s.mp3", &offloadData));
     ASSERT_FALSE(offloadData.empty());
     ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
@@ -923,38 +961,70 @@
     const int delay = 576 + 1000;
     const int padding = 756 + 1000;
     const int durationMs = 3000 - 44;
-    // StreamWriter plays 'offloadData' in a loop, possibly using multiple calls to 'write',
-    // this depends on the relative sizes of 'offloadData' and the HAL buffer. Writer calls
-    // 'onDataWrap' callback each time it wraps around the buffer.
+    auto start = std::chrono::steady_clock::now();
+    auto callbacks = sp<OffloadCallbacks>::make();
+    std::mutex presentationEndLock;
+    std::vector<float> presentationEndTimes;
+    // StreamWriter plays 'offloadData' in a loop, possibly using multiple calls to 'write', this
+    // depends on the relative sizes of 'offloadData' and the HAL buffer. Writer calls 'onDataStart'
+    // each time it starts writing the buffer from the beginning, and 'onDataWrap' callback each
+    // time it wraps around the buffer.
     StreamWriter writer(
-            stream.get(), stream->getBufferSize(), std::move(offloadData), [&]() /* onDataWrap */ {
-                Parameters::set(stream,
-                                {{AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, std::to_string(delay)},
+            stream.get(), stream->getBufferSize(), std::move(offloadData),
+            [&]() /* onDataStart */ { start = std::chrono::steady_clock::now(); },
+            [&]() /* onDataWrap */ {
+                Return<Result> ret(Result::OK);
+                // Decrease the volume since the test plays a loud sine wave.
+                ret = stream->setVolume(0.1, 0.1);
+                if (!ret.isOk() || ret != Result::OK) {
+                    ALOGE("%s: setVolume failed: %s", __func__, toString(ret).c_str());
+                    return false;
+                }
+                ret = Parameters::set(
+                        stream, {{AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES, std::to_string(delay)},
                                  {AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES, std::to_string(padding)}});
-                stream->drain(AudioDrain::EARLY_NOTIFY);
+                if (!ret.isOk() || ret != Result::OK) {
+                    ALOGE("%s: setParameters failed: %s", __func__, toString(ret).c_str());
+                    return false;
+                }
+                ret = stream->drain(AudioDrain::EARLY_NOTIFY);
+                if (!ret.isOk() || ret != Result::OK) {
+                    ALOGE("%s: drain failed: %s", __func__, toString(ret).c_str());
+                    return false;
+                }
+                // FIXME: On some SoCs intermittent errors are possible, ignore them.
+                if (callbacks->waitForDrainReadyOrError()) {
+                    const float duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+                                                   std::chrono::steady_clock::now() - start)
+                                                   .count();
+                    std::lock_guard lg(presentationEndLock);
+                    presentationEndTimes.push_back(duration);
+                }
+                return true;
             });
+    ASSERT_OK(stream->setCallback(callbacks));
     ASSERT_TRUE(writer.start());
-    ASSERT_TRUE(writer.waitForAtLeastOneCycle());
-    // Decrease the volume since the test plays a loud sine wave.
-    ASSERT_OK(stream->setVolume(0.1, 0.1));
     // How many times to loop the track so that the sum of gapless delay and padding from
     // the first presentation end to the last is at least 'presentationeEndPrecisionMs'.
     const int playbackNumber = (int)(significantSampleNumber / ((float)delay + padding) + 1);
-    std::vector<float> presentationEndTimes;
-    uint64_t previousPosition = std::numeric_limits<uint64_t>::max();
-    for (int i = 0; i < playbackNumber; ++i) {
-        const auto start = std::chrono::steady_clock::now();
-        ASSERT_NO_FATAL_FAILURE(
-                waitForPresentationPositionAdvance(writer, &previousPosition, &previousPosition));
-        presentationEndTimes.push_back(std::chrono::duration_cast<std::chrono::milliseconds>(
-                                               std::chrono::steady_clock::now() - start)
-                                               .count());
+    for (bool done = false; !done;) {
+        usleep(presentationeEndPrecisionMs * 1000);
+        {
+            std::lock_guard lg(presentationEndLock);
+            done = presentationEndTimes.size() >= playbackNumber;
+        }
+        ASSERT_FALSE(writer.hasError()) << "Recent write or drain operation has failed";
     }
     const float avgDuration =
             std::accumulate(presentationEndTimes.begin(), presentationEndTimes.end(), 0.0) /
             presentationEndTimes.size();
-    EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1);
+    std::stringstream observedEndTimes;
+    std::copy(presentationEndTimes.begin(), presentationEndTimes.end(),
+              std::ostream_iterator<float>(observedEndTimes, ", "));
+    EXPECT_NEAR(durationMs, avgDuration, presentationeEndPrecisionMs * 0.1)
+            << "Observed durations: " << observedEndTimes.str();
     writer.stop();
+    EXPECT_OK(stream->clearCallback());
     releasePatchIfNeeded();
 }
 
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index 6c5584d..e46e5b4 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -938,12 +938,13 @@
     StreamWriter(IStreamOut* stream, size_t bufferSize)
         : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {}
     StreamWriter(IStreamOut* stream, size_t bufferSize, std::vector<uint8_t>&& data,
-                 std::function<void()> onDataWrap)
+                 std::function<void()> onDataStart, std::function<bool()> onDataWrap)
         : mStream(stream),
           mBufferSize(bufferSize),
           mData(std::move(data)),
+          mOnDataStart(onDataStart),
           mOnDataWrap(onDataWrap) {
-        ALOGW("StreamWriter data size: %d", (int)mData.size());
+        ALOGI("StreamWriter data size: %d", (int)mData.size());
     }
     ~StreamWriter() {
         stop();
@@ -1010,6 +1011,7 @@
             ALOGE("command message queue write failed");
             return false;
         }
+        if (mDataPosition == 0) mOnDataStart();
         const size_t dataSize = std::min(mData.size() - mDataPosition, mDataMQ->availableToWrite());
         bool success = mDataMQ->write(mData.data() + mDataPosition, dataSize);
         ALOGE_IF(!success, "data message queue write failed");
@@ -1040,7 +1042,9 @@
             ALOGE("bad wait status: %d", ret);
             success = false;
         }
-        if (success && mDataPosition == 0) mOnDataWrap();
+        if (success && mDataPosition == 0) {
+            success = mOnDataWrap();
+        }
         return success;
     }
 
@@ -1048,7 +1052,8 @@
     IStreamOut* const mStream;
     const size_t mBufferSize;
     std::vector<uint8_t> mData;
-    std::function<void()> mOnDataWrap = []() {};
+    std::function<void()> mOnDataStart = []() {};
+    std::function<bool()> mOnDataWrap = []() { return true; };
     size_t mDataPosition = 0;
     std::unique_ptr<CommandMQ> mCommandMQ;
     std::unique_ptr<DataMQ> mDataMQ;