| /* |
| * Copyright (C) 2025 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "MmapStreamInterface" |
| |
| #include <android/media/BnMmapStreamCallback.h> |
| #include <media/AudioSystem.h> |
| #include <media/IAudioFlinger.h> |
| #include <media/MmapStreamCallback.h> |
| #include <media/MmapStreamInterface.h> |
| |
| namespace android { |
| |
| using media::IMmapStream; |
| using aidl_utils::statusTFromBinderStatus; |
| |
| class MmapStreamCallbackAdapter : public android::media::BnMmapStreamCallback { |
| public: |
| explicit MmapStreamCallbackAdapter(const sp<MmapStreamCallback>& callback) |
| : mCallback(callback) {} |
| |
| ::android::binder::Status onTearDown(int32_t portId) final { |
| const audio_port_handle_t handle = VALUE_OR_RETURN_BINDER_STATUS( |
| aidl2legacy_int32_t_audio_port_handle_t(portId)); |
| if (const auto callback = mCallback.promote()) { |
| callback->onTearDown(handle); |
| } else { |
| ALOGW_IF(mCallbackWarn++ < kSpamLimit, "%s: null callback", __func__); |
| } |
| return binder::Status::ok(); |
| } |
| |
| ::android::binder::Status onVolumeChanged(float volume) final { |
| if (const auto callback = mCallback.promote()) { |
| callback->onVolumeChanged(volume); |
| } else { |
| ALOGW_IF(mCallbackWarn++ < kSpamLimit, "%s: null callback", __func__); |
| } |
| return binder::Status::ok(); |
| } |
| |
| ::android::binder::Status onRoutingChanged(const ::std::vector<int32_t>& deviceIds) final { |
| const std::vector<audio_port_handle_t> legacyDeviceIds = VALUE_OR_RETURN_BINDER_STATUS( |
| convertContainer<std::vector<audio_port_handle_t>>( |
| deviceIds, aidl2legacy_int32_t_audio_port_handle_t)); |
| if (const auto callback = mCallback.promote()) { |
| callback->onRoutingChanged(legacyDeviceIds); |
| } else { |
| ALOGW_IF(mCallbackWarn++ < kSpamLimit, "%s: null callback", __func__); |
| } |
| return binder::Status::ok(); |
| } |
| |
| private: |
| static constexpr uint32_t kSpamLimit = 30; |
| const wp<MmapStreamCallback> mCallback; |
| std::atomic_uint32_t mCallbackWarn = 0; |
| }; |
| |
| |
| // static |
| status_t MmapStreamInterface::buildRequest(bool isOutput, |
| const audio_attributes_t& attr, |
| const audio_config_base_t& config, |
| const AudioClient& client, |
| const DeviceIdVector& deviceIds, |
| audio_session_t sessionId, |
| const sp<MmapStreamCallback>& callback, |
| const audio_offload_info_t* offloadInfo, |
| media::OpenMmapRequest* request) |
| { |
| request->isOutput = isOutput; |
| request->attr = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_audio_attributes_t_AudioAttributes(attr)); |
| request->config = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_audio_config_base_t_AudioConfigBase( |
| config, !isOutput)); |
| request->client = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_AudioClient_AudioClient(client)); |
| request->deviceIds = VALUE_OR_RETURN_STATUS( |
| convertContainer<std::vector<int32_t>>( |
| deviceIds, legacy2aidl_audio_port_handle_t_int32_t)); |
| request->sessionId = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_audio_session_t_int32_t(sessionId)); |
| request->callback = sp<MmapStreamCallbackAdapter>::make(callback); |
| request->offloadInfo = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_audio_offload_info_t_AudioOffloadInfo( |
| offloadInfo ? *offloadInfo : AUDIO_INFO_INITIALIZER)); |
| return NO_ERROR; |
| } |
| |
| // static |
| status_t MmapStreamInterface::parseResponse(const media::OpenMmapResponse& response, |
| bool isOutput, |
| audio_config_base_t* config, |
| DeviceIdVector* deviceIds, |
| audio_session_t* sessionId, |
| audio_port_handle_t* handle) |
| { |
| *config = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_AudioConfigBase_audio_config_base_t( |
| response.config, !isOutput)); |
| *deviceIds = VALUE_OR_RETURN_STATUS( |
| convertContainer<std::vector<audio_port_handle_t>>( |
| response.deviceIds, aidl2legacy_int32_t_audio_port_handle_t)); |
| *sessionId = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_int32_t_audio_session_t(response.sessionId)); |
| *handle = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_int32_t_audio_port_handle_t(response.portId)); |
| return NO_ERROR; |
| } |
| |
| // static |
| status_t MmapStreamInterface::openMmapStream(bool isOutput, |
| const audio_attributes_t& attr, |
| audio_config_base_t* config, |
| const AudioClient& client, |
| DeviceIdVector* deviceIds, |
| audio_session_t* sessionId, |
| const sp<MmapStreamCallback>& callback, |
| const audio_offload_info_t* offloadInfo, |
| sp<MmapStreamInterface>& interface, |
| audio_port_handle_t* handle) |
| { |
| const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); |
| if (af == nullptr) { |
| return NO_INIT; |
| } |
| media::OpenMmapRequest request; |
| sp<MmapStreamCallbackAdapter> callbackAdapter; |
| status_t status = buildRequest(isOutput, attr, *config, client, *deviceIds, |
| *sessionId, callback, offloadInfo, &request); |
| if (status != NO_ERROR) { |
| ALOGW("%s: buildRequest failed with status: %d", __func__, status); |
| return status; |
| } |
| |
| media::OpenMmapResponse response; |
| status = af->openMmapStream(request, &response); |
| if (status != NO_ERROR) { |
| ALOGW("%s: openMmapStream failed with status: %d", __func__, status); |
| return status; |
| } |
| status = parseResponse(response, isOutput, config, deviceIds, sessionId, handle); |
| if (status != NO_ERROR) { |
| ALOGW("%s: parseResponse failed with status: %d", __func__, status); |
| return status; |
| } |
| interface = sp<MmapStreamInterface>::make(*config, response.stream, request.callback); |
| return NO_ERROR; |
| } |
| |
| // static |
| status_t MmapStreamInterface::parseRequest(const media::OpenMmapRequest& request, |
| bool* isOutput, |
| audio_attributes_t* attr, |
| audio_config_base_t* config, |
| AudioClient* client, |
| DeviceIdVector* deviceIds, |
| audio_session_t* sessionId, |
| sp<media::IMmapStreamCallback>* callback, |
| audio_offload_info_t* offloadInfo) { |
| *isOutput = request.isOutput; |
| *attr = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_AudioAttributes_audio_attributes_t(request.attr)); |
| *config = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_AudioConfigBase_audio_config_base_t( |
| request.config, !*isOutput)); |
| *client = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_AudioClient_AudioClient(request.client)); |
| *deviceIds = VALUE_OR_RETURN_STATUS( |
| convertContainer<DeviceIdVector>( |
| request.deviceIds, aidl2legacy_int32_t_audio_port_handle_t)); |
| *sessionId = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_int32_t_audio_session_t(request.sessionId)); |
| *callback = request.callback; |
| if (offloadInfo != nullptr) { |
| *offloadInfo = VALUE_OR_RETURN_STATUS( |
| aidl2legacy_AudioOffloadInfo_audio_offload_info_t(request.offloadInfo)); |
| } |
| return NO_ERROR; |
| } |
| |
| // static |
| status_t MmapStreamInterface::buildResponse(bool isOutput, |
| const audio_config_base_t& config, |
| const DeviceIdVector& deviceIds, |
| audio_session_t sessionId, |
| const sp<IMmapStream>& interface, |
| audio_port_handle_t portId, |
| media::OpenMmapResponse* response) { |
| response->config = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_audio_config_base_t_AudioConfigBase( |
| config, !isOutput)); |
| response->deviceIds = VALUE_OR_RETURN_STATUS( |
| convertContainer<std::vector<int32_t>>( |
| deviceIds, legacy2aidl_audio_port_handle_t_int32_t)); |
| response->sessionId = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_audio_session_t_int32_t(sessionId)); |
| response->stream = interface; |
| response->portId = VALUE_OR_RETURN_STATUS( |
| legacy2aidl_audio_port_handle_t_int32_t(portId)); |
| return NO_ERROR; |
| } |
| |
| MmapStreamInterface::MmapStreamInterface(const audio_config_base_t& config, |
| const sp<media::IMmapStream>& stream, |
| const sp<media::IMmapStreamCallback>& callback) |
| : mConfig(config), |
| mStream(stream), |
| mCallback(callback) |
| {} |
| |
| status_t MmapStreamInterface::createMmapBuffer( |
| int32_t minSizeFrames, struct audio_mmap_buffer_info* info) { |
| media::MmapBufferInfo bufferInfo; |
| RETURN_STATUS_IF_ERROR( |
| statusTFromBinderStatus(mStream->createMmapBuffer(minSizeFrames, &bufferInfo))); |
| info->shared_memory_address = nullptr; // local only |
| binder::unique_fd ufd(bufferInfo.sharedFd.release()); |
| info->shared_memory_fd = ufd.release(); |
| info->buffer_size_frames = bufferInfo.bufferSizeFrames; |
| info->burst_size_frames = bufferInfo.burstSizeFrames; |
| info->flags = (audio_mmap_buffer_flag)bufferInfo.flags; |
| ALOGD("%s: bptr: %p buffer_size_frames: %d burst_size_frames: %d flags:%#x", |
| __func__, &bufferInfo, bufferInfo.bufferSizeFrames, |
| bufferInfo.burstSizeFrames, bufferInfo.flags); |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::getMmapPosition(struct audio_mmap_position* position) { |
| media::IMmapStream::MmapStreamPosition aidlPosition; |
| RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mStream->getMmapPosition(&aidlPosition))); |
| position->time_nanoseconds = aidlPosition.timeNanos; |
| position->position_frames = aidlPosition.positionFrames; |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::getObservablePosition(uint64_t* position, int64_t* timeNanos) { |
| media::IMmapStream::MmapObservablePosition observablePosition; |
| RETURN_STATUS_IF_ERROR( |
| statusTFromBinderStatus(mStream->getObservablePosition(&observablePosition))); |
| *position = observablePosition.positionFrames; |
| *timeNanos = observablePosition.timeNanos; |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::start( |
| const AudioClient& client, const audio_attributes_t* attr, audio_port_handle_t *handle) { |
| const auto aidlClient = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioClient_AudioClient(client)); |
| ::std::optional<::android::media::audio::common::AudioAttributes> aidlAttr; |
| if (attr) { |
| aidlAttr = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_attributes_t_AudioAttributes(*attr)); |
| } |
| const int32_t aidlPriorPortId = |
| VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(*handle)); |
| int32_t portId; |
| |
| RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( |
| mStream->start(aidlClient, aidlAttr, aidlPriorPortId, &portId))); |
| *handle = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(portId)); |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::stop(audio_port_handle_t handle) { |
| const int32_t portId = |
| VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(handle)); |
| RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mStream->stop(portId))); |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::standby() { |
| RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mStream->standby())); |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::reportData(const void* buffer, size_t frameCount) { |
| const auto* const begin = static_cast<const uint8_t*>(buffer); |
| std::vector<uint8_t> aidlBuffer( |
| begin, begin + frameCount * audio_bytes_per_sample(mConfig.format)); |
| RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mStream->reportData(aidlBuffer))); |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::drain() { |
| RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mStream->drain())); |
| return NO_ERROR; |
| } |
| |
| status_t MmapStreamInterface::activate() { |
| RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mStream->activate())); |
| return NO_ERROR; |
| } |
| |
| } // namespace android |