blob: 2b85f97a28fea45ef4fa7c27af8a8dbc67ac4859 [file] [log] [blame]
/*
* 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.
*/
#define LOG_TAG "StreamHalAidl"
//#define LOG_NDEBUG 0
#include <algorithm>
#include <cstdint>
#include <aidl/android/hardware/audio/core/BnStreamCallback.h>
#include <audio_utils/clock.h>
#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
#include "DeviceHalAidl.h"
#include "StreamHalAidl.h"
using ::aidl::android::hardware::audio::core::IStreamCommon;
using ::aidl::android::hardware::audio::core::IStreamIn;
using ::aidl::android::hardware::audio::core::IStreamOut;
using ::aidl::android::hardware::audio::core::StreamDescriptor;
namespace android {
using HalCommand = StreamDescriptor::Command;
namespace {
template<HalCommand::Tag cmd> HalCommand makeHalCommand() {
return HalCommand::make<cmd>(::aidl::android::media::audio::common::Void{});
}
template<HalCommand::Tag cmd, typename T> HalCommand makeHalCommand(T data) {
return HalCommand::make<cmd>(data);
}
} // namespace
// static
template<class T>
std::shared_ptr<IStreamCommon> StreamHalAidl::getStreamCommon(const std::shared_ptr<T>& stream) {
std::shared_ptr<::aidl::android::hardware::audio::core::IStreamCommon> streamCommon;
if (stream != nullptr) {
if (ndk::ScopedAStatus status = stream->getStreamCommon(&streamCommon);
!status.isOk()) {
ALOGE("%s: failed to retrieve IStreamCommon instance: %s", __func__,
status.getDescription().c_str());
}
}
return streamCommon;
}
StreamHalAidl::StreamHalAidl(
std::string_view className, bool isInput, const audio_config& config,
int32_t nominalLatency, StreamContextAidl&& context,
const std::shared_ptr<IStreamCommon>& stream)
: ConversionHelperAidl(className),
mIsInput(isInput),
mConfig(configToBase(config)),
mContext(std::move(context)),
mStream(stream) {
{
std::lock_guard l(mLock);
mLastReply.latencyMs = nominalLatency;
}
// Instrument audio signal power logging.
// Note: This assumes channel mask, format, and sample rate do not change after creation.
if (audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
/* mStreamPowerLog.isUserDebugOrEngBuild() && */
StreamHalAidl::getAudioProperties(&config) == NO_ERROR) {
mStreamPowerLog.init(config.sample_rate, config.channel_mask, config.format);
}
}
StreamHalAidl::~StreamHalAidl() {
if (mStream != nullptr) {
ndk::ScopedAStatus status = mStream->close();
ALOGE_IF(!status.isOk(), "%s: status %s", __func__, status.getDescription().c_str());
}
}
status_t StreamHalAidl::getBufferSize(size_t *size) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
if (size == nullptr) {
return BAD_VALUE;
}
if (mContext.getFrameSizeBytes() == 0 || mContext.getBufferSizeFrames() == 0 ||
!mStream) {
return NO_INIT;
}
*size = mContext.getBufferSizeBytes();
return OK;
}
status_t StreamHalAidl::getAudioProperties(audio_config_base_t *configBase) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
if (configBase == nullptr) {
return BAD_VALUE;
}
if (!mStream) return NO_INIT;
*configBase = mConfig;
return OK;
}
status_t StreamHalAidl::setParameters(const String8& kvPairs __unused) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::getParameters(const String8& keys __unused, String8 *values) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
values->clear();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::getFrameSize(size_t *size) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
if (size == nullptr) {
return BAD_VALUE;
}
if (mContext.getFrameSizeBytes() == 0 || !mStream) {
return NO_INIT;
}
*size = mContext.getFrameSizeBytes();
return OK;
}
status_t StreamHalAidl::addEffect(sp<EffectHalInterface> effect __unused) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::removeEffect(sp<EffectHalInterface> effect __unused) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::standby() {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
const auto state = getState();
StreamDescriptor::Reply reply;
switch (state) {
case StreamDescriptor::State::ACTIVE:
if (status_t status = pause(&reply); status != OK) return status;
if (reply.state != StreamDescriptor::State::PAUSED) {
ALOGE("%s: unexpected stream state: %s (expected PAUSED)",
__func__, toString(reply.state).c_str());
return INVALID_OPERATION;
}
FALLTHROUGH_INTENDED;
case StreamDescriptor::State::PAUSED:
case StreamDescriptor::State::DRAIN_PAUSED:
return flush();
case StreamDescriptor::State::IDLE:
if (status_t status = sendCommand(makeHalCommand<HalCommand::Tag::standby>(),
&reply); status != OK) {
return status;
}
if (reply.state != StreamDescriptor::State::STANDBY) {
ALOGE("%s: unexpected stream state: %s (expected STANDBY)",
__func__, toString(reply.state).c_str());
return INVALID_OPERATION;
}
FALLTHROUGH_INTENDED;
case StreamDescriptor::State::STANDBY:
return OK;
default:
ALOGE("%s: not supported from %s stream state %s",
__func__, mIsInput ? "input" : "output", toString(state).c_str());
return INVALID_OPERATION;
}
}
status_t StreamHalAidl::dump(int fd, const Vector<String16>& args) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
return mStream->dump(fd, Args(args).args(), args.size());
}
status_t StreamHalAidl::start() {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::stop() {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::getLatency(uint32_t *latency) {
ALOGV("%p %s::%s", this, getClassName().c_str(), __func__);
if (!mStream) return NO_INIT;
StreamDescriptor::Reply reply;
if (status_t status = updateCountersIfNeeded(&reply); status != OK) {
return status;
}
*latency = std::max<int32_t>(0, reply.latencyMs);
return OK;
}
status_t StreamHalAidl::getObservablePosition(int64_t *frames, int64_t *timestamp) {
ALOGV("%p %s::%s", this, getClassName().c_str(), __func__);
if (!mStream) return NO_INIT;
StreamDescriptor::Reply reply;
if (status_t status = updateCountersIfNeeded(&reply); status != OK) {
return status;
}
*frames = reply.observable.frames;
*timestamp = reply.observable.timeNs;
return OK;
}
status_t StreamHalAidl::getXruns(int32_t *frames) {
ALOGV("%p %s::%s", this, getClassName().c_str(), __func__);
if (!mStream) return NO_INIT;
StreamDescriptor::Reply reply;
if (status_t status = updateCountersIfNeeded(&reply); status != OK) {
return status;
}
*frames = reply.xrunFrames;
return OK;
}
status_t StreamHalAidl::transfer(void *buffer, size_t bytes, size_t *transferred) {
ALOGV("%p %s::%s", this, getClassName().c_str(), __func__);
// TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (!mStream || mContext.getDataMQ() == nullptr) return NO_INIT;
mWorkerTid.store(gettid(), std::memory_order_release);
// Switch the stream into an active state if needed.
// Note: in future we may add support for priming the audio pipeline
// with data prior to enabling output (thus we can issue a "burst" command in the "standby"
// stream state), however this scenario wasn't supported by the HIDL HAL.
if (getState() == StreamDescriptor::State::STANDBY) {
StreamDescriptor::Reply reply;
if (status_t status = sendCommand(makeHalCommand<HalCommand::Tag::start>(), &reply);
status != OK) {
return status;
}
if (reply.state != StreamDescriptor::State::IDLE) {
ALOGE("%s: failed to get the stream out of standby, actual state: %s",
__func__, toString(reply.state).c_str());
return INVALID_OPERATION;
}
}
if (!mIsInput) {
bytes = std::min(bytes, mContext.getDataMQ()->availableToWrite());
}
StreamDescriptor::Command burst =
StreamDescriptor::Command::make<StreamDescriptor::Command::Tag::burst>(bytes);
if (!mIsInput) {
if (!mContext.getDataMQ()->write(static_cast<const int8_t*>(buffer), bytes)) {
ALOGE("%s: failed to write %zu bytes to data MQ", __func__, bytes);
return NOT_ENOUGH_DATA;
}
}
StreamDescriptor::Reply reply;
if (status_t status = sendCommand(burst, &reply); status != OK) {
return status;
}
if (mIsInput) {
*transferred = reply.fmqByteCount;
LOG_ALWAYS_FATAL_IF(*transferred > bytes,
"%s: HAL module read %zu bytes, which exceeds requested count %zu",
__func__, *transferred, bytes);
if (!mContext.getDataMQ()->read(static_cast<int8_t*>(buffer), *transferred)) {
ALOGE("%s: failed to read %zu bytes to data MQ", __func__, *transferred);
return NOT_ENOUGH_DATA;
}
}
mStreamPowerLog.log(buffer, *transferred);
return OK;
}
status_t StreamHalAidl::pause(StreamDescriptor::Reply* reply) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
return sendCommand(makeHalCommand<HalCommand::Tag::pause>(), reply,
true /*safeFromNonWorkerThread*/); // The workers stops its I/O activity first.
}
status_t StreamHalAidl::resume(StreamDescriptor::Reply* reply) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
if (mIsInput) {
return sendCommand(makeHalCommand<HalCommand::Tag::burst>(0), reply);
} else {
return sendCommand(makeHalCommand<HalCommand::Tag::start>(), reply);
}
}
status_t StreamHalAidl::drain(bool earlyNotify, StreamDescriptor::Reply* reply) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
return sendCommand(makeHalCommand<HalCommand::Tag::drain>(
mIsInput ? StreamDescriptor::DrainMode::DRAIN_UNSPECIFIED :
earlyNotify ? StreamDescriptor::DrainMode::DRAIN_EARLY_NOTIFY :
StreamDescriptor::DrainMode::DRAIN_ALL), reply);
}
status_t StreamHalAidl::flush(StreamDescriptor::Reply* reply) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
return sendCommand(makeHalCommand<HalCommand::Tag::flush>(), reply,
true /*safeFromNonWorkerThread*/); // The workers stops its I/O activity first.
}
status_t StreamHalAidl::exit() {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::createMmapBuffer(int32_t minSizeFrames __unused,
struct audio_mmap_buffer_info *info __unused) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::getMmapPosition(struct audio_mmap_position *position __unused) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamHalAidl::setHalThreadPriority(int priority __unused) {
// Obsolete, must be done by the HAL module.
return OK;
}
status_t StreamHalAidl::getHalPid(pid_t *pid __unused) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
bool StreamHalAidl::requestHalThreadPriority(pid_t threadPid __unused, pid_t threadId __unused) {
// Obsolete, must be done by the HAL module.
return true;
}
status_t StreamHalAidl::legacyCreateAudioPatch(const struct audio_port_config& port __unused,
std::optional<audio_source_t> source __unused,
audio_devices_t type __unused) {
// Obsolete since 'DeviceHalAidl.supportsAudioPatches' always returns 'true'.
return INVALID_OPERATION;
}
status_t StreamHalAidl::legacyReleaseAudioPatch() {
// Obsolete since 'DeviceHalAidl.supportsAudioPatches' always returns 'true'.
return INVALID_OPERATION;
}
status_t StreamHalAidl::sendCommand(
const ::aidl::android::hardware::audio::core::StreamDescriptor::Command &command,
::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply,
bool safeFromNonWorkerThread) {
// TIME_CHECK(); // TODO(b/238654698) reenable only when optimized.
if (!safeFromNonWorkerThread) {
const pid_t workerTid = mWorkerTid.load(std::memory_order_acquire);
LOG_ALWAYS_FATAL_IF(workerTid != gettid(),
"%s %s: must be invoked from the worker thread (%d)",
__func__, command.toString().c_str(), workerTid);
}
if (!mContext.getCommandMQ()->writeBlocking(&command, 1)) {
ALOGE("%s: failed to write command %s to MQ", __func__, command.toString().c_str());
return NOT_ENOUGH_DATA;
}
StreamDescriptor::Reply localReply{};
if (reply == nullptr) {
reply = &localReply;
}
if (!mContext.getReplyMQ()->readBlocking(reply, 1)) {
ALOGE("%s: failed to read from reply MQ, command %s", __func__, command.toString().c_str());
return NOT_ENOUGH_DATA;
}
{
std::lock_guard l(mLock);
mLastReply = *reply;
}
switch (reply->status) {
case STATUS_OK: return OK;
case STATUS_BAD_VALUE: return BAD_VALUE;
case STATUS_INVALID_OPERATION: return INVALID_OPERATION;
case STATUS_NOT_ENOUGH_DATA: return NOT_ENOUGH_DATA;
default:
ALOGE("%s: unexpected status %d returned for command %s",
__func__, reply->status, command.toString().c_str());
return INVALID_OPERATION;
}
}
status_t StreamHalAidl::updateCountersIfNeeded(
::aidl::android::hardware::audio::core::StreamDescriptor::Reply* reply) {
if (mWorkerTid.load(std::memory_order_acquire) == gettid()) {
if (const auto state = getState(); state != StreamDescriptor::State::ACTIVE &&
state != StreamDescriptor::State::DRAINING &&
state != StreamDescriptor::State::TRANSFERRING) {
return sendCommand(makeHalCommand<HalCommand::Tag::getStatus>(), reply);
}
}
if (reply != nullptr) {
std::lock_guard l(mLock);
*reply = mLastReply;
}
return OK;
}
namespace {
/* Notes on callback ownership.
This is how Binder ownership model looks like. The server implementation
is owned by Binder framework (via sp<>). Proxies are owned by clients.
When the last proxy disappears, Binder framework releases the server impl.
Thus, it is not needed to keep any references to StreamCallback (this is
the server impl) -- it will live as long as HAL server holds a strong ref to
IStreamCallback proxy.
The callback only keeps a weak reference to the stream. The stream is owned
by AudioFlinger.
*/
class StreamCallback : public ::aidl::android::hardware::audio::core::BnStreamCallback {
ndk::ScopedAStatus onTransferReady() override {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus onError() override {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus onDrainReady() override {
return ndk::ScopedAStatus::ok();
}
};
} // namespace
StreamOutHalAidl::StreamOutHalAidl(
const audio_config& config, StreamContextAidl&& context, int32_t nominalLatency,
const std::shared_ptr<IStreamOut>& stream)
: StreamHalAidl("StreamOutHalAidl", false /*isInput*/, config, nominalLatency,
std::move(context), getStreamCommon(stream)),
mStream(stream) {}
status_t StreamOutHalAidl::getLatency(uint32_t *latency) {
return StreamHalAidl::getLatency(latency);
}
status_t StreamOutHalAidl::setVolume(float left __unused, float right __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::selectPresentation(int presentationId __unused, int programId __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::write(const void *buffer, size_t bytes, size_t *written) {
if (buffer == nullptr || written == nullptr) {
return BAD_VALUE;
}
// For the output scenario, 'transfer' does not modify the buffer.
return transfer(const_cast<void*>(buffer), bytes, written);
}
status_t StreamOutHalAidl::getRenderPosition(uint32_t *dspFrames) {
if (dspFrames == nullptr) {
return BAD_VALUE;
}
int64_t aidlFrames = 0, aidlTimestamp = 0;
if (status_t status = getObservablePosition(&aidlFrames, &aidlTimestamp); status != OK) {
return OK;
}
*dspFrames = std::clamp<int64_t>(aidlFrames, 0, UINT32_MAX);
return OK;
}
status_t StreamOutHalAidl::getNextWriteTimestamp(int64_t *timestamp __unused) {
// Obsolete, use getPresentationPosition.
return INVALID_OPERATION;
}
status_t StreamOutHalAidl::setCallback(wp<StreamOutHalInterfaceCallback> callback __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
if (supportsPause == nullptr || supportsResume == nullptr) {
return BAD_VALUE;
}
TIME_CHECK();
if (!mStream) return NO_INIT;
*supportsPause = *supportsResume = true;
return OK;
}
status_t StreamOutHalAidl::pause() {
return StreamHalAidl::pause();
}
status_t StreamOutHalAidl::resume() {
return StreamHalAidl::resume();
}
status_t StreamOutHalAidl::supportsDrain(bool *supportsDrain) {
if (supportsDrain == nullptr) {
return BAD_VALUE;
}
TIME_CHECK();
if (!mStream) return NO_INIT;
*supportsDrain = true;
return OK;
}
status_t StreamOutHalAidl::drain(bool earlyNotify) {
return StreamHalAidl::drain(earlyNotify);
}
status_t StreamOutHalAidl::flush() {
return StreamHalAidl::flush();
}
status_t StreamOutHalAidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
if (frames == nullptr || timestamp == nullptr) {
return BAD_VALUE;
}
int64_t aidlFrames = 0, aidlTimestamp = 0;
if (status_t status = getObservablePosition(&aidlFrames, &aidlTimestamp); status != OK) {
return status;
}
*frames = std::max<int64_t>(0, aidlFrames);
timestamp->tv_sec = aidlTimestamp / NANOS_PER_SECOND;
timestamp->tv_nsec = aidlTimestamp - timestamp->tv_sec * NANOS_PER_SECOND;
return OK;
}
status_t StreamOutHalAidl::updateSourceMetadata(
const StreamOutHalInterface::SourceMetadata& sourceMetadata __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::getDualMonoMode(audio_dual_mono_mode_t* mode __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::setDualMonoMode(audio_dual_mono_mode_t mode __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::getAudioDescriptionMixLevel(float* leveldB __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::setAudioDescriptionMixLevel(float leveldB __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::getPlaybackRateParameters(
audio_playback_rate_t* playbackRate __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::setPlaybackRateParameters(
const audio_playback_rate_t& playbackRate __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamOutHalAidl::setEventCallback(
const sp<StreamOutHalInterfaceEventCallback>& callback __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
namespace {
struct StreamOutEventCallback {
StreamOutEventCallback(const wp<StreamOutHalAidl>& stream) : mStream(stream) {}
private:
wp<StreamOutHalAidl> mStream;
};
} // namespace
status_t StreamOutHalAidl::setLatencyMode(audio_latency_mode_t mode __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
};
status_t StreamOutHalAidl::getRecommendedLatencyModes(
std::vector<audio_latency_mode_t> *modes __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
};
status_t StreamOutHalAidl::setLatencyModeCallback(
const sp<StreamOutHalInterfaceLatencyModeCallback>& callback __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
};
void StreamOutHalAidl::onWriteReady() {
sp<StreamOutHalInterfaceCallback> callback = mCallback.load().promote();
if (callback == 0) return;
ALOGV("asyncCallback onWriteReady");
callback->onWriteReady();
}
void StreamOutHalAidl::onDrainReady() {
sp<StreamOutHalInterfaceCallback> callback = mCallback.load().promote();
if (callback == 0) return;
ALOGV("asyncCallback onDrainReady");
callback->onDrainReady();
}
void StreamOutHalAidl::onError() {
sp<StreamOutHalInterfaceCallback> callback = mCallback.load().promote();
if (callback == 0) return;
ALOGV("asyncCallback onError");
callback->onError();
}
void StreamOutHalAidl::onCodecFormatChanged(const std::basic_string<uint8_t>& metadataBs __unused) {
sp<StreamOutHalInterfaceEventCallback> callback = mEventCallback.load().promote();
if (callback == nullptr) return;
ALOGV("asyncCodecFormatCallback %s", __func__);
callback->onCodecFormatChanged(metadataBs);
}
void StreamOutHalAidl::onRecommendedLatencyModeChanged(
const std::vector<audio_latency_mode_t>& modes __unused) {
sp<StreamOutHalInterfaceLatencyModeCallback> callback = mLatencyModeCallback.load().promote();
if (callback == nullptr) return;
callback->onRecommendedLatencyModeChanged(modes);
}
status_t StreamOutHalAidl::exit() {
return StreamHalAidl::exit();
}
StreamInHalAidl::StreamInHalAidl(
const audio_config& config, StreamContextAidl&& context, int32_t nominalLatency,
const std::shared_ptr<IStreamIn>& stream)
: StreamHalAidl("StreamInHalAidl", true /*isInput*/, config, nominalLatency,
std::move(context), getStreamCommon(stream)),
mStream(stream) {}
status_t StreamInHalAidl::setGain(float gain __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamInHalAidl::read(void *buffer, size_t bytes, size_t *read) {
if (buffer == nullptr || read == nullptr) {
return BAD_VALUE;
}
return transfer(buffer, bytes, read);
}
status_t StreamInHalAidl::getInputFramesLost(uint32_t *framesLost) {
if (framesLost == nullptr) {
return BAD_VALUE;
}
int32_t aidlXruns = 0;
if (status_t status = getXruns(&aidlXruns); status != OK) {
return status;
}
*framesLost = std::max<int32_t>(0, aidlXruns);
return OK;
}
status_t StreamInHalAidl::getCapturePosition(int64_t *frames, int64_t *time) {
if (frames == nullptr || time == nullptr) {
return BAD_VALUE;
}
return getObservablePosition(frames, time);
}
status_t StreamInHalAidl::getActiveMicrophones(
std::vector<media::MicrophoneInfo> *microphones __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamInHalAidl::updateSinkMetadata(
const StreamInHalInterface::SinkMetadata& sinkMetadata __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamInHalAidl::setPreferredMicrophoneDirection(
audio_microphone_direction_t direction __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
status_t StreamInHalAidl::setPreferredMicrophoneFieldDimension(float zoom __unused) {
TIME_CHECK();
if (!mStream) return NO_INIT;
ALOGE("%s not implemented yet", __func__);
return OK;
}
} // namespace android