• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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