Merge "LE Audio Software offload: Handle death of client" into main
diff --git a/audio/aidl/common/include/Utils.h b/audio/aidl/common/include/Utils.h
index 52ae936..dc411ff 100644
--- a/audio/aidl/common/include/Utils.h
+++ b/audio/aidl/common/include/Utils.h
@@ -186,6 +186,12 @@
template <typename E, typename U = std::underlying_type_t<E>,
typename = std::enable_if_t<is_bit_position_enum<E>::value>>
+constexpr bool areAllBitPositionFlagsSet(U mask, std::initializer_list<E> flags) {
+ return (mask & makeBitPositionFlagMask<E>(flags)) == makeBitPositionFlagMask<E>(flags);
+}
+
+template <typename E, typename U = std::underlying_type_t<E>,
+ typename = std::enable_if_t<is_bit_position_enum<E>::value>>
constexpr bool isAnyBitPositionFlagSet(U mask, std::initializer_list<E> flags) {
return (mask & makeBitPositionFlagMask<E>(flags)) != 0;
}
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 73d7626..14082eb 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -77,8 +77,10 @@
"r_submix/ModuleRemoteSubmix.cpp",
"r_submix/SubmixRoute.cpp",
"r_submix/StreamRemoteSubmix.cpp",
+ "stub/ApeHeader.cpp",
"stub/DriverStubImpl.cpp",
"stub/ModuleStub.cpp",
+ "stub/StreamOffloadStub.cpp",
"stub/StreamStub.cpp",
"usb/ModuleUsb.cpp",
"usb/StreamUsb.cpp",
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index f9fa799..077d80b 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -211,9 +211,9 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
const auto& flags = portConfigIt->flags.value();
- StreamContext::DebugParameters params{
- mDebug.streamTransientStateDelayMs, mVendorDebug.forceTransientBurst,
- mVendorDebug.forceSynchronousDrain, mVendorDebug.forceDrainToDraining};
+ StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
+ mVendorDebug.forceTransientBurst,
+ mVendorDebug.forceSynchronousDrain};
std::unique_ptr<StreamContext::DataMQ> dataMQ = nullptr;
std::shared_ptr<IStreamCallback> streamAsyncCallback = nullptr;
std::shared_ptr<ISoundDose> soundDose;
@@ -1546,7 +1546,6 @@
const std::string Module::VendorDebug::kForceTransientBurstName = "aosp.forceTransientBurst";
const std::string Module::VendorDebug::kForceSynchronousDrainName = "aosp.forceSynchronousDrain";
-const std::string Module::VendorDebug::kForceDrainToDrainingName = "aosp.forceDrainToDraining";
ndk::ScopedAStatus Module::getVendorParameters(const std::vector<std::string>& in_ids,
std::vector<VendorParameter>* _aidl_return) {
@@ -1561,10 +1560,6 @@
VendorParameter forceSynchronousDrain{.id = id};
forceSynchronousDrain.ext.setParcelable(Boolean{mVendorDebug.forceSynchronousDrain});
_aidl_return->push_back(std::move(forceSynchronousDrain));
- } else if (id == VendorDebug::kForceDrainToDrainingName) {
- VendorParameter forceDrainToDraining{.id = id};
- forceDrainToDraining.ext.setParcelable(Boolean{mVendorDebug.forceDrainToDraining});
- _aidl_return->push_back(std::move(forceDrainToDraining));
} else {
allParametersKnown = false;
LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << id << "\"";
@@ -1605,10 +1600,6 @@
if (!extractParameter<Boolean>(p, &mVendorDebug.forceSynchronousDrain)) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- } else if (p.id == VendorDebug::kForceDrainToDrainingName) {
- if (!extractParameter<Boolean>(p, &mVendorDebug.forceDrainToDraining)) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
- }
} else {
allParametersKnown = false;
LOG(VERBOSE) << __func__ << ": " << mType << ": unrecognized parameter \"" << p.id
diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp
index 3da6d48..2a1dba9 100644
--- a/audio/aidl/default/ModulePrimary.cpp
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -21,12 +21,16 @@
#include <android-base/logging.h>
#include "core-impl/ModulePrimary.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::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
+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::MicrophoneInfo;
@@ -43,6 +47,17 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus ModulePrimary::calculateBufferSizeFrames(
+ const ::aidl::android::media::audio::common::AudioFormatDescription& format,
+ int32_t latencyMs, int32_t sampleRateHz, int32_t* bufferSizeFrames) {
+ if (format.type != ::aidl::android::media::audio::common::AudioFormatType::PCM &&
+ StreamOffloadStub::getSupportedEncodings().count(format.encoding)) {
+ *bufferSizeFrames = sampleRateHz / 2; // 1/2 of a second.
+ return ndk::ScopedAStatus::ok();
+ }
+ return Module::calculateBufferSizeFrames(format, latencyMs, sampleRateHz, bufferSizeFrames);
+}
+
ndk::ScopedAStatus ModulePrimary::createInputStream(StreamContext&& context,
const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
@@ -54,8 +69,18 @@
ndk::ScopedAStatus ModulePrimary::createOutputStream(
StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
- return createStreamInstance<StreamOutPrimary>(result, std::move(context), sourceMetadata,
- offloadInfo);
+ if (!areAllBitPositionFlagsSet(
+ context.getFlags().get<AudioIoFlags::output>(),
+ {AudioOutputFlags::COMPRESS_OFFLOAD, AudioOutputFlags::NON_BLOCKING})) {
+ return createStreamInstance<StreamOutPrimary>(result, std::move(context), sourceMetadata,
+ offloadInfo);
+ } else {
+ // "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);
+ }
}
int32_t ModulePrimary::getNominalLatencyMs(const AudioPortConfig&) {
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index c138095..c6c1b5d 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -142,12 +142,16 @@
", size in bytes: " + std::to_string(mDataBufferSize);
}
}
- if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
+ if (::android::status_t status = mDriver->init(this /*DriverCallbackInterface*/);
+ status != STATUS_OK) {
return "Failed to initialize the driver: " + std::to_string(status);
}
return "";
}
+void StreamWorkerCommonLogic::onBufferStateChange(size_t /*bufferFramesLeft*/) {}
+void StreamWorkerCommonLogic::onClipStateChange(size_t /*clipFramesLeft*/, bool /*hasNextClip*/) {}
+
void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
bool isConnected) const {
static const StreamDescriptor::Position kUnknownPosition = {
@@ -381,48 +385,60 @@
const std::string StreamOutWorkerLogic::kThreadName = "writer";
-StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
- if (mState == StreamDescriptor::State::DRAINING && mContext->getForceDrainToDraining() &&
- mOnDrainReadyStatus == OnDrainReadyStatus::UNSENT) {
+void StreamOutWorkerLogic::onBufferStateChange(size_t bufferFramesLeft) {
+ const StreamDescriptor::State state = mState;
+ LOG(DEBUG) << __func__ << ": state: " << toString(state)
+ << ", bufferFramesLeft: " << bufferFramesLeft;
+ if (state == StreamDescriptor::State::TRANSFERRING) {
+ mState = StreamDescriptor::State::ACTIVE;
std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
if (asyncCallback != nullptr) {
+ ndk::ScopedAStatus status = asyncCallback->onTransferReady();
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
+ }
+ }
+ }
+}
+
+void StreamOutWorkerLogic::onClipStateChange(size_t clipFramesLeft, bool hasNextClip) {
+ const DrainState drainState = mDrainState;
+ std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
+ LOG(DEBUG) << __func__ << ": drainState: " << drainState << "; clipFramesLeft "
+ << clipFramesLeft << "; hasNextClip? " << hasNextClip << "; asyncCallback? "
+ << (asyncCallback != nullptr);
+ if (drainState != DrainState::NONE && clipFramesLeft == 0) {
+ mState =
+ hasNextClip ? StreamDescriptor::State::TRANSFERRING : StreamDescriptor::State::IDLE;
+ mDrainState = DrainState::NONE;
+ if (drainState == DrainState::ALL && asyncCallback != nullptr) {
+ LOG(DEBUG) << __func__ << ": sending onDrainReady";
ndk::ScopedAStatus status = asyncCallback->onDrainReady();
if (!status.isOk()) {
LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
}
- // This sets the timeout for moving into IDLE on next iterations.
- switchToTransientState(StreamDescriptor::State::DRAINING);
- mOnDrainReadyStatus = OnDrainReadyStatus::SENT;
}
- } else if (mState == StreamDescriptor::State::DRAINING ||
- mState == StreamDescriptor::State::TRANSFERRING) {
+ } else if (drainState == DrainState::EN && clipFramesLeft > 0) {
+ // The stream state does not change, it is still draining.
+ mDrainState = DrainState::EN_SENT;
+ if (asyncCallback != nullptr) {
+ LOG(DEBUG) << __func__ << ": sending onDrainReady";
+ ndk::ScopedAStatus status = asyncCallback->onDrainReady();
+ if (!status.isOk()) {
+ LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
+ }
+ }
+ }
+}
+
+StreamOutWorkerLogic::Status StreamOutWorkerLogic::cycle() {
+ // Non-blocking mode is handled within 'onClipStateChange'
+ if (std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
+ mState == StreamDescriptor::State::DRAINING && asyncCallback == nullptr) {
if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - mTransientStateStart);
stateDurationMs >= mTransientStateDelayMs) {
- std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
- if (asyncCallback == nullptr) {
- // In blocking mode, mState can only be DRAINING.
- mState = StreamDescriptor::State::IDLE;
- } else {
- // In a real implementation, the driver should notify the HAL about
- // drain or transfer completion. In the stub, we switch unconditionally.
- if (mState == StreamDescriptor::State::DRAINING) {
- mState = StreamDescriptor::State::IDLE;
- if (mOnDrainReadyStatus != OnDrainReadyStatus::SENT) {
- ndk::ScopedAStatus status = asyncCallback->onDrainReady();
- if (!status.isOk()) {
- LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
- }
- mOnDrainReadyStatus = OnDrainReadyStatus::SENT;
- }
- } else {
- mState = StreamDescriptor::State::ACTIVE;
- ndk::ScopedAStatus status = asyncCallback->onTransferReady();
- if (!status.isOk()) {
- LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
- }
- }
- }
+ mState = StreamDescriptor::State::IDLE;
if (mTransientStateDelayMs.count() != 0) {
LOG(DEBUG) << __func__ << ": switched to state " << toString(mState)
<< " after a timeout";
@@ -552,10 +568,9 @@
mState = StreamDescriptor::State::IDLE;
} else {
switchToTransientState(StreamDescriptor::State::DRAINING);
- mOnDrainReadyStatus =
- mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY
- ? OnDrainReadyStatus::UNSENT
- : OnDrainReadyStatus::IGNORE;
+ mDrainState = mode == StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY
+ ? DrainState::EN
+ : DrainState::ALL;
}
} else {
LOG(ERROR) << __func__ << ": drain failed: " << status;
diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp
index 210c26b..7a44cc7 100644
--- a/audio/aidl/default/alsa/StreamAlsa.cpp
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -72,7 +72,7 @@
return source;
}
-::android::status_t StreamAlsa::init() {
+::android::status_t StreamAlsa::init(DriverCallbackInterface* /*callback*/) {
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
}
diff --git a/audio/aidl/default/bluetooth/StreamBluetooth.cpp b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
index 6e1a811..77ce121 100644
--- a/audio/aidl/default/bluetooth/StreamBluetooth.cpp
+++ b/audio/aidl/default/bluetooth/StreamBluetooth.cpp
@@ -70,7 +70,7 @@
cleanupWorker();
}
-::android::status_t StreamBluetooth::init() {
+::android::status_t StreamBluetooth::init(DriverCallbackInterface*) {
std::lock_guard guard(mLock);
if (mBtDeviceProxy == nullptr) {
// This is a normal situation in VTS tests.
diff --git a/audio/aidl/default/include/core-impl/DriverStubImpl.h b/audio/aidl/default/include/core-impl/DriverStubImpl.h
index 40a9fea..a1a6c82 100644
--- a/audio/aidl/default/include/core-impl/DriverStubImpl.h
+++ b/audio/aidl/default/include/core-impl/DriverStubImpl.h
@@ -24,7 +24,7 @@
public:
explicit DriverStubImpl(const StreamContext& context);
- ::android::status_t init() override;
+ ::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
@@ -34,7 +34,7 @@
int32_t* latencyMs) override;
void shutdown() override;
- private:
+ protected:
const size_t mBufferSizeFrames;
const size_t mFrameSizeBytes;
const int mSampleRate;
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index cbc13d1..6a43102 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -148,10 +148,8 @@
struct VendorDebug {
static const std::string kForceTransientBurstName;
static const std::string kForceSynchronousDrainName;
- static const std::string kForceDrainToDrainingName;
bool forceTransientBurst = false;
bool forceSynchronousDrain = false;
- bool forceDrainToDraining = false;
};
// ids of device ports created at runtime via 'connectExternalDevice'.
// Also stores a list of ids of mix ports with dynamic profiles that were populated from
diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h
index 82c8a03..a657dc5 100644
--- a/audio/aidl/default/include/core-impl/ModulePrimary.h
+++ b/audio/aidl/default/include/core-impl/ModulePrimary.h
@@ -28,6 +28,9 @@
protected:
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
+ ndk::ScopedAStatus calculateBufferSizeFrames(
+ const ::aidl::android::media::audio::common::AudioFormatDescription& format,
+ int32_t latencyMs, int32_t sampleRateHz, int32_t* bufferSizeFrames) override;
ndk::ScopedAStatus createInputStream(
StreamContext&& context,
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index d8bac82..f0139b4 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -78,10 +78,6 @@
bool forceTransientBurst = false;
// Force the "drain" command to be synchronous, going directly to the IDLE state.
bool forceSynchronousDrain = false;
- // Force the "drain early notify" command to keep the SM in the DRAINING state
- // after sending 'onDrainReady' callback. The SM moves to IDLE after
- // 'transientStateDelayMs'.
- bool forceDrainToDraining = false;
};
StreamContext() = default;
@@ -123,7 +119,6 @@
::aidl::android::media::audio::common::AudioIoFlags getFlags() const { return mFlags; }
bool getForceTransientBurst() const { return mDebugParameters.forceTransientBurst; }
bool getForceSynchronousDrain() const { return mDebugParameters.forceSynchronousDrain; }
- bool getForceDrainToDraining() const { return mDebugParameters.forceDrainToDraining; }
size_t getFrameSize() const;
int getInternalCommandCookie() const { return mInternalCommandCookie; }
int32_t getMixPortHandle() const { return mMixPortHandle; }
@@ -168,11 +163,27 @@
int64_t mFrameCount = 0;
};
+// Driver callbacks are executed on a dedicated thread, not on the worker thread.
+struct DriverCallbackInterface {
+ virtual ~DriverCallbackInterface() = default;
+ // Both callbacks are used to notify the worker about the progress of the playback
+ // offloaded to the DSP.
+
+ // 'bufferFramesLeft' is how many *encoded* frames are left in the buffer until
+ // it depletes.
+ virtual void onBufferStateChange(size_t bufferFramesLeft) = 0;
+ // 'clipFramesLeft' is how many *decoded* frames are left until the end of the currently
+ // playing clip. '0' frames left means that the clip has ended (by itself or due
+ // to draining).
+ // 'hasNextClip' indicates whether the DSP has audio data for the next clip.
+ virtual void onClipStateChange(size_t clipFramesLeft, bool hasNextClip) = 0;
+};
+
// This interface provides operations of the stream which are executed on the worker thread.
struct DriverInterface {
virtual ~DriverInterface() = default;
// All the methods below are called on the worker thread.
- virtual ::android::status_t init() = 0; // This function is only called once.
+ virtual ::android::status_t init(DriverCallbackInterface* callback) = 0; // Called once.
virtual ::android::status_t drain(StreamDescriptor::DrainMode mode) = 0;
virtual ::android::status_t flush() = 0;
virtual ::android::status_t pause() = 0;
@@ -194,7 +205,8 @@
virtual void shutdown() = 0; // This function is only called once.
};
-class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
+class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic,
+ public DriverCallbackInterface {
public:
bool isClosed() const { return mState == StreamContext::STATE_CLOSED; }
StreamDescriptor::State setClosed() {
@@ -214,7 +226,13 @@
mDriver(driver),
mTransientStateDelayMs(context->getTransientStateDelayMs()) {}
pid_t getTid() const;
+
+ // ::android::hardware::audio::common::StreamLogic
std::string init() override;
+ // DriverCallbackInterface
+ void onBufferStateChange(size_t bufferFramesLeft) override;
+ void onClipStateChange(size_t clipFramesLeft, bool hasNextClip) override;
+
void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
void populateReplyWrongState(StreamDescriptor::Reply* reply,
const StreamDescriptor::Command& command) const;
@@ -301,14 +319,17 @@
protected:
Status cycle() override;
+ // DriverCallbackInterface
+ void onBufferStateChange(size_t bufferFramesLeft) override;
+ void onClipStateChange(size_t clipFramesLeft, bool hasNextClip) override;
private:
bool write(size_t clientSize, StreamDescriptor::Reply* reply);
std::shared_ptr<IStreamOutEventCallback> mEventCallback;
- enum OnDrainReadyStatus : int32_t { IGNORE /*used for DRAIN_ALL*/, UNSENT, SENT };
- OnDrainReadyStatus mOnDrainReadyStatus = OnDrainReadyStatus::IGNORE;
+ enum DrainState : int32_t { NONE, ALL, EN /*early notify*/, EN_SENT };
+ std::atomic<DrainState> mDrainState = DrainState::NONE;
};
using StreamOutWorker = StreamWorkerImpl<StreamOutWorkerLogic>;
diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h
index 7e0f0ac..c0dcb63 100644
--- a/audio/aidl/default/include/core-impl/StreamAlsa.h
+++ b/audio/aidl/default/include/core-impl/StreamAlsa.h
@@ -40,7 +40,7 @@
~StreamAlsa();
// Methods of 'DriverInterface'.
- ::android::status_t init() override;
+ ::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
diff --git a/audio/aidl/default/include/core-impl/StreamBluetooth.h b/audio/aidl/default/include/core-impl/StreamBluetooth.h
index 357a546..2bdd6b2 100644
--- a/audio/aidl/default/include/core-impl/StreamBluetooth.h
+++ b/audio/aidl/default/include/core-impl/StreamBluetooth.h
@@ -44,7 +44,7 @@
~StreamBluetooth();
// Methods of 'DriverInterface'.
- ::android::status_t init() override;
+ ::android::status_t init(DriverCallbackInterface*) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
diff --git a/audio/aidl/default/include/core-impl/StreamOffloadStub.h b/audio/aidl/default/include/core-impl/StreamOffloadStub.h
new file mode 100644
index 0000000..3b452f9
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamOffloadStub.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <set>
+#include <string>
+
+#include "core-impl/DriverStubImpl.h"
+#include "core-impl/Stream.h"
+
+namespace aidl::android::hardware::audio::core {
+
+struct DspSimulatorState {
+ const std::string formatEncoding;
+ const int sampleRate;
+ const int64_t earlyNotifyFrames;
+ const int64_t bufferNotifyFrames;
+ DriverCallbackInterface* callback = nullptr; // set before starting DSP worker
+ std::mutex lock;
+ std::vector<int64_t> clipFramesLeft GUARDED_BY(lock);
+ int64_t bufferFramesLeft GUARDED_BY(lock);
+};
+
+class DspSimulatorLogic : public ::android::hardware::audio::common::StreamLogic {
+ protected:
+ explicit DspSimulatorLogic(DspSimulatorState& sharedState) : mSharedState(sharedState) {}
+ std::string init() override;
+ Status cycle() override;
+
+ private:
+ DspSimulatorState& mSharedState;
+};
+
+class DspSimulatorWorker
+ : public ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic> {
+ public:
+ explicit DspSimulatorWorker(DspSimulatorState& sharedState)
+ : ::android::hardware::audio::common::StreamWorker<DspSimulatorLogic>(sharedState) {}
+};
+
+class DriverOffloadStubImpl : public DriverStubImpl {
+ public:
+ DriverOffloadStubImpl(const StreamContext& context);
+ ::android::status_t init(DriverCallbackInterface* callback) override;
+ ::android::status_t drain(StreamDescriptor::DrainMode drainMode) override;
+ ::android::status_t flush() override;
+ ::android::status_t pause() override;
+ ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
+ int32_t* latencyMs) override;
+ void shutdown() override;
+
+ private:
+ DspSimulatorState mState;
+ DspSimulatorWorker mDspWorker;
+ bool mDspWorkerStarted = false;
+};
+
+class StreamOffloadStub : public StreamCommonImpl, public DriverOffloadStubImpl {
+ public:
+ static const std::set<std::string>& getSupportedEncodings();
+
+ StreamOffloadStub(StreamContext* context, const Metadata& metadata);
+ ~StreamOffloadStub();
+};
+
+class StreamOutOffloadStub final : public StreamOut, public StreamOffloadStub {
+ public:
+ friend class ndk::SharedRefBase;
+ StreamOutOffloadStub(
+ StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+ offloadInfo);
+
+ private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h
index 4f19a46..06f8bc3 100644
--- a/audio/aidl/default/include/core-impl/StreamPrimary.h
+++ b/audio/aidl/default/include/core-impl/StreamPrimary.h
@@ -32,7 +32,7 @@
StreamPrimary(StreamContext* context, const Metadata& metadata);
// Methods of 'DriverInterface'.
- ::android::status_t init() override;
+ ::android::status_t init(DriverCallbackInterface* callback) override;
::android::status_t drain(StreamDescriptor::DrainMode mode) override;
::android::status_t flush() override;
::android::status_t pause() override;
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 5e52ad0..28a446a 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -32,7 +32,7 @@
~StreamRemoteSubmix();
// Methods of 'DriverInterface'.
- ::android::status_t init() override;
+ ::android::status_t init(DriverCallbackInterface*) override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp
index 46e384e..8455680 100644
--- a/audio/aidl/default/primary/StreamPrimary.cpp
+++ b/audio/aidl/default/primary/StreamPrimary.cpp
@@ -46,9 +46,9 @@
context->startStreamDataProcessor();
}
-::android::status_t StreamPrimary::init() {
- RETURN_STATUS_IF_ERROR(mStubDriver.init());
- return StreamAlsa::init();
+::android::status_t StreamPrimary::init(DriverCallbackInterface* callback) {
+ RETURN_STATUS_IF_ERROR(mStubDriver.init(callback));
+ return StreamAlsa::init(callback);
}
::android::status_t StreamPrimary::drain(StreamDescriptor::DrainMode mode) {
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index f8ead16..cc3c644 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -51,7 +51,7 @@
cleanupWorker();
}
-::android::status_t StreamRemoteSubmix::init() {
+::android::status_t StreamRemoteSubmix::init(DriverCallbackInterface*) {
mCurrentRoute = SubmixRoute::findOrCreateRoute(mDeviceAddress, mStreamConfig);
if (mCurrentRoute == nullptr) {
return ::android::NO_INIT;
diff --git a/audio/aidl/default/stub/ApeHeader.cpp b/audio/aidl/default/stub/ApeHeader.cpp
new file mode 100644
index 0000000..9112377
--- /dev/null
+++ b/audio/aidl/default/stub/ApeHeader.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#define LOG_TAG "AHAL_OffloadStream"
+#include <android-base/logging.h>
+
+#include "ApeHeader.h"
+
+namespace aidl::android::hardware::audio::core {
+
+static constexpr uint32_t kApeSignature1 = 0x2043414d; // 'MAC ';
+static constexpr uint32_t kApeSignature2 = 0x4643414d; // 'MACF';
+static constexpr uint16_t kMinimumVersion = 3980;
+
+void* findApeHeader(void* buffer, size_t bufferSizeBytes, ApeHeader** header) {
+ auto advanceBy = [&](size_t bytes) -> void* {
+ buffer = static_cast<uint8_t*>(buffer) + bytes;
+ bufferSizeBytes -= bytes;
+ return buffer;
+ };
+
+ while (bufferSizeBytes >= sizeof(ApeDescriptor) + sizeof(ApeHeader)) {
+ ApeDescriptor* descPtr = static_cast<ApeDescriptor*>(buffer);
+ if (descPtr->signature != kApeSignature1 && descPtr->signature != kApeSignature2) {
+ advanceBy(sizeof(descPtr->signature));
+ continue;
+ }
+ if (descPtr->version < kMinimumVersion) {
+ LOG(ERROR) << __func__ << ": Unsupported APE version: " << descPtr->version
+ << ", minimum supported version: " << kMinimumVersion;
+ // Older versions only have a header, which is of the size similar to the modern header.
+ advanceBy(sizeof(ApeHeader));
+ continue;
+ }
+ if (descPtr->descriptorSizeBytes > bufferSizeBytes) {
+ LOG(ERROR) << __func__
+ << ": Invalid APE descriptor size: " << descPtr->descriptorSizeBytes
+ << ", overruns remaining buffer size: " << bufferSizeBytes;
+ advanceBy(sizeof(ApeDescriptor));
+ continue;
+ }
+ advanceBy(descPtr->descriptorSizeBytes);
+ if (sizeof(ApeHeader) > bufferSizeBytes) {
+ LOG(ERROR) << __func__ << ": APE header is incomplete, want: " << sizeof(ApeHeader)
+ << " bytes, have: " << bufferSizeBytes;
+ return nullptr;
+ }
+ *header = static_cast<ApeHeader*>(buffer);
+ return advanceBy(sizeof(ApeHeader));
+ }
+ return nullptr;
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/stub/ApeHeader.h b/audio/aidl/default/stub/ApeHeader.h
new file mode 100644
index 0000000..df30335
--- /dev/null
+++ b/audio/aidl/default/stub/ApeHeader.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+namespace aidl::android::hardware::audio::core {
+
+// Simplified APE (Monkey Audio) header definition sufficient to figure out
+// the basic parameters of the encoded file. Only supports the "current"
+// versions of the header (>= 3980).
+
+#pragma pack(push, 4)
+
+// Only the beginning of the descriptor is needed to find the header which
+// follows the descriptor.
+struct ApeDescriptor {
+ uint32_t signature; // 'MAC ' or 'MACF'
+ uint16_t version;
+ uint16_t padding;
+ uint32_t descriptorSizeBytes;
+ uint32_t headerSizeBytes;
+};
+
+struct ApeHeader {
+ uint16_t compressionLevel;
+ uint16_t flags;
+ uint32_t blocksPerFrame; // "frames" are encoder frames, while "blocks" are audio frames
+ uint32_t lastFrameBlocks; // number of "blocks" in the last encoder "frame"
+ uint32_t totalFrames; // total number of encoder "frames"
+ uint16_t bitsPerSample;
+ uint16_t channelCount;
+ uint32_t sampleRate;
+};
+
+#pragma pack(pop)
+
+// Tries to find APE descriptor and header in the buffer. Returns the position
+// after the header or nullptr if it was not found.
+void* findApeHeader(void* buffer, size_t bufferSizeBytes, ApeHeader** header);
+
+// Clip duration in audio frames ("blocks" in the APE terminology).
+inline int64_t getApeClipDurationFrames(const ApeHeader* header) {
+ return header->totalFrames != 0
+ ? (header->totalFrames - 1) * header->blocksPerFrame + header->lastFrameBlocks
+ : 0;
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/stub/DriverStubImpl.cpp b/audio/aidl/default/stub/DriverStubImpl.cpp
index beb0114..107affb 100644
--- a/audio/aidl/default/stub/DriverStubImpl.cpp
+++ b/audio/aidl/default/stub/DriverStubImpl.cpp
@@ -31,7 +31,7 @@
mIsAsynchronous(!!context.getAsyncCallback()),
mIsInput(context.isInput()) {}
-::android::status_t DriverStubImpl::init() {
+::android::status_t DriverStubImpl::init(DriverCallbackInterface* /*callback*/) {
mIsInitialized = true;
return ::android::OK;
}
diff --git a/audio/aidl/default/stub/StreamOffloadStub.cpp b/audio/aidl/default/stub/StreamOffloadStub.cpp
new file mode 100644
index 0000000..fb12697
--- /dev/null
+++ b/audio/aidl/default/stub/StreamOffloadStub.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+#define LOG_TAG "AHAL_OffloadStream"
+#include <android-base/logging.h>
+#include <audio_utils/clock.h>
+#include <error/Result.h>
+#include <utils/SystemClock.h>
+
+#include "ApeHeader.h"
+#include "core-impl/StreamOffloadStub.h"
+
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::media::audio::common::AudioDevice;
+using aidl::android::media::audio::common::AudioOffloadInfo;
+using aidl::android::media::audio::common::MicrophoneInfo;
+
+namespace aidl::android::hardware::audio::core {
+
+std::string DspSimulatorLogic::init() {
+ return "";
+}
+
+DspSimulatorLogic::Status DspSimulatorLogic::cycle() {
+ std::vector<std::pair<int64_t, bool>> clipNotifies;
+ // Simulate playback.
+ const int64_t timeBeginNs = ::android::uptimeNanos();
+ usleep(1000);
+ const int64_t clipFramesPlayed =
+ (::android::uptimeNanos() - timeBeginNs) * mSharedState.sampleRate / NANOS_PER_SECOND;
+ const int64_t bufferFramesConsumed = clipFramesPlayed / 2; // assume 1:2 compression ratio
+ int64_t bufferFramesLeft = 0;
+ {
+ std::lock_guard l(mSharedState.lock);
+ mSharedState.bufferFramesLeft =
+ mSharedState.bufferFramesLeft > bufferFramesConsumed
+ ? mSharedState.bufferFramesLeft - bufferFramesConsumed
+ : 0;
+ bufferFramesLeft = mSharedState.bufferFramesLeft;
+ int64_t framesPlayed = clipFramesPlayed;
+ while (framesPlayed > 0 && !mSharedState.clipFramesLeft.empty()) {
+ LOG(VERBOSE) << __func__ << ": clips: "
+ << ::android::internal::ToString(mSharedState.clipFramesLeft);
+ const bool hasNextClip = mSharedState.clipFramesLeft.size() > 1;
+ if (mSharedState.clipFramesLeft[0] > framesPlayed) {
+ mSharedState.clipFramesLeft[0] -= framesPlayed;
+ framesPlayed = 0;
+ if (mSharedState.clipFramesLeft[0] <= mSharedState.earlyNotifyFrames) {
+ clipNotifies.emplace_back(mSharedState.clipFramesLeft[0], hasNextClip);
+ }
+ } else {
+ clipNotifies.emplace_back(0 /*clipFramesLeft*/, hasNextClip);
+ framesPlayed -= mSharedState.clipFramesLeft[0];
+ mSharedState.clipFramesLeft.erase(mSharedState.clipFramesLeft.begin());
+ }
+ }
+ }
+ if (bufferFramesLeft <= mSharedState.bufferNotifyFrames) {
+ LOG(DEBUG) << __func__ << ": sending onBufferStateChange: " << bufferFramesLeft;
+ mSharedState.callback->onBufferStateChange(bufferFramesLeft);
+ }
+ for (const auto& notify : clipNotifies) {
+ LOG(DEBUG) << __func__ << ": sending onClipStateChange: " << notify.first << ", "
+ << notify.second;
+ mSharedState.callback->onClipStateChange(notify.first, notify.second);
+ }
+ return Status::CONTINUE;
+}
+
+DriverOffloadStubImpl::DriverOffloadStubImpl(const StreamContext& context)
+ : DriverStubImpl(context),
+ mState{context.getFormat().encoding, context.getSampleRate(),
+ 250 /*earlyNotifyMs*/ * context.getSampleRate() / MILLIS_PER_SECOND,
+ static_cast<int64_t>(context.getBufferSizeInFrames()) / 2},
+ mDspWorker(mState) {}
+
+::android::status_t DriverOffloadStubImpl::init(DriverCallbackInterface* callback) {
+ RETURN_STATUS_IF_ERROR(DriverStubImpl::init(callback));
+ if (!StreamOffloadStub::getSupportedEncodings().count(mState.formatEncoding)) {
+ LOG(ERROR) << __func__ << ": encoded format \"" << mState.formatEncoding
+ << "\" is not supported";
+ return ::android::NO_INIT;
+ }
+ mState.callback = callback;
+ return ::android::OK;
+}
+
+::android::status_t DriverOffloadStubImpl::drain(StreamDescriptor::DrainMode drainMode) {
+ // Does not call into the DriverStubImpl::drain.
+ if (!mIsInitialized) {
+ LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+ }
+ std::lock_guard l(mState.lock);
+ if (!mState.clipFramesLeft.empty()) {
+ // Cut playback of the current clip.
+ mState.clipFramesLeft[0] = std::min(mState.earlyNotifyFrames * 2, mState.clipFramesLeft[0]);
+ if (drainMode == StreamDescriptor::DrainMode::DRAIN_ALL) {
+ // Make sure there are no clips after the current one.
+ mState.clipFramesLeft.resize(1);
+ }
+ }
+ return ::android::OK;
+}
+
+::android::status_t DriverOffloadStubImpl::flush() {
+ RETURN_STATUS_IF_ERROR(DriverStubImpl::flush());
+ mDspWorker.pause();
+ {
+ std::lock_guard l(mState.lock);
+ mState.clipFramesLeft.clear();
+ mState.bufferFramesLeft = 0;
+ }
+ return ::android::OK;
+}
+
+::android::status_t DriverOffloadStubImpl::pause() {
+ RETURN_STATUS_IF_ERROR(DriverStubImpl::pause());
+ mDspWorker.pause();
+ return ::android::OK;
+}
+
+::android::status_t DriverOffloadStubImpl::transfer(void* buffer, size_t frameCount,
+ size_t* actualFrameCount,
+ int32_t* /*latencyMs*/) {
+ // Does not call into the DriverStubImpl::transfer.
+ if (!mIsInitialized) {
+ LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
+ }
+ if (mIsStandby) {
+ LOG(FATAL) << __func__ << ": must not happen while in standby";
+ }
+ if (!mDspWorkerStarted) {
+ // This is an "audio service thread," must have elevated priority.
+ if (!mDspWorker.start("dsp_sim", ANDROID_PRIORITY_URGENT_AUDIO)) {
+ return ::android::NO_INIT;
+ }
+ mDspWorkerStarted = true;
+ }
+ // Scan the buffer for clip headers.
+ *actualFrameCount = frameCount;
+ while (buffer != nullptr && frameCount > 0) {
+ ApeHeader* apeHeader = nullptr;
+ void* prevBuffer = buffer;
+ buffer = findApeHeader(prevBuffer, frameCount * mFrameSizeBytes, &apeHeader);
+ if (buffer != nullptr && apeHeader != nullptr) {
+ // Frame count does not include the size of the header data.
+ const size_t headerSizeFrames =
+ (static_cast<uint8_t*>(buffer) - static_cast<uint8_t*>(prevBuffer)) /
+ mFrameSizeBytes;
+ frameCount -= headerSizeFrames;
+ *actualFrameCount = frameCount;
+ // Stage the clip duration into the DSP worker's queue.
+ const int64_t clipDurationFrames = getApeClipDurationFrames(apeHeader);
+ const int32_t clipSampleRate = apeHeader->sampleRate;
+ LOG(DEBUG) << __func__ << ": found APE clip of " << clipDurationFrames << " frames, "
+ << "sample rate: " << clipSampleRate;
+ if (clipSampleRate == mState.sampleRate) {
+ std::lock_guard l(mState.lock);
+ mState.clipFramesLeft.push_back(clipDurationFrames);
+ } else {
+ LOG(ERROR) << __func__ << ": clip sample rate " << clipSampleRate
+ << " does not match stream sample rate " << mState.sampleRate;
+ }
+ } else {
+ frameCount = 0;
+ }
+ }
+ {
+ std::lock_guard l(mState.lock);
+ mState.bufferFramesLeft = *actualFrameCount;
+ }
+ mDspWorker.resume();
+ return ::android::OK;
+}
+
+void DriverOffloadStubImpl::shutdown() {
+ LOG(DEBUG) << __func__ << ": stopping the DSP simulator worker";
+ mDspWorker.stop();
+}
+
+// static
+const std::set<std::string>& StreamOffloadStub::getSupportedEncodings() {
+ static const std::set<std::string> kSupportedEncodings = {
+ "audio/x-ape",
+ };
+ return kSupportedEncodings;
+}
+
+StreamOffloadStub::StreamOffloadStub(StreamContext* context, const Metadata& metadata)
+ : StreamCommonImpl(context, metadata), DriverOffloadStubImpl(getContext()) {}
+
+StreamOffloadStub::~StreamOffloadStub() {
+ cleanupWorker();
+}
+
+StreamOutOffloadStub::StreamOutOffloadStub(StreamContext&& context,
+ const SourceMetadata& sourceMetadata,
+ const std::optional<AudioOffloadInfo>& offloadInfo)
+ : StreamOut(std::move(context), offloadInfo),
+ StreamOffloadStub(&mContextInstance, sourceMetadata) {}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/stub/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp
index f6c87e1..2278880 100644
--- a/audio/aidl/default/stub/StreamStub.cpp
+++ b/audio/aidl/default/stub/StreamStub.cpp
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-#include <cmath>
-
#define LOG_TAG "AHAL_Stream"
#include <android-base/logging.h>
-#include <audio_utils/clock.h>
#include "core-impl/Module.h"
#include "core-impl/StreamStub.h"
diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp
index 14e70ef..f855038 100644
--- a/audio/aidl/vts/Android.bp
+++ b/audio/aidl/vts/Android.bp
@@ -41,7 +41,6 @@
"-Wthread-safety",
"-Wno-error=unused-parameter",
],
- test_config_template: "VtsHalAudioTargetTestTemplate.xml",
test_suites: [
"general-tests",
"vts",
@@ -60,6 +59,7 @@
srcs: [
":effectCommonFile",
],
+ test_config_template: "VtsHalAudioEffectTargetTestTemplate.xml",
}
cc_test {
@@ -77,6 +77,11 @@
"VtsHalAudioCoreConfigTargetTest.cpp",
"VtsHalAudioCoreModuleTargetTest.cpp",
],
+ data: [
+ "data/sine882hz_44100_3s.ape",
+ "data/sine960hz_48000_3s.ape",
+ ],
+ test_config_template: "VtsHalAudioCoreTargetTestTemplate.xml",
}
cc_test {
diff --git a/audio/aidl/vts/ModuleConfig.cpp b/audio/aidl/vts/ModuleConfig.cpp
index d24c4c8..7d4cc70 100644
--- a/audio/aidl/vts/ModuleConfig.cpp
+++ b/audio/aidl/vts/ModuleConfig.cpp
@@ -36,12 +36,10 @@
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
-using aidl::android::media::audio::common::AudioEncapsulationMode;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
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;
@@ -51,26 +49,6 @@
using aidl::android::media::audio::common::Int;
// static
-std::optional<AudioOffloadInfo> ModuleConfig::generateOffloadInfoIfNeeded(
- const AudioPortConfig& portConfig) {
- if (portConfig.flags.has_value() &&
- portConfig.flags.value().getTag() == AudioIoFlags::Tag::output &&
- isBitPositionFlagSet(portConfig.flags.value().get<AudioIoFlags::Tag::output>(),
- AudioOutputFlags::COMPRESS_OFFLOAD)) {
- AudioOffloadInfo offloadInfo;
- offloadInfo.base.sampleRate = portConfig.sampleRate.value().value;
- offloadInfo.base.channelMask = portConfig.channelMask.value();
- offloadInfo.base.format = portConfig.format.value();
- offloadInfo.bitRatePerSecond = 256000; // Arbitrary value.
- offloadInfo.durationUs = std::chrono::microseconds(1min).count(); // Arbitrary value.
- offloadInfo.usage = AudioUsage::MEDIA;
- offloadInfo.encapsulationMode = AudioEncapsulationMode::NONE;
- return offloadInfo;
- }
- return {};
-}
-
-// static
std::vector<aidl::android::media::audio::common::AudioPort>
ModuleConfig::getAudioPortsForDeviceTypes(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
diff --git a/audio/aidl/vts/ModuleConfig.h b/audio/aidl/vts/ModuleConfig.h
index 27286e5..d45ccda 100644
--- a/audio/aidl/vts/ModuleConfig.h
+++ b/audio/aidl/vts/ModuleConfig.h
@@ -34,10 +34,6 @@
using SrcSinkGroup =
std::pair<aidl::android::hardware::audio::core::AudioRoute, std::vector<SrcSinkPair>>;
- static std::optional<aidl::android::media::audio::common::AudioOffloadInfo>
- generateOffloadInfoIfNeeded(
- const aidl::android::media::audio::common::AudioPortConfig& portConfig);
-
static std::vector<aidl::android::media::audio::common::AudioPort> getAudioPortsForDeviceTypes(
const std::vector<aidl::android::media::audio::common::AudioPort>& ports,
const std::vector<aidl::android::media::audio::common::AudioDeviceType>& deviceTypes,
diff --git a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
index 750e54d..8bbb60b 100644
--- a/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalAudioCoreModuleTargetTest.cpp
@@ -19,6 +19,7 @@
#include <cmath>
#include <condition_variable>
#include <forward_list>
+#include <fstream>
#include <limits>
#include <memory>
#include <mutex>
@@ -81,12 +82,15 @@
using aidl::android::hardware::audio::core::sounddose::ISoundDose;
using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::AudioConfigBase;
using aidl::android::media::audio::common::AudioContentType;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioDualMonoMode;
+using aidl::android::media::audio::common::AudioEncapsulationMode;
+using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioGainConfig;
using aidl::android::media::audio::common::AudioInputFlags;
@@ -96,6 +100,7 @@
using aidl::android::media::audio::common::AudioMMapPolicyInfo;
using aidl::android::media::audio::common::AudioMMapPolicyType;
using aidl::android::media::audio::common::AudioMode;
+using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioOutputFlags;
using aidl::android::media::audio::common::AudioPlaybackRate;
using aidl::android::media::audio::common::AudioPort;
@@ -217,6 +222,59 @@
return result;
}
+static const AudioFormatDescription kApeFileAudioFormat = {.encoding = "audio/x-ape"};
+static const AudioChannelLayout kApeFileChannelMask =
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(AudioChannelLayout::LAYOUT_MONO);
+struct MediaFileInfo {
+ std::string path;
+ int32_t bps;
+ int32_t durationMs;
+};
+static const std::map<AudioConfigBase, MediaFileInfo> kMediaFileDataInfos = {
+ {{44100, kApeFileChannelMask, kApeFileAudioFormat},
+ {"/data/local/tmp/sine882hz_44100_3s.ape", 217704, 3000}},
+ {{48000, kApeFileChannelMask, kApeFileAudioFormat},
+ {"/data/local/tmp/sine960hz_48000_3s.ape", 236256, 3000}},
+};
+
+std::optional<MediaFileInfo> getMediaFileInfoForConfig(const AudioConfigBase& config) {
+ const auto it = kMediaFileDataInfos.find(config);
+ if (it != kMediaFileDataInfos.end()) return it->second;
+ return std::nullopt;
+}
+
+std::optional<MediaFileInfo> getMediaFileInfoForConfig(const AudioPortConfig& config) {
+ if (!config.sampleRate.has_value() || !config.format.has_value() ||
+ !config.channelMask.has_value()) {
+ return std::nullopt;
+ }
+ return getMediaFileInfoForConfig(AudioConfigBase{
+ config.sampleRate->value, config.channelMask.value(), config.format.value()});
+}
+
+std::optional<AudioOffloadInfo> generateOffloadInfoIfNeeded(const AudioPortConfig& portConfig) {
+ if (portConfig.flags.has_value() &&
+ portConfig.flags.value().getTag() == AudioIoFlags::Tag::output &&
+ isBitPositionFlagSet(portConfig.flags.value().get<AudioIoFlags::Tag::output>(),
+ AudioOutputFlags::COMPRESS_OFFLOAD)) {
+ AudioOffloadInfo offloadInfo;
+ offloadInfo.base.sampleRate = portConfig.sampleRate.value().value;
+ offloadInfo.base.channelMask = portConfig.channelMask.value();
+ offloadInfo.base.format = portConfig.format.value();
+ if (auto info = getMediaFileInfoForConfig(portConfig); info.has_value()) {
+ offloadInfo.bitRatePerSecond = info->bps;
+ offloadInfo.durationUs = info->durationMs * 1000LL;
+ } else {
+ offloadInfo.bitRatePerSecond = 256000; // Arbitrary value.
+ offloadInfo.durationUs = std::chrono::microseconds(1min).count(); // Arbitrary value.
+ }
+ offloadInfo.usage = AudioUsage::MEDIA;
+ offloadInfo.encapsulationMode = AudioEncapsulationMode::NONE;
+ return offloadInfo;
+ }
+ return {};
+}
+
// All 'With*' classes are move-only because they are associated with some
// resource or state of a HAL module.
class WithDebugFlags {
@@ -652,11 +710,14 @@
typedef AidlMessageQueue<StreamDescriptor::Reply, SynchronizedReadWrite> ReplyMQ;
typedef AidlMessageQueue<int8_t, SynchronizedReadWrite> DataMQ;
- explicit StreamContext(const StreamDescriptor& descriptor)
+ explicit StreamContext(const StreamDescriptor& descriptor, const AudioConfigBase& config,
+ AudioIoFlags flags)
: mFrameSizeBytes(descriptor.frameSizeBytes),
+ mConfig(config),
mCommandMQ(new CommandMQ(descriptor.command)),
mReplyMQ(new ReplyMQ(descriptor.reply)),
mBufferSizeFrames(descriptor.bufferSizeFrames),
+ mFlags(flags),
mDataMQ(maybeCreateDataMQ(descriptor)),
mIsMmapped(isMmapped(descriptor)),
mSharedMemoryFd(maybeGetMmapFd(descriptor)) {
@@ -695,9 +756,12 @@
size_t getBufferSizeBytes() const { return mFrameSizeBytes * mBufferSizeFrames; }
size_t getBufferSizeFrames() const { return mBufferSizeFrames; }
CommandMQ* getCommandMQ() const { return mCommandMQ.get(); }
+ const AudioConfigBase& getConfig() const { return mConfig; }
DataMQ* getDataMQ() const { return mDataMQ.get(); }
+ AudioIoFlags getFlags() const { return mFlags; }
size_t getFrameSizeBytes() const { return mFrameSizeBytes; }
ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); }
+ int getSampleRate() const { return mConfig.sampleRate; }
bool isMmapped() const { return mIsMmapped; }
int8_t* getMmapMemory() const { return mSharedMemory; }
@@ -722,9 +786,11 @@
}
const size_t mFrameSizeBytes;
+ const AudioConfigBase mConfig;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<ReplyMQ> mReplyMQ;
const size_t mBufferSizeFrames;
+ const AudioIoFlags mFlags;
std::unique_ptr<DataMQ> mDataMQ;
const bool mIsMmapped;
const int32_t mSharedMemoryFd;
@@ -926,12 +992,19 @@
mDriver(driver),
mEventReceiver(eventReceiver),
mIsMmapped(context.isMmapped()),
- mSharedMemory(context.getMmapMemory()) {}
+ mSharedMemory(context.getMmapMemory()),
+ mIsCompressOffload(context.getFlags().getTag() == AudioIoFlags::output &&
+ isBitPositionFlagSet(context.getFlags().get<AudioIoFlags::output>(),
+ AudioOutputFlags::COMPRESS_OFFLOAD)),
+ mConfig(context.getConfig()) {}
StreamContext::CommandMQ* getCommandMQ() const { return mCommandMQ; }
+ const AudioConfigBase& getConfig() const { return mConfig; }
StreamContext::ReplyMQ* getReplyMQ() const { return mReplyMQ; }
StreamContext::DataMQ* getDataMQ() const { return mDataMQ; }
StreamLogicDriver* getDriver() const { return mDriver; }
StreamEventReceiver* getEventReceiver() const { return mEventReceiver; }
+ int getSampleRate() const { return mConfig.sampleRate; }
+ bool isCompressOffload() const { return mIsCompressOffload; }
bool isMmapped() const { return mIsMmapped; }
std::string init() override {
@@ -940,6 +1013,10 @@
}
const std::vector<int8_t>& getData() const { return mData; }
void fillData(int8_t filler) { std::fill(mData.begin(), mData.end(), filler); }
+ void loadData(std::ifstream& is, size_t* size) {
+ *size = std::min(*size, mData.size());
+ is.read(reinterpret_cast<char*>(mData.data()), *size);
+ }
std::optional<StreamDescriptor::Command> maybeGetNextCommand(int* actualSize = nullptr) {
TransitionTrigger trigger = mDriver->getNextTrigger(mData.size(), actualSize);
if (StreamEventReceiver::Event* expEvent =
@@ -1002,6 +1079,8 @@
int mLastEventSeq = StreamEventReceiver::kEventSeqInit;
const bool mIsMmapped;
int8_t* mSharedMemory = nullptr;
+ const bool mIsCompressOffload;
+ const AudioConfigBase mConfig;
};
class StreamReaderLogic : public StreamCommonLogic {
@@ -1102,6 +1181,24 @@
const std::vector<int8_t>& getData() const { return StreamCommonLogic::getData(); }
protected:
+ std::string init() override {
+ if (auto status = StreamCommonLogic::init(); !status.empty()) return status;
+ if (isCompressOffload()) {
+ const auto info = getMediaFileInfoForConfig(getConfig());
+ if (info) {
+ mCompressedMedia.open(info->path, std::ios::in | std::ios::binary);
+ if (!mCompressedMedia.is_open()) {
+ return std::string("failed to open media file \"") + info->path + "\"";
+ }
+ mCompressedMedia.seekg(0, mCompressedMedia.end);
+ mCompressedMediaSize = mCompressedMedia.tellg();
+ mCompressedMedia.seekg(0, mCompressedMedia.beg);
+ LOG(DEBUG) << __func__ << ": using media file \"" << info->path << "\", size "
+ << mCompressedMediaSize << " bytes";
+ }
+ }
+ return "";
+ }
Status cycle() override {
if (getDriver()->done()) {
LOG(DEBUG) << __func__ << ": clean exit";
@@ -1115,13 +1212,31 @@
LOG(ERROR) << __func__ << ": no next command";
return Status::ABORT;
}
- if (actualSize != 0) {
+ if (actualSize > 0) {
if (command.getTag() == StreamDescriptor::Command::burst) {
- fillData(mBurstIteration);
- if (mBurstIteration < std::numeric_limits<int8_t>::max()) {
- mBurstIteration++;
+ if (!isCompressOffload()) {
+ fillData(mBurstIteration);
+ if (mBurstIteration < std::numeric_limits<int8_t>::max()) {
+ mBurstIteration++;
+ } else {
+ mBurstIteration = 0;
+ }
} else {
- mBurstIteration = 0;
+ fillData(0);
+ size_t size = std::min(static_cast<size_t>(actualSize),
+ mCompressedMediaSize - mCompressedMediaPos);
+ loadData(mCompressedMedia, &size);
+ if (!mCompressedMedia.good()) {
+ LOG(ERROR) << __func__ << ": read failed";
+ return Status::ABORT;
+ }
+ LOG(DEBUG) << __func__ << ": read from file " << size << " bytes";
+ mCompressedMediaPos += size;
+ if (mCompressedMediaPos >= mCompressedMediaSize) {
+ mCompressedMedia.seekg(0, mCompressedMedia.beg);
+ mCompressedMediaPos = 0;
+ LOG(DEBUG) << __func__ << ": rewound to the beginning of the file";
+ }
}
}
if (isMmapped() ? !writeDataToMmap() : !writeDataToMQ()) {
@@ -1185,6 +1300,9 @@
private:
int8_t mBurstIteration = 1;
+ std::ifstream mCompressedMedia;
+ size_t mCompressedMediaSize = 0;
+ size_t mCompressedMediaPos = 0;
};
using StreamWriter = StreamWorker<StreamWriterLogic>;
@@ -1293,7 +1411,13 @@
ASSERT_NE(nullptr, mStream) << "port config id " << getPortId();
EXPECT_GE(mDescriptor.bufferSizeFrames, bufferSizeFrames)
<< "actual buffer size must be no less than requested";
- mContext.emplace(mDescriptor);
+ const auto& config = mPortConfig.get();
+ ASSERT_TRUE(config.channelMask.has_value());
+ ASSERT_TRUE(config.format.has_value());
+ ASSERT_TRUE(config.sampleRate.has_value());
+ ASSERT_TRUE(config.flags.has_value());
+ const AudioConfigBase cfg{config.sampleRate->value, *config.channelMask, *config.format};
+ mContext.emplace(mDescriptor, cfg, config.flags.value());
ASSERT_NO_FATAL_FAILURE(mContext.value().checkIsValid());
}
void SetUp(IModule* module, long bufferSizeFrames) {
@@ -1364,7 +1488,7 @@
aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
args.portConfigId = portConfig.id;
args.sourceMetadata = GenerateSourceMetadata(portConfig);
- args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig);
+ args.offloadInfo = generateOffloadInfoIfNeeded(portConfig);
args.bufferSizeFrames = bufferSizeFrames;
auto callback = ndk::SharedRefBase::make<DefaultStreamCallback>();
args.callback = callback;
@@ -3192,10 +3316,12 @@
{AudioInputFlags::MMAP_NOIRQ, AudioInputFlags::VOIP_TX,
AudioInputFlags::HW_HOTWORD, AudioInputFlags::HOTWORD_TAP})) ||
(portConfig.flags.value().getTag() == AudioIoFlags::output &&
- isAnyBitPositionFlagSet(
- portConfig.flags.value().template get<AudioIoFlags::output>(),
- {AudioOutputFlags::MMAP_NOIRQ, AudioOutputFlags::VOIP_RX,
- AudioOutputFlags::COMPRESS_OFFLOAD, AudioOutputFlags::INCALL_MUSIC}));
+ (isAnyBitPositionFlagSet(portConfig.flags.value().template get<AudioIoFlags::output>(),
+ {AudioOutputFlags::MMAP_NOIRQ, AudioOutputFlags::VOIP_RX,
+ AudioOutputFlags::INCALL_MUSIC}) ||
+ (isBitPositionFlagSet(portConfig.flags.value().template get<AudioIoFlags::output>(),
+ AudioOutputFlags::COMPRESS_OFFLOAD) &&
+ !getMediaFileInfoForConfig(portConfig))));
}
// Certain types of devices can not be used without special preconditions.
@@ -3863,7 +3989,7 @@
aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
args.portConfigId = portConfig.id;
args.sourceMetadata = GenerateSourceMetadata(portConfig);
- args.offloadInfo = ModuleConfig::generateOffloadInfoIfNeeded(portConfig);
+ args.offloadInfo = generateOffloadInfoIfNeeded(portConfig);
args.bufferSizeFrames = stream.getPatch().minimumStreamBufferSizeFrames;
aidl::android::hardware::audio::core::IModule::OpenOutputStreamReturn ret;
EXPECT_STATUS(EX_ILLEGAL_ARGUMENT, module->openOutputStream(args, &ret))
@@ -4185,18 +4311,6 @@
std::get<NAMED_CMD_DELAY_MS>(std::get<PARAM_CMD_SEQ>(GetParam()));
ASSERT_NO_FATAL_FAILURE(delayTransientStates.SetUp(module.get()));
ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
- if (aidlVersion >= kAidlVersion3 && isNonBlocking && !IOTraits<Stream>::is_input) {
- // Also try running the same sequence with "aosp.forceDrainToDraining" set.
- // This will only work with the default implementation. When it works, the stream
- // tries always to move to the 'DRAINING' state after an "early notify" drain.
- // This helps to check more paths for our test scenarios.
- WithModuleParameter forceDrainToDraining("aosp.forceDrainToDraining",
- Boolean{true});
- if (forceDrainToDraining.SetUpNoChecks(module.get(), true /*failureExpected*/)
- .isOk()) {
- ASSERT_NO_FATAL_FAILURE(runStreamIoCommands(portConfig));
- }
- }
if (isNonBlocking) {
// Also try running the same sequence with "aosp.forceTransientBurst" set.
// This will only work with the default implementation. When it works, the stream
@@ -4744,9 +4858,14 @@
std::shared_ptr<StateSequence> makeDrainEarlyOutCommands() {
using State = StreamDescriptor::State;
auto d = std::make_unique<StateDag>();
- StateDag::Node last = d->makeFinalNode(State::IDLE);
- StateDag::Node draining = d->makeNode(State::DRAINING, kDrainReadyEvent, last);
- draining.children().push_back(d->makeNode(State::DRAINING, kGetStatusCommand, last));
+ // In the "early notify" case, the transition to the `IDLE` state following
+ // the 'onDrainReady' event can take some time. Waiting for an arbitrary amount
+ // of time may make the test fragile. Instead, for successful completion
+ // is registered if the stream has entered `IDLE` or `DRAINING` state.
+ StateDag::Node lastIdle = d->makeFinalNode(State::IDLE);
+ StateDag::Node lastDraining = d->makeFinalNode(State::DRAINING);
+ StateDag::Node draining =
+ d->makeNode(State::DRAINING, kDrainReadyEvent, lastIdle, lastDraining);
StateDag::Node active = d->makeNode(State::ACTIVE, kDrainOutEarlyCommand, draining);
StateDag::Node idle = d->makeNode(State::IDLE, kBurstCommand, active);
idle.children().push_back(d->makeNode(State::TRANSFERRING, kTransferReadyEvent, active));
diff --git a/audio/aidl/vts/VtsHalAudioCoreTargetTestTemplate.xml b/audio/aidl/vts/VtsHalAudioCoreTargetTestTemplate.xml
new file mode 100644
index 0000000..94db58d
--- /dev/null
+++ b/audio/aidl/vts/VtsHalAudioCoreTargetTestTemplate.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 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.
+-->
+<configuration description="Runs {MODULE}.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.StopServicesSetup"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop vts.native_server.on 1"/>
+ <option name="teardown-command" value="setprop vts.native_server.on 0"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}" />
+ <option name="push" value="sine882hz_44100_3s.ape->/data/local/tmp/sine882hz_44100_3s.ape" />
+ <option name="push" value="sine960hz_48000_3s.ape->/data/local/tmp/sine960hz_48000_3s.ape" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="{MODULE}" />
+ <option name="native-test-timeout" value="30m" />
+ </test>
+</configuration>
diff --git a/audio/aidl/vts/VtsHalAudioTargetTestTemplate.xml b/audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
similarity index 100%
rename from audio/aidl/vts/VtsHalAudioTargetTestTemplate.xml
rename to audio/aidl/vts/VtsHalAudioEffectTargetTestTemplate.xml
diff --git a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
index 2802bf9..1b0b681 100644
--- a/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
+++ b/audio/aidl/vts/VtsHalHapticGeneratorTargetTest.cpp
@@ -275,6 +275,9 @@
enum DataTestParam { EFFECT_INSTANCE, LAYOUT };
using HapticGeneratorDataTestParam = std::tuple<EffectInstance, int32_t>;
+// minimal HAL interface version to run the data path test
+constexpr int32_t kMinDataTestHalVersion = 3;
+
class HapticGeneratorDataTest : public ::testing::TestWithParam<HapticGeneratorDataTestParam>,
public HapticGeneratorHelper {
public:
@@ -293,7 +296,14 @@
mOutput.resize(mHapticSamples + mAudioSamples, 0);
}
- void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator(mChMask)); }
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(SetUpHapticGenerator(mChMask));
+ if (int32_t version;
+ mEffect->getInterfaceVersion(&version).isOk() && version < kMinDataTestHalVersion) {
+ GTEST_SKIP() << "Skipping the data test for version: " << version << "\n";
+ }
+ }
+
void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownHapticGenerator()); }
void generateSinePeriod() {
diff --git a/audio/aidl/vts/data/sine882hz_44100_3s.ape b/audio/aidl/vts/data/sine882hz_44100_3s.ape
new file mode 100644
index 0000000..1cefb15
--- /dev/null
+++ b/audio/aidl/vts/data/sine882hz_44100_3s.ape
Binary files differ
diff --git a/audio/aidl/vts/data/sine960hz_48000_3s.ape b/audio/aidl/vts/data/sine960hz_48000_3s.ape
new file mode 100644
index 0000000..149c42a
--- /dev/null
+++ b/audio/aidl/vts/data/sine960hz_48000_3s.ape
Binary files differ
diff --git a/automotive/can/OWNERS b/automotive/can/OWNERS
index ffa4828..b738dac 100644
--- a/automotive/can/OWNERS
+++ b/automotive/can/OWNERS
@@ -1,3 +1,2 @@
-kevinme@google.com
chrisweir@google.com
twasilczyk@google.com
diff --git a/automotive/vehicle/OWNERS b/automotive/vehicle/OWNERS
index f099287..066af9a 100644
--- a/automotive/vehicle/OWNERS
+++ b/automotive/vehicle/OWNERS
@@ -1,9 +1,6 @@
ericjeong@google.com
shanyu@google.com
-# GRPC VHAL
-per-file aidl/impl/grpc/** =egranata@google.com
-
# Property definition
per-file aidl_property/** = tylertrephan@google.com
per-file aidl/generated_lib/** = tylertrephan@google.com
diff --git a/power/stats/1.0/default/OWNERS b/power/stats/1.0/default/OWNERS
index 2d95a97..0557220 100644
--- a/power/stats/1.0/default/OWNERS
+++ b/power/stats/1.0/default/OWNERS
@@ -1,3 +1,2 @@
krossmo@google.com
bsschwar@google.com
-tstrudel@google.com