1 /*
2 * Copyright (C) 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <unistd.h>
18 #include <cstdlib>
19
20 #define LOG_TAG "AHAL_MmapStream"
21 #include <android-base/logging.h>
22 #include <audio_utils/clock.h>
23 #include <error/Result.h>
24 #include <utils/SystemClock.h>
25
26 #include "core-impl/StreamMmapStub.h"
27
28 using aidl::android::hardware::audio::common::SinkMetadata;
29 using aidl::android::hardware::audio::common::SourceMetadata;
30 using aidl::android::media::audio::common::AudioOffloadInfo;
31 using aidl::android::media::audio::common::MicrophoneInfo;
32
33 namespace aidl::android::hardware::audio::core {
34
35 namespace mmap {
36
init()37 std::string DspSimulatorLogic::init() {
38 {
39 std::lock_guard l(mSharedState.lock);
40 mSharedState.mmapPos.timeNs = StreamDescriptor::Position::UNKNOWN;
41 mSharedState.mmapPos.frames = StreamDescriptor::Position::UNKNOWN;
42 }
43 // Progress in buffer size chunks to make sure that VTS tolerates infrequent position updates
44 // (see b/350998390).
45 mCycleDurationUs = (mSharedState.bufferSizeBytes / mSharedState.frameSizeBytes) *
46 MICROS_PER_SECOND / mSharedState.sampleRate;
47 return "";
48 }
49
cycle()50 DspSimulatorLogic::Status DspSimulatorLogic::cycle() {
51 // Simulate DSP moving along in real time.
52 const int64_t timeBeginNs = ::android::uptimeNanos();
53 usleep(mCycleDurationUs);
54 int64_t newFrames;
55 std::lock_guard l(mSharedState.lock);
56 if (mMemBegin != mSharedState.sharedMemory) {
57 mMemBegin = mSharedState.sharedMemory;
58 if (mMemBegin != nullptr) mMemPos = mMemBegin;
59 }
60 if (mMemBegin != nullptr) {
61 mSharedState.mmapPos.timeNs = ::android::uptimeNanos();
62 newFrames = (mSharedState.mmapPos.timeNs - timeBeginNs) * mSharedState.sampleRate /
63 NANOS_PER_SECOND;
64 // Restore the reported frames position to ensure continuity.
65 if (mSharedState.mmapPos.frames == StreamDescriptor::Position::UNKNOWN) {
66 mSharedState.mmapPos.frames = mLastFrames;
67 }
68 mSharedState.mmapPos.frames += newFrames;
69 mLastFrames = mSharedState.mmapPos.frames;
70 if (mSharedState.isInput) {
71 for (size_t i = 0; i < static_cast<size_t>(newFrames) * mSharedState.frameSizeBytes;
72 ++i) {
73 *mMemPos++ = std::rand() % 255;
74 if (mMemPos >= mMemBegin + mSharedState.bufferSizeBytes) mMemPos = mMemBegin;
75 }
76 }
77 } else {
78 LOG(WARNING) << "No shared memory but the DSP is active";
79 mSharedState.mmapPos.timeNs = StreamDescriptor::Position::UNKNOWN;
80 mSharedState.mmapPos.frames = StreamDescriptor::Position::UNKNOWN;
81 }
82 return Status::CONTINUE;
83 }
84
85 } // namespace mmap
86
87 using mmap::DspSimulatorState;
88
DriverMmapStubImpl(const StreamContext & context)89 DriverMmapStubImpl::DriverMmapStubImpl(const StreamContext& context)
90 : DriverStubImpl(context, 0 /*asyncSleepTimeUs*/),
91 mState{mIsInput, mSampleRate, static_cast<int>(mFrameSizeBytes),
92 mBufferSizeFrames * mFrameSizeBytes},
93 mDspWorker(mState) {
94 LOG_IF(FATAL, !context.isMmap()) << "The steam must be used in MMAP mode";
95 }
96
init(DriverCallbackInterface * callback)97 ::android::status_t DriverMmapStubImpl::init(DriverCallbackInterface* callback) {
98 RETURN_STATUS_IF_ERROR(DriverStubImpl::init(callback));
99 return ::android::OK;
100 }
101
drain(StreamDescriptor::DrainMode drainMode)102 ::android::status_t DriverMmapStubImpl::drain(StreamDescriptor::DrainMode drainMode) {
103 RETURN_STATUS_IF_ERROR(DriverStubImpl::drain(drainMode));
104 mDspWorker.pause();
105 return ::android::OK;
106 }
107
pause()108 ::android::status_t DriverMmapStubImpl::pause() {
109 RETURN_STATUS_IF_ERROR(DriverStubImpl::pause());
110 mDspWorker.pause();
111 return ::android::OK;
112 }
113
start()114 ::android::status_t DriverMmapStubImpl::start() {
115 RETURN_STATUS_IF_ERROR(DriverStubImpl::start());
116 RETURN_STATUS_IF_ERROR(startWorkerIfNeeded());
117 mDspWorker.resume();
118 return ::android::OK;
119 }
120
transfer(void *,size_t,size_t *,int32_t *)121 ::android::status_t DriverMmapStubImpl::transfer(void*, size_t, size_t*, int32_t*) {
122 // Do not call into DriverStubImpl::transfer
123 if (!mIsInitialized) {
124 LOG(FATAL) << __func__ << ": must not happen for an uninitialized driver";
125 }
126 if (mIsStandby) {
127 LOG(FATAL) << __func__ << ": must not happen while in standby";
128 }
129 RETURN_STATUS_IF_ERROR(startWorkerIfNeeded());
130 mDspWorker.resume();
131 return ::android::OK;
132 }
133
shutdown()134 void DriverMmapStubImpl::shutdown() {
135 LOG(DEBUG) << __func__ << ": stopping the DSP simulator worker";
136 mDspWorker.stop();
137 std::lock_guard l(mState.lock);
138 releaseSharedMemory();
139 DriverStubImpl::shutdown();
140 }
141
initSharedMemory(int ashmemFd)142 ::android::status_t DriverMmapStubImpl::initSharedMemory(int ashmemFd) {
143 {
144 std::lock_guard l(mState.lock);
145 if (ashmemFd == -1) {
146 mState.sharedMemory = nullptr;
147 return ::android::BAD_VALUE;
148 }
149 RETURN_STATUS_IF_ERROR(releaseSharedMemory());
150 }
151 uint8_t* sharedMemory = static_cast<uint8_t*>(::mmap(
152 nullptr, mState.bufferSizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
153 if (sharedMemory == reinterpret_cast<uint8_t*>(MAP_FAILED) || sharedMemory == nullptr) {
154 PLOG(ERROR) << "mmap failed for size " << mState.bufferSizeBytes << ", fd " << ashmemFd;
155 return ::android::NO_INIT;
156 }
157 std::lock_guard l(mState.lock);
158 mState.sharedMemory = sharedMemory;
159 return ::android::OK;
160 }
161
releaseSharedMemory()162 ::android::status_t DriverMmapStubImpl::releaseSharedMemory() {
163 if (mState.sharedMemory != nullptr) {
164 LOG(DEBUG) << __func__ << ": unmapping shared memory";
165 if (munmap(mState.sharedMemory, mState.bufferSizeBytes) != 0) {
166 PLOG(ERROR) << "munmap failed for size " << mState.bufferSizeBytes;
167 return ::android::INVALID_OPERATION;
168 }
169 mState.sharedMemory = nullptr;
170 }
171 return ::android::OK;
172 }
173
startWorkerIfNeeded()174 ::android::status_t DriverMmapStubImpl::startWorkerIfNeeded() {
175 if (!mDspWorkerStarted) {
176 // This is an "audio service thread," must have elevated priority.
177 if (!mDspWorker.start("dsp_sim", ANDROID_PRIORITY_URGENT_AUDIO)) {
178 return ::android::NO_INIT;
179 }
180 mDspWorkerStarted = true;
181 }
182 return ::android::OK;
183 }
184
refinePosition(StreamDescriptor::Position * position)185 ::android::status_t DriverMmapStubImpl::refinePosition(StreamDescriptor::Position* position) {
186 std::lock_guard l(mState.lock);
187 *position = mState.mmapPos;
188 return ::android::OK;
189 }
190
getMmapPositionAndLatency(StreamDescriptor::Position * position,int32_t * latencyMs)191 ::android::status_t DriverMmapStubImpl::getMmapPositionAndLatency(
192 StreamDescriptor::Position* position, int32_t* latencyMs) {
193 {
194 std::lock_guard l(mState.lock);
195 *position = mState.mmapPos;
196 }
197 const size_t latencyFrames = mBufferSizeFrames / 2;
198 if (position->frames != StreamDescriptor::Position::UNKNOWN) {
199 position->frames += latencyFrames;
200 }
201 *latencyMs = latencyFrames * MILLIS_PER_SECOND / mSampleRate;
202 return ::android::OK;
203 }
204
205 const std::string StreamMmapStub::kCreateMmapBufferName = "aosp.createMmapBuffer";
206
StreamMmapStub(StreamContext * context,const Metadata & metadata)207 StreamMmapStub::StreamMmapStub(StreamContext* context, const Metadata& metadata)
208 : StreamCommonImpl(context, metadata), DriverMmapStubImpl(getContext()) {}
209
~StreamMmapStub()210 StreamMmapStub::~StreamMmapStub() {
211 cleanupWorker();
212 }
213
getVendorParameters(const std::vector<std::string> & in_ids,std::vector<VendorParameter> * _aidl_return)214 ndk::ScopedAStatus StreamMmapStub::getVendorParameters(const std::vector<std::string>& in_ids,
215 std::vector<VendorParameter>* _aidl_return) {
216 std::vector<std::string> unprocessedIds;
217 for (const auto& id : in_ids) {
218 if (id == kCreateMmapBufferName) {
219 LOG(DEBUG) << __func__ << ": " << id;
220 MmapBufferDescriptor mmapDesc;
221 RETURN_STATUS_IF_ERROR(createMmapBuffer(&mmapDesc));
222 VendorParameter createMmapBuffer{.id = id};
223 createMmapBuffer.ext.setParcelable(mmapDesc);
224 LOG(DEBUG) << __func__ << ": returning " << mmapDesc.toString();
225 _aidl_return->push_back(std::move(createMmapBuffer));
226 } else {
227 unprocessedIds.push_back(id);
228 }
229 }
230 if (!unprocessedIds.empty()) {
231 return StreamCommonImpl::getVendorParameters(unprocessedIds, _aidl_return);
232 }
233 return ndk::ScopedAStatus::ok();
234 }
235
setVendorParameters(const std::vector<VendorParameter> & in_parameters,bool in_async)236 ndk::ScopedAStatus StreamMmapStub::setVendorParameters(
237 const std::vector<VendorParameter>& in_parameters, bool in_async) {
238 std::vector<VendorParameter> unprocessedParameters;
239 for (const auto& param : in_parameters) {
240 if (param.id == kCreateMmapBufferName) {
241 LOG(DEBUG) << __func__ << ": " << param.id;
242 // The value is irrelevant. The fact that this parameter can be "set" is an
243 // indication that the method can be used by the client via 'getVendorParameters'.
244 } else {
245 unprocessedParameters.push_back(param);
246 }
247 }
248 if (!unprocessedParameters.empty()) {
249 return StreamCommonImpl::setVendorParameters(unprocessedParameters, in_async);
250 }
251 return ndk::ScopedAStatus::ok();
252 }
253
createMmapBuffer(MmapBufferDescriptor * desc)254 ndk::ScopedAStatus StreamMmapStub::createMmapBuffer(MmapBufferDescriptor* desc) {
255 const size_t bufferSizeFrames = mContext.getBufferSizeInFrames();
256 const size_t bufferSizeBytes = static_cast<size_t>(bufferSizeFrames) * mContext.getFrameSize();
257 const std::string regionName =
258 std::string("mmap-sim-") + std::to_string(mContext.getMixPortHandle());
259 int fd = ashmem_create_region(regionName.c_str(), bufferSizeBytes);
260 if (fd < 0) {
261 PLOG(ERROR) << __func__ << ": failed to create shared memory region of " << bufferSizeBytes
262 << " bytes";
263 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
264 }
265 mSharedMemoryFd = ndk::ScopedFileDescriptor(fd);
266 if (initSharedMemory(mSharedMemoryFd.get()) != ::android::OK) {
267 return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
268 }
269 desc->sharedMemory.fd = mSharedMemoryFd.dup();
270 desc->sharedMemory.size = bufferSizeBytes;
271 desc->burstSizeFrames = bufferSizeFrames / 2;
272 desc->flags = 0;
273 LOG(DEBUG) << __func__ << ": " << desc->toString();
274 return ndk::ScopedAStatus::ok();
275 }
276
StreamInMmapStub(StreamContext && context,const SinkMetadata & sinkMetadata,const std::vector<MicrophoneInfo> & microphones)277 StreamInMmapStub::StreamInMmapStub(StreamContext&& context, const SinkMetadata& sinkMetadata,
278 const std::vector<MicrophoneInfo>& microphones)
279 : StreamIn(std::move(context), microphones), StreamMmapStub(&mContextInstance, sinkMetadata) {}
280
StreamOutMmapStub(StreamContext && context,const SourceMetadata & sourceMetadata,const std::optional<AudioOffloadInfo> & offloadInfo)281 StreamOutMmapStub::StreamOutMmapStub(StreamContext&& context, const SourceMetadata& sourceMetadata,
282 const std::optional<AudioOffloadInfo>& offloadInfo)
283 : StreamOut(std::move(context), offloadInfo),
284 StreamMmapStub(&mContextInstance, sourceMetadata) {}
285
286 } // namespace aidl::android::hardware::audio::core
287