Merge "Fix a segmentation fault in NoStaleEvents tests" into main
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
index 818b18e..6b69845 100644
--- a/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-in-async-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-in-async-sm.gv -o stream-in-async-sm.png
digraph stream_in_async_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=tomato] CLOSED;
diff --git a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
index 805dc32..aa7af54 100644
--- a/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-in-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-in-sm.gv -o stream-in-sm.png
digraph stream_in_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=tomato] CLOSED;
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
index 501dc01..a3f0de9 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-async-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-out-async-sm.gv -o stream-out-async-sm.png
digraph stream_out_async_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=lightgreen] IDLE; // buffer is empty
diff --git a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
index 47e7fda..23fb5d9 100644
--- a/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
+++ b/audio/aidl/android/hardware/audio/core/stream-out-sm.gv
@@ -14,8 +14,8 @@
// To render: dot -Tpng stream-out-sm.gv -o stream-out-sm.png
digraph stream_out_state_machine {
- node [shape=doublecircle style=filled fillcolor=black width=0.5] I;
- node [shape=point width=0.5] F;
+ node [shape=point style=filled fillcolor=black width=0.5] I;
+ node [shape=doublecircle width=0.5] F;
node [shape=oval width=1];
node [fillcolor=lightgreen] STANDBY; // buffer is empty
node [fillcolor=lightgreen] IDLE; // buffer is empty
diff --git a/audio/aidl/android/hardware/audio/effect/state.gv b/audio/aidl/android/hardware/audio/effect/state.gv
index c88521e..ce369ba 100644
--- a/audio/aidl/android/hardware/audio/effect/state.gv
+++ b/audio/aidl/android/hardware/audio/effect/state.gv
@@ -25,12 +25,12 @@
I -> INIT[label = "IFactory.createEffect" labelfontcolor = "navy"];
INIT -> F[label = "IFactory.destroyEffect"];
- INIT -> IDLE[label = "open()" labelfontcolor = "lime"];
- IDLE -> PROCESSING[label = "command(START"];
- PROCESSING -> IDLE[label = "command(STOP)\ncommand(RESET)"];
- IDLE -> INIT[label = "close()"];
+ INIT -> IDLE[label = "IEffect.open()" labelfontcolor = "lime"];
+ IDLE -> PROCESSING[label = "IEffect.command(START"];
+ PROCESSING -> IDLE[label = "IEffect.command(STOP)\nIEffect.command(RESET)"];
+ IDLE -> INIT[label = "IEffect.close()"];
- INIT -> INIT[label = "getState\ngetDescriptor"];
- IDLE -> IDLE[label = "getXXX\nsetParameter\ncommand(RESET)"];
- PROCESSING -> PROCESSING[label = "getXXX\nsetParameter"];
+ INIT -> INIT[label = "IEffect.getState\nIEffect.getDescriptor"];
+ IDLE -> IDLE[label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor\nIEffect.command(RESET)"];
+ PROCESSING -> PROCESSING[label = "IEffect.getParameter\nIEffect.setParameter\nIEffect.getDescriptor"];
}
diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp
index 19b2397..4e583a4 100644
--- a/audio/aidl/default/Android.bp
+++ b/audio/aidl/default/Android.bp
@@ -76,6 +76,7 @@
"ModulePrimary.cpp",
"SoundDose.cpp",
"Stream.cpp",
+ "StreamSwitcher.cpp",
"Telephony.cpp",
"alsa/Mixer.cpp",
"alsa/ModuleAlsa.cpp",
diff --git a/audio/aidl/default/EffectFactory.cpp b/audio/aidl/default/EffectFactory.cpp
index 62f3c7e..96f13ba 100644
--- a/audio/aidl/default/EffectFactory.cpp
+++ b/audio/aidl/default/EffectFactory.cpp
@@ -49,18 +49,18 @@
if (auto spEffect = it.first.lock()) {
LOG(ERROR) << __func__ << " erase remaining instance UUID "
<< ::android::audio::utils::toString(it.second.first);
- destroyEffectImpl(spEffect);
+ destroyEffectImpl_l(spEffect);
}
}
}
}
-ndk::ScopedAStatus Factory::getDescriptorWithUuid(const AudioUuid& uuid, Descriptor* desc) {
+ndk::ScopedAStatus Factory::getDescriptorWithUuid_l(const AudioUuid& uuid, Descriptor* desc) {
RETURN_IF(!desc, EX_NULL_POINTER, "nullDescriptor");
if (mEffectLibMap.count(uuid)) {
auto& entry = mEffectLibMap[uuid];
- getDlSyms(entry);
+ getDlSyms_l(entry);
auto& libInterface = std::get<kMapEntryInterfaceIndex>(entry);
RETURN_IF(!libInterface || !libInterface->queryEffectFunc, EX_NULL_POINTER,
"dlNullQueryEffectFunc");
@@ -75,6 +75,7 @@
const std::optional<AudioUuid>& in_impl_uuid,
const std::optional<AudioUuid>& in_proxy_uuid,
std::vector<Descriptor>* _aidl_return) {
+ std::lock_guard lg(mMutex);
// get the matching list
std::vector<Descriptor::Identity> idList;
std::copy_if(mIdentitySet.begin(), mIdentitySet.end(), std::back_inserter(idList),
@@ -88,7 +89,8 @@
for (const auto& id : idList) {
if (mEffectLibMap.count(id.uuid)) {
Descriptor desc;
- RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid(id.uuid, &desc), "getDescriptorFailed");
+ RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid_l(id.uuid, &desc),
+ "getDescriptorFailed");
// update proxy UUID with information from config xml
desc.common.id.proxy = id.proxy;
_aidl_return->emplace_back(std::move(desc));
@@ -99,6 +101,7 @@
ndk::ScopedAStatus Factory::queryProcessing(const std::optional<Processing::Type>& in_type,
std::vector<Processing>* _aidl_return) {
+ std::lock_guard lg(mMutex);
const auto& processings = mConfig.getProcessingMap();
// Processing stream type
for (const auto& procIter : processings) {
@@ -110,7 +113,7 @@
if (libs.proxyLibrary.has_value()) {
desc.common.id.proxy = libs.proxyLibrary.value().uuid;
}
- RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid(lib.uuid, &desc),
+ RETURN_IF_ASTATUS_NOT_OK(getDescriptorWithUuid_l(lib.uuid, &desc),
"getDescriptorFailed");
process.ids.emplace_back(desc);
}
@@ -125,9 +128,10 @@
ndk::ScopedAStatus Factory::createEffect(const AudioUuid& in_impl_uuid,
std::shared_ptr<IEffect>* _aidl_return) {
LOG(DEBUG) << __func__ << ": UUID " << ::android::audio::utils::toString(in_impl_uuid);
+ std::lock_guard lg(mMutex);
if (mEffectLibMap.count(in_impl_uuid)) {
auto& entry = mEffectLibMap[in_impl_uuid];
- getDlSyms(entry);
+ getDlSyms_l(entry);
auto& libInterface = std::get<kMapEntryInterfaceIndex>(entry);
RETURN_IF(!libInterface || !libInterface->createEffectFunc, EX_NULL_POINTER,
@@ -152,7 +156,7 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Factory::destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle) {
+ndk::ScopedAStatus Factory::destroyEffectImpl_l(const std::shared_ptr<IEffect>& in_handle) {
std::weak_ptr<IEffect> wpHandle(in_handle);
// find the effect entry with key (std::weak_ptr<IEffect>)
if (auto effectIt = mEffectMap.find(wpHandle); effectIt != mEffectMap.end()) {
@@ -177,7 +181,7 @@
}
// go over the map and cleanup all expired weak_ptrs.
-void Factory::cleanupEffectMap() {
+void Factory::cleanupEffectMap_l() {
for (auto it = mEffectMap.begin(); it != mEffectMap.end();) {
if (nullptr == it->first.lock()) {
it = mEffectMap.erase(it);
@@ -189,13 +193,15 @@
ndk::ScopedAStatus Factory::destroyEffect(const std::shared_ptr<IEffect>& in_handle) {
LOG(DEBUG) << __func__ << ": instance " << in_handle.get();
- ndk::ScopedAStatus status = destroyEffectImpl(in_handle);
+ std::lock_guard lg(mMutex);
+ ndk::ScopedAStatus status = destroyEffectImpl_l(in_handle);
// always do the cleanup
- cleanupEffectMap();
+ cleanupEffectMap_l();
return status;
}
-bool Factory::openEffectLibrary(const AudioUuid& impl, const std::string& path) {
+bool Factory::openEffectLibrary(const AudioUuid& impl,
+ const std::string& path) NO_THREAD_SAFETY_ANALYSIS {
std::function<void(void*)> dlClose = [](void* handle) -> void {
if (handle && dlclose(handle)) {
LOG(ERROR) << "dlclose failed " << dlerror();
@@ -219,9 +225,9 @@
return true;
}
-void Factory::createIdentityWithConfig(const EffectConfig::Library& configLib,
- const AudioUuid& typeUuid,
- const std::optional<AudioUuid> proxyUuid) {
+void Factory::createIdentityWithConfig(
+ const EffectConfig::Library& configLib, const AudioUuid& typeUuid,
+ const std::optional<AudioUuid> proxyUuid) NO_THREAD_SAFETY_ANALYSIS {
static const auto& libMap = mConfig.getLibraryMap();
const std::string& libName = configLib.name;
if (auto path = libMap.find(libName); path != libMap.end()) {
@@ -263,7 +269,7 @@
}
}
-void Factory::getDlSyms(DlEntry& entry) {
+void Factory::getDlSyms_l(DlEntry& entry) {
auto& dlHandle = std::get<kMapEntryHandleIndex>(entry);
RETURN_VALUE_IF(!dlHandle, void(), "dlNullHandle");
// Get the reference of the DL interfaces in library map tuple.
diff --git a/audio/aidl/default/EffectImpl.cpp b/audio/aidl/default/EffectImpl.cpp
index da1ad11..c81c731 100644
--- a/audio/aidl/default/EffectImpl.cpp
+++ b/audio/aidl/default/EffectImpl.cpp
@@ -76,7 +76,7 @@
}
ndk::ScopedAStatus EffectImpl::setParameter(const Parameter& param) {
- LOG(DEBUG) << getEffectName() << __func__ << " with: " << param.toString();
+ LOG(VERBOSE) << getEffectName() << __func__ << " with: " << param.toString();
const auto tag = param.getTag();
switch (tag) {
@@ -100,7 +100,6 @@
}
ndk::ScopedAStatus EffectImpl::getParameter(const Parameter::Id& id, Parameter* param) {
- LOG(DEBUG) << getEffectName() << __func__ << id.toString();
auto tag = id.getTag();
switch (tag) {
case Parameter::Id::commonTag: {
@@ -117,7 +116,7 @@
break;
}
}
- LOG(DEBUG) << getEffectName() << __func__ << param->toString();
+ LOG(VERBOSE) << getEffectName() << __func__ << id.toString() << param->toString();
return ndk::ScopedAStatus::ok();
}
@@ -254,7 +253,7 @@
for (int i = 0; i < samples; i++) {
*out++ = *in++;
}
- LOG(DEBUG) << getEffectName() << __func__ << " done processing " << samples << " samples";
+ LOG(VERBOSE) << getEffectName() << __func__ << " done processing " << samples << " samples";
return {STATUS_OK, samples, samples};
}
diff --git a/audio/aidl/default/EffectThread.cpp b/audio/aidl/default/EffectThread.cpp
index 574dc69..cd2ba53 100644
--- a/audio/aidl/default/EffectThread.cpp
+++ b/audio/aidl/default/EffectThread.cpp
@@ -149,8 +149,8 @@
IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples);
outputMQ->write(buffer, status.fmqProduced);
statusMQ->writeBlocking(&status, 1);
- LOG(DEBUG) << mName << __func__ << ": done processing, effect consumed "
- << status.fmqConsumed << " produced " << status.fmqProduced;
+ LOG(VERBOSE) << mName << __func__ << ": done processing, effect consumed "
+ << status.fmqConsumed << " produced " << status.fmqProduced;
}
}
diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp
index 5478633..b59bd7c 100644
--- a/audio/aidl/default/Module.cpp
+++ b/audio/aidl/default/Module.cpp
@@ -675,7 +675,7 @@
nullptr, nullptr, &context));
context.fillDescriptor(&_aidl_return->desc);
std::shared_ptr<StreamIn> stream;
- RETURN_STATUS_IF_ERROR(createInputStream(in_args.sinkMetadata, std::move(context),
+ RETURN_STATUS_IF_ERROR(createInputStream(std::move(context), in_args.sinkMetadata,
mConfig->microphones, &stream));
StreamWrapper streamWrapper(stream);
if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
@@ -721,7 +721,7 @@
in_args.eventCallback, &context));
context.fillDescriptor(&_aidl_return->desc);
std::shared_ptr<StreamOut> stream;
- RETURN_STATUS_IF_ERROR(createOutputStream(in_args.sourceMetadata, std::move(context),
+ RETURN_STATUS_IF_ERROR(createOutputStream(std::move(context), in_args.sourceMetadata,
in_args.offloadInfo, &stream));
StreamWrapper streamWrapper(stream);
if (auto patchIt = mPatches.find(in_args.portConfigId); patchIt != mPatches.end()) {
@@ -1129,7 +1129,7 @@
if (!mSoundDose) {
mSoundDose = ndk::SharedRefBase::make<sounddose::SoundDose>();
}
- *_aidl_return = mSoundDose.getPtr();
+ *_aidl_return = mSoundDose.getInstance();
LOG(DEBUG) << __func__ << ": returning instance of ISoundDose: " << _aidl_return->get();
return ndk::ScopedAStatus::ok();
}
diff --git a/audio/aidl/default/ModulePrimary.cpp b/audio/aidl/default/ModulePrimary.cpp
index cbb6730..29e8126 100644
--- a/audio/aidl/default/ModulePrimary.cpp
+++ b/audio/aidl/default/ModulePrimary.cpp
@@ -37,23 +37,24 @@
if (!mTelephony) {
mTelephony = ndk::SharedRefBase::make<Telephony>();
}
- *_aidl_return = mTelephony.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
+ *_aidl_return = mTelephony.getInstance();
+ LOG(DEBUG) << __func__
+ << ": returning instance of ITelephony: " << _aidl_return->get()->asBinder().get();
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus ModulePrimary::createInputStream(const SinkMetadata& sinkMetadata,
- StreamContext&& context,
+ndk::ScopedAStatus ModulePrimary::createInputStream(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
- return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
+ return createStreamInstance<StreamInStub>(result, std::move(context), sinkMetadata,
microphones);
}
ndk::ScopedAStatus ModulePrimary::createOutputStream(
- const SourceMetadata& sourceMetadata, StreamContext&& context,
+ StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
- return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
+ return createStreamInstance<StreamOutStub>(result, std::move(context), sourceMetadata,
offloadInfo);
}
diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp
index 215de94..d2be48c 100644
--- a/audio/aidl/default/Stream.cpp
+++ b/audio/aidl/default/Stream.cpp
@@ -47,13 +47,19 @@
desc->reply = mReplyMQ->dupeDesc();
}
if (mDataMQ) {
- const size_t frameSize = getFrameSize();
- desc->frameSizeBytes = frameSize;
- desc->bufferSizeFrames = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / frameSize;
+ desc->frameSizeBytes = getFrameSize();
+ desc->bufferSizeFrames = getBufferSizeInFrames();
desc->audio.set<StreamDescriptor::AudioBuffer::Tag::fmq>(mDataMQ->dupeDesc());
}
}
+size_t StreamContext::getBufferSizeInFrames() const {
+ if (mDataMQ) {
+ return mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize() / getFrameSize();
+ }
+ return 0;
+}
+
size_t StreamContext::getFrameSize() const {
return getFrameSizeInBytes(mFormat, mChannelLayout);
}
@@ -85,17 +91,18 @@
}
std::string StreamWorkerCommonLogic::init() {
- if (mCommandMQ == nullptr) return "Command MQ is null";
- if (mReplyMQ == nullptr) return "Reply MQ is null";
- if (mDataMQ == nullptr) return "Data MQ is null";
- if (sizeof(DataBufferElement) != mDataMQ->getQuantumSize()) {
- return "Unexpected Data MQ quantum size: " + std::to_string(mDataMQ->getQuantumSize());
+ if (mContext->getCommandMQ() == nullptr) return "Command MQ is null";
+ if (mContext->getReplyMQ() == nullptr) return "Reply MQ is null";
+ StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
+ if (dataMQ == nullptr) return "Data MQ is null";
+ if (sizeof(DataBufferElement) != dataMQ->getQuantumSize()) {
+ return "Unexpected Data MQ quantum size: " + std::to_string(dataMQ->getQuantumSize());
}
- mDataBufferSize = mDataMQ->getQuantumCount() * mDataMQ->getQuantumSize();
+ mDataBufferSize = dataMQ->getQuantumCount() * dataMQ->getQuantumSize();
mDataBuffer.reset(new (std::nothrow) DataBufferElement[mDataBufferSize]);
if (mDataBuffer == nullptr) {
return "Failed to allocate data buffer for element count " +
- std::to_string(mDataMQ->getQuantumCount()) +
+ std::to_string(dataMQ->getQuantumCount()) +
", size in bytes: " + std::to_string(mDataBufferSize);
}
if (::android::status_t status = mDriver->init(); status != STATUS_OK) {
@@ -108,7 +115,7 @@
bool isConnected) const {
reply->status = STATUS_OK;
if (isConnected) {
- reply->observable.frames = mFrameCount;
+ reply->observable.frames = mContext->getFrameCount();
reply->observable.timeNs = ::android::elapsedRealtimeNano();
if (auto status = mDriver->getPosition(&reply->observable); status == ::android::OK) {
return;
@@ -135,7 +142,7 @@
// TODO: Add a delay for transitions of async operations when/if they added.
StreamDescriptor::Command command{};
- if (!mCommandMQ->readBlocking(&command, 1)) {
+ if (!mContext->getCommandMQ()->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -153,7 +160,7 @@
switch (command.getTag()) {
case Tag::halReservedExit:
if (const int32_t cookie = command.get<Tag::halReservedExit>();
- cookie == mInternalCommandCookie) {
+ cookie == mContext->getInternalCommandCookie()) {
mDriver->shutdown();
setClosed();
// This is an internal command, no need to reply.
@@ -271,7 +278,7 @@
}
reply.state = mState;
LOG(severity) << __func__ << ": writing reply " << reply.toString();
- if (!mReplyMQ->writeBlocking(&reply, 1)) {
+ if (!mContext->getReplyMQ()->writeBlocking(&reply, 1)) {
LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -280,14 +287,16 @@
}
bool StreamInWorkerLogic::read(size_t clientSize, StreamDescriptor::Reply* reply) {
- const size_t byteCount = std::min({clientSize, mDataMQ->availableToWrite(), mDataBufferSize});
+ StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
+ const size_t byteCount = std::min({clientSize, dataMQ->availableToWrite(), mDataBufferSize});
const bool isConnected = mIsConnected;
+ const size_t frameSize = mContext->getFrameSize();
size_t actualFrameCount = 0;
bool fatal = false;
int32_t latency = Module::kLatencyMs;
if (isConnected) {
- if (::android::status_t status = mDriver->transfer(
- mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
+ if (::android::status_t status = mDriver->transfer(mDataBuffer.get(), byteCount / frameSize,
+ &actualFrameCount, &latency);
status != ::android::OK) {
fatal = true;
LOG(ERROR) << __func__ << ": read failed: " << status;
@@ -295,17 +304,16 @@
} else {
usleep(3000); // Simulate blocking transfer delay.
for (size_t i = 0; i < byteCount; ++i) mDataBuffer[i] = 0;
- actualFrameCount = byteCount / mFrameSize;
+ actualFrameCount = byteCount / frameSize;
}
- const size_t actualByteCount = actualFrameCount * mFrameSize;
- if (bool success =
- actualByteCount > 0 ? mDataMQ->write(&mDataBuffer[0], actualByteCount) : true;
+ const size_t actualByteCount = actualFrameCount * frameSize;
+ if (bool success = actualByteCount > 0 ? dataMQ->write(&mDataBuffer[0], actualByteCount) : true;
success) {
LOG(VERBOSE) << __func__ << ": writing of " << actualByteCount << " bytes into data MQ"
<< " succeeded; connected? " << isConnected;
// Frames are provided and counted regardless of connection status.
reply->fmqByteCount += actualByteCount;
- mFrameCount += actualFrameCount;
+ mContext->advanceFrameCount(actualFrameCount);
populateReply(reply, isConnected);
} else {
LOG(WARNING) << __func__ << ": writing of " << actualByteCount
@@ -324,7 +332,8 @@
if (auto stateDurationMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - mTransientStateStart);
stateDurationMs >= mTransientStateDelayMs) {
- if (mAsyncCallback == nullptr) {
+ std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
+ if (asyncCallback == nullptr) {
// In blocking mode, mState can only be DRAINING.
mState = StreamDescriptor::State::IDLE;
} else {
@@ -332,13 +341,13 @@
// drain or transfer completion. In the stub, we switch unconditionally.
if (mState == StreamDescriptor::State::DRAINING) {
mState = StreamDescriptor::State::IDLE;
- ndk::ScopedAStatus status = mAsyncCallback->onDrainReady();
+ ndk::ScopedAStatus status = asyncCallback->onDrainReady();
if (!status.isOk()) {
LOG(ERROR) << __func__ << ": error from onDrainReady: " << status;
}
} else {
mState = StreamDescriptor::State::ACTIVE;
- ndk::ScopedAStatus status = mAsyncCallback->onTransferReady();
+ ndk::ScopedAStatus status = asyncCallback->onTransferReady();
if (!status.isOk()) {
LOG(ERROR) << __func__ << ": error from onTransferReady: " << status;
}
@@ -352,7 +361,7 @@
}
StreamDescriptor::Command command{};
- if (!mCommandMQ->readBlocking(&command, 1)) {
+ if (!mContext->getCommandMQ()->readBlocking(&command, 1)) {
LOG(ERROR) << __func__ << ": reading of command from MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -371,7 +380,7 @@
switch (command.getTag()) {
case Tag::halReservedExit:
if (const int32_t cookie = command.get<Tag::halReservedExit>();
- cookie == mInternalCommandCookie) {
+ cookie == mContext->getInternalCommandCookie()) {
mDriver->shutdown();
setClosed();
// This is an internal command, no need to reply.
@@ -426,10 +435,11 @@
if (!write(fmqByteCount, &reply)) {
mState = StreamDescriptor::State::ERROR;
}
+ std::shared_ptr<IStreamCallback> asyncCallback = mContext->getAsyncCallback();
if (mState == StreamDescriptor::State::STANDBY ||
mState == StreamDescriptor::State::DRAIN_PAUSED ||
mState == StreamDescriptor::State::PAUSED) {
- if (mAsyncCallback == nullptr ||
+ if (asyncCallback == nullptr ||
mState != StreamDescriptor::State::DRAIN_PAUSED) {
mState = StreamDescriptor::State::PAUSED;
} else {
@@ -438,7 +448,7 @@
} else if (mState == StreamDescriptor::State::IDLE ||
mState == StreamDescriptor::State::DRAINING ||
mState == StreamDescriptor::State::ACTIVE) {
- if (mAsyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
+ if (asyncCallback == nullptr || reply.fmqByteCount == fmqByteCount) {
mState = StreamDescriptor::State::ACTIVE;
} else {
switchToTransientState(StreamDescriptor::State::TRANSFERRING);
@@ -460,7 +470,8 @@
if (::android::status_t status = mDriver->drain(mode);
status == ::android::OK) {
populateReply(&reply, mIsConnected);
- if (mState == StreamDescriptor::State::ACTIVE && mForceSynchronousDrain) {
+ if (mState == StreamDescriptor::State::ACTIVE &&
+ mContext->getForceSynchronousDrain()) {
mState = StreamDescriptor::State::IDLE;
} else {
switchToTransientState(StreamDescriptor::State::DRAINING);
@@ -535,7 +546,7 @@
}
reply.state = mState;
LOG(severity) << __func__ << ": writing reply " << reply.toString();
- if (!mReplyMQ->writeBlocking(&reply, 1)) {
+ if (!mContext->getReplyMQ()->writeBlocking(&reply, 1)) {
LOG(ERROR) << __func__ << ": writing of reply " << reply.toString() << " to MQ failed";
mState = StreamDescriptor::State::ERROR;
return Status::ABORT;
@@ -544,38 +555,40 @@
}
bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* reply) {
- const size_t readByteCount = mDataMQ->availableToRead();
+ StreamContext::DataMQ* const dataMQ = mContext->getDataMQ();
+ const size_t readByteCount = dataMQ->availableToRead();
+ const size_t frameSize = mContext->getFrameSize();
bool fatal = false;
int32_t latency = Module::kLatencyMs;
- if (bool success = readByteCount > 0 ? mDataMQ->read(&mDataBuffer[0], readByteCount) : true) {
+ if (bool success = readByteCount > 0 ? dataMQ->read(&mDataBuffer[0], readByteCount) : true) {
const bool isConnected = mIsConnected;
LOG(VERBOSE) << __func__ << ": reading of " << readByteCount << " bytes from data MQ"
<< " succeeded; connected? " << isConnected;
// Amount of data that the HAL module is going to actually use.
size_t byteCount = std::min({clientSize, readByteCount, mDataBufferSize});
- if (byteCount >= mFrameSize && mForceTransientBurst) {
+ if (byteCount >= frameSize && mContext->getForceTransientBurst()) {
// In order to prevent the state machine from going to ACTIVE state,
// simulate partial write.
- byteCount -= mFrameSize;
+ byteCount -= frameSize;
}
size_t actualFrameCount = 0;
if (isConnected) {
if (::android::status_t status = mDriver->transfer(
- mDataBuffer.get(), byteCount / mFrameSize, &actualFrameCount, &latency);
+ mDataBuffer.get(), byteCount / frameSize, &actualFrameCount, &latency);
status != ::android::OK) {
fatal = true;
LOG(ERROR) << __func__ << ": write failed: " << status;
}
} else {
- if (mAsyncCallback == nullptr) {
+ if (mContext->getAsyncCallback() == nullptr) {
usleep(3000); // Simulate blocking transfer delay.
}
- actualFrameCount = byteCount / mFrameSize;
+ actualFrameCount = byteCount / frameSize;
}
- const size_t actualByteCount = actualFrameCount * mFrameSize;
+ const size_t actualByteCount = actualFrameCount * frameSize;
// Frames are consumed and counted regardless of the connection status.
reply->fmqByteCount += actualByteCount;
- mFrameCount += actualFrameCount;
+ mContext->advanceFrameCount(actualFrameCount);
populateReply(reply, isConnected);
} else {
LOG(WARNING) << __func__ << ": reading of " << readByteCount
@@ -597,18 +610,16 @@
ndk::ScopedAStatus StreamCommonImpl::initInstance(
const std::shared_ptr<StreamCommonInterface>& delegate) {
mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
- mCommonBinder = mCommon->asBinder();
- AIBinder_setMinSchedulerPolicy(mCommonBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
return mWorker->start() ? ndk::ScopedAStatus::ok()
: ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
ndk::ScopedAStatus StreamCommonImpl::getStreamCommonCommon(
std::shared_ptr<IStreamCommon>* _aidl_return) {
- if (mCommon == nullptr) {
+ if (!mCommon) {
LOG(FATAL) << __func__ << ": the common interface was not created";
}
- *_aidl_return = mCommon;
+ *_aidl_return = mCommon.getInstance();
LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
return ndk::ScopedAStatus::ok();
}
@@ -659,8 +670,7 @@
LOG(DEBUG) << __func__ << ": joining the worker thread...";
mWorker->stop();
LOG(DEBUG) << __func__ << ": worker thread joined";
- mContext.reset();
- mWorker->setClosed();
+ onClose(mWorker->setClosed());
return ndk::ScopedAStatus::ok();
} else {
LOG(ERROR) << __func__ << ": stream was already closed";
@@ -723,11 +733,15 @@
}
} // namespace
-StreamIn::StreamIn(const std::vector<MicrophoneInfo>& microphones)
- : mMicrophones(transformMicrophones(microphones)) {
+StreamIn::StreamIn(StreamContext&& context, const std::vector<MicrophoneInfo>& microphones)
+ : mContext(std::move(context)), mMicrophones(transformMicrophones(microphones)) {
LOG(DEBUG) << __func__;
}
+void StreamIn::defaultOnClose() {
+ mContext.reset();
+}
+
ndk::ScopedAStatus StreamIn::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) {
std::vector<MicrophoneDynamicInfo> result;
@@ -780,11 +794,15 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-StreamOut::StreamOut(const std::optional<AudioOffloadInfo>& offloadInfo)
- : mOffloadInfo(offloadInfo) {
+StreamOut::StreamOut(StreamContext&& context, const std::optional<AudioOffloadInfo>& offloadInfo)
+ : mContext(std::move(context)), mOffloadInfo(offloadInfo) {
LOG(DEBUG) << __func__;
}
+void StreamOut::defaultOnClose() {
+ mContext.reset();
+}
+
ndk::ScopedAStatus StreamOut::updateOffloadMetadata(
const AudioOffloadMetadata& in_offloadMetadata) {
LOG(DEBUG) << __func__;
diff --git a/audio/aidl/default/StreamSwitcher.cpp b/audio/aidl/default/StreamSwitcher.cpp
new file mode 100644
index 0000000..956f413
--- /dev/null
+++ b/audio/aidl/default/StreamSwitcher.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2023 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 <limits>
+
+#define LOG_TAG "AHAL_StreamSwitcher"
+
+#include <Utils.h>
+#include <android-base/logging.h>
+#include <error/expected_utils.h>
+
+#include "core-impl/StreamStub.h"
+#include "core-impl/StreamSwitcher.h"
+
+using aidl::android::hardware::audio::effect::IEffect;
+using aidl::android::media::audio::common::AudioDevice;
+
+namespace aidl::android::hardware::audio::core {
+
+StreamSwitcher::StreamSwitcher(StreamContext* context, const Metadata& metadata)
+ : mMetadata(metadata), mStream(new InnerStreamWrapper<StreamStub>(context, mMetadata)) {}
+
+ndk::ScopedAStatus StreamSwitcher::closeCurrentStream(bool validateStreamState) {
+ if (!mStream) return ndk::ScopedAStatus::ok();
+ RETURN_STATUS_IF_ERROR(mStream->prepareToClose());
+ RETURN_STATUS_IF_ERROR(mStream->close());
+ if (validateStreamState && !isValidClosingStreamState(mStream->getStatePriorToClosing())) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ mStream.reset();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::close() {
+ if (mStream != nullptr) {
+ auto status = closeCurrentStream(false /*validateStreamState*/);
+ // The actual state is irrelevant since only StreamSwitcher cares about it.
+ onClose(StreamDescriptor::State::STANDBY);
+ return status;
+ }
+ LOG(ERROR) << __func__ << ": stream was already closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+}
+
+ndk::ScopedAStatus StreamSwitcher::prepareToClose() {
+ if (mStream != nullptr) {
+ return mStream->prepareToClose();
+ }
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+}
+
+ndk::ScopedAStatus StreamSwitcher::updateHwAvSyncId(int32_t in_hwAvSyncId) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ RETURN_STATUS_IF_ERROR(mStream->updateHwAvSyncId(in_hwAvSyncId));
+ mHwAvSyncId = in_hwAvSyncId;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::getVendorParameters(const std::vector<std::string>& in_ids,
+ std::vector<VendorParameter>* _aidl_return) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (mIsStubStream) {
+ LOG(ERROR) << __func__ << ": the stream is not connected";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return mStream->getVendorParameters(in_ids, _aidl_return);
+}
+
+ndk::ScopedAStatus StreamSwitcher::setVendorParameters(
+ const std::vector<VendorParameter>& in_parameters, bool in_async) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (mIsStubStream) {
+ mMissedParameters.emplace_back(in_parameters, in_async);
+ return ndk::ScopedAStatus::ok();
+ }
+ return mStream->setVendorParameters(in_parameters, in_async);
+}
+
+ndk::ScopedAStatus StreamSwitcher::addEffect(const std::shared_ptr<IEffect>& in_effect) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ if (!mIsStubStream) {
+ RETURN_STATUS_IF_ERROR(mStream->addEffect(in_effect));
+ }
+ mEffects.push_back(in_effect);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::removeEffect(const std::shared_ptr<IEffect>& in_effect) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ for (auto it = mEffects.begin(); it != mEffects.end();) {
+ if ((*it)->asBinder() == in_effect->asBinder()) {
+ it = mEffects.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ return !mIsStubStream ? mStream->removeEffect(in_effect) : ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::getStreamCommonCommon(
+ std::shared_ptr<IStreamCommon>* _aidl_return) {
+ if (!mCommon) {
+ LOG(FATAL) << __func__ << ": the common interface was not created";
+ }
+ *_aidl_return = mCommon.getInstance();
+ LOG(DEBUG) << __func__ << ": returning " << _aidl_return->get()->asBinder().get();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::updateMetadataCommon(const Metadata& metadata) {
+ if (mStream == nullptr) {
+ LOG(ERROR) << __func__ << ": stream was closed";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ mMetadata = metadata;
+ return !mIsStubStream ? mStream->updateMetadataCommon(metadata) : ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus StreamSwitcher::initInstance(
+ const std::shared_ptr<StreamCommonInterface>& delegate) {
+ mCommon = ndk::SharedRefBase::make<StreamCommonDelegator>(delegate);
+ // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
+ return mStream->initInstance(nullptr);
+}
+
+const StreamContext& StreamSwitcher::getContext() const {
+ return *mContext;
+}
+
+bool StreamSwitcher::isClosed() const {
+ return mStream == nullptr || mStream->isClosed();
+}
+
+const StreamCommonInterface::ConnectedDevices& StreamSwitcher::getConnectedDevices() const {
+ return mStream->getConnectedDevices();
+}
+
+ndk::ScopedAStatus StreamSwitcher::setConnectedDevices(const std::vector<AudioDevice>& devices) {
+ LOG(DEBUG) << __func__ << ": " << ::android::internal::ToString(devices);
+ if (mStream->getConnectedDevices() == devices) return ndk::ScopedAStatus::ok();
+ const DeviceSwitchBehavior behavior = switchCurrentStream(devices);
+ if (behavior == DeviceSwitchBehavior::UNSUPPORTED_DEVICES) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ } else if (behavior == DeviceSwitchBehavior::SWITCH_TO_STUB_STREAM && !devices.empty()) {
+ // This is an error in the extending class.
+ LOG(FATAL) << __func__
+ << ": switching to stub stream with connected devices is not allowed";
+ }
+ if (behavior == USE_CURRENT_STREAM) {
+ mIsStubStream = false;
+ } else {
+ LOG(DEBUG) << __func__ << ": connected devices changed, switching stream";
+ // Two streams can't be opened for the same context, thus we always need to close
+ // the current one before creating a new one.
+ RETURN_STATUS_IF_ERROR(closeCurrentStream(true /*validateStreamState*/));
+ if (behavior == CREATE_NEW_STREAM) {
+ mStream = createNewStream(devices, mContext, mMetadata);
+ mIsStubStream = false;
+ } else { // SWITCH_TO_STUB_STREAM
+ mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
+ mIsStubStream = true;
+ }
+ // The delegate is null because StreamSwitcher handles IStreamCommon methods by itself.
+ if (ndk::ScopedAStatus status = mStream->initInstance(nullptr); !status.isOk()) {
+ // Need to close the current failed stream, and report an error.
+ // Since we can't operate without a stream implementation, put a stub in.
+ RETURN_STATUS_IF_ERROR(closeCurrentStream(false /*validateStreamState*/));
+ mStream.reset(new InnerStreamWrapper<StreamStub>(mContext, mMetadata));
+ (void)mStream->initInstance(nullptr);
+ (void)mStream->setConnectedDevices(devices);
+ return status;
+ }
+ }
+ RETURN_STATUS_IF_ERROR(mStream->setConnectedDevices(devices));
+ if (behavior == CREATE_NEW_STREAM) {
+ // These updates are less critical, only log warning on failure.
+ if (mHwAvSyncId.has_value()) {
+ if (auto status = mStream->updateHwAvSyncId(*mHwAvSyncId); !status.isOk()) {
+ LOG(WARNING) << __func__ << ": could not update HW AV Sync for a new stream: "
+ << status.getDescription();
+ }
+ }
+ for (const auto& vndParam : mMissedParameters) {
+ if (auto status = mStream->setVendorParameters(vndParam.first, vndParam.second);
+ !status.isOk()) {
+ LOG(WARNING) << __func__ << ": error while setting parameters for a new stream: "
+ << status.getDescription();
+ }
+ }
+ mMissedParameters.clear();
+ for (const auto& effect : mEffects) {
+ if (auto status = mStream->addEffect(effect); !status.isOk()) {
+ LOG(WARNING) << __func__ << ": error while adding effect for a new stream: "
+ << status.getDescription();
+ }
+ }
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp
index f0393e3..126c033 100644
--- a/audio/aidl/default/alsa/Mixer.cpp
+++ b/audio/aidl/default/alsa/Mixer.cpp
@@ -14,44 +14,17 @@
* limitations under the License.
*/
-#define LOG_TAG "AHAL_AlsaMixer"
-#include <android-base/logging.h>
-
+#include <algorithm>
#include <cmath>
+#define LOG_TAG "AHAL_AlsaMixer"
+#include <android-base/logging.h>
#include <android/binder_status.h>
#include "Mixer.h"
namespace aidl::android::hardware::audio::core::alsa {
-//-----------------------------------------------------------------------------
-
-MixerControl::MixerControl(struct mixer_ctl* ctl)
- : mCtl(ctl),
- mNumValues(mixer_ctl_get_num_values(ctl)),
- mMinValue(mixer_ctl_get_range_min(ctl)),
- mMaxValue(mixer_ctl_get_range_max(ctl)) {}
-
-unsigned int MixerControl::getNumValues() const {
- return mNumValues;
-}
-
-int MixerControl::getMaxValue() const {
- return mMaxValue;
-}
-
-int MixerControl::getMinValue() const {
- return mMinValue;
-}
-
-int MixerControl::setArray(const void* array, size_t count) {
- const std::lock_guard guard(mLock);
- return mixer_ctl_set_array(mCtl, array, count);
-}
-
-//-----------------------------------------------------------------------------
-
// static
const std::map<Mixer::Control, std::vector<Mixer::ControlNamesAndExpectedCtlType>>
Mixer::kPossibleControls = {
@@ -60,18 +33,20 @@
{Mixer::HW_VOLUME,
{{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
{"Headset Playback Volume", MIXER_CTL_TYPE_INT},
- {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
+ {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}},
+ {Mixer::MIC_SWITCH, {{"Capture Switch", MIXER_CTL_TYPE_BOOL}}},
+ {Mixer::MIC_GAIN, {{"Capture Volume", MIXER_CTL_TYPE_INT}}}};
// static
-std::map<Mixer::Control, std::shared_ptr<MixerControl>> Mixer::initializeMixerControls(
- struct mixer* mixer) {
- std::map<Mixer::Control, std::shared_ptr<MixerControl>> mixerControls;
+Mixer::Controls Mixer::initializeMixerControls(struct mixer* mixer) {
+ if (mixer == nullptr) return {};
+ Controls mixerControls;
std::string mixerCtlNames;
for (const auto& [control, possibleCtls] : kPossibleControls) {
for (const auto& [ctlName, expectedCtlType] : possibleCtls) {
struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str());
if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) {
- mixerControls.emplace(control, std::make_unique<MixerControl>(ctl));
+ mixerControls.emplace(control, ctl);
if (!mixerCtlNames.empty()) {
mixerCtlNames += ",";
}
@@ -84,71 +59,141 @@
return mixerControls;
}
-Mixer::Mixer(struct mixer* mixer)
- : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
+std::ostream& operator<<(std::ostream& s, Mixer::Control c) {
+ switch (c) {
+ case Mixer::Control::MASTER_SWITCH:
+ s << "master mute";
+ break;
+ case Mixer::Control::MASTER_VOLUME:
+ s << "master volume";
+ break;
+ case Mixer::Control::HW_VOLUME:
+ s << "volume";
+ break;
+ case Mixer::Control::MIC_SWITCH:
+ s << "mic mute";
+ break;
+ case Mixer::Control::MIC_GAIN:
+ s << "mic gain";
+ break;
+ }
+ return s;
+}
+
+Mixer::Mixer(int card) : mMixer(mixer_open(card)), mMixerControls(initializeMixerControls(mMixer)) {
+ if (!isValid()) {
+ PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
+ }
+}
Mixer::~Mixer() {
- mixer_close(mMixer);
+ if (isValid()) {
+ std::lock_guard l(mMixerAccess);
+ mixer_close(mMixer);
+ }
}
-namespace {
-
-int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
- return minValue + std::ceil((maxValue - minValue) * fValue);
-}
-
-} // namespace
-
ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
- auto it = mMixerControls.find(Mixer::MASTER_SWITCH);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
- const int numValues = it->second->getNumValues();
- std::vector<int> values(numValues, muted ? 0 : 1);
- if (int err = it->second->setArray(values.data(), numValues); err != 0) {
- LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- return ndk::ScopedAStatus::ok();
+ return setMixerControlMute(MASTER_SWITCH, muted);
}
ndk::ScopedAStatus Mixer::setMasterVolume(float volume) {
- auto it = mMixerControls.find(Mixer::MASTER_VOLUME);
- if (it == mMixerControls.end()) {
- return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
- }
- const int numValues = it->second->getNumValues();
- std::vector<int> values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(),
- it->second->getMinValue()));
- if (int err = it->second->setArray(values.data(), numValues); err != 0) {
- LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err;
- return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
- }
- return ndk::ScopedAStatus::ok();
+ return setMixerControlVolume(MASTER_VOLUME, volume);
+}
+
+ndk::ScopedAStatus Mixer::setMicGain(float gain) {
+ return setMixerControlVolume(MIC_GAIN, gain);
+}
+
+ndk::ScopedAStatus Mixer::setMicMute(bool muted) {
+ return setMixerControlMute(MIC_SWITCH, muted);
}
ndk::ScopedAStatus Mixer::setVolumes(const std::vector<float>& volumes) {
+ if (!isValid()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
auto it = mMixerControls.find(Mixer::HW_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
- const int numValues = it->second->getNumValues();
- if (numValues < 0) {
- LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
- }
- const int maxValue = it->second->getMaxValue();
- const int minValue = it->second->getMinValue();
- std::vector<int> values;
- size_t i = 0;
- for (; i < static_cast<size_t>(numValues) && i < values.size(); ++i) {
- values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue));
- }
- if (int err = it->second->setArray(values.data(), values.size()); err != 0) {
+ std::vector<int> percents;
+ std::transform(
+ volumes.begin(), volumes.end(), std::back_inserter(percents),
+ [](float volume) -> int { return std::floor(std::clamp(volume, 0.0f, 1.0f) * 100); });
+ std::lock_guard l(mMixerAccess);
+ if (int err = setMixerControlPercent(it->second, percents); err != 0) {
LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) {
+ if (!isValid()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto it = mMixerControls.find(ctl);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ std::lock_guard l(mMixerAccess);
+ if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) {
+ if (!isValid()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ auto it = mMixerControls.find(ctl);
+ if (it == mMixerControls.end()) {
+ return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ }
+ volume = std::clamp(volume, 0.0f, 1.0f);
+ std::lock_guard l(mMixerAccess);
+ if (int err = setMixerControlPercent(it->second, std::floor(volume * 100)); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err;
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
+int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) {
+ int ret = 0;
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int error = mixer_ctl_set_percent(ctl, id, percent); error != 0) {
+ ret = error;
+ }
+ }
+ return ret;
+}
+
+int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents) {
+ int ret = 0;
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int error = mixer_ctl_set_percent(ctl, id, id < percents.size() ? percents[id] : 0);
+ error != 0) {
+ ret = error;
+ }
+ }
+ return ret;
+}
+
+int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) {
+ int ret = 0;
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int id = 0; id < n; id++) {
+ if (int error = mixer_ctl_set_value(ctl, id, value); error != 0) {
+ ret = error;
+ }
+ }
+ return ret;
+}
+
} // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h
index de9e6f4..78728c2 100644
--- a/audio/aidl/default/alsa/Mixer.h
+++ b/audio/aidl/default/alsa/Mixer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <iostream>
#include <map>
#include <memory>
#include <mutex>
@@ -31,34 +32,17 @@
namespace aidl::android::hardware::audio::core::alsa {
-class MixerControl {
- public:
- explicit MixerControl(struct mixer_ctl* ctl);
-
- unsigned int getNumValues() const;
- int getMaxValue() const;
- int getMinValue() const;
- int setArray(const void* array, size_t count);
-
- private:
- std::mutex mLock;
- // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed.
- struct mixer_ctl* mCtl GUARDED_BY(mLock);
- const unsigned int mNumValues;
- const int mMinValue;
- const int mMaxValue;
-};
-
class Mixer {
public:
- explicit Mixer(struct mixer* mixer);
-
+ explicit Mixer(int card);
~Mixer();
bool isValid() const { return mMixer != nullptr; }
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
+ ndk::ScopedAStatus setMicGain(float gain);
+ ndk::ScopedAStatus setMicMute(bool muted);
ndk::ScopedAStatus setVolumes(const std::vector<float>& volumes);
private:
@@ -66,17 +50,32 @@
MASTER_SWITCH,
MASTER_VOLUME,
HW_VOLUME,
+ MIC_SWITCH,
+ MIC_GAIN,
};
using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
- static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
- static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
- struct mixer* mixer);
+ using Controls = std::map<Control, struct mixer_ctl*>;
+ friend std::ostream& operator<<(std::ostream&, Control);
+ static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
+ static Controls initializeMixerControls(struct mixer* mixer);
+
+ ndk::ScopedAStatus setMixerControlMute(Control ctl, bool muted);
+ ndk::ScopedAStatus setMixerControlVolume(Control ctl, float volume);
+
+ int setMixerControlPercent(struct mixer_ctl* ctl, int percent) REQUIRES(mMixerAccess);
+ int setMixerControlPercent(struct mixer_ctl* ctl, const std::vector<int>& percents)
+ REQUIRES(mMixerAccess);
+ int setMixerControlValue(struct mixer_ctl* ctl, int value) REQUIRES(mMixerAccess);
+
+ // Since ALSA functions do not use internal locking, enforce thread safety at our level.
+ std::mutex mMixerAccess;
// The mixer object is owned by ALSA and will be released when the mixer is closed.
- struct mixer* mMixer;
+ struct mixer* const mMixer;
// `mMixerControls` will only be initialized in constructor. After that, it wil only be
- // read but not be modified.
- const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
+ // read but not be modified. Each mixer_ctl object is owned by ALSA, it's life span is
+ // the same as of the mixer itself.
+ const Controls mMixerControls;
};
} // namespace aidl::android::hardware::audio::core::alsa
diff --git a/audio/aidl/default/alsa/StreamAlsa.cpp b/audio/aidl/default/alsa/StreamAlsa.cpp
index 17c7feb..00a7a84 100644
--- a/audio/aidl/default/alsa/StreamAlsa.cpp
+++ b/audio/aidl/default/alsa/StreamAlsa.cpp
@@ -27,16 +27,32 @@
namespace aidl::android::hardware::audio::core {
-StreamAlsa::StreamAlsa(const Metadata& metadata, StreamContext&& context)
- : StreamCommonImpl(metadata, std::move(context)),
+StreamAlsa::StreamAlsa(StreamContext* context, const Metadata& metadata, int readWriteRetries)
+ : StreamCommonImpl(context, metadata),
mFrameSizeBytes(getContext().getFrameSize()),
mIsInput(isInput(metadata)),
- mConfig(alsa::getPcmConfig(getContext(), mIsInput)) {}
+ mConfig(alsa::getPcmConfig(getContext(), mIsInput)),
+ mReadWriteRetries(readWriteRetries) {}
::android::status_t StreamAlsa::init() {
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
}
+::android::status_t StreamAlsa::drain(StreamDescriptor::DrainMode) {
+ usleep(1000);
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::flush() {
+ usleep(1000);
+ return ::android::OK;
+}
+
+::android::status_t StreamAlsa::pause() {
+ usleep(1000);
+ return ::android::OK;
+}
+
::android::status_t StreamAlsa::standby() {
mAlsaDeviceProxies.clear();
return ::android::OK;
@@ -45,27 +61,21 @@
::android::status_t StreamAlsa::start() {
decltype(mAlsaDeviceProxies) alsaDeviceProxies;
for (const auto& device : getDeviceProfiles()) {
- auto profile = alsa::readAlsaDeviceInfo(device);
- if (!profile.has_value()) {
- LOG(ERROR) << __func__ << ": unable to read device info, device address=" << device;
- return ::android::UNKNOWN_ERROR;
+ alsa::DeviceProxy proxy;
+ if (device.isExternal) {
+ // Always ask alsa configure as required since the configuration should be supported
+ // by the connected device. That is guaranteed by `setAudioPortConfig` and
+ // `setAudioPatch`.
+ proxy = alsa::openProxyForExternalDevice(
+ device, const_cast<struct pcm_config*>(&mConfig.value()),
+ true /*require_exact_match*/);
+ } else {
+ proxy = alsa::openProxyForAttachedDevice(
+ device, const_cast<struct pcm_config*>(&mConfig.value()),
+ getContext().getBufferSizeInFrames());
}
-
- auto proxy = alsa::makeDeviceProxy();
- // Always ask for alsa configure as required since the configuration should be supported
- // by the connected device. That is guaranteed by `setAudioPortConfig` and `setAudioPatch`.
- if (int err = proxy_prepare(proxy.get(), &profile.value(),
- const_cast<struct pcm_config*>(&mConfig.value()),
- true /*require_exact_match*/);
- err != 0) {
- LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device
- << " error=" << err;
- return ::android::UNKNOWN_ERROR;
- }
- if (int err = proxy_open(proxy.get()); err != 0) {
- LOG(ERROR) << __func__ << ": failed to open device, address=" << device
- << " error=" << err;
- return ::android::UNKNOWN_ERROR;
+ if (!proxy) {
+ return ::android::NO_INIT;
}
alsaDeviceProxies.push_back(std::move(proxy));
}
@@ -83,11 +93,12 @@
return ::android::NO_INIT;
}
// For input case, only support single device.
- proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
+ proxy_read_with_retries(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer,
+ mReadWriteRetries);
maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
} else {
for (auto& proxy : mAlsaDeviceProxies) {
- proxy_write(proxy.get(), buffer, bytesToTransfer);
+ proxy_write_with_retries(proxy.get(), buffer, bytesToTransfer, mReadWriteRetries);
maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
}
}
diff --git a/audio/aidl/default/alsa/Utils.cpp b/audio/aidl/default/alsa/Utils.cpp
index 162f852..20f7797 100644
--- a/audio/aidl/default/alsa/Utils.cpp
+++ b/audio/aidl/default/alsa/Utils.cpp
@@ -217,7 +217,8 @@
}
return DeviceProfile{.card = alsaAddress[0],
.device = alsaAddress[1],
- .direction = isInput ? PCM_IN : PCM_OUT};
+ .direction = isInput ? PCM_IN : PCM_OUT,
+ .isExternal = !audioDevice.type.connection.empty()};
}
std::optional<DeviceProfile> getDeviceProfile(
@@ -269,6 +270,57 @@
});
}
+DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, size_t bufferFrameCount) {
+ if (deviceProfile.isExternal) {
+ LOG(FATAL) << __func__ << ": called for an external device, address=" << deviceProfile;
+ }
+ alsa_device_profile profile;
+ profile_init(&profile, deviceProfile.direction);
+ profile.card = deviceProfile.card;
+ profile.device = deviceProfile.device;
+ if (!profile_fill_builtin_device_info(&profile, pcmConfig, bufferFrameCount)) {
+ LOG(FATAL) << __func__ << ": failed to init for built-in device, address=" << deviceProfile;
+ }
+ auto proxy = makeDeviceProxy();
+ if (int err = proxy_prepare_from_default_config(proxy.get(), &profile); err != 0) {
+ LOG(FATAL) << __func__ << ": fail to prepare for device address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ if (int err = proxy_open(proxy.get()); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ return proxy;
+}
+
+DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, bool requireExactMatch) {
+ if (!deviceProfile.isExternal) {
+ LOG(FATAL) << __func__ << ": called for an attached device, address=" << deviceProfile;
+ }
+ auto profile = readAlsaDeviceInfo(deviceProfile);
+ if (!profile.has_value()) {
+ LOG(ERROR) << __func__ << ": unable to read device info, device address=" << deviceProfile;
+ return nullptr;
+ }
+ auto proxy = makeDeviceProxy();
+ if (int err = proxy_prepare(proxy.get(), &profile.value(), pcmConfig, requireExactMatch);
+ err != 0) {
+ LOG(ERROR) << __func__ << ": fail to prepare for device address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ if (int err = proxy_open(proxy.get()); err != 0) {
+ LOG(ERROR) << __func__ << ": failed to open device, address=" << deviceProfile
+ << " error=" << err;
+ return nullptr;
+ }
+ return proxy;
+}
+
std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile) {
alsa_device_profile profile;
profile_init(&profile, deviceProfile.direction);
diff --git a/audio/aidl/default/alsa/Utils.h b/audio/aidl/default/alsa/Utils.h
index c1b9b38..615e657 100644
--- a/audio/aidl/default/alsa/Utils.h
+++ b/audio/aidl/default/alsa/Utils.h
@@ -40,6 +40,7 @@
int card;
int device;
int direction; /* PCM_OUT or PCM_IN */
+ bool isExternal;
};
std::ostream& operator<<(std::ostream& os, const DeviceProfile& device);
using DeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
@@ -60,6 +61,10 @@
std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput);
std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile);
DeviceProxy makeDeviceProxy();
+DeviceProxy openProxyForAttachedDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, size_t bufferFrameCount);
+DeviceProxy openProxyForExternalDevice(const DeviceProfile& deviceProfile,
+ struct pcm_config* pcmConfig, bool requireExactMatch);
std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
::aidl::android::media::audio::common::AudioFormatDescription
diff --git a/audio/aidl/default/include/core-impl/ChildInterface.h b/audio/aidl/default/include/core-impl/ChildInterface.h
index 1b31691..2421b59 100644
--- a/audio/aidl/default/include/core-impl/ChildInterface.h
+++ b/audio/aidl/default/include/core-impl/ChildInterface.h
@@ -35,14 +35,20 @@
}
ChildInterface& operator=(std::shared_ptr<C>&& c) {
this->first = std::move(c);
- this->second = this->first->asBinder();
- AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
return *this;
}
explicit operator bool() const { return !!this->first; }
C& operator*() const { return *(this->first); }
C* operator->() const { return this->first; }
- std::shared_ptr<C> getPtr() const { return this->first; }
+ // Use 'getInstance' when returning the interface instance.
+ std::shared_ptr<C> getInstance() {
+ if (this->second.get() == nullptr) {
+ this->second = this->first->asBinder();
+ AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL,
+ ANDROID_PRIORITY_AUDIO);
+ }
+ return this->first;
+ }
};
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h
index 294cc0e..539221d 100644
--- a/audio/aidl/default/include/core-impl/Module.h
+++ b/audio/aidl/default/include/core-impl/Module.h
@@ -159,13 +159,13 @@
// The following virtual functions are intended for vendor extension via inheritance.
virtual ndk::ScopedAStatus createInputStream(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) = 0;
virtual ndk::ScopedAStatus createOutputStream(
- const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) = 0;
diff --git a/audio/aidl/default/include/core-impl/ModulePrimary.h b/audio/aidl/default/include/core-impl/ModulePrimary.h
index bc808ab..6264237 100644
--- a/audio/aidl/default/include/core-impl/ModulePrimary.h
+++ b/audio/aidl/default/include/core-impl/ModulePrimary.h
@@ -28,13 +28,13 @@
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus createInputStream(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
- const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
diff --git a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
index ccfcdd9..e87be3d 100644
--- a/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/ModuleRemoteSubmix.h
@@ -33,13 +33,13 @@
// Module interfaces
ndk::ScopedAStatus createInputStream(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
- const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
diff --git a/audio/aidl/default/include/core-impl/ModuleStub.h b/audio/aidl/default/include/core-impl/ModuleStub.h
index 59c343f..4f77161 100644
--- a/audio/aidl/default/include/core-impl/ModuleStub.h
+++ b/audio/aidl/default/include/core-impl/ModuleStub.h
@@ -30,13 +30,13 @@
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
ndk::ScopedAStatus createInputStream(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
- const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
diff --git a/audio/aidl/default/include/core-impl/ModuleUsb.h b/audio/aidl/default/include/core-impl/ModuleUsb.h
index e6b3e66..a296b8c 100644
--- a/audio/aidl/default/include/core-impl/ModuleUsb.h
+++ b/audio/aidl/default/include/core-impl/ModuleUsb.h
@@ -33,13 +33,13 @@
// Module interfaces
ndk::ScopedAStatus createInputStream(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) override;
ndk::ScopedAStatus createOutputStream(
- const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo,
std::shared_ptr<StreamOut>* result) override;
diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h
index e64c578..fa2b760 100644
--- a/audio/aidl/default/include/core-impl/Stream.h
+++ b/audio/aidl/default/include/core-impl/Stream.h
@@ -43,6 +43,7 @@
#include <system/thread_defs.h>
#include <utils/Errors.h>
+#include "core-impl/ChildInterface.h"
#include "core-impl/utils.h"
namespace aidl::android::hardware::audio::core {
@@ -65,7 +66,8 @@
DataMQ;
// Ensure that this value is not used by any of StreamDescriptor.State enums
- static constexpr int32_t STATE_CLOSED = -1;
+ static constexpr StreamDescriptor::State STATE_CLOSED =
+ static_cast<StreamDescriptor::State>(-1);
struct DebugParameters {
// An extra delay for transient states, in ms.
@@ -112,7 +114,8 @@
mDataMQ(std::move(other.mDataMQ)),
mAsyncCallback(std::move(other.mAsyncCallback)),
mOutEventCallback(std::move(other.mOutEventCallback)),
- mDebugParameters(std::move(other.mDebugParameters)) {}
+ mDebugParameters(std::move(other.mDebugParameters)),
+ mFrameCount(other.mFrameCount) {}
StreamContext& operator=(StreamContext&& other) {
mCommandMQ = std::move(other.mCommandMQ);
mInternalCommandCookie = other.mInternalCommandCookie;
@@ -127,11 +130,13 @@
mAsyncCallback = std::move(other.mAsyncCallback);
mOutEventCallback = std::move(other.mOutEventCallback);
mDebugParameters = std::move(other.mDebugParameters);
+ mFrameCount = other.mFrameCount;
return *this;
}
void fillDescriptor(StreamDescriptor* desc);
std::shared_ptr<IStreamCallback> getAsyncCallback() const { return mAsyncCallback; }
+ size_t getBufferSizeInFrames() const;
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayout() const {
return mChannelLayout;
}
@@ -154,7 +159,12 @@
int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; }
int getSampleRate() const { return mSampleRate; }
bool isValid() const;
+ // 'reset' is called on a Binder thread when closing the stream. Does not use
+ // locking because it only cleans MQ pointers which were also set on the Binder thread.
void reset();
+ // 'advanceFrameCount' and 'getFrameCount' are only called on the worker thread.
+ long advanceFrameCount(size_t increase) { return mFrameCount += increase; }
+ long getFrameCount() const { return mFrameCount; }
private:
std::unique_ptr<CommandMQ> mCommandMQ;
@@ -170,6 +180,7 @@
std::shared_ptr<IStreamCallback> mAsyncCallback;
std::shared_ptr<IStreamOutEventCallback> mOutEventCallback; // Only used by output streams
DebugParameters mDebugParameters;
+ long mFrameCount = 0;
};
// This interface provides operations of the stream which are executed on the worker thread.
@@ -195,26 +206,23 @@
class StreamWorkerCommonLogic : public ::android::hardware::audio::common::StreamLogic {
public:
- bool isClosed() const {
- return static_cast<int32_t>(mState.load()) == StreamContext::STATE_CLOSED;
+ bool isClosed() const { return mState == StreamContext::STATE_CLOSED; }
+ StreamDescriptor::State setClosed() {
+ auto prevState = mState.exchange(StreamContext::STATE_CLOSED);
+ if (prevState != StreamContext::STATE_CLOSED) {
+ mStatePriorToClosing = prevState;
+ }
+ return mStatePriorToClosing;
}
- void setClosed() { mState = static_cast<StreamDescriptor::State>(StreamContext::STATE_CLOSED); }
void setIsConnected(bool connected) { mIsConnected = connected; }
protected:
using DataBufferElement = int8_t;
- StreamWorkerCommonLogic(const StreamContext& context, DriverInterface* driver)
- : mDriver(driver),
- mInternalCommandCookie(context.getInternalCommandCookie()),
- mFrameSize(context.getFrameSize()),
- mCommandMQ(context.getCommandMQ()),
- mReplyMQ(context.getReplyMQ()),
- mDataMQ(context.getDataMQ()),
- mAsyncCallback(context.getAsyncCallback()),
- mTransientStateDelayMs(context.getTransientStateDelayMs()),
- mForceTransientBurst(context.getForceTransientBurst()),
- mForceSynchronousDrain(context.getForceSynchronousDrain()) {}
+ StreamWorkerCommonLogic(StreamContext* context, DriverInterface* driver)
+ : mContext(context),
+ mDriver(driver),
+ mTransientStateDelayMs(context->getTransientStateDelayMs()) {}
std::string init() override;
void populateReply(StreamDescriptor::Reply* reply, bool isConnected) const;
void populateReplyWrongState(StreamDescriptor::Reply* reply,
@@ -224,38 +232,35 @@
mTransientStateStart = std::chrono::steady_clock::now();
}
+ // The context is only used for reading, except for updating the frame count,
+ // which happens on the worker thread only.
+ StreamContext* const mContext;
DriverInterface* const mDriver;
+ // This is the state the stream was in before being closed. It is retrieved by the main
+ // thread after joining the worker thread.
+ StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY;
// Atomic fields are used both by the main and worker threads.
std::atomic<bool> mIsConnected = false;
static_assert(std::atomic<StreamDescriptor::State>::is_always_lock_free);
std::atomic<StreamDescriptor::State> mState = StreamDescriptor::State::STANDBY;
- // All fields are used on the worker thread only.
- const int mInternalCommandCookie;
- const size_t mFrameSize;
- StreamContext::CommandMQ* const mCommandMQ;
- StreamContext::ReplyMQ* const mReplyMQ;
- StreamContext::DataMQ* const mDataMQ;
- std::shared_ptr<IStreamCallback> mAsyncCallback;
+ // All fields below are used on the worker thread only.
const std::chrono::duration<int, std::milli> mTransientStateDelayMs;
std::chrono::time_point<std::chrono::steady_clock> mTransientStateStart;
- const bool mForceTransientBurst;
- const bool mForceSynchronousDrain;
// We use an array and the "size" field instead of a vector to be able to detect
// memory allocation issues.
std::unique_ptr<DataBufferElement[]> mDataBuffer;
size_t mDataBufferSize;
- long mFrameCount = 0;
};
// This interface is used to decouple stream implementations from a concrete StreamWorker
// implementation.
struct StreamWorkerInterface {
- using CreateInstance = std::function<StreamWorkerInterface*(const StreamContext& context,
- DriverInterface* driver)>;
+ using CreateInstance =
+ std::function<StreamWorkerInterface*(StreamContext* context, DriverInterface* driver)>;
virtual ~StreamWorkerInterface() = default;
virtual bool isClosed() const = 0;
virtual void setIsConnected(bool isConnected) = 0;
- virtual void setClosed() = 0;
+ virtual StreamDescriptor::State setClosed() = 0;
virtual bool start() = 0;
virtual void stop() = 0;
};
@@ -266,11 +271,11 @@
using WorkerImpl = ::android::hardware::audio::common::StreamWorker<WorkerLogic>;
public:
- StreamWorkerImpl(const StreamContext& context, DriverInterface* driver)
+ StreamWorkerImpl(StreamContext* context, DriverInterface* driver)
: WorkerImpl(context, driver) {}
bool isClosed() const override { return WorkerImpl::isClosed(); }
void setIsConnected(bool isConnected) override { WorkerImpl::setIsConnected(isConnected); }
- void setClosed() override { WorkerImpl::setClosed(); }
+ StreamDescriptor::State setClosed() override { return WorkerImpl::setClosed(); }
bool start() override {
return WorkerImpl::start(WorkerImpl::kThreadName, ANDROID_PRIORITY_AUDIO);
}
@@ -280,7 +285,7 @@
class StreamInWorkerLogic : public StreamWorkerCommonLogic {
public:
static const std::string kThreadName;
- StreamInWorkerLogic(const StreamContext& context, DriverInterface* driver)
+ StreamInWorkerLogic(StreamContext* context, DriverInterface* driver)
: StreamWorkerCommonLogic(context, driver) {}
protected:
@@ -294,8 +299,9 @@
class StreamOutWorkerLogic : public StreamWorkerCommonLogic {
public:
static const std::string kThreadName;
- StreamOutWorkerLogic(const StreamContext& context, DriverInterface* driver)
- : StreamWorkerCommonLogic(context, driver), mEventCallback(context.getOutEventCallback()) {}
+ StreamOutWorkerLogic(StreamContext* context, DriverInterface* driver)
+ : StreamWorkerCommonLogic(context, driver),
+ mEventCallback(context->getOutEventCallback()) {}
protected:
Status cycle() override;
@@ -409,16 +415,17 @@
};
// The implementation of DriverInterface must be provided by each concrete stream implementation.
+// Note that StreamCommonImpl does not own the context. This is to support swapping on the fly
+// implementations of the stream while keeping the same IStreamIn/Out instance. It's that instance
+// who must be owner of the context.
class StreamCommonImpl : virtual public StreamCommonInterface, virtual public DriverInterface {
public:
- StreamCommonImpl(const Metadata& metadata, StreamContext&& context,
+ StreamCommonImpl(StreamContext* context, const Metadata& metadata,
const StreamWorkerInterface::CreateInstance& createWorker)
- : mMetadata(metadata),
- mContext(std::move(context)),
- mWorker(createWorker(mContext, this)) {}
- StreamCommonImpl(const Metadata& metadata, StreamContext&& context)
+ : mContext(*context), mMetadata(metadata), mWorker(createWorker(context, this)) {}
+ StreamCommonImpl(StreamContext* context, const Metadata& metadata)
: StreamCommonImpl(
- metadata, std::move(context),
+ context, metadata,
isInput(metadata) ? getDefaultInWorkerCreator() : getDefaultOutWorkerCreator()) {}
~StreamCommonImpl();
@@ -450,23 +457,23 @@
protected:
static StreamWorkerInterface::CreateInstance getDefaultInWorkerCreator() {
- return [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
+ return [](StreamContext* ctx, DriverInterface* driver) -> StreamWorkerInterface* {
return new StreamInWorker(ctx, driver);
};
}
static StreamWorkerInterface::CreateInstance getDefaultOutWorkerCreator() {
- return [](const StreamContext& ctx, DriverInterface* driver) -> StreamWorkerInterface* {
+ return [](StreamContext* ctx, DriverInterface* driver) -> StreamWorkerInterface* {
return new StreamOutWorker(ctx, driver);
};
}
+ virtual void onClose(StreamDescriptor::State statePriorToClosing) = 0;
void stopWorker();
+ const StreamContext& mContext;
Metadata mMetadata;
- StreamContext mContext;
std::unique_ptr<StreamWorkerInterface> mWorker;
- std::shared_ptr<StreamCommonDelegator> mCommon;
- ndk::SpAIBinder mCommonBinder;
+ ChildInterface<StreamCommonDelegator> mCommon;
ConnectedDevices mConnectedDevices;
};
@@ -474,6 +481,8 @@
// concrete input/output stream implementations.
class StreamIn : virtual public StreamCommonInterface, public BnStreamIn {
protected:
+ void defaultOnClose();
+
ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
return getStreamCommonCommon(_aidl_return);
}
@@ -493,14 +502,17 @@
friend class ndk::SharedRefBase;
- explicit StreamIn(
- const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+ StreamIn(StreamContext&& context,
+ const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+ StreamContext mContext;
const std::map<::aidl::android::media::audio::common::AudioDevice, std::string> mMicrophones;
};
class StreamOut : virtual public StreamCommonInterface, public BnStreamOut {
protected:
+ void defaultOnClose();
+
ndk::ScopedAStatus getStreamCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override {
return getStreamCommonCommon(_aidl_return);
}
@@ -534,10 +546,12 @@
friend class ndk::SharedRefBase;
- explicit StreamOut(const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
- offloadInfo);
+ StreamOut(StreamContext&& context,
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
+ offloadInfo);
- std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
+ StreamContext mContext;
+ const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo> mOffloadInfo;
std::optional<::aidl::android::hardware::audio::common::AudioOffloadMetadata> mOffloadMetadata;
};
diff --git a/audio/aidl/default/include/core-impl/StreamAlsa.h b/audio/aidl/default/include/core-impl/StreamAlsa.h
index 5744d66..f98a922 100644
--- a/audio/aidl/default/include/core-impl/StreamAlsa.h
+++ b/audio/aidl/default/include/core-impl/StreamAlsa.h
@@ -31,9 +31,12 @@
// provide necessary overrides for all interface methods omitted here.
class StreamAlsa : public StreamCommonImpl {
public:
- StreamAlsa(const Metadata& metadata, StreamContext&& context);
+ StreamAlsa(StreamContext* context, const Metadata& metadata, int readWriteRetries);
// Methods of 'DriverInterface'.
::android::status_t init() override;
+ ::android::status_t drain(StreamDescriptor::DrainMode) override;
+ ::android::status_t flush() override;
+ ::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
@@ -48,6 +51,7 @@
const size_t mFrameSizeBytes;
const bool mIsInput;
const std::optional<struct pcm_config> mConfig;
+ const int mReadWriteRetries;
// All fields below are only used on the worker thread.
std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
};
diff --git a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
index 1bca910..b39583e 100644
--- a/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
+++ b/audio/aidl/default/include/core-impl/StreamRemoteSubmix.h
@@ -29,7 +29,7 @@
class StreamRemoteSubmix : public StreamCommonImpl {
public:
- StreamRemoteSubmix(const Metadata& metadata, StreamContext&& context);
+ StreamRemoteSubmix(StreamContext* context, const Metadata& metadata);
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
@@ -72,28 +72,32 @@
static constexpr int kReadAttemptSleepUs = 5000;
};
-class StreamInRemoteSubmix final : public StreamRemoteSubmix, public StreamIn {
+class StreamInRemoteSubmix final : public StreamIn, public StreamRemoteSubmix {
public:
friend class ndk::SharedRefBase;
StreamInRemoteSubmix(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
-class StreamOutRemoteSubmix final : public StreamRemoteSubmix, public StreamOut {
+class StreamOutRemoteSubmix final : public StreamOut, public StreamRemoteSubmix {
public:
friend class ndk::SharedRefBase;
StreamOutRemoteSubmix(
- const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
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/StreamStub.h b/audio/aidl/default/include/core-impl/StreamStub.h
index 6b1b2dd..a8a3fc4 100644
--- a/audio/aidl/default/include/core-impl/StreamStub.h
+++ b/audio/aidl/default/include/core-impl/StreamStub.h
@@ -22,7 +22,7 @@
class StreamStub : public StreamCommonImpl {
public:
- StreamStub(const Metadata& metadata, StreamContext&& context);
+ StreamStub(StreamContext* context, const Metadata& metadata);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
@@ -43,22 +43,28 @@
bool mIsStandby = true; // Used for validating the state machine logic.
};
-class StreamInStub final : public StreamStub, public StreamIn {
+class StreamInStub final : public StreamIn, public StreamStub {
public:
friend class ndk::SharedRefBase;
StreamInStub(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
+
+ private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
};
-class StreamOutStub final : public StreamStub, public StreamOut {
+class StreamOutStub final : public StreamOut, public StreamStub {
public:
friend class ndk::SharedRefBase;
- StreamOutStub(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
- StreamContext&& context,
+ StreamOutStub(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/StreamSwitcher.h b/audio/aidl/default/include/core-impl/StreamSwitcher.h
new file mode 100644
index 0000000..e462481
--- /dev/null
+++ b/audio/aidl/default/include/core-impl/StreamSwitcher.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2023 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 "Stream.h"
+
+namespace aidl::android::hardware::audio::core {
+
+// 'StreamSwitcher' is implementation of 'StreamCommonInterface' which allows
+// dynamically switching the underlying stream implementation based on currently
+// connected devices. This is achieved by replacing inheritance from
+// 'StreamCommonImpl' with owning an instance of it. StreamSwitcher must be
+// extended in order to supply the logic for choosing the stream
+// implementation. When there are no connected devices, for instance, upon the
+// creation, the StreamSwitcher engages an instance of a stub stream in order to
+// keep serving requests coming via 'StreamDescriptor'.
+//
+// StreamSwitcher implements the 'IStreamCommon' interface directly, with
+// necessary delegation to the current stream implementation. While the stub
+// stream is engaged, any requests made via 'IStreamCommon' (parameters, effects
+// setting, etc) are postponed and only delivered on device connection change
+// to the "real" stream implementation provided by the extending class. This is why
+// the behavior of StreamSwitcher in the "stub" state is not identical to behavior
+// of 'StreamStub'. It can become a full substitute for 'StreamStub' once
+// device connection change event occurs and the extending class returns
+// 'LEAVE_CURRENT_STREAM' from 'switchCurrentStream' method.
+//
+// There is a natural limitation that the current stream implementation may only
+// be switched when the stream is in the 'STANDBY' state. Thus, when the event
+// to switch the stream occurs, the current stream is stopped and joined, and
+// its last state is validated. Since the change of the set of connected devices
+// normally occurs on patch updates, if the stream was not in standby, this is
+// reported to the caller of 'IModule.setAudioPatch' as the 'EX_ILLEGAL_STATE'
+// error.
+//
+// The simplest use case, when the implementor just needs to emulate the legacy HAL API
+// behavior of receiving the connected devices upon stream creation, the implementation
+// of the extending class can look as follows. We assume that 'StreamLegacy' implementation
+// is the one requiring to know connected devices on creation:
+//
+// class StreamLegacy : public StreamCommonImpl {
+// public:
+// StreamLegacy(StreamContext* context, const Metadata& metadata,
+// const std::vector<AudioDevice>& devices);
+// };
+//
+// class StreamOutLegacy final : public StreamOut, public StreamSwitcher {
+// public:
+// StreamOutLegacy(StreamContext&& context, metatadata etc.)
+// private:
+// DeviceSwitchBehavior switchCurrentStream(const std::vector<AudioDevice>&) override {
+// // This implementation effectively postpones stream creation until
+// // receiving the first call to 'setConnectedDevices' with a non-empty list.
+// return isStubStream() ? DeviceSwitchBehavior::CREATE_NEW_STREAM :
+// DeviceSwitchBehavior::USE_CURRENT_STREAM;
+// }
+// std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
+// const std::vector<AudioDevice>& devices,
+// StreamContext* context, const Metadata& metadata) override {
+// return std::unique_ptr<StreamCommonInterfaceEx>(new InnerStreamWrapper<StreamLegacy>(
+// context, metadata, devices));
+// }
+// void onClose(StreamDescriptor::State) override { defaultOnClose(); }
+// }
+//
+
+class StreamCommonInterfaceEx : virtual public StreamCommonInterface {
+ public:
+ virtual StreamDescriptor::State getStatePriorToClosing() const = 0;
+};
+
+template <typename T>
+class InnerStreamWrapper : public T, public StreamCommonInterfaceEx {
+ public:
+ InnerStreamWrapper(StreamContext* context, const Metadata& metadata) : T(context, metadata) {}
+ StreamDescriptor::State getStatePriorToClosing() const override { return mStatePriorToClosing; }
+
+ private:
+ // Do not need to do anything on close notification from the inner stream
+ // because StreamSwitcher handles IStreamCommon::close by itself.
+ void onClose(StreamDescriptor::State statePriorToClosing) override {
+ mStatePriorToClosing = statePriorToClosing;
+ }
+
+ StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY;
+};
+
+class StreamSwitcher : virtual public StreamCommonInterface {
+ public:
+ StreamSwitcher(StreamContext* context, const Metadata& metadata);
+
+ ndk::ScopedAStatus close() override;
+ ndk::ScopedAStatus prepareToClose() override;
+ ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override;
+ ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids,
+ std::vector<VendorParameter>* _aidl_return) override;
+ ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters,
+ bool in_async) override;
+ ndk::ScopedAStatus addEffect(
+ const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
+ override;
+ ndk::ScopedAStatus removeEffect(
+ const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect)
+ override;
+
+ ndk::ScopedAStatus getStreamCommonCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override;
+ ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override;
+
+ ndk::ScopedAStatus initInstance(
+ const std::shared_ptr<StreamCommonInterface>& delegate) override;
+ const StreamContext& getContext() const override;
+ bool isClosed() const override;
+ const ConnectedDevices& getConnectedDevices() const override;
+ ndk::ScopedAStatus setConnectedDevices(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
+ override;
+
+ protected:
+ // Since switching a stream requires closing down the current stream, StreamSwitcher
+ // asks the extending class its intent on the connected devices change.
+ enum DeviceSwitchBehavior {
+ // Continue using the current stream implementation. If it's the stub implementation,
+ // StreamSwitcher starts treating the stub stream as a "real" implementation,
+ // without effectively closing it and starting again.
+ USE_CURRENT_STREAM,
+ // This is the normal case when the extending class provides a "real" implementation
+ // which is not a stub implementation.
+ CREATE_NEW_STREAM,
+ // This is the case when the extending class wants to revert back to the initial
+ // condition of using a stub stream provided by the StreamSwitcher. This behavior
+ // is only allowed when the list of connected devices is empty.
+ SWITCH_TO_STUB_STREAM,
+ // Use when the set of devices is not supported by the extending class. This returns
+ // 'EX_UNSUPPORTED_OPERATION' from 'setConnectedDevices'.
+ UNSUPPORTED_DEVICES,
+ };
+ // StreamSwitcher will call these methods from 'setConnectedDevices'. If the switch behavior
+ // is 'CREATE_NEW_STREAM', the 'createwNewStream' function will be called (with the same
+ // device vector) for obtaining a new stream implementation, assuming that closing
+ // the current stream was a success.
+ virtual DeviceSwitchBehavior switchCurrentStream(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0;
+ virtual std::unique_ptr<StreamCommonInterfaceEx> createNewStream(
+ const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices,
+ StreamContext* context, const Metadata& metadata) = 0;
+ virtual void onClose(StreamDescriptor::State streamPriorToClosing) = 0;
+
+ bool isStubStream() const { return mIsStubStream; }
+ StreamCommonInterfaceEx* getCurrentStream() const { return mStream.get(); }
+
+ private:
+ using VndParam = std::pair<std::vector<VendorParameter>, bool /*isAsync*/>;
+
+ static constexpr bool isValidClosingStreamState(StreamDescriptor::State state) {
+ return state == StreamDescriptor::State::STANDBY || state == StreamDescriptor::State::ERROR;
+ }
+
+ ndk::ScopedAStatus closeCurrentStream(bool validateStreamState);
+
+ // StreamSwitcher does not own the context.
+ StreamContext* mContext;
+ Metadata mMetadata;
+ ChildInterface<StreamCommonDelegator> mCommon;
+ // The current stream.
+ std::unique_ptr<StreamCommonInterfaceEx> mStream;
+ // Indicates whether 'mCurrentStream' is a stub stream implementation
+ // maintained by StreamSwitcher until the extending class provides a "real"
+ // implementation. The invariant of this state is that there are no connected
+ // devices.
+ bool mIsStubStream = true;
+ // Storage for the data from commands received via 'IStreamCommon'.
+ std::optional<int32_t> mHwAvSyncId;
+ std::vector<VndParam> mMissedParameters;
+ std::vector<std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>> mEffects;
+};
+
+} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/include/core-impl/StreamUsb.h b/audio/aidl/default/include/core-impl/StreamUsb.h
index 44f742a..74e30ff 100644
--- a/audio/aidl/default/include/core-impl/StreamUsb.h
+++ b/audio/aidl/default/include/core-impl/StreamUsb.h
@@ -28,11 +28,8 @@
class StreamUsb : public StreamAlsa {
public:
- StreamUsb(const Metadata& metadata, StreamContext&& context);
+ StreamUsb(StreamContext* context, const Metadata& metadata);
// Methods of 'DriverInterface'.
- ::android::status_t drain(StreamDescriptor::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;
@@ -47,29 +44,31 @@
std::atomic<bool> mConnectedDevicesUpdated = false;
};
-class StreamInUsb final : public StreamUsb, public StreamIn {
+class StreamInUsb final : public StreamIn, public StreamUsb {
public:
friend class ndk::SharedRefBase;
StreamInUsb(
- const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
StreamContext&& context,
+ const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones);
private:
+ void onClose(StreamDescriptor::State) override { defaultOnClose(); }
ndk::ScopedAStatus getActiveMicrophones(
std::vector<::aidl::android::media::audio::common::MicrophoneDynamicInfo>* _aidl_return)
override;
};
-class StreamOutUsb final : public StreamUsb, public StreamOut {
+class StreamOutUsb final : public StreamOut, public StreamUsb {
public:
friend class ndk::SharedRefBase;
- StreamOutUsb(const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
- StreamContext&& context,
+ StreamOutUsb(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(); }
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h
index 22cdb6b..698e7a5 100644
--- a/audio/aidl/default/include/effect-impl/EffectContext.h
+++ b/audio/aidl/default/include/effect-impl/EffectContext.h
@@ -124,11 +124,11 @@
virtual RetCode setCommon(const Parameter::Common& common) {
mCommon = common;
- LOG(INFO) << __func__ << mCommon.toString();
+ LOG(VERBOSE) << __func__ << mCommon.toString();
return RetCode::SUCCESS;
}
virtual Parameter::Common getCommon() {
- LOG(DEBUG) << __func__ << mCommon.toString();
+ LOG(VERBOSE) << __func__ << mCommon.toString();
return mCommon;
}
diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h
index b456817..421429a 100644
--- a/audio/aidl/default/include/effect-impl/EffectWorker.h
+++ b/audio/aidl/default/include/effect-impl/EffectWorker.h
@@ -45,8 +45,8 @@
auto readSamples = inputMQ->availableToRead(), writeSamples = outputMQ->availableToWrite();
if (readSamples && writeSamples) {
auto processSamples = std::min(readSamples, writeSamples);
- LOG(DEBUG) << __func__ << " available to read " << readSamples << " available to write "
- << writeSamples << " process " << processSamples;
+ LOG(VERBOSE) << __func__ << " available to read " << readSamples
+ << " available to write " << writeSamples << " process " << processSamples;
auto buffer = mContext->getWorkBuffer();
inputMQ->read(buffer, processSamples);
@@ -54,8 +54,8 @@
IEffect::Status status = effectProcessImpl(buffer, buffer, processSamples);
outputMQ->write(buffer, status.fmqProduced);
statusMQ->writeBlocking(&status, 1);
- LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqConsumed
- << " produced " << status.fmqProduced;
+ LOG(VERBOSE) << __func__ << " done processing, effect consumed " << status.fmqConsumed
+ << " produced " << status.fmqProduced;
} else {
// TODO: maybe add some sleep here to avoid busy waiting
}
diff --git a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
index 1401db0..d0b8204 100644
--- a/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
+++ b/audio/aidl/default/include/effectFactory-impl/EffectFactory.h
@@ -24,6 +24,7 @@
#include <vector>
#include <aidl/android/hardware/audio/effect/BnFactory.h>
+#include <android-base/thread_annotations.h>
#include "EffectConfig.h"
namespace aidl::android::hardware::audio::effect {
@@ -82,9 +83,11 @@
private:
const EffectConfig mConfig;
~Factory();
+
+ std::mutex mMutex;
// Set of effect descriptors supported by the devices.
- std::set<Descriptor> mDescSet;
- std::set<Descriptor::Identity> mIdentitySet;
+ std::set<Descriptor> mDescSet GUARDED_BY(mMutex);
+ std::set<Descriptor::Identity> mIdentitySet GUARDED_BY(mMutex);
static constexpr int kMapEntryHandleIndex = 0;
static constexpr int kMapEntryInterfaceIndex = 1;
@@ -94,13 +97,15 @@
std::string /* library name */>
DlEntry;
- std::map<aidl::android::media::audio::common::AudioUuid /* implUUID */, DlEntry> mEffectLibMap;
+ std::map<aidl::android::media::audio::common::AudioUuid /* implUUID */, DlEntry> mEffectLibMap
+ GUARDED_BY(mMutex);
typedef std::pair<aidl::android::media::audio::common::AudioUuid, ndk::SpAIBinder> EffectEntry;
- std::map<std::weak_ptr<IEffect>, EffectEntry, std::owner_less<>> mEffectMap;
+ std::map<std::weak_ptr<IEffect>, EffectEntry, std::owner_less<>> mEffectMap GUARDED_BY(mMutex);
- ndk::ScopedAStatus destroyEffectImpl(const std::shared_ptr<IEffect>& in_handle);
- void cleanupEffectMap();
+ ndk::ScopedAStatus destroyEffectImpl_l(const std::shared_ptr<IEffect>& in_handle)
+ REQUIRES(mMutex);
+ void cleanupEffectMap_l() REQUIRES(mMutex);
bool openEffectLibrary(const ::aidl::android::media::audio::common::AudioUuid& impl,
const std::string& path);
void createIdentityWithConfig(
@@ -108,12 +113,13 @@
const ::aidl::android::media::audio::common::AudioUuid& typeUuidStr,
const std::optional<::aidl::android::media::audio::common::AudioUuid> proxyUuid);
- ndk::ScopedAStatus getDescriptorWithUuid(
- const aidl::android::media::audio::common::AudioUuid& uuid, Descriptor* desc);
+ ndk::ScopedAStatus getDescriptorWithUuid_l(
+ const aidl::android::media::audio::common::AudioUuid& uuid, Descriptor* desc)
+ REQUIRES(mMutex);
void loadEffectLibs();
/* Get effect_dl_interface_s from library handle */
- void getDlSyms(DlEntry& entry);
+ void getDlSyms_l(DlEntry& entry) REQUIRES(mMutex);
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
index 2b79f51..9be7837 100644
--- a/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/ModuleRemoteSubmix.cpp
@@ -56,16 +56,16 @@
}
ndk::ScopedAStatus ModuleRemoteSubmix::createInputStream(
- const SinkMetadata& sinkMetadata, StreamContext&& context,
+ StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones, std::shared_ptr<StreamIn>* result) {
- return createStreamInstance<StreamInRemoteSubmix>(result, sinkMetadata, std::move(context),
+ return createStreamInstance<StreamInRemoteSubmix>(result, std::move(context), sinkMetadata,
microphones);
}
ndk::ScopedAStatus ModuleRemoteSubmix::createOutputStream(
- const SourceMetadata& sourceMetadata, StreamContext&& context,
+ StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
- return createStreamInstance<StreamOutRemoteSubmix>(result, sourceMetadata, std::move(context),
+ return createStreamInstance<StreamOutRemoteSubmix>(result, std::move(context), sourceMetadata,
offloadInfo);
}
diff --git a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
index 6d5185b..9537ebc 100644
--- a/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
+++ b/audio/aidl/default/r_submix/StreamRemoteSubmix.cpp
@@ -29,14 +29,14 @@
namespace aidl::android::hardware::audio::core {
-StreamRemoteSubmix::StreamRemoteSubmix(const Metadata& metadata, StreamContext&& context)
- : StreamCommonImpl(metadata, std::move(context)),
- mPortId(context.getPortId()),
+StreamRemoteSubmix::StreamRemoteSubmix(StreamContext* context, const Metadata& metadata)
+ : StreamCommonImpl(context, metadata),
+ mPortId(context->getPortId()),
mIsInput(isInput(metadata)) {
- mStreamConfig.frameSize = context.getFrameSize();
- mStreamConfig.format = context.getFormat();
- mStreamConfig.channelLayout = context.getChannelLayout();
- mStreamConfig.sampleRate = context.getSampleRate();
+ mStreamConfig.frameSize = context->getFrameSize();
+ mStreamConfig.format = context->getFormat();
+ mStreamConfig.channelLayout = context->getChannelLayout();
+ mStreamConfig.sampleRate = context->getSampleRate();
}
std::mutex StreamRemoteSubmix::sSubmixRoutesLock;
@@ -353,10 +353,11 @@
return ::android::OK;
}
-StreamInRemoteSubmix::StreamInRemoteSubmix(const SinkMetadata& sinkMetadata,
- StreamContext&& context,
+StreamInRemoteSubmix::StreamInRemoteSubmix(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
- : StreamRemoteSubmix(sinkMetadata, std::move(context)), StreamIn(microphones) {}
+ : StreamIn(std::move(context), microphones),
+ StreamRemoteSubmix(&(StreamIn::mContext), sinkMetadata) {}
ndk::ScopedAStatus StreamInRemoteSubmix::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return) {
@@ -365,9 +366,10 @@
return ndk::ScopedAStatus::ok();
}
-StreamOutRemoteSubmix::StreamOutRemoteSubmix(const SourceMetadata& sourceMetadata,
- StreamContext&& context,
+StreamOutRemoteSubmix::StreamOutRemoteSubmix(StreamContext&& context,
+ const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : StreamRemoteSubmix(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
+ : StreamOut(std::move(context), offloadInfo),
+ StreamRemoteSubmix(&(StreamOut::mContext), sourceMetadata) {}
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/r_submix/SubmixRoute.cpp b/audio/aidl/default/r_submix/SubmixRoute.cpp
index 8f5b8cb..ddac64d 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.cpp
+++ b/audio/aidl/default/r_submix/SubmixRoute.cpp
@@ -27,7 +27,7 @@
namespace aidl::android::hardware::audio::core::r_submix {
// Verify a submix input or output stream can be opened.
-bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig streamConfig) {
+bool SubmixRoute::isStreamConfigValid(bool isInput, const AudioConfig& streamConfig) {
// If the stream is already open, don't open it again.
// ENABLE_LEGACY_INPUT_OPEN is default behaviour
if (!isInput && isStreamOutOpen()) {
@@ -43,7 +43,7 @@
// Compare this stream config with existing pipe config, returning false if they do *not*
// match, true otherwise.
-bool SubmixRoute::isStreamConfigCompatible(const AudioConfig streamConfig) {
+bool SubmixRoute::isStreamConfigCompatible(const AudioConfig& streamConfig) {
if (streamConfig.channelLayout != mPipeConfig.channelLayout) {
LOG(ERROR) << __func__ << ": channel count mismatch, stream channels = "
<< streamConfig.channelLayout.toString()
@@ -126,7 +126,7 @@
// If SubmixRoute doesn't exist for a port, create a pipe for the submix audio device of size
// buffer_size_frames and store config of the submix audio device.
-::android::status_t SubmixRoute::createPipe(const AudioConfig streamConfig) {
+::android::status_t SubmixRoute::createPipe(const AudioConfig& streamConfig) {
const int channelCount = getChannelCount(streamConfig.channelLayout);
const audio_format_t audioFormat = VALUE_OR_RETURN_STATUS(
aidl2legacy_AudioFormatDescription_audio_format_t(streamConfig.format));
@@ -201,9 +201,9 @@
if (isInput) {
mStreamInStandby = true;
- } else {
+ } else if (!mStreamOutStandby) {
mStreamOutStandby = true;
- mStreamOutStandbyTransition = !mStreamOutStandbyTransition;
+ mStreamOutStandbyTransition = true;
}
}
diff --git a/audio/aidl/default/r_submix/SubmixRoute.h b/audio/aidl/default/r_submix/SubmixRoute.h
index 5f7ea75..1a98df2 100644
--- a/audio/aidl/default/r_submix/SubmixRoute.h
+++ b/audio/aidl/default/r_submix/SubmixRoute.h
@@ -93,9 +93,9 @@
return mSource;
}
- bool isStreamConfigValid(bool isInput, const AudioConfig streamConfig);
+ bool isStreamConfigValid(bool isInput, const AudioConfig& streamConfig);
void closeStream(bool isInput);
- ::android::status_t createPipe(const AudioConfig streamConfig);
+ ::android::status_t createPipe(const AudioConfig& streamConfig);
void exitStandby(bool isInput);
bool hasAtleastOneStreamOpen();
int notifyReadError();
@@ -107,7 +107,7 @@
long updateReadCounterFrames(size_t frameCount);
private:
- bool isStreamConfigCompatible(const AudioConfig streamConfig);
+ bool isStreamConfigCompatible(const AudioConfig& streamConfig);
std::mutex mLock;
diff --git a/audio/aidl/default/stub/ModuleStub.cpp b/audio/aidl/default/stub/ModuleStub.cpp
index a600752..9f6e0b4 100644
--- a/audio/aidl/default/stub/ModuleStub.cpp
+++ b/audio/aidl/default/stub/ModuleStub.cpp
@@ -37,8 +37,9 @@
if (!mBluetooth) {
mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
}
- *_aidl_return = mBluetooth.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
+ *_aidl_return = mBluetooth.getInstance();
+ LOG(DEBUG) << __func__
+ << ": returning instance of IBluetooth: " << _aidl_return->get()->asBinder().get();
return ndk::ScopedAStatus::ok();
}
@@ -46,8 +47,9 @@
if (!mBluetoothA2dp) {
mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
}
- *_aidl_return = mBluetoothA2dp.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
+ *_aidl_return = mBluetoothA2dp.getInstance();
+ LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: "
+ << _aidl_return->get()->asBinder().get();
return ndk::ScopedAStatus::ok();
}
@@ -55,23 +57,24 @@
if (!mBluetoothLe) {
mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
}
- *_aidl_return = mBluetoothLe.getPtr();
- LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
+ *_aidl_return = mBluetoothLe.getInstance();
+ LOG(DEBUG) << __func__
+ << ": returning instance of IBluetoothLe: " << _aidl_return->get()->asBinder().get();
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus ModuleStub::createInputStream(const SinkMetadata& sinkMetadata,
- StreamContext&& context,
+ndk::ScopedAStatus ModuleStub::createInputStream(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
- return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
+ return createStreamInstance<StreamInStub>(result, std::move(context), sinkMetadata,
microphones);
}
ndk::ScopedAStatus ModuleStub::createOutputStream(
- const SourceMetadata& sourceMetadata, StreamContext&& context,
+ StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
- return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
+ return createStreamInstance<StreamOutStub>(result, std::move(context), sourceMetadata,
offloadInfo);
}
diff --git a/audio/aidl/default/stub/StreamStub.cpp b/audio/aidl/default/stub/StreamStub.cpp
index 2dcf4d4..66f4605 100644
--- a/audio/aidl/default/stub/StreamStub.cpp
+++ b/audio/aidl/default/stub/StreamStub.cpp
@@ -31,8 +31,8 @@
namespace aidl::android::hardware::audio::core {
-StreamStub::StreamStub(const Metadata& metadata, StreamContext&& context)
- : StreamCommonImpl(metadata, std::move(context)),
+StreamStub::StreamStub(StreamContext* context, const Metadata& metadata)
+ : StreamCommonImpl(context, metadata),
mFrameSizeBytes(getContext().getFrameSize()),
mSampleRate(getContext().getSampleRate()),
mIsAsynchronous(!!getContext().getAsyncCallback()),
@@ -118,12 +118,13 @@
mIsInitialized = false;
}
-StreamInStub::StreamInStub(const SinkMetadata& sinkMetadata, StreamContext&& context,
+StreamInStub::StreamInStub(StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
- : StreamStub(sinkMetadata, std::move(context)), StreamIn(microphones) {}
+ : StreamIn(std::move(context), microphones), StreamStub(&(StreamIn::mContext), sinkMetadata) {}
-StreamOutStub::StreamOutStub(const SourceMetadata& sourceMetadata, StreamContext&& context,
+StreamOutStub::StreamOutStub(StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : StreamStub(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {}
+ : StreamOut(std::move(context), offloadInfo),
+ StreamStub(&(StreamOut::mContext), sourceMetadata) {}
} // namespace aidl::android::hardware::audio::core
diff --git a/audio/aidl/default/usb/ModuleUsb.cpp b/audio/aidl/default/usb/ModuleUsb.cpp
index a812e4d..f926e09 100644
--- a/audio/aidl/default/usb/ModuleUsb.cpp
+++ b/audio/aidl/default/usb/ModuleUsb.cpp
@@ -68,22 +68,22 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-ndk::ScopedAStatus ModuleUsb::createInputStream(const SinkMetadata& sinkMetadata,
- StreamContext&& context,
+ndk::ScopedAStatus ModuleUsb::createInputStream(StreamContext&& context,
+ const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones,
std::shared_ptr<StreamIn>* result) {
- return createStreamInstance<StreamInUsb>(result, sinkMetadata, std::move(context), microphones);
+ return createStreamInstance<StreamInUsb>(result, std::move(context), sinkMetadata, microphones);
}
-ndk::ScopedAStatus ModuleUsb::createOutputStream(const SourceMetadata& sourceMetadata,
- StreamContext&& context,
+ndk::ScopedAStatus ModuleUsb::createOutputStream(StreamContext&& context,
+ const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo,
std::shared_ptr<StreamOut>* result) {
if (offloadInfo.has_value()) {
LOG(ERROR) << __func__ << ": offload is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
- return createStreamInstance<StreamOutUsb>(result, sourceMetadata, std::move(context),
+ return createStreamInstance<StreamOutUsb>(result, std::move(context), sourceMetadata,
offloadInfo);
}
diff --git a/audio/aidl/default/usb/StreamUsb.cpp b/audio/aidl/default/usb/StreamUsb.cpp
index da0ad11..9684a87 100644
--- a/audio/aidl/default/usb/StreamUsb.cpp
+++ b/audio/aidl/default/usb/StreamUsb.cpp
@@ -35,8 +35,8 @@
namespace aidl::android::hardware::audio::core {
-StreamUsb::StreamUsb(const Metadata& metadata, StreamContext&& context)
- : StreamAlsa(metadata, std::move(context)) {}
+StreamUsb::StreamUsb(StreamContext* context, const Metadata& metadata)
+ : StreamAlsa(context, metadata, 1 /*readWriteRetries*/) {}
ndk::ScopedAStatus StreamUsb::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices) {
@@ -55,28 +55,13 @@
}
connectedDeviceProfiles.push_back(*profile);
}
- RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
+ RETURN_STATUS_IF_ERROR(setConnectedDevices(connectedDevices));
std::lock_guard guard(mLock);
mConnectedDeviceProfiles = std::move(connectedDeviceProfiles);
mConnectedDevicesUpdated.store(true, std::memory_order_release);
return ndk::ScopedAStatus::ok();
}
-::android::status_t StreamUsb::drain(StreamDescriptor::DrainMode) {
- usleep(1000);
- return ::android::OK;
-}
-
-::android::status_t StreamUsb::flush() {
- usleep(1000);
- return ::android::OK;
-}
-
-::android::status_t StreamUsb::pause() {
- usleep(1000);
- return ::android::OK;
-}
-
::android::status_t StreamUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
if (mConnectedDevicesUpdated.load(std::memory_order_acquire)) {
@@ -98,9 +83,9 @@
return connectedDevices;
}
-StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
+StreamInUsb::StreamInUsb(StreamContext&& context, const SinkMetadata& sinkMetadata,
const std::vector<MicrophoneInfo>& microphones)
- : StreamUsb(sinkMetadata, std::move(context)), StreamIn(microphones) {}
+ : StreamIn(std::move(context), microphones), StreamUsb(&(StreamIn::mContext), sinkMetadata) {}
ndk::ScopedAStatus StreamInUsb::getActiveMicrophones(
std::vector<MicrophoneDynamicInfo>* _aidl_return __unused) {
@@ -108,10 +93,10 @@
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
-StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
+StreamOutUsb::StreamOutUsb(StreamContext&& context, const SourceMetadata& sourceMetadata,
const std::optional<AudioOffloadInfo>& offloadInfo)
- : StreamUsb(sourceMetadata, std::move(context)),
- StreamOut(offloadInfo),
+ : StreamOut(std::move(context), offloadInfo),
+ StreamUsb(&(StreamOut::mContext), sourceMetadata),
mChannelCount(getChannelCount(getContext().getChannelLayout())) {}
ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
index 769d739..0a49446 100644
--- a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
+++ b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp
@@ -33,12 +33,10 @@
bool connected) {
LOG(DEBUG) << __func__ << ": card=" << card << ", connected=" << connected;
if (connected) {
- struct mixer* mixer = mixer_open(card);
- if (mixer == nullptr) {
- PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
+ auto alsaMixer = std::make_shared<alsa::Mixer>(card);
+ if (!alsaMixer->isValid()) {
return;
}
- auto alsaMixer = std::make_shared<alsa::Mixer>(mixer);
alsaMixer->setMasterMute(masterMuted);
alsaMixer->setMasterVolume(masterVolume);
const std::lock_guard guard(mLock);
diff --git a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
index 28bacb0..7d88810 100644
--- a/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
+++ b/automotive/vehicle/aidl_property/android/hardware/automotive/vehicle/VehicleProperty.aidl
@@ -169,7 +169,7 @@
* int32Values[4] = wheel base
* int32Values[5] = track width front
* int32Values[6] = track width rear
- * int32Values[7] = curb to curb turning radius
+ * int32Values[7] = curb to curb turning diameter
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
@@ -421,6 +421,7 @@
*
* @change_mode VehiclePropertyChangeMode.CONTINUOUS
* @access VehiclePropertyAccess.READ_WRITE
+ * @access VehiclePropertyAccess.READ
* @unit VehicleUnit:METER
*/
RANGE_REMAINING = 0x0308 + 0x10000000 + 0x01000000
@@ -4476,6 +4477,9 @@
* powers on the vehicle. VEHICLE_IN_USE is set to true. After a driving session, user powers
* off the vehicle, VEHICLE_IN_USE is set to false.
*
+ * <p>This property is defined as VehiclePropertyAccess.READ_WRITE, but OEMs have the option to
+ * implement it as VehiclePropertyAccess.READ only.
+ *
* @change_mode VehiclePropertyChangeMode.ON_CHANGE
* @access VehiclePropertyAccess.READ_WRITE
* @access VehiclePropertyAccess.READ
diff --git a/automotive/vehicle/tools/generate_annotation_enums.py b/automotive/vehicle/tools/generate_annotation_enums.py
index 7276fe6..c432e9d 100755
--- a/automotive/vehicle/tools/generate_annotation_enums.py
+++ b/automotive/vehicle/tools/generate_annotation_enums.py
@@ -49,6 +49,8 @@
RE_COMMENT_END = re.compile('\s*\*\/')
RE_CHANGE_MODE = re.compile('\s*\* @change_mode (\S+)\s*')
RE_ACCESS = re.compile('\s*\* @access (\S+)\s*')
+RE_DATA_ENUM = re.compile('\s*\* @data_enum (\S+)\s*')
+RE_UNIT = re.compile('\s*\* @unit (\S+)\s+')
RE_VALUE = re.compile('\s*(\w+)\s*=(.*)')
LICENSE = """/*
@@ -166,55 +168,121 @@
"""
-class Converter:
+class PropertyConfig:
+ """Represents one VHAL property definition in VehicleProperty.aidl."""
- def __init__(self, name, annotation_re):
- self.name = name
- self.annotation_re = annotation_re
+ def __init__(self):
+ self.name = None
+ self.description = None
+ self.change_mode = None
+ self.access_modes = []
+ self.enum_types = []
+ self.unit_type = None
- def convert(self, input, output, header, footer, cpp):
+ def __repr__(self):
+ return self.__str__()
+
+ def __str__(self):
+ return ('PropertyConfig{{' +
+ 'name: {}, description: {}, change_mode: {}, access_modes: {}, enum_types: {}' +
+ ', unit_type: {}}}').format(self.name, self.description, self.change_mode,
+ self.access_modes, self.enum_types, self.unit_type)
+
+
+class FileParser:
+
+ def __init__(self):
+ self.configs = None
+
+ def parseFile(self, input_file):
+ """Parses the input VehicleProperty.aidl file into a list of property configs."""
processing = False
in_comment = False
- content = LICENSE + header
- annotation = None
- id = 0
- with open(input, 'r') as f:
+ configs = []
+ config = None
+ with open(input_file, 'r') as f:
for line in f.readlines():
if RE_ENUM_START.match(line):
processing = True
- annotation = None
elif RE_ENUM_END.match(line):
processing = False
if not processing:
continue
if RE_COMMENT_BEGIN.match(line):
in_comment = True
- annotation = None
+ config = PropertyConfig()
+ description = ''
if RE_COMMENT_END.match(line):
in_comment = False
if in_comment:
- match = self.annotation_re.match(line)
- if match and not annotation:
- annotation = match.group(1)
+ if not config.description:
+ sline = line.strip()
+ # Skip the first line of comment
+ if sline.startswith('*'):
+ # Remove the '*'.
+ sline = sline[1:].strip()
+ # We reach an empty line of comment, the description part is ending.
+ if sline == '':
+ config.description = description
+ else:
+ if description != '':
+ description += ' '
+ description += sline
+ match = RE_CHANGE_MODE.match(line)
+ if match:
+ config.change_mode = match.group(1).replace('VehiclePropertyChangeMode.', '')
+ match = RE_ACCESS.match(line)
+ if match:
+ config.access_modes.append(match.group(1).replace('VehiclePropertyAccess.', ''))
+ match = RE_UNIT.match(line)
+ if match:
+ config.unit_type = match.group(1)
+ match = RE_DATA_ENUM.match(line)
+ if match:
+ config.enum_types.append(match.group(1))
else:
match = RE_VALUE.match(line)
if match:
prop_name = match.group(1)
if prop_name == 'INVALID':
continue
- if not annotation:
+ if not config.change_mode:
raise Exception(
- 'No @' + self.name + ' annotation for property: ' + prop_name)
- if id != 0:
- content += '\n'
- if cpp:
- annotation = annotation.replace('.', '::')
- content += (TAB + TAB + '{VehicleProperty::' + prop_name + ', ' +
- annotation + '},')
- else:
- content += (TAB + TAB + 'Map.entry(VehicleProperty.' + prop_name + ', ' +
- annotation + '),')
- id += 1
+ 'No change_mode annotation for property: ' + prop_name)
+ if not config.access_modes:
+ raise Exception(
+ 'No access_mode annotation for property: ' + prop_name)
+ config.name = prop_name
+ configs.append(config)
+
+ self.configs = configs
+
+ def convert(self, output, header, footer, cpp, field):
+ """Converts the property config file to C++/Java output file."""
+ counter = 0
+ content = LICENSE + header
+ for config in self.configs:
+ if field == 'change_mode':
+ if cpp:
+ annotation = "VehiclePropertyChangeMode::" + config.change_mode
+ else:
+ annotation = "VehiclePropertyChangeMode." + config.change_mode
+ elif field == 'access_mode':
+ if cpp:
+ annotation = "VehiclePropertyAccess::" + config.access_modes[0]
+ else:
+ annotation = "VehiclePropertyAccess." + config.access_modes[0]
+ else:
+ raise Exception('Unknown field: ' + field)
+ if counter != 0:
+ content += '\n'
+ if cpp:
+ content += (TAB + TAB + '{VehicleProperty::' + config.name + ', ' +
+ annotation + '},')
+ else:
+ content += (TAB + TAB + 'Map.entry(VehicleProperty.' + config.name + ', ' +
+ annotation + '),')
+ counter += 1
# Remove the additional ',' at the end for the Java file.
if not cpp:
@@ -225,6 +293,30 @@
with open(output, 'w') as f:
f.write(content)
+ def outputAsCsv(self, output):
+ content = 'name,description,change mode,access mode,enum type,unit type\n'
+ for config in self.configs:
+ enum_types = None
+ if not config.enum_types:
+ enum_types = '/'
+ else:
+ enum_types = '/'.join(config.enum_types)
+ unit_type = config.unit_type
+ if not unit_type:
+ unit_type = '/'
+ access_modes = ''
+ content += '"{}","{}","{}","{}","{}","{}"\n'.format(
+ config.name,
+ # Need to escape quote as double quote.
+ config.description.replace('"', '""'),
+ config.change_mode,
+ '/'.join(config.access_modes),
+ enum_types,
+ unit_type)
+
+ with open(output, 'w+') as f:
+ f.write(content)
+
def createTempFile():
f = tempfile.NamedTemporaryFile(delete=False);
@@ -239,6 +331,8 @@
parser.add_argument('--preupload_files', nargs='+', required=False, help='modified files')
parser.add_argument('--check_only', required=False, action='store_true',
help='only check whether the generated files need update')
+ parser.add_argument('--output_csv', required=False,
+ help='Path to the parsing result in CSV style, useful for doc generation')
args = parser.parse_args();
android_top = None
output_folder = None
@@ -258,6 +352,12 @@
'at the android root')
aidl_file = os.path.join(android_top, PROP_AIDL_FILE_PATH)
+ f = FileParser();
+ f.parseFile(aidl_file)
+
+ if args.output_csv:
+ f.outputAsCsv(args.output_csv)
+ return
change_mode_cpp_file = os.path.join(android_top, CHANGE_MODE_CPP_FILE_PATH);
access_cpp_file = os.path.join(android_top, ACCESS_CPP_FILE_PATH);
@@ -281,14 +381,12 @@
temp_files.append(access_java_output)
try:
- c = Converter('change_mode', RE_CHANGE_MODE);
- c.convert(aidl_file, change_mode_cpp_output, CHANGE_MODE_CPP_HEADER, CHANGE_MODE_CPP_FOOTER,
- True)
- c.convert(aidl_file, change_mode_java_output, CHANGE_MODE_JAVA_HEADER,
- CHANGE_MODE_JAVA_FOOTER, False)
- c = Converter('access', RE_ACCESS)
- c.convert(aidl_file, access_cpp_output, ACCESS_CPP_HEADER, ACCESS_CPP_FOOTER, True)
- c.convert(aidl_file, access_java_output, ACCESS_JAVA_HEADER, ACCESS_JAVA_FOOTER, False)
+ f.convert(change_mode_cpp_output, CHANGE_MODE_CPP_HEADER, CHANGE_MODE_CPP_FOOTER,
+ True, 'change_mode')
+ f.convert(change_mode_java_output, CHANGE_MODE_JAVA_HEADER,
+ CHANGE_MODE_JAVA_FOOTER, False, 'change_mode')
+ f.convert(access_cpp_output, ACCESS_CPP_HEADER, ACCESS_CPP_FOOTER, True, 'access_mode')
+ f.convert(access_java_output, ACCESS_JAVA_HEADER, ACCESS_JAVA_FOOTER, False, 'access_mode')
if not args.check_only:
return
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 5f5455a..9f9ca96 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -89,24 +89,29 @@
}
void FakeFingerprintEngine::fingerDownAction() {
+ bool isTerminal = false;
LOG(INFO) << __func__;
switch (mWorkMode) {
case WorkMode::kAuthenticate:
- onAuthenticateFingerDown(mCb, mOperationId, mCancel);
+ isTerminal = onAuthenticateFingerDown(mCb, mOperationId, mCancel);
break;
case WorkMode::kEnroll:
- onEnrollFingerDown(mCb, mHat, mCancel);
+ isTerminal = onEnrollFingerDown(mCb, mHat, mCancel);
break;
case WorkMode::kDetectInteract:
- onDetectInteractFingerDown(mCb, mCancel);
+ isTerminal = onDetectInteractFingerDown(mCb, mCancel);
break;
default:
LOG(WARNING) << "unexpected mode: on fingerDownAction(), " << (int)mWorkMode;
break;
}
+
+ if (isTerminal) {
+ mWorkMode = WorkMode::kIdle;
+ }
}
-void FakeFingerprintEngine::onEnrollFingerDown(ISessionCallback* cb,
+bool FakeFingerprintEngine::onEnrollFingerDown(ISessionCallback* cb,
const keymaster::HardwareAuthToken& hat,
const std::future<void>& cancel) {
BEGIN_OP(getLatency(FingerprintHalProperties::operation_enroll_latency()));
@@ -115,7 +120,7 @@
if (hat.mac.empty()) {
LOG(ERROR) << "Fail: hat";
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
- return;
+ return true;
}
// Force error-out
@@ -124,7 +129,7 @@
LOG(ERROR) << "Fail: operation_enroll_error";
auto ec = convertError(err);
cb->onError(ec.first, ec.second);
- return;
+ return true;
}
// Format is "<id>:<progress_ms-[acquiredInfo..]>,...:<result>
@@ -133,7 +138,7 @@
if (parts.size() != 3) {
LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll;
cb->onError(Error::VENDOR, 0 /* vendorError */);
- return;
+ return true;
}
auto enrollmentId = std::stoi(parts[0]);
auto progress = parseEnrollmentCapture(parts[1]);
@@ -149,7 +154,7 @@
if (shouldCancel(cancel)) {
LOG(ERROR) << "Fail: cancel";
cb->onError(Error::CANCELED, 0 /* vendorCode */);
- return;
+ return true;
}
auto ac = convertAcquiredInfo(acquired[j]);
cb->onAcquired(ac.first, ac.second);
@@ -175,9 +180,11 @@
cb->onEnrollmentProgress(enrollmentId, left);
}
}
+
+ return true;
}
-void FakeFingerprintEngine::onAuthenticateFingerDown(ISessionCallback* cb,
+bool FakeFingerprintEngine::onAuthenticateFingerDown(ISessionCallback* cb,
int64_t /* operationId */,
const std::future<void>& cancel) {
BEGIN_OP(getLatency(FingerprintHalProperties::operation_authenticate_latency()));
@@ -191,11 +198,13 @@
if (N == 0) {
LOG(ERROR) << "Fail to parse authentiate acquired info: " + acquired;
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
- return;
+ return true;
}
// got lockout?
- if (checkSensorLockout(cb)) return;
+ if (checkSensorLockout(cb)) {
+ return FakeLockoutTracker::LockoutMode::kPermanent == mLockoutTracker.getMode();
+ }
int i = 0;
do {
@@ -203,7 +212,7 @@
LOG(ERROR) << "Fail: operation_authenticate_fails";
mLockoutTracker.addFailedAttempt();
cb->onAuthenticationFailed();
- return;
+ return false;
}
auto err = FingerprintHalProperties::operation_authenticate_error().value_or(0);
@@ -211,20 +220,21 @@
LOG(ERROR) << "Fail: operation_authenticate_error";
auto ec = convertError(err);
cb->onError(ec.first, ec.second);
- return;
+ return true; /* simply terminating current operation for any user inserted error,
+ revisit if tests need*/
}
if (FingerprintHalProperties::lockout().value_or(false)) {
LOG(ERROR) << "Fail: lockout";
cb->onLockoutPermanent();
cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
- return;
+ return true;
}
if (shouldCancel(cancel)) {
LOG(ERROR) << "Fail: cancel";
cb->onError(Error::CANCELED, 0 /* vendorCode */);
- return;
+ return true;
}
if (i < N) {
@@ -242,16 +252,17 @@
if (id > 0 && isEnrolled) {
cb->onAuthenticationSucceeded(id, {} /* hat */);
mLockoutTracker.reset();
- return;
+ return true;
} else {
LOG(ERROR) << "Fail: fingerprint not enrolled";
cb->onAuthenticationFailed();
mLockoutTracker.addFailedAttempt();
checkSensorLockout(cb);
+ return false;
}
}
-void FakeFingerprintEngine::onDetectInteractFingerDown(ISessionCallback* cb,
+bool FakeFingerprintEngine::onDetectInteractFingerDown(ISessionCallback* cb,
const std::future<void>& cancel) {
BEGIN_OP(getLatency(FingerprintHalProperties::operation_detect_interaction_latency()));
@@ -266,7 +277,7 @@
if (N == 0) {
LOG(ERROR) << "Fail to parse detect interaction acquired info: " + acquired;
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
- return;
+ return true;
}
int i = 0;
@@ -276,13 +287,13 @@
LOG(ERROR) << "Fail: operation_detect_interaction_error";
auto ec = convertError(err);
cb->onError(ec.first, ec.second);
- return;
+ return true;
}
if (shouldCancel(cancel)) {
LOG(ERROR) << "Fail: cancel";
cb->onError(Error::CANCELED, 0 /* vendorCode */);
- return;
+ return true;
}
if (i < N) {
@@ -299,10 +310,12 @@
if (id <= 0 || !isEnrolled) {
LOG(ERROR) << "Fail: not enrolled";
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
- return;
+ return true;
}
cb->onInteractionDetected();
+
+ return true;
}
void FakeFingerprintEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) {
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index 8ac7a95..a06b786 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -94,10 +94,10 @@
virtual void updateContext(WorkMode mode, ISessionCallback* cb, std::future<void>& cancel,
int64_t operationId, const keymaster::HardwareAuthToken& hat);
- void onEnrollFingerDown(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ bool onEnrollFingerDown(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
const std::future<void>& cancel);
- void onAuthenticateFingerDown(ISessionCallback* cb, int64_t, const std::future<void>& cancel);
- void onDetectInteractFingerDown(ISessionCallback* cb, const std::future<void>& cancel);
+ bool onAuthenticateFingerDown(ISessionCallback* cb, int64_t, const std::future<void>& cancel);
+ bool onDetectInteractFingerDown(ISessionCallback* cb, const std::future<void>& cancel);
WorkMode mWorkMode;
ISessionCallback* mCb;
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
index 86207a5..bc235a6 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -132,6 +132,8 @@
FingerprintHalProperties::operation_enroll_latency({});
FingerprintHalProperties::operation_authenticate_latency({});
FingerprintHalProperties::operation_detect_interaction_latency({});
+ FingerprintHalProperties::operation_authenticate_fails(false);
+ FingerprintHalProperties::operation_detect_interaction_latency({});
}
FakeFingerprintEngine mEngine;
@@ -185,6 +187,7 @@
ASSERT_EQ(4, FingerprintHalProperties::enrollments()[0].value());
ASSERT_EQ(4, mCallback->mLastEnrolled);
ASSERT_EQ(1, mCallback->mLastAcquiredInfo);
+ ASSERT_EQ(mEngine.getWorkMode(), FakeFingerprintEngine::WorkMode::kIdle);
}
TEST_F(FakeFingerprintEngineTest, EnrollCancel) {
@@ -239,6 +242,7 @@
ASSERT_FALSE(mCallback->mAuthenticateFailed);
ASSERT_EQ(2, mCallback->mLastAuthenticated);
ASSERT_EQ(1, mCallback->mLastAcquiredInfo);
+ ASSERT_EQ(mEngine.getWorkMode(), FakeFingerprintEngine::WorkMode::kIdle);
}
TEST_F(FakeFingerprintEngineTest, AuthenticateCancel) {
@@ -265,6 +269,7 @@
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
mEngine.fingerDownAction();
ASSERT_TRUE(mCallback->mAuthenticateFailed);
+ ASSERT_EQ(mEngine.getWorkMode(), FakeFingerprintEngine::WorkMode::kAuthenticate);
}
TEST_F(FakeFingerprintEngineTest, AuthenticateLockout) {
@@ -293,6 +298,14 @@
ASSERT_EQ(mCallback->mErrorVendorCode, 9);
}
+TEST_F(FakeFingerprintEngineTest, AuthenticateFails) {
+ FingerprintHalProperties::operation_authenticate_fails(true);
+ mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
+ mEngine.fingerDownAction();
+ ASSERT_TRUE(mCallback->mAuthenticateFailed);
+ ASSERT_EQ(mEngine.getWorkMode(), FakeFingerprintEngine::WorkMode::kAuthenticate);
+}
+
TEST_F(FakeFingerprintEngineTest, AuthenticateAcquired) {
FingerprintHalProperties::lockout(false);
FingerprintHalProperties::enrollments({1, 2});
@@ -318,6 +331,7 @@
mEngine.fingerDownAction();
ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
ASSERT_EQ(1, mCallback->mLastAcquiredInfo);
+ ASSERT_EQ(mEngine.getWorkMode(), FakeFingerprintEngine::WorkMode::kIdle);
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectCancel) {
@@ -483,7 +497,6 @@
FingerprintHalProperties::operation_detect_interaction_latency()));
}
ASSERT_TRUE(latencySet.size() > 95);
- FingerprintHalProperties::operation_detect_interaction_latency({});
}
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
index e5222a7..24eb4d0 100644
--- a/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
+++ b/bluetooth/aidl/vts/VtsHalBluetoothTargetTest.cpp
@@ -222,6 +222,8 @@
int wait_for_completed_packets_event(uint16_t handle);
void send_and_wait_for_cmd_complete(std::unique_ptr<CommandBuilder> cmd,
std::vector<uint8_t>& cmd_complete);
+ void reassemble_sco_loopback_pkt(std::vector<uint8_t>& scoPackets,
+ size_t size);
// A simple test implementation of BluetoothHciCallbacks.
class BluetoothHciCallbacks
@@ -569,6 +571,11 @@
ASSERT_TRUE(
sco_queue.tryPopWithTimeout(sco_loopback, kWaitForScoDataTimeout));
+ if (sco_loopback.size() < size) {
+ // The packets may have been split for USB. Reassemble before checking.
+ reassemble_sco_loopback_pkt(sco_loopback, size);
+ }
+
ASSERT_EQ(sco_packet, sco_loopback);
}
logger.setTotalBytes(num_packets * size * 2);
@@ -703,6 +710,22 @@
wait_for_command_complete_event(view.GetOpCode(), cmd_complete));
}
+// Handle the loopback packet.
+void BluetoothAidlTest::reassemble_sco_loopback_pkt(std::vector<uint8_t>& scoPackets,
+ size_t size) {
+ std::vector<uint8_t> sco_packet_whole;
+ sco_packet_whole.assign(scoPackets.begin(), scoPackets.end());
+ while (size + 3 > sco_packet_whole.size()) {
+ std::vector<uint8_t> sco_packets;
+ ASSERT_TRUE(
+ sco_queue.tryPopWithTimeout(sco_packets, kWaitForScoDataTimeout));
+ sco_packet_whole.insert(sco_packet_whole.end(), sco_packets.begin() + 3,
+ sco_packets.end());
+ }
+ scoPackets.assign(sco_packet_whole.begin(), sco_packet_whole.end());
+ scoPackets[2] = size;
+}
+
// Empty test: Initialize()/Close() are called in SetUp()/TearDown().
TEST_P(BluetoothAidlTest, InitializeAndClose) {}
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index c0c475a..4d097c1 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -421,20 +421,26 @@
return ScopedAStatus::ok();
}
-ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, [[maybe_unused]] bool* returnIsSet) {
+ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) {
LOG(DEBUG) << __func__ << ": flag = " << toString(flag);
- LOG(INFO) << __func__ << ": getting ConfigFlag is not supported";
- return ScopedAStatus::fromServiceSpecificErrorWithMessage(
- resultToInt(Result::NOT_SUPPORTED), "getting ConfigFlag is not supported");
+ int flagBit = static_cast<int>(flag);
+ lock_guard<mutex> lk(mMutex);
+ *returnIsSet = ((mConfigFlagValues >> flagBit) & 1) == 1;
+ return ScopedAStatus::ok();
}
ScopedAStatus BroadcastRadio::setConfigFlag(ConfigFlag flag, bool value) {
LOG(DEBUG) << __func__ << ": flag = " << toString(flag) << ", value = " << value;
- LOG(INFO) << __func__ << ": setting ConfigFlag is not supported";
- return ScopedAStatus::fromServiceSpecificErrorWithMessage(
- resultToInt(Result::NOT_SUPPORTED), "setting ConfigFlag is not supported");
+ int flagBitMask = 1 << (static_cast<int>(flag));
+ lock_guard<mutex> lk(mMutex);
+ if (value) {
+ mConfigFlagValues |= flagBitMask;
+ } else {
+ mConfigFlagValues &= ~flagBitMask;
+ }
+ return ScopedAStatus::ok();
}
ScopedAStatus BroadcastRadio::setParameters(
diff --git a/broadcastradio/aidl/default/BroadcastRadio.h b/broadcastradio/aidl/default/BroadcastRadio.h
index 1c85ddc..092776f 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.h
+++ b/broadcastradio/aidl/default/BroadcastRadio.h
@@ -75,6 +75,9 @@
ProgramSelector mCurrentProgram GUARDED_BY(mMutex) = {};
std::shared_ptr<ITunerCallback> mCallback GUARDED_BY(mMutex);
+ // Bitmap for all ConfigFlag values
+ int mConfigFlagValues GUARDED_BY(mMutex) = 0;
+
std::optional<AmFmBandRange> getAmFmRangeLocked() const;
void cancelLocked();
ProgramInfo tuneInternalLocked(const ProgramSelector& sel);
diff --git a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
index 8bee1b2..790d60b 100644
--- a/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
+++ b/broadcastradio/aidl/vts/src/VtsHalBroadcastradioAidlTargetTest.cpp
@@ -997,13 +997,12 @@
LOG(DEBUG) << "SetConfigFlags Test";
auto get = [&](ConfigFlag flag) -> bool {
- bool* gotValue = nullptr;
+ bool gotValue;
- auto halResult = mModule->isConfigFlagSet(flag, gotValue);
+ auto halResult = mModule->isConfigFlagSet(flag, &gotValue);
- EXPECT_FALSE(gotValue == nullptr);
EXPECT_TRUE(halResult.isOk());
- return *gotValue;
+ return gotValue;
};
auto notSupportedError = resultToInt(Result::NOT_SUPPORTED);
diff --git a/camera/device/default/Android.bp b/camera/device/default/Android.bp
index b577597..b9f10d6 100644
--- a/camera/device/default/Android.bp
+++ b/camera/device/default/Android.bp
@@ -25,7 +25,10 @@
cc_library_shared {
name: "camera.device-external-impl",
- defaults: ["hidl_defaults"],
+ defaults: [
+ "android.hardware.graphics.common-ndk_shared",
+ "hidl_defaults",
+ ],
proprietary: true,
srcs: [
"ExternalCameraDevice.cpp",
@@ -38,7 +41,6 @@
"android.hardware.camera.common-V1-ndk",
"android.hardware.camera.device-V1-ndk",
"android.hardware.graphics.allocator-V1-ndk",
- "android.hardware.graphics.common-V4-ndk",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
diff --git a/cas/aidl/default/Android.bp b/cas/aidl/default/Android.bp
old mode 100755
new mode 100644
index 3c16d57..6ce5681
--- a/cas/aidl/default/Android.bp
+++ b/cas/aidl/default/Android.bp
@@ -68,6 +68,7 @@
defaults: ["cas_service_example_defaults"],
init_rc: ["cas-default-lazy.rc"],
cflags: ["-DLAZY_SERVICE"],
+ overrides: ["android.hardware.cas-service.example"],
}
cc_fuzz {
diff --git a/cas/aidl/default/CasImpl.cpp b/cas/aidl/default/CasImpl.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/CasImpl.h b/cas/aidl/default/CasImpl.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/DescramblerImpl.cpp b/cas/aidl/default/DescramblerImpl.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/DescramblerImpl.h b/cas/aidl/default/DescramblerImpl.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/FactoryLoader.h b/cas/aidl/default/FactoryLoader.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/MediaCasService.cpp b/cas/aidl/default/MediaCasService.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/MediaCasService.h b/cas/aidl/default/MediaCasService.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/SharedLibrary.cpp b/cas/aidl/default/SharedLibrary.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/SharedLibrary.h b/cas/aidl/default/SharedLibrary.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/TypeConvert.cpp b/cas/aidl/default/TypeConvert.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/TypeConvert.h b/cas/aidl/default/TypeConvert.h
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/android.hardware.cas-service.xml b/cas/aidl/default/android.hardware.cas-service.xml
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/cas-default-lazy.rc b/cas/aidl/default/cas-default-lazy.rc
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/cas-default.rc b/cas/aidl/default/cas-default.rc
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/fuzzer.cpp b/cas/aidl/default/fuzzer.cpp
old mode 100755
new mode 100644
diff --git a/cas/aidl/default/service.cpp b/cas/aidl/default/service.cpp
old mode 100755
new mode 100644
diff --git a/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index 204b83b..bb7637a 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -281,9 +281,15 @@
<version>1.0</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index bbf7055..dad1558 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -319,11 +319,21 @@
<version>1.0-1</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
<version>1.0</version>
<interface>
diff --git a/compatibility_matrices/compatibility_matrix.6.xml b/compatibility_matrices/compatibility_matrix.6.xml
index 1b812ed..23f634d 100644
--- a/compatibility_matrices/compatibility_matrix.6.xml
+++ b/compatibility_matrices/compatibility_matrix.6.xml
@@ -368,11 +368,21 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
<version>1.0</version>
<interface>
diff --git a/compatibility_matrices/compatibility_matrix.7.xml b/compatibility_matrices/compatibility_matrix.7.xml
index 4419796..33c3148 100644
--- a/compatibility_matrices/compatibility_matrix.7.xml
+++ b/compatibility_matrices/compatibility_matrix.7.xml
@@ -430,11 +430,21 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
<hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
+ <hal format="hidl" optional="true">
<name>android.hardware.media.omx</name>
<version>1.0</version>
<interface>
diff --git a/compatibility_matrices/compatibility_matrix.8.xml b/compatibility_matrices/compatibility_matrix.8.xml
index 4aa832b..04a4674 100644
--- a/compatibility_matrices/compatibility_matrix.8.xml
+++ b/compatibility_matrices/compatibility_matrix.8.xml
@@ -361,10 +361,20 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
<hal format="aidl" optional="true">
<name>android.hardware.memtrack</name>
<version>1</version>
diff --git a/compatibility_matrices/compatibility_matrix.9.xml b/compatibility_matrices/compatibility_matrix.9.xml
index 559e7c2..5bcb349 100644
--- a/compatibility_matrices/compatibility_matrix.9.xml
+++ b/compatibility_matrices/compatibility_matrix.9.xml
@@ -257,9 +257,6 @@
<!-- Either the native or the HIDL mapper HAL must exist on the device -->
<hal format="hidl" optional="true">
<name>android.hardware.graphics.mapper</name>
- <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
- <version>2.1</version>
- <version>3.0</version>
<version>4.0</version>
<interface>
<name>IMapper</name>
@@ -352,10 +349,20 @@
<version>1.0-2</version>
<interface>
<name>IComponentStore</name>
+ <instance>software</instance>
<regex-instance>default[0-9]*</regex-instance>
<regex-instance>vendor[0-9]*_software</regex-instance>
</interface>
</hal>
+ <hal format="hidl" optional="true">
+ <name>android.hardware.media.c2</name>
+ <version>1.0</version>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ <instance>software</instance>
+ </interface>
+ </hal>
<hal format="aidl" optional="true">
<name>android.hardware.media.c2</name>
<version>1</version>
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
index f3374c3..fc3a8b3 100644
--- a/compatibility_matrices/exclude/fcm_exclude.cpp
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <functional>
#include <string>
#include <vector>
diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp
index 13f2b11..f84775e 100644
--- a/graphics/composer/2.3/vts/functional/Android.bp
+++ b/graphics/composer/2.3/vts/functional/Android.bp
@@ -40,25 +40,14 @@
"libhidlbase",
"libsync",
"android.hardware.common-V2-ndk",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
- "android.hardware.graphics.mapper@4.0",
],
static_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.1-vts",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.2-vts",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.3-vts",
- "android.hardware.graphics.mapper@2.0-vts",
- "android.hardware.graphics.mapper@2.1-vts",
- "android.hardware.graphics.mapper@3.0-vts",
- "android.hardware.graphics.mapper@4.0-vts",
"libaidlcommonsupport",
],
header_libs: [
diff --git a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
index ecfe66c..c072ef0 100644
--- a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
+++ b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
@@ -21,7 +21,6 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
#include <composer-vts/2.1/GraphicsComposerCallback.h>
#include <composer-vts/2.1/TestCommandReader.h>
@@ -29,7 +28,6 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
-#include <mapper-vts/2.0/MapperVts.h>
namespace android {
namespace hardware {
@@ -43,7 +41,6 @@
using common::V1_2::ColorMode;
using common::V1_2::Dataspace;
using common::V1_2::PixelFormat;
-using V2_2::vts::Gralloc;
class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
protected:
@@ -128,8 +125,6 @@
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp());
- ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
-
mWriter = std::make_unique<CommandWriterBase>(1024);
mReader = std::make_unique<V2_1::vts::TestCommandReader>();
}
@@ -143,9 +138,6 @@
std::unique_ptr<CommandWriterBase> mWriter;
std::unique_ptr<V2_1::vts::TestCommandReader> mReader;
-
- private:
- std::unique_ptr<Gralloc> mGralloc;
};
/**
diff --git a/graphics/composer/2.4/vts/functional/Android.bp b/graphics/composer/2.4/vts/functional/Android.bp
index b4ab259..0e53cbe 100644
--- a/graphics/composer/2.4/vts/functional/Android.bp
+++ b/graphics/composer/2.4/vts/functional/Android.bp
@@ -38,16 +38,10 @@
"libbinder_ndk",
"libfmq",
"libsync",
+ "libui",
"android.hardware.common-V2-ndk",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
- "android.hardware.graphics.mapper@4.0",
],
static_libs: [
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.allocator@4.0",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.1-vts",
"android.hardware.graphics.composer@2.2",
@@ -56,10 +50,6 @@
"android.hardware.graphics.composer@2.3-vts",
"android.hardware.graphics.composer@2.4",
"android.hardware.graphics.composer@2.4-vts",
- "android.hardware.graphics.mapper@2.0-vts",
- "android.hardware.graphics.mapper@2.1-vts",
- "android.hardware.graphics.mapper@3.0-vts",
- "android.hardware.graphics.mapper@4.0-vts",
"libaidlcommonsupport",
],
header_libs: [
diff --git a/graphics/composer/2.4/vts/functional/AndroidTest.xml b/graphics/composer/2.4/vts/functional/AndroidTest.xml
index 773db93..7626995 100644
--- a/graphics/composer/2.4/vts/functional/AndroidTest.xml
+++ b/graphics/composer/2.4/vts/functional/AndroidTest.xml
@@ -31,6 +31,6 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="VtsHalGraphicsComposerV2_4TargetTest" />
- <option name="native-test-timeout" value="900000"/>
+ <option name="native-test-timeout" value="1800000"/>
</test>
</configuration>
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 35225d9..956c762 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -22,7 +22,6 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
-#include <android/hardware/graphics/mapper/2.0/IMapper.h>
#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
#include <composer-vts/2.4/ComposerVts.h>
#include <composer-vts/2.4/GraphicsComposerCallback.h>
@@ -30,9 +29,7 @@
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
-#include <mapper-vts/2.0/MapperVts.h>
-#include <mapper-vts/3.0/MapperVts.h>
-#include <mapper-vts/4.0/MapperVts.h>
+#include <ui/GraphicBuffer.h>
#include <utils/Timers.h>
namespace android {
@@ -53,7 +50,6 @@
using V2_1::Layer;
using V2_1::vts::NativeHandleWrapper;
using V2_2::Transform;
-using V2_2::vts::Gralloc;
using ContentType = IComposerClient::ContentType;
using DisplayCapability = IComposerClient::DisplayCapability;
@@ -103,8 +99,6 @@
}
mComposerCallback->setVsyncAllowed(false);
- ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
-
mWriter = std::make_unique<CommandWriterBase>(1024);
mReader = std::make_unique<TestCommandReader>();
}
@@ -157,12 +151,15 @@
void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
- NativeHandleWrapper allocate(int32_t width, int32_t height) {
- return mGralloc->allocate(
- width, height, /*layerCount*/ 1,
- static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
+ sp<GraphicBuffer> allocate(int32_t width, int32_t height) {
+ auto result = sp<GraphicBuffer>::make(
+ width, height, static_cast<int32_t>(PixelFormat::RGBA_8888), /*layerCount*/ 1,
static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN |
BufferUsage::COMPOSER_OVERLAY));
+ if (result->initCheck() != STATUS_OK) {
+ return nullptr;
+ }
+ return result;
}
struct TestParameters {
@@ -256,7 +253,6 @@
std::unique_ptr<CommandWriterBase> mWriter;
std::unique_ptr<TestCommandReader> mReader;
sp<GraphicsComposerCallback> mComposerCallback;
- std::unique_ptr<Gralloc> mGralloc;
};
TEST_P(GraphicsComposerHidlTest, getDisplayCapabilitiesBadDisplay) {
@@ -458,7 +454,7 @@
mWriter->setLayerBlendMode(IComposerClient::BlendMode::NONE);
mWriter->setLayerSurfaceDamage(
std::vector<IComposerClient::Rect>(1, display.getFrameRect()));
- mWriter->setLayerBuffer(0, handle.get(), -1);
+ mWriter->setLayerBuffer(0, handle->handle, -1);
mWriter->setLayerDataspace(Dataspace::UNKNOWN);
mWriter->validateDisplay();
@@ -476,7 +472,7 @@
ASSERT_NE(nullptr, handle.get());
mWriter->selectLayer(layer);
- mWriter->setLayerBuffer(0, handle.get(), -1);
+ mWriter->setLayerBuffer(0, handle->handle, -1);
mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, {0, 0, 10, 10}));
mWriter->validateDisplay();
execute();
@@ -544,10 +540,12 @@
setActiveConfigWithConstraints(display, config2, constraints, &timeline));
EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos >= constraints.desiredTimeNanos);
- // Refresh rate should change within a reasonable time
- constexpr std::chrono::nanoseconds kReasonableTimeForChange = 1s; // 1 second
- EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos - constraints.desiredTimeNanos <=
- kReasonableTimeForChange.count());
+ if (configGroup1 == configGroup2) {
+ // Refresh rate should change within a reasonable time
+ constexpr std::chrono::nanoseconds kReasonableTimeForChange = 1s;
+ EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos - constraints.desiredTimeNanos <=
+ kReasonableTimeForChange.count());
+ }
if (timeline.refreshRequired) {
if (params.refreshMiss) {
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCommand.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCommand.aidl
index 662240e..cce35e7 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCommand.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayCommand.aidl
@@ -45,4 +45,5 @@
boolean acceptDisplayChanges;
boolean presentDisplay;
boolean presentOrValidateDisplay;
+ int frameIntervalNs;
}
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayConfiguration.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayConfiguration.aidl
index 908842a..040afd7 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayConfiguration.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/DisplayConfiguration.aidl
@@ -40,6 +40,7 @@
@nullable android.hardware.graphics.composer3.DisplayConfiguration.Dpi dpi;
int configGroup;
int vsyncPeriod;
+ @nullable android.hardware.graphics.composer3.VrrConfig vrrConfig;
parcelable Dpi {
float x;
float y;
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
index 2f08b6f..a60c93d 100644
--- a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -82,7 +82,8 @@
android.hardware.graphics.common.HdrConversionCapability[] getHdrConversionCapabilities();
android.hardware.graphics.common.Hdr setHdrConversionStrategy(in android.hardware.graphics.common.HdrConversionStrategy conversionStrategy);
void setRefreshRateChangedCallbackDebugEnabled(long display, boolean enabled);
- android.hardware.graphics.composer3.DisplayConfiguration[] getDisplayConfigurations(long display);
+ android.hardware.graphics.composer3.DisplayConfiguration[] getDisplayConfigurations(long display, int maxFrameIntervalNs);
+ oneway void notifyExpectedPresent(long display, in android.hardware.graphics.composer3.ClockMonotonicTimestamp expectedPresentTime, int frameIntervalNs);
const int EX_BAD_CONFIG = 1;
const int EX_BAD_DISPLAY = 2;
const int EX_BAD_LAYER = 3;
diff --git a/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/VrrConfig.aidl b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/VrrConfig.aidl
new file mode 100644
index 0000000..bb2569f
--- /dev/null
+++ b/graphics/composer/aidl/aidl_api/android.hardware.graphics.composer3/current/android/hardware/graphics/composer3/VrrConfig.aidl
@@ -0,0 +1,48 @@
+/**
+ * Copyright 2023, 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.graphics.composer3;
+@VintfStability
+parcelable VrrConfig {
+ int minFrameIntervalNs;
+ @nullable android.hardware.graphics.composer3.VrrConfig.FrameIntervalPowerHint[] frameIntervalPowerHints;
+ @nullable android.hardware.graphics.composer3.VrrConfig.NotifyExpectedPresentConfig notifyExpectedPresentConfig;
+ parcelable FrameIntervalPowerHint {
+ int frameIntervalNs;
+ int averageRefreshPeriodNs;
+ }
+ parcelable NotifyExpectedPresentConfig {
+ int notifyExpectedPresentHeadsUpNs;
+ int notifyExpectedPresentTimeoutNs;
+ }
+}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
index ea54a89..0bd72a3 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.aidl
@@ -39,14 +39,12 @@
/**
* The stage in which dimming operations should be performed when compositing
* the client target.
+ *
* Note that with a COLORIMETRIC RenderIntent, DimmingSpace must be LINEAR. That is, dimming
- * is defined to occur in linear space.
- * However, some composer implementations may, with other vendor-defined RenderIntents,
- * configure their hardware such as image quality adjustments is intended to occur after
- * composition. In this scenario, if the dimming operation were applied in linear space,
- * then the resulting dimming operation may comepl those image quality adjustments to
- * incorrectly alter the gamma curve. To avoid this issue, those implementations must opt to
- * dim in gamma space.
+ * is defined to occur in linear space. However, some composer implementations may, with
+ * other vendor-defined RenderIntents, apply certain image quality adjustments that are
+ * sensitive to gamma shift when dimming in linear space. To avoid this issue, those
+ * implementations must opt to dim in gamma space.
*/
DimmingStage dimmingStage;
}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
index 4f69aee..02c1389 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayCommand.aidl
@@ -174,4 +174,15 @@
* or perform a VALIDATE_DISPLAY action instead.
*/
boolean presentOrValidateDisplay;
+
+ /**
+ * If a value greater than 0 is set, it provides a hint about the next frame(s)
+ * cadence. This parameter represents the time in nanoseconds of when to expect the
+ * next frames to arrive. For example. frameIntervalNs=33333333 indicates that the
+ * cadence of the next frames is 30Hz.
+ *
+ * The implementation should take the necessary steps to present the next frames as
+ * close as possible to the cadence.
+ */
+ int frameIntervalNs;
}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayConfiguration.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayConfiguration.aidl
index b0095d2..791078d 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayConfiguration.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/DisplayConfiguration.aidl
@@ -15,6 +15,7 @@
*/
package android.hardware.graphics.composer3;
+import android.hardware.graphics.composer3.VrrConfig;
@VintfStability
parcelable DisplayConfiguration {
@@ -60,4 +61,10 @@
* must be signaled on a vsync boundary.
*/
int vsyncPeriod;
+
+ /**
+ * Represents the specific configurations for VRR (Variable Refresh Rate) display modes.
+ * Non-VRR modes should set this to null.
+ */
+ @nullable VrrConfig vrrConfig;
}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
index 5d04a28..c57f94e 100644
--- a/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/IComposerClient.aidl
@@ -22,6 +22,7 @@
import android.hardware.graphics.common.HdrConversionStrategy;
import android.hardware.graphics.common.Transform;
import android.hardware.graphics.composer3.ClientTargetProperty;
+import android.hardware.graphics.composer3.ClockMonotonicTimestamp;
import android.hardware.graphics.composer3.ColorMode;
import android.hardware.graphics.composer3.CommandResultPayload;
import android.hardware.graphics.composer3.ContentType;
@@ -869,8 +870,38 @@
* getDisplayConfigs should return at least one config.
*
* @param display is the display for which the configurations are requested.
+ * @param maxFrameIntervalNs refers to the largest frameInterval to be set for
+ * VrrConfig.frameIntervalPowerHints in nanoseconds
*
* @see getDisplayConfigs
*/
- DisplayConfiguration[] getDisplayConfigurations(long display);
+ DisplayConfiguration[] getDisplayConfigurations(long display, int maxFrameIntervalNs);
+
+ /**
+ * Provides an early hint for a frame that is likely to be presented.
+ * This is used for the implementation to take the necessary steps to ensure that
+ * the next frame(s) could be presented as close as possible to the expectedPresentTime and
+ * according to the frameIntervalNs cadence.
+ * See DisplayCommand.expectedPresentTime and DisplayCommand.frameIntervalNs.
+ *
+ * The framework will call this function based on the parameters specified in
+ * DisplayConfiguration.VrrConfig:
+ * - notifyExpectedPresentTimeoutNs specifies the idle time from the previous present command
+ * where the framework must send the early hint for the next frame.
+ * - notifyExpectedPresentHeadsUpNs specifies minimal time that framework must send
+ * the early hint before the next frame.
+ *
+ * The framework can omit calling this API when the next present command matches
+ * the cadence of the previous present command frameIntervalNs.
+ *
+ * If DisplayConfiguration.notifyExpectedPresentConfig is null, this function will never be
+ * called.
+ *
+ * @param display is the display for which the notifyExpectedPresent is called.
+ * @param expectedPresentTime is the expectedPresentTime that will be provided in the next
+ * present command
+ * @param frameIntervalNs is a hint about the cadence of the next frames in nanoseconds.
+ */
+ oneway void notifyExpectedPresent(
+ long display, in ClockMonotonicTimestamp expectedPresentTime, int frameIntervalNs);
}
diff --git a/graphics/composer/aidl/android/hardware/graphics/composer3/VrrConfig.aidl b/graphics/composer/aidl/android/hardware/graphics/composer3/VrrConfig.aidl
new file mode 100644
index 0000000..3b241ba
--- /dev/null
+++ b/graphics/composer/aidl/android/hardware/graphics/composer3/VrrConfig.aidl
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2023, 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.
+ */
+
+package android.hardware.graphics.composer3;
+
+@VintfStability
+parcelable VrrConfig {
+ /**
+ * The minimal time (in nanoseconds) that needs to pass between the previously presented frame
+ * and when the next frame can be presented.
+ */
+ int minFrameIntervalNs;
+
+ /**
+ * An optional mapping between frame intervals, and the physical display refresh period on
+ * average. This provides useful information to the framework when picking a specific frame rate
+ * (which is a divisor of the vsync rate) about the real display refresh rate, which could be
+ * used for power optimizations. The implementation should populate this map for frame rates
+ * that requires the display to run at a higher refresh rate due to self refresh frames. The
+ * lowest frame rate provided should be according to the parameter `maxFrameIntervalNs`
+ * specified in IComposerClient.getDisplayConfigurations, as the framework would generally not
+ * try to run at a lower frame rate.
+ */
+ parcelable FrameIntervalPowerHint {
+ int frameIntervalNs;
+ int averageRefreshPeriodNs;
+ }
+ @nullable FrameIntervalPowerHint[] frameIntervalPowerHints;
+
+ parcelable NotifyExpectedPresentConfig {
+ /**
+ * The minimal time in nanoseconds that IComposerClient.notifyExpectedPresent needs to be
+ * called ahead of an expectedPresentTime provided on a presentDisplay command.
+ */
+ int notifyExpectedPresentHeadsUpNs;
+
+ /**
+ * The time in nanoseconds that represents a timeout from the previous presentDisplay, which
+ * after this point the display needs a call to IComposerClient.notifyExpectedPresent before
+ * sending the next frame. If set to 0, there is no need to call
+ * IComposerClient.notifyExpectedPresent for timeout.
+ */
+ int notifyExpectedPresentTimeoutNs;
+ }
+
+ /**
+ * Parameters for when to call IComposerClient.notifyExpectedPresent.
+ *
+ * When set to null, the framework will not call IComposerClient.notifyExpectedPresent.
+ */
+ @nullable NotifyExpectedPresentConfig notifyExpectedPresentConfig;
+}
diff --git a/graphics/composer/aidl/vts/Android.bp b/graphics/composer/aidl/vts/Android.bp
index 88b5de4..e60e1a7 100644
--- a/graphics/composer/aidl/vts/Android.bp
+++ b/graphics/composer/aidl/vts/Android.bp
@@ -55,13 +55,6 @@
"libhidlbase",
"libprocessgroup",
"libtinyxml2",
- "android.hardware.graphics.mapper@2.0",
- "android.hardware.graphics.mapper@2.1",
- "android.hardware.graphics.mapper@3.0",
- "android.hardware.graphics.mapper@4.0",
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.allocator@4.0",
"libvndksupport",
],
header_libs: [
@@ -71,13 +64,6 @@
"android.hardware.graphics.common@1.2",
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.allocator@3.0",
- "android.hardware.graphics.allocator@4.0",
- "android.hardware.graphics.mapper@2.0-vts",
- "android.hardware.graphics.mapper@2.1-vts",
- "android.hardware.graphics.mapper@3.0-vts",
- "android.hardware.graphics.mapper@4.0-vts",
"libaidlcommonsupport",
"libarect",
"libbase",
diff --git a/graphics/composer/aidl/vts/ReadbackVts.h b/graphics/composer/aidl/vts/ReadbackVts.h
index ee9f0d5..d5602c1 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.h
+++ b/graphics/composer/aidl/vts/ReadbackVts.h
@@ -20,7 +20,6 @@
#include <android-base/unique_fd.h>
#include <android/hardware/graphics/composer3/ComposerClientReader.h>
#include <android/hardware/graphics/composer3/ComposerClientWriter.h>
-#include <mapper-vts/2.1/MapperVts.h>
#include <renderengine/RenderEngine.h>
#include <ui/GraphicBuffer.h>
#include <memory>
@@ -32,7 +31,6 @@
using ::android::renderengine::LayerSettings;
using common::Dataspace;
using common::PixelFormat;
-using IMapper2_1 = ::android::hardware::graphics::mapper::V2_1::IMapper;
static const Color BLACK = {0.0f, 0.0f, 0.0f, 1.0f};
static const Color RED = {1.0f, 0.0f, 0.0f, 1.0f};
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.cpp b/graphics/composer/aidl/vts/RenderEngineVts.cpp
index 66779c8..19e8a9b 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.cpp
+++ b/graphics/composer/aidl/vts/RenderEngineVts.cpp
@@ -19,7 +19,6 @@
namespace aidl::android::hardware::graphics::composer3::vts {
-using ::android::hardware::graphics::mapper::V2_1::IMapper;
using ::android::renderengine::DisplaySettings;
using ::android::renderengine::LayerSettings;
using ::android::renderengine::RenderEngineCreationArgs;
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.h b/graphics/composer/aidl/vts/RenderEngineVts.h
index 43d3a42..69f7ab4 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.h
+++ b/graphics/composer/aidl/vts/RenderEngineVts.h
@@ -15,7 +15,6 @@
*/
#pragma once
-#include <mapper-vts/2.1/MapperVts.h>
#include <math/half.h>
#include <math/vec3.h>
#include <renderengine/ExternalTexture.h>
@@ -29,7 +28,6 @@
namespace aidl::android::hardware::graphics::composer3::vts {
-using ::android::hardware::graphics::mapper::V2_1::IMapper;
using ::android::renderengine::DisplaySettings;
using ::android::renderengine::ExternalTexture;
using ::android::renderengine::RenderEngineCreationArgs;
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.cpp b/graphics/composer/aidl/vts/VtsComposerClient.cpp
index bf42d88..11b995e 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.cpp
+++ b/graphics/composer/aidl/vts/VtsComposerClient.cpp
@@ -312,7 +312,14 @@
std::pair<ScopedAStatus, std::vector<DisplayConfiguration>>
VtsComposerClient::getDisplayConfigurations(int64_t display) {
std::vector<DisplayConfiguration> outConfigs;
- return {mComposerClient->getDisplayConfigurations(display, &outConfigs), outConfigs};
+ return {mComposerClient->getDisplayConfigurations(display, kMaxFrameIntervalNs, &outConfigs),
+ outConfigs};
+}
+
+ScopedAStatus VtsComposerClient::notifyExpectedPresent(int64_t display,
+ ClockMonotonicTimestamp expectedPresentTime,
+ int frameIntervalNs) {
+ return mComposerClient->notifyExpectedPresent(display, expectedPresentTime, frameIntervalNs);
}
std::pair<ScopedAStatus, int32_t> VtsComposerClient::getDisplayVsyncPeriod(int64_t display) {
diff --git a/graphics/composer/aidl/vts/VtsComposerClient.h b/graphics/composer/aidl/vts/VtsComposerClient.h
index 1add23c..20dc733 100644
--- a/graphics/composer/aidl/vts/VtsComposerClient.h
+++ b/graphics/composer/aidl/vts/VtsComposerClient.h
@@ -145,6 +145,10 @@
std::pair<ScopedAStatus, std::vector<DisplayConfiguration>> getDisplayConfigurations(
int64_t display);
+ ScopedAStatus notifyExpectedPresent(int64_t display,
+ ClockMonotonicTimestamp expectedPresentTime,
+ int frameIntervalNs);
+
std::pair<ScopedAStatus, int32_t> getDisplayVsyncPeriod(int64_t display);
ScopedAStatus setAutoLowLatencyMode(int64_t display, bool isEnabled);
@@ -192,6 +196,8 @@
std::vector<RefreshRateChangedDebugData> takeListOfRefreshRateChangedDebugData();
+ static constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20fps
+
private:
void addDisplayConfigs(VtsDisplay*, const std::vector<DisplayConfiguration>&);
ScopedAStatus addDisplayConfigLegacy(VtsDisplay*, int32_t config);
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
index b047220..269abd1 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
@@ -129,33 +129,20 @@
return {false, graphicBuffer};
}
- uint64_t getStableDisplayId(int64_t display) {
- const auto& [status, identification] =
- mComposerClient->getDisplayIdentificationData(display);
- EXPECT_TRUE(status.isOk());
-
- if (const auto info = ::android::parseDisplayIdentificationData(
- static_cast<uint8_t>(identification.port), identification.data)) {
- return info->id.value;
- }
-
- return ::android::PhysicalDisplayId::fromPort(static_cast<uint8_t>(identification.port))
- .value;
- }
-
// Gets the per-display XML config
std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXml(int64_t display) {
- std::stringstream pathBuilder;
- pathBuilder << "/vendor/etc/displayconfig/display_id_" << getStableDisplayId(display)
- << ".xml";
- const std::string path = pathBuilder.str();
- auto document = std::make_unique<tinyxml2::XMLDocument>();
- const tinyxml2::XMLError error = document->LoadFile(path.c_str());
- if (error == tinyxml2::XML_SUCCESS) {
+
+ if (auto document = getDisplayConfigXmlByStableId(getStableDisplayId(display));
+ document != nullptr) {
return document;
- } else {
- return nullptr;
}
+
+ // Fallback to looking up a per-port config if no config exists for the full ID
+ if (auto document = getDisplayConfigXmlByPort(getPort(display)); document != nullptr) {
+ return document;
+ }
+
+ return nullptr;
}
// Gets the max display brightness for this display.
@@ -256,6 +243,53 @@
}
}
}
+
+ uint8_t getPort(int64_t display) {
+ const auto& [status, identification] =
+ mComposerClient->getDisplayIdentificationData(display);
+ EXPECT_TRUE(status.isOk());
+ return static_cast<uint8_t>(identification.port);
+ }
+
+ uint64_t getStableDisplayId(int64_t display) {
+ const auto& [status, identification] =
+ mComposerClient->getDisplayIdentificationData(display);
+ EXPECT_TRUE(status.isOk());
+
+ if (const auto info = ::android::parseDisplayIdentificationData(
+ static_cast<uint8_t>(identification.port), identification.data)) {
+ return info->id.value;
+ }
+
+ return ::android::PhysicalDisplayId::fromPort(static_cast<uint8_t>(identification.port))
+ .value;
+ }
+
+ std::unique_ptr<tinyxml2::XMLDocument> loadXml(const std::string& path) {
+ auto document = std::make_unique<tinyxml2::XMLDocument>();
+ const tinyxml2::XMLError error = document->LoadFile(path.c_str());
+ if (error != tinyxml2::XML_SUCCESS) {
+ ALOGD("%s: Failed to load config file: %s", __func__, path.c_str());
+ return nullptr;
+ }
+
+ ALOGD("%s: Successfully loaded config file: %s", __func__, path.c_str());
+ return document;
+ }
+
+ std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXmlByPort(uint8_t port) {
+ std::stringstream pathBuilder;
+ pathBuilder << "/vendor/etc/displayconfig/display_port_" << static_cast<uint32_t>(port)
+ << ".xml";
+ return loadXml(pathBuilder.str());
+ }
+
+ std::unique_ptr<tinyxml2::XMLDocument> getDisplayConfigXmlByStableId(uint64_t stableId) {
+ std::stringstream pathBuilder;
+ pathBuilder << "/vendor/etc/displayconfig/display_id_" << stableId
+ << ".xml";
+ return loadXml(pathBuilder.str());
+ }
};
class GraphicsCompositionTest : public GraphicsCompositionTestBase,
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
index d559213..90944d5 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_TargetTest.cpp
@@ -1234,6 +1234,43 @@
EXPECT_NE(-1, displayConfig.dpi->x);
EXPECT_NE(-1, displayConfig.dpi->y);
}
+ if (displayConfig.vrrConfig) {
+ const auto& vrrConfig = *displayConfig.vrrConfig;
+ EXPECT_GE(vrrConfig.minFrameIntervalNs, displayConfig.vsyncPeriod);
+
+ const auto verifyFrameIntervalIsDivisorOfVsync = [&](int32_t frameIntervalNs) {
+ constexpr auto kThreshold = 0.05f; // 5%
+ const auto ratio =
+ static_cast<float>(frameIntervalNs) / displayConfig.vsyncPeriod;
+ return ratio - std::round(ratio) <= kThreshold;
+ };
+
+ EXPECT_TRUE(verifyFrameIntervalIsDivisorOfVsync(vrrConfig.minFrameIntervalNs));
+
+ if (vrrConfig.frameIntervalPowerHints) {
+ const auto& frameIntervalPowerHints = *vrrConfig.frameIntervalPowerHints;
+ EXPECT_FALSE(frameIntervalPowerHints.empty());
+
+ const auto minFrameInterval = *min_element(frameIntervalPowerHints.cbegin(),
+ frameIntervalPowerHints.cend());
+ EXPECT_LE(minFrameInterval->frameIntervalNs,
+ VtsComposerClient::kMaxFrameIntervalNs);
+
+ EXPECT_TRUE(std::all_of(frameIntervalPowerHints.cbegin(),
+ frameIntervalPowerHints.cend(),
+ [&](const auto& frameIntervalPowerHint) {
+ return verifyFrameIntervalIsDivisorOfVsync(
+ frameIntervalPowerHint->frameIntervalNs);
+ }));
+ }
+
+ if (vrrConfig.notifyExpectedPresentConfig) {
+ const auto& notifyExpectedPresentConfig =
+ *vrrConfig.notifyExpectedPresentConfig;
+ EXPECT_GT(0, notifyExpectedPresentConfig.notifyExpectedPresentHeadsUpNs);
+ EXPECT_GE(0, notifyExpectedPresentConfig.notifyExpectedPresentTimeoutNs);
+ }
+ }
}
}
}
@@ -1312,6 +1349,17 @@
}
}
+// TODO(b/291792736) Add detailed VTS test cases for NotifyExpectedPresent
+TEST_P(GraphicsComposerAidlV3Test, NotifyExpectedPresent) {
+ for (const auto& display : mDisplays) {
+ EXPECT_TRUE(mComposerClient
+ ->notifyExpectedPresent(display.getDisplayId(),
+ ClockMonotonicTimestamp{0},
+ std::chrono::nanoseconds{8ms}.count())
+ .isOk());
+ }
+}
+
// Tests for Command.
class GraphicsComposerAidlCommandTest : public GraphicsComposerAidlTest {
protected:
diff --git a/health/utils/libhealthloop/include/health/HealthLoop.h b/health/utils/libhealthloop/include/health/HealthLoop.h
index 54b2740..fc3066e 100644
--- a/health/utils/libhealthloop/include/health/HealthLoop.h
+++ b/health/utils/libhealthloop/include/health/HealthLoop.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <functional>
#include <memory>
#include <mutex>
#include <vector>
diff --git a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataCallFailCause.aidl b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataCallFailCause.aidl
index 362be3a..009b428 100644
--- a/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataCallFailCause.aidl
+++ b/radio/aidl/aidl_api/android.hardware.radio.data/current/android/hardware/radio/data/DataCallFailCause.aidl
@@ -379,5 +379,4 @@
SLICE_REJECTED = 0x8CC,
MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD,
ALL_MATCHING_RULES_FAILED = 0x8CE,
- SATELLITE_ENABLED = 0x8CF,
}
diff --git a/radio/aidl/android/hardware/radio/data/DataCallFailCause.aidl b/radio/aidl/android/hardware/radio/data/DataCallFailCause.aidl
index ef68c8c..e015e8e 100644
--- a/radio/aidl/android/hardware/radio/data/DataCallFailCause.aidl
+++ b/radio/aidl/android/hardware/radio/data/DataCallFailCause.aidl
@@ -1306,8 +1306,4 @@
* If connection failed for all matching URSP rules.
*/
ALL_MATCHING_RULES_FAILED = 0x8CE,
- /**
- * Data call is not allowed as device is connected to satellite.
- */
- SATELLITE_ENABLED = 0x8CF,
}
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
index 0499079..8a8eaa4 100644
--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -89,6 +89,29 @@
return imei;
}
+// Use `ro.product.<property>_for_attestation` property for attestation if it is present else
+// fallback to use `ro.product.vendor.<property>` if it is present else fallback to
+// `ro.product.<property>`. Similar logic can be seen in Java method `getVendorDeviceIdProperty`
+// in frameworks/base/core/java/android/os/Build.java.
+template <Tag tag>
+void add_attestation_id(AuthorizationSetBuilder* attestation_id_tags,
+ TypedTag<TagType::BYTES, tag> tag_type, const char* prop) {
+ ::android::String8 prop_name =
+ ::android::String8::format("ro.product.%s_for_attestation", prop);
+ std::string prop_value = ::android::base::GetProperty(prop_name.string(), /* default= */ "");
+ if (!prop_value.empty()) {
+ add_tag_from_prop(attestation_id_tags, tag_type, prop_name.string());
+ } else {
+ prop_name = ::android::String8::format("ro.product.vendor.%s", prop);
+ prop_value = ::android::base::GetProperty(prop_name.string(), /* default= */ "");
+ if (!prop_value.empty()) {
+ add_tag_from_prop(attestation_id_tags, tag_type, prop_name.string());
+ } else {
+ prop_name = ::android::String8::format("ro.product.%s", prop);
+ add_tag_from_prop(attestation_id_tags, tag_type, prop_name.string());
+ }
+ }
+}
} // namespace
class AttestKeyTest : public KeyMintAidlTestBase {
@@ -798,11 +821,6 @@
}
TEST_P(AttestKeyTest, EcdsaAttestationID) {
- if (is_gsi_image()) {
- // GSI sets up a standard set of device identifiers that may not match
- // the device identifiers held by the device.
- GTEST_SKIP() << "Test not applicable under GSI";
- }
// Create attestation key.
AttestationKey attest_key;
vector<KeyCharacteristics> attest_key_characteristics;
@@ -822,39 +840,12 @@
// Collection of valid attestation ID tags.
auto attestation_id_tags = AuthorizationSetBuilder();
- // Use ro.product.brand_for_attestation property for attestation if it is present else fallback
- // to ro.product.brand
- std::string prop_value =
- ::android::base::GetProperty("ro.product.brand_for_attestation", /* default= */ "");
- if (!prop_value.empty()) {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND,
- "ro.product.brand_for_attestation");
- } else {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
- }
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
- // Use ro.product.name_for_attestation property for attestation if it is present else fallback
- // to ro.product.name
- prop_value = ::android::base::GetProperty("ro.product.name_for_attestation", /* default= */ "");
- if (!prop_value.empty()) {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT,
- "ro.product.name_for_attestation");
- } else {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
- }
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "brand");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "device");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "name");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER, "manufacturer");
+ add_attestation_id(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "model");
add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
- "ro.product.manufacturer");
- // Use ro.product.model_for_attestation property for attestation if it is present else fallback
- // to ro.product.model
- prop_value =
- ::android::base::GetProperty("ro.product.model_for_attestation", /* default= */ "");
- if (!prop_value.empty()) {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL,
- "ro.product.model_for_attestation");
- } else {
- add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
- }
string imei = get_imei(0);
if (!imei.empty()) {
@@ -955,12 +946,6 @@
}
TEST_P(AttestKeyTest, SecondIMEIAttestationIDSuccess) {
- if (is_gsi_image()) {
- // GSI sets up a standard set of device identifiers that may not match
- // the device identifiers held by the device.
- GTEST_SKIP() << "Test not applicable under GSI";
- }
-
// Skip the test if there is no second IMEI exists.
string second_imei = get_imei(1);
if (second_imei.empty()) {
@@ -1029,12 +1014,6 @@
}
TEST_P(AttestKeyTest, MultipleIMEIAttestationIDSuccess) {
- if (is_gsi_image()) {
- // GSI sets up a standard set of device identifiers that may not match
- // the device identifiers held by the device.
- GTEST_SKIP() << "Test not applicable under GSI";
- }
-
// Skip the test if there is no first IMEI exists.
string imei = get_imei(0);
if (imei.empty()) {
diff --git a/security/rkp/README.md b/security/rkp/README.md
index f8e1d5e..8cd1582 100644
--- a/security/rkp/README.md
+++ b/security/rkp/README.md
@@ -52,7 +52,7 @@
* Degenerate DICE (Phase 1): A TEE root of trust key pair is used to sign
certificate requests; a single self-signed certificate signifies this phase.
* DICE (Phase 2): A hardware root of trust key pair is only accessible to ROM
- code; the boot process follows the [Android Profile for
+ or ROM extension code; the boot process follows the [Android Profile for
DICE](#android-profile-for-dice).
* SoC vendor certified DICE (Phase 3): This is identical to Phase 2, except the
SoC vendor also does the UDS\_pub extraction or certification in their
diff --git a/weaver/1.0/vts/functional/Android.bp b/weaver/1.0/vts/functional/Android.bp
deleted file mode 100644
index cc1d284..0000000
--- a/weaver/1.0/vts/functional/Android.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// Copyright (C) 2017 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.
-//
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "hardware_interfaces_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["hardware_interfaces_license"],
-}
-
-cc_test {
- name: "VtsHalWeaverV1_0TargetTest",
- defaults: ["VtsHalTargetTestDefaults"],
- srcs: ["VtsHalWeaverV1_0TargetTest.cpp"],
- static_libs: ["android.hardware.weaver@1.0"],
- test_suites: ["general-tests", "vts"],
-}
diff --git a/weaver/1.0/vts/functional/OWNERS b/weaver/1.0/vts/functional/OWNERS
deleted file mode 100644
index ec8c304..0000000
--- a/weaver/1.0/vts/functional/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 186411
-chengyouho@google.com
-frankwoo@google.com
diff --git a/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp b/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
deleted file mode 100644
index 66465a9..0000000
--- a/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (C) 2017 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 <android/hardware/weaver/1.0/IWeaver.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-
-#include <limits>
-
-using ::android::hardware::weaver::V1_0::IWeaver;
-using ::android::hardware::weaver::V1_0::WeaverConfig;
-using ::android::hardware::weaver::V1_0::WeaverReadStatus;
-using ::android::hardware::weaver::V1_0::WeaverReadResponse;
-using ::android::hardware::weaver::V1_0::WeaverStatus;
-using ::android::hardware::Return;
-using ::android::sp;
-
-const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
-const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
-const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
-
-struct WeaverHidlTest : public ::testing::TestWithParam<std::string> {
- virtual void SetUp() override {
- weaver = IWeaver::getService(GetParam());
- ASSERT_NE(weaver, nullptr);
- }
-
- virtual void TearDown() override {}
-
- sp<IWeaver> weaver;
-};
-
-/*
- * Checks config values are suitably large
- */
-TEST_P(WeaverHidlTest, GetConfig) {
- WeaverStatus status;
- WeaverConfig config;
-
- bool callbackCalled = false;
- auto ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- callbackCalled = true;
- status = s;
- config = c;
- });
- ASSERT_TRUE(ret.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverStatus::OK);
-
- EXPECT_GE(config.slots, 16u);
- EXPECT_GE(config.keySize, 16u);
- EXPECT_GE(config.valueSize, 16u);
-}
-
-/*
- * Gets the config twice and checks they are the same
- */
-TEST_P(WeaverHidlTest, GettingConfigMultipleTimesGivesSameResult) {
- WeaverConfig config1;
- WeaverConfig config2;
-
- WeaverStatus status;
- bool callbackCalled = false;
- auto ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- callbackCalled = true;
- status = s;
- config1 = c;
- });
- ASSERT_TRUE(ret.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverStatus::OK);
-
- callbackCalled = false;
- ret = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- callbackCalled = true;
- status = s;
- config2 = c;
- });
- ASSERT_TRUE(ret.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverStatus::OK);
-
- EXPECT_EQ(config1, config2);
-}
-
-/*
- * Gets the number of slots from the config and writes a key and value to the last one
- */
-TEST_P(WeaverHidlTest, WriteToLastSlot) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- const uint32_t lastSlot = config.slots - 1;
- const auto writeRet = weaver->write(lastSlot, KEY, VALUE);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::OK);
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with the same key and receives the value that was previously written
- */
-TEST_P(WeaverHidlTest, WriteFollowedByReadGivesTheSameValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(ret, WeaverStatus::OK);
-
- bool callbackCalled = false;
- WeaverReadStatus status;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet = weaver->read(slotId, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- status = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(readRet.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverReadStatus::OK);
- EXPECT_EQ(readValue, VALUE);
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writes a key and value to a slot
- * Overwrites the slot with a new key and value
- * Reads the slot with the new key and receives the new value
- */
-TEST_P(WeaverHidlTest, OverwritingSlotUpdatesTheValue) {
- constexpr uint32_t slotId = 0;
- const auto initialWriteRet = weaver->write(slotId, WRONG_KEY, VALUE);
- ASSERT_TRUE(initialWriteRet.isOk());
- ASSERT_EQ(initialWriteRet, WeaverStatus::OK);
-
- const auto overwriteRet = weaver->write(slotId, KEY, OTHER_VALUE);
- ASSERT_TRUE(overwriteRet.isOk());
- ASSERT_EQ(overwriteRet, WeaverStatus::OK);
-
- bool callbackCalled = false;
- WeaverReadStatus status;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet = weaver->read(slotId, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- status = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(readRet.isOk());
- ASSERT_TRUE(callbackCalled);
- ASSERT_EQ(status, WeaverReadStatus::OK);
- EXPECT_EQ(readValue, OTHER_VALUE);
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with a different key so does not receive the value
- */
-TEST_P(WeaverHidlTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
- ASSERT_EQ(ret, WeaverStatus::OK);
-
- bool callbackCalled = false;
- WeaverReadStatus status;
- std::vector<uint8_t> readValue;
- const auto readRet =
- weaver->read(slotId, WRONG_KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- status = s;
- readValue = r.value;
- });
- ASSERT_TRUE(callbackCalled);
- ASSERT_TRUE(readRet.isOk());
- ASSERT_EQ(status, WeaverReadStatus::INCORRECT_KEY);
- EXPECT_TRUE(readValue.empty());
-}
-
-/*
- * Writing to an invalid slot fails
- */
-TEST_P(WeaverHidlTest, WritingToInvalidSlotFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- const auto writeRet = weaver->write(config.slots, KEY, VALUE);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::FAILED);
-}
-
-/*
- * Reading from an invalid slot fails rather than incorrect key
- */
-TEST_P(WeaverHidlTest, ReadingFromInvalidSlotFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- bool callbackCalled = false;
- WeaverReadStatus readStatus;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet =
- weaver->read(config.slots, KEY, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- readStatus = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(callbackCalled);
- ASSERT_TRUE(readRet.isOk());
- ASSERT_EQ(readStatus, WeaverReadStatus::FAILED);
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
-}
-
-/*
- * Writing a key that is too large fails
- */
-TEST_P(WeaverHidlTest, WriteWithTooLargeKeyFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, bigKey, VALUE);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::FAILED);
-}
-
-/*
- * Writing a value that is too large fails
- */
-TEST_P(WeaverHidlTest, WriteWithTooLargeValueFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- std::vector<uint8_t> bigValue(config.valueSize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, KEY, bigValue);
- ASSERT_TRUE(writeRet.isOk());
- ASSERT_EQ(writeRet, WeaverStatus::FAILED);
-}
-
-/*
- * Reading with a key that is loo large fails
- */
-TEST_P(WeaverHidlTest, ReadWithTooLargeKeyFails) {
- WeaverStatus status;
- WeaverConfig config;
- const auto configRet = weaver->getConfig([&](WeaverStatus s, WeaverConfig c) {
- status = s;
- config = c;
- });
- ASSERT_TRUE(configRet.isOk());
- ASSERT_EQ(status, WeaverStatus::OK);
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- bool callbackCalled = false;
- WeaverReadStatus readStatus;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- const auto readRet =
- weaver->read(slotId, bigKey, [&](WeaverReadStatus s, WeaverReadResponse r) {
- callbackCalled = true;
- readStatus = s;
- readValue = r.value;
- timeout = r.timeout;
- });
- ASSERT_TRUE(callbackCalled);
- ASSERT_TRUE(readRet.isOk());
- ASSERT_EQ(readStatus, WeaverReadStatus::FAILED);
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
-}
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverHidlTest);
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, WeaverHidlTest,
- testing::ValuesIn(android::hardware::getAllHalInstanceNames(IWeaver::descriptor)),
- android::hardware::PrintInstanceNameToString);
diff --git a/weaver/OWNERS b/weaver/OWNERS
new file mode 100644
index 0000000..7e579f6
--- /dev/null
+++ b/weaver/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 1081729
+ebiggers@google.com
+paulcrowley@google.com
+swillden@google.com
+wfrichar@google.com
+chengyouho@google.com
diff --git a/weaver/aidl/vts/OWNERS b/weaver/aidl/vts/OWNERS
deleted file mode 100644
index 40d95e4..0000000
--- a/weaver/aidl/vts/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chengyouho@google.com
-frankwoo@google.com
diff --git a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
deleted file mode 100644
index f016515..0000000
--- a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2020 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 <aidl/Gtest.h>
-#include <aidl/Vintf.h>
-
-#include <aidl/android/hardware/weaver/IWeaver.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-
-#include <limits>
-
-using ::aidl::android::hardware::weaver::IWeaver;
-using ::aidl::android::hardware::weaver::WeaverConfig;
-using ::aidl::android::hardware::weaver::WeaverReadResponse;
-using ::aidl::android::hardware::weaver::WeaverReadStatus;
-
-using ::ndk::SpAIBinder;
-
-const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
-const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
-const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
-
-struct WeaverAidlTest : public ::testing::TestWithParam<std::string> {
- virtual void SetUp() override {
- weaver = IWeaver::fromBinder(
- SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
- ASSERT_NE(weaver, nullptr);
- }
-
- virtual void TearDown() override {}
-
- std::shared_ptr<IWeaver> weaver;
-};
-
-/*
- * Checks config values are suitably large
- */
-TEST_P(WeaverAidlTest, GetConfig) {
- WeaverConfig config;
-
- auto ret = weaver->getConfig(&config);
-
- ASSERT_TRUE(ret.isOk());
-
- EXPECT_GE(config.slots, 16u);
- EXPECT_GE(config.keySize, 16u);
- EXPECT_GE(config.valueSize, 16u);
-}
-
-/*
- * Gets the config twice and checks they are the same
- */
-TEST_P(WeaverAidlTest, GettingConfigMultipleTimesGivesSameResult) {
- WeaverConfig config1;
- WeaverConfig config2;
-
- auto ret = weaver->getConfig(&config1);
- ASSERT_TRUE(ret.isOk());
-
- ret = weaver->getConfig(&config2);
- ASSERT_TRUE(ret.isOk());
-
- EXPECT_EQ(config1, config2);
-}
-
-/*
- * Gets the number of slots from the config and writes a key and value to the last one
- */
-TEST_P(WeaverAidlTest, WriteToLastSlot) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
-
- ASSERT_TRUE(configRet.isOk());
-
- const uint32_t lastSlot = config.slots - 1;
- const auto writeRet = weaver->write(lastSlot, KEY, VALUE);
- ASSERT_TRUE(writeRet.isOk());
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with the same key and receives the value that was previously written
- */
-TEST_P(WeaverAidlTest, WriteFollowedByReadGivesTheSameValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- WeaverReadStatus status;
- const auto readRet = weaver->read(slotId, KEY, &response);
-
- readValue = response.value;
- timeout = response.timeout;
- status = response.status;
-
- ASSERT_TRUE(readRet.isOk());
- EXPECT_EQ(readValue, VALUE);
- EXPECT_EQ(timeout, 0u);
- EXPECT_EQ(status, WeaverReadStatus::OK);
-}
-
-/*
- * Writes a key and value to a slot
- * Overwrites the slot with a new key and value
- * Reads the slot with the new key and receives the new value
- */
-TEST_P(WeaverAidlTest, OverwritingSlotUpdatesTheValue) {
- constexpr uint32_t slotId = 0;
- const auto initialWriteRet = weaver->write(slotId, WRONG_KEY, VALUE);
- ASSERT_TRUE(initialWriteRet.isOk());
-
- const auto overwriteRet = weaver->write(slotId, KEY, OTHER_VALUE);
- ASSERT_TRUE(overwriteRet.isOk());
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- WeaverReadStatus status;
- const auto readRet = weaver->read(slotId, KEY, &response);
-
- readValue = response.value;
- timeout = response.timeout;
- status = response.status;
-
- ASSERT_TRUE(readRet.isOk());
- EXPECT_EQ(readValue, OTHER_VALUE);
- EXPECT_EQ(timeout, 0u);
- EXPECT_EQ(status, WeaverReadStatus::OK);
-}
-
-/*
- * Writes a key and value to a slot
- * Reads the slot with a different key so does not receive the value
- */
-TEST_P(WeaverAidlTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
- constexpr uint32_t slotId = 0;
- const auto ret = weaver->write(slotId, KEY, VALUE);
- ASSERT_TRUE(ret.isOk());
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- WeaverReadStatus status;
- const auto readRet =
- weaver->read(slotId, WRONG_KEY, &response);
-
- readValue = response.value;
- status = response.status;
-
- ASSERT_TRUE(readRet.isOk());
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(status, WeaverReadStatus::INCORRECT_KEY);
-}
-
-/*
- * Writing to an invalid slot fails
- */
-TEST_P(WeaverAidlTest, WritingToInvalidSlotFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- const auto writeRet = weaver->write(config.slots, KEY, VALUE);
- ASSERT_FALSE(writeRet.isOk());
-}
-
-/*
- * Reading from an invalid slot fails rather than incorrect key
- */
-TEST_P(WeaverAidlTest, ReadingFromInvalidSlotFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- if (config.slots == std::numeric_limits<uint32_t>::max()) {
- // If there are no invalid slots then pass
- return;
- }
-
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- WeaverReadStatus status;
- const auto readRet =
- weaver->read(config.slots, KEY, &response);
-
- readValue = response.value;
- timeout = response.timeout;
- status = response.status;
-
- ASSERT_TRUE(readRet.isOk());
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
- EXPECT_EQ(status, WeaverReadStatus::FAILED);
-}
-
-/*
- * Writing a key that is too large fails
- */
-TEST_P(WeaverAidlTest, WriteWithTooLargeKeyFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, bigKey, VALUE);
- ASSERT_FALSE(writeRet.isOk());
-}
-
-/*
- * Writing a value that is too large fails
- */
-TEST_P(WeaverAidlTest, WriteWithTooLargeValueFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- std::vector<uint8_t> bigValue(config.valueSize + 1);
-
- constexpr uint32_t slotId = 0;
- const auto writeRet = weaver->write(slotId, KEY, bigValue);
- ASSERT_FALSE(writeRet.isOk());
-}
-
-/*
- * Reading with a key that is loo large fails
- */
-TEST_P(WeaverAidlTest, ReadWithTooLargeKeyFails) {
- WeaverConfig config;
- const auto configRet = weaver->getConfig(&config);
- ASSERT_TRUE(configRet.isOk());
-
- std::vector<uint8_t> bigKey(config.keySize + 1);
-
- constexpr uint32_t slotId = 0;
- WeaverReadResponse response;
- std::vector<uint8_t> readValue;
- uint32_t timeout;
- WeaverReadStatus status;
- const auto readRet =
- weaver->read(slotId, bigKey, &response);
-
- readValue = response.value;
- timeout = response.timeout;
- status = response.status;
-
- ASSERT_TRUE(readRet.isOk());
- EXPECT_TRUE(readValue.empty());
- EXPECT_EQ(timeout, 0u);
- EXPECT_EQ(status, WeaverReadStatus::FAILED);
-}
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverAidlTest);
-INSTANTIATE_TEST_SUITE_P(
- PerInstance, WeaverAidlTest,
- testing::ValuesIn(android::getAidlHalInstanceNames(IWeaver::descriptor)),
- android::PrintInstanceNameToString);
-
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
- ABinderProcess_setThreadPoolMaxThreadCount(1);
- ABinderProcess_startThreadPool();
- return RUN_ALL_TESTS();
-}
diff --git a/weaver/aidl/vts/Android.bp b/weaver/vts/Android.bp
similarity index 92%
rename from weaver/aidl/vts/Android.bp
rename to weaver/vts/Android.bp
index 557fe47..ee03b28 100644
--- a/weaver/aidl/vts/Android.bp
+++ b/weaver/vts/Android.bp
@@ -34,7 +34,10 @@
"libbinder_ndk",
"libbase",
],
- static_libs: ["android.hardware.weaver-V2-ndk"],
+ static_libs: [
+ "android.hardware.weaver-V2-ndk",
+ "android.hardware.weaver@1.0",
+ ],
test_suites: [
"general-tests",
"vts",
diff --git a/weaver/vts/VtsHalWeaverTargetTest.cpp b/weaver/vts/VtsHalWeaverTargetTest.cpp
new file mode 100644
index 0000000..754d467
--- /dev/null
+++ b/weaver/vts/VtsHalWeaverTargetTest.cpp
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2020 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/weaver/IWeaver.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <android/hardware/weaver/1.0/IWeaver.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <limits>
+
+using ::aidl::android::hardware::weaver::IWeaver;
+using ::aidl::android::hardware::weaver::WeaverConfig;
+using ::aidl::android::hardware::weaver::WeaverReadResponse;
+using ::aidl::android::hardware::weaver::WeaverReadStatus;
+
+using HidlIWeaver = ::android::hardware::weaver::V1_0::IWeaver;
+using HidlWeaverConfig = ::android::hardware::weaver::V1_0::WeaverConfig;
+using HidlWeaverReadStatus = ::android::hardware::weaver::V1_0::WeaverReadStatus;
+using HidlWeaverReadResponse = ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using HidlWeaverStatus = ::android::hardware::weaver::V1_0::WeaverStatus;
+
+const std::string kSlotMapFile = "/metadata/password_slots/slot_map";
+const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
+
+class WeaverAdapter {
+ public:
+ virtual ~WeaverAdapter() {}
+ virtual bool isReady() = 0;
+ virtual ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) = 0;
+ virtual ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ WeaverReadResponse* _aidl_return) = 0;
+ virtual ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ const std::vector<uint8_t>& in_value) = 0;
+};
+
+class WeaverAidlAdapter : public WeaverAdapter {
+ public:
+ WeaverAidlAdapter(const std::string& param)
+ : aidl_weaver_(IWeaver::fromBinder(
+ ::ndk::SpAIBinder(AServiceManager_waitForService(param.c_str())))) {}
+ ~WeaverAidlAdapter() {}
+
+ bool isReady() { return aidl_weaver_ != nullptr; }
+
+ ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) {
+ return aidl_weaver_->getConfig(_aidl_return);
+ }
+
+ ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ WeaverReadResponse* _aidl_return) {
+ return aidl_weaver_->read(in_slotId, in_key, _aidl_return);
+ }
+
+ ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ const std::vector<uint8_t>& in_value) {
+ return aidl_weaver_->write(in_slotId, in_key, in_value);
+ }
+
+ private:
+ std::shared_ptr<IWeaver> aidl_weaver_;
+};
+
+class WeaverHidlAdapter : public WeaverAdapter {
+ public:
+ WeaverHidlAdapter(const std::string& param) : hidl_weaver_(HidlIWeaver::getService(param)) {}
+ ~WeaverHidlAdapter() {}
+
+ bool isReady() { return hidl_weaver_ != nullptr; }
+
+ ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) {
+ bool callbackCalled = false;
+ HidlWeaverStatus status;
+ HidlWeaverConfig config;
+ auto ret = hidl_weaver_->getConfig([&](HidlWeaverStatus s, HidlWeaverConfig c) {
+ callbackCalled = true;
+ status = s;
+ config = c;
+ });
+ if (!ret.isOk() || !callbackCalled || status != HidlWeaverStatus::OK) {
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ }
+ _aidl_return->slots = config.slots;
+ _aidl_return->keySize = config.keySize;
+ _aidl_return->valueSize = config.valueSize;
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ WeaverReadResponse* _aidl_return) {
+ bool callbackCalled = false;
+ HidlWeaverReadStatus status;
+ std::vector<uint8_t> value;
+ uint32_t timeout;
+ auto ret = hidl_weaver_->read(in_slotId, in_key,
+ [&](HidlWeaverReadStatus s, HidlWeaverReadResponse r) {
+ callbackCalled = true;
+ status = s;
+ value = r.value;
+ timeout = r.timeout;
+ });
+ if (!ret.isOk() || !callbackCalled) {
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ }
+ switch (status) {
+ case HidlWeaverReadStatus::OK:
+ _aidl_return->status = WeaverReadStatus::OK;
+ break;
+ case HidlWeaverReadStatus::FAILED:
+ _aidl_return->status = WeaverReadStatus::FAILED;
+ break;
+ case HidlWeaverReadStatus::INCORRECT_KEY:
+ _aidl_return->status = WeaverReadStatus::INCORRECT_KEY;
+ break;
+ case HidlWeaverReadStatus::THROTTLE:
+ _aidl_return->status = WeaverReadStatus::THROTTLE;
+ break;
+ default:
+ ADD_FAILURE() << "Unknown HIDL read status: " << static_cast<uint32_t>(status);
+ _aidl_return->status = WeaverReadStatus::FAILED;
+ break;
+ }
+ _aidl_return->value = value;
+ _aidl_return->timeout = timeout;
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key,
+ const std::vector<uint8_t>& in_value) {
+ auto status = hidl_weaver_->write(in_slotId, in_key, in_value);
+ switch (status) {
+ case HidlWeaverStatus::OK:
+ return ::ndk::ScopedAStatus::ok();
+ case HidlWeaverStatus::FAILED:
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ default:
+ ADD_FAILURE() << "Unknown HIDL write status: " << status.description();
+ return ::ndk::ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
+ }
+ }
+
+ private:
+ android::sp<HidlIWeaver> hidl_weaver_;
+};
+
+class WeaverTest : public ::testing::TestWithParam<std::tuple<std::string, std::string>> {
+ protected:
+ void SetUp() override;
+ void TearDown() override {}
+ void FindFreeSlots();
+
+ std::unique_ptr<WeaverAdapter> weaver_;
+ WeaverConfig config_;
+ uint32_t first_free_slot_;
+ uint32_t last_free_slot_;
+};
+
+void WeaverTest::SetUp() {
+ std::string api, instance_name;
+ std::tie(api, instance_name) = GetParam();
+ if (api == "hidl") {
+ weaver_.reset(new WeaverHidlAdapter(instance_name));
+ } else if (api == "aidl") {
+ weaver_.reset(new WeaverAidlAdapter(instance_name));
+ } else {
+ FAIL() << "Bad test parameterization";
+ }
+ ASSERT_TRUE(weaver_->isReady());
+
+ auto ret = weaver_->getConfig(&config_);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_GT(config_.slots, 0);
+ GTEST_LOG_(INFO) << "WeaverConfig: slots=" << config_.slots << ", keySize=" << config_.keySize
+ << ", valueSize=" << config_.valueSize;
+
+ FindFreeSlots();
+ GTEST_LOG_(INFO) << "First free slot is " << first_free_slot_ << ", last free slot is "
+ << last_free_slot_;
+}
+
+void WeaverTest::FindFreeSlots() {
+ // Determine which Weaver slots are in use by the system. These slots can't be used by the test.
+ std::set<uint32_t> used_slots;
+ if (access(kSlotMapFile.c_str(), F_OK) == 0) {
+ std::string contents;
+ ASSERT_TRUE(android::base::ReadFileToString(kSlotMapFile, &contents))
+ << "Failed to read " << kSlotMapFile;
+ for (const auto& line : android::base::Split(contents, "\n")) {
+ auto trimmed_line = android::base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line[0] == '\0') continue;
+ auto slot_and_user = android::base::Split(trimmed_line, "=");
+ uint32_t slot;
+ ASSERT_TRUE(slot_and_user.size() == 2 &&
+ android::base::ParseUint(slot_and_user[0], &slot))
+ << "Error parsing " << kSlotMapFile << " at \"" << line << "\"";
+ GTEST_LOG_(INFO) << "Slot " << slot << " is in use by " << slot_and_user[1];
+ ASSERT_LT(slot, config_.slots);
+ used_slots.insert(slot);
+ }
+ }
+ // Starting in Android 14, the system will always use at least one Weaver slot if Weaver is
+ // supported at all. Make sure we saw at least one.
+ // TODO: uncomment after Android 14 is merged into AOSP
+ // ASSERT_FALSE(used_slots.empty())
+ //<< "Could not determine which Weaver slots are in use by the system";
+
+ // Find the first free slot.
+ int found = 0;
+ for (uint32_t i = 0; i < config_.slots; i++) {
+ if (used_slots.find(i) == used_slots.end()) {
+ first_free_slot_ = i;
+ found++;
+ break;
+ }
+ }
+ // Find the last free slot.
+ for (uint32_t i = config_.slots; i > 0; i--) {
+ if (used_slots.find(i - 1) == used_slots.end()) {
+ last_free_slot_ = i - 1;
+ found++;
+ break;
+ }
+ }
+ ASSERT_EQ(found, 2) << "All Weaver slots are already in use by the system";
+}
+
+/*
+ * Checks config values are suitably large
+ */
+TEST_P(WeaverTest, GetConfig) {
+ EXPECT_GE(config_.slots, 16u);
+ EXPECT_GE(config_.keySize, 16u);
+ EXPECT_GE(config_.valueSize, 16u);
+}
+
+/*
+ * Gets the config twice and checks they are the same
+ */
+TEST_P(WeaverTest, GettingConfigMultipleTimesGivesSameResult) {
+ WeaverConfig config2;
+
+ auto ret = weaver_->getConfig(&config2);
+ ASSERT_TRUE(ret.isOk());
+
+ EXPECT_EQ(config_, config2);
+}
+
+/*
+ * Writes a key and value to the last free slot
+ */
+TEST_P(WeaverTest, WriteToLastSlot) {
+ const auto writeRet = weaver_->write(last_free_slot_, KEY, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with the same key and receives the value that was previously written
+ */
+TEST_P(WeaverTest, WriteFollowedByReadGivesTheSameValue) {
+ const uint32_t slotId = first_free_slot_;
+ const auto ret = weaver_->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(ret.isOk());
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(slotId, KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_EQ(response.value, VALUE);
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::OK);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Overwrites the slot with a new key and value
+ * Reads the slot with the new key and receives the new value
+ */
+TEST_P(WeaverTest, OverwritingSlotUpdatesTheValue) {
+ const uint32_t slotId = first_free_slot_;
+ const auto initialWriteRet = weaver_->write(slotId, WRONG_KEY, VALUE);
+ ASSERT_TRUE(initialWriteRet.isOk());
+
+ const auto overwriteRet = weaver_->write(slotId, KEY, OTHER_VALUE);
+ ASSERT_TRUE(overwriteRet.isOk());
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(slotId, KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_EQ(response.value, OTHER_VALUE);
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::OK);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with a different key so does not receive the value
+ */
+TEST_P(WeaverTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
+ const uint32_t slotId = first_free_slot_;
+ const auto writeRet = weaver_->write(slotId, KEY, VALUE);
+ ASSERT_TRUE(writeRet.isOk());
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(slotId, WRONG_KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_TRUE(response.value.empty());
+ EXPECT_EQ(response.status, WeaverReadStatus::INCORRECT_KEY);
+}
+
+/*
+ * Writing to an invalid slot fails
+ */
+TEST_P(WeaverTest, WritingToInvalidSlotFails) {
+ if (config_.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ const auto writeRet = weaver_->write(config_.slots, KEY, VALUE);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading from an invalid slot fails rather than incorrect key
+ */
+TEST_P(WeaverTest, ReadingFromInvalidSlotFails) {
+ if (config_.slots == std::numeric_limits<uint32_t>::max()) {
+ // If there are no invalid slots then pass
+ return;
+ }
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(config_.slots, KEY, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_TRUE(response.value.empty());
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::FAILED);
+}
+
+/*
+ * Writing a key that is too large fails
+ */
+TEST_P(WeaverTest, WriteWithTooLargeKeyFails) {
+ std::vector<uint8_t> bigKey(config_.keySize + 1);
+
+ const auto writeRet = weaver_->write(first_free_slot_, bigKey, VALUE);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Writing a value that is too large fails
+ */
+TEST_P(WeaverTest, WriteWithTooLargeValueFails) {
+ std::vector<uint8_t> bigValue(config_.valueSize + 1);
+
+ const auto writeRet = weaver_->write(first_free_slot_, KEY, bigValue);
+ ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading with a key that is too large fails
+ */
+TEST_P(WeaverTest, ReadWithTooLargeKeyFails) {
+ std::vector<uint8_t> bigKey(config_.keySize + 1);
+
+ WeaverReadResponse response;
+ const auto readRet = weaver_->read(first_free_slot_, bigKey, &response);
+ ASSERT_TRUE(readRet.isOk());
+ EXPECT_TRUE(response.value.empty());
+ EXPECT_EQ(response.timeout, 0u);
+ EXPECT_EQ(response.status, WeaverReadStatus::FAILED);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverTest);
+
+// Instantiate the test for each HIDL Weaver service.
+INSTANTIATE_TEST_SUITE_P(
+ PerHidlInstance, WeaverTest,
+ testing::Combine(testing::Values("hidl"),
+ testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+ HidlIWeaver::descriptor))),
+ [](const testing::TestParamInfo<std::tuple<std::string, std::string>>& info) {
+ return android::hardware::PrintInstanceNameToString(
+ testing::TestParamInfo<std::string>{std::get<1>(info.param), info.index});
+ });
+
+// Instantiate the test for each AIDL Weaver service.
+INSTANTIATE_TEST_SUITE_P(
+ PerAidlInstance, WeaverTest,
+ testing::Combine(testing::Values("aidl"),
+ testing::ValuesIn(android::getAidlHalInstanceNames(IWeaver::descriptor))),
+ [](const testing::TestParamInfo<std::tuple<std::string, std::string>>& info) {
+ // This name_generator makes the instance name be included in the test case names, e.g.
+ // "PerAidlInstance/WeaverTest#GetConfig/0_android_hardware_weaver_IWeaver_default"
+ // instead of "PerAidlInstance/WeaverTest#GetConfig/0".
+ return android::PrintInstanceNameToString(
+ testing::TestParamInfo<std::string>{std::get<1>(info.param), info.index});
+ });
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_setThreadPoolMaxThreadCount(1);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}