| /* | 
 |  * Copyright (C) 2016 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 <time.h> | 
 |  | 
 | #define LOG_TAG "StreamHalHidl" | 
 | //#define LOG_NDEBUG 0 | 
 |  | 
 | #include <android/hardware/audio/2.0/IStreamOutCallback.h> | 
 | #include <utils/Log.h> | 
 |  | 
 | #include "DeviceHalHidl.h" | 
 | #include "EffectHalHidl.h" | 
 | #include "StreamHalHidl.h" | 
 |  | 
 | using ::android::hardware::audio::common::V2_0::AudioChannelMask; | 
 | using ::android::hardware::audio::common::V2_0::AudioFormat; | 
 | using ::android::hardware::audio::V2_0::AudioDrain; | 
 | using ::android::hardware::audio::V2_0::IStreamOutCallback; | 
 | using ::android::hardware::audio::V2_0::MessageQueueFlagBits; | 
 | using ::android::hardware::audio::V2_0::MmapBufferInfo; | 
 | using ::android::hardware::audio::V2_0::MmapPosition; | 
 | using ::android::hardware::audio::V2_0::ParameterValue; | 
 | using ::android::hardware::audio::V2_0::Result; | 
 | using ::android::hardware::audio::V2_0::ThreadPriority; | 
 | using ::android::hardware::audio::V2_0::TimeSpec; | 
 | using ::android::hardware::MQDescriptorSync; | 
 | using ::android::hardware::Return; | 
 | using ::android::hardware::Void; | 
 |  | 
 | namespace android { | 
 |  | 
 | StreamHalHidl::StreamHalHidl(IStream *stream) | 
 |         : ConversionHelperHidl("Stream"), | 
 |           mHalThreadPriority(static_cast<int>(ThreadPriority::NORMAL)), | 
 |           mStream(stream) { | 
 | } | 
 |  | 
 | StreamHalHidl::~StreamHalHidl() { | 
 |     mStream = nullptr; | 
 | } | 
 |  | 
 | status_t StreamHalHidl::getSampleRate(uint32_t *rate) { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("getSampleRate", mStream->getSampleRate(), rate); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::getBufferSize(size_t *size) { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("getBufferSize", mStream->getBufferSize(), size); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::getChannelMask(audio_channel_mask_t *mask) { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("getChannelMask", mStream->getChannelMask(), mask); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::getFormat(audio_format_t *format) { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("getFormat", mStream->getFormat(), format); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::getAudioProperties( | 
 |         uint32_t *sampleRate, audio_channel_mask_t *mask, audio_format_t *format) { | 
 |     if (!mStream) return NO_INIT; | 
 |     Return<void> ret = mStream->getAudioProperties( | 
 |             [&](uint32_t sr, AudioChannelMask m, AudioFormat f) { | 
 |                 *sampleRate = sr; | 
 |                 *mask = static_cast<audio_channel_mask_t>(m); | 
 |                 *format = static_cast<audio_format_t>(f); | 
 |             }); | 
 |     return processReturn("getAudioProperties", ret); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::setParameters(const String8& kvPairs) { | 
 |     if (!mStream) return NO_INIT; | 
 |     hidl_vec<ParameterValue> hidlParams; | 
 |     status_t status = parametersFromHal(kvPairs, &hidlParams); | 
 |     if (status != OK) return status; | 
 |     return processReturn("setParameters", mStream->setParameters(hidlParams)); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { | 
 |     values->clear(); | 
 |     if (!mStream) return NO_INIT; | 
 |     hidl_vec<hidl_string> hidlKeys; | 
 |     status_t status = keysFromHal(keys, &hidlKeys); | 
 |     if (status != OK) return status; | 
 |     Result retval; | 
 |     Return<void> ret = mStream->getParameters( | 
 |             hidlKeys, | 
 |             [&](Result r, const hidl_vec<ParameterValue>& parameters) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     parametersToHal(parameters, values); | 
 |                 } | 
 |             }); | 
 |     return processReturn("getParameters", ret, retval); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::addEffect(sp<EffectHalInterface> effect) { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("addEffect", mStream->addEffect( | 
 |                     static_cast<EffectHalHidl*>(effect.get())->effectId())); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::removeEffect(sp<EffectHalInterface> effect) { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("removeEffect", mStream->removeEffect( | 
 |                     static_cast<EffectHalHidl*>(effect.get())->effectId())); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::standby() { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("standby", mStream->standby()); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::dump(int fd) { | 
 |     if (!mStream) return NO_INIT; | 
 |     native_handle_t* hidlHandle = native_handle_create(1, 0); | 
 |     hidlHandle->data[0] = fd; | 
 |     Return<void> ret = mStream->debugDump(hidlHandle); | 
 |     native_handle_delete(hidlHandle); | 
 |     return processReturn("dump", ret); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::start() { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("start", mStream->start()); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::stop() { | 
 |     if (!mStream) return NO_INIT; | 
 |     return processReturn("stop", mStream->stop()); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames, | 
 |                                   struct audio_mmap_buffer_info *info) { | 
 |     Result retval; | 
 |     Return<void> ret = mStream->createMmapBuffer( | 
 |             minSizeFrames, | 
 |             [&](Result r, const MmapBufferInfo& hidlInfo) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     const native_handle *handle = hidlInfo.sharedMemory.handle(); | 
 |                     if (handle->numFds > 0) { | 
 |                         info->shared_memory_fd = dup(handle->data[0]); | 
 |                         info->buffer_size_frames = hidlInfo.bufferSizeFrames; | 
 |                         info->burst_size_frames = hidlInfo.burstSizeFrames; | 
 |                         // info->shared_memory_address is not needed in HIDL context | 
 |                         info->shared_memory_address = NULL; | 
 |                     } else { | 
 |                         retval = Result::NOT_INITIALIZED; | 
 |                     } | 
 |                 } | 
 |             }); | 
 |     return processReturn("createMmapBuffer", ret, retval); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::getMmapPosition(struct audio_mmap_position *position) { | 
 |     Result retval; | 
 |     Return<void> ret = mStream->getMmapPosition( | 
 |             [&](Result r, const MmapPosition& hidlPosition) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     position->time_nanoseconds = hidlPosition.timeNanoseconds; | 
 |                     position->position_frames = hidlPosition.positionFrames; | 
 |                 } | 
 |             }); | 
 |     return processReturn("getMmapPosition", ret, retval); | 
 | } | 
 |  | 
 | status_t StreamHalHidl::setHalThreadPriority(int priority) { | 
 |     mHalThreadPriority = priority; | 
 |     return OK; | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | /* Notes on callback ownership. | 
 |  | 
 | This is how (Hw)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 StreamOutCallback (this is | 
 | the server impl) -- it will live as long as HAL server holds a strong ref to | 
 | IStreamOutCallback proxy. We clear that reference by calling 'clearCallback' | 
 | from the destructor of StreamOutHalHidl. | 
 |  | 
 | The callback only keeps a weak reference to the stream. The stream is owned | 
 | by AudioFlinger. | 
 |  | 
 | */ | 
 |  | 
 | struct StreamOutCallback : public IStreamOutCallback { | 
 |     StreamOutCallback(const wp<StreamOutHalHidl>& stream) : mStream(stream) {} | 
 |  | 
 |     // IStreamOutCallback implementation | 
 |     Return<void> onWriteReady()  override { | 
 |         sp<StreamOutHalHidl> stream = mStream.promote(); | 
 |         if (stream != 0) { | 
 |             stream->onWriteReady(); | 
 |         } | 
 |         return Void(); | 
 |     } | 
 |  | 
 |     Return<void> onDrainReady()  override { | 
 |         sp<StreamOutHalHidl> stream = mStream.promote(); | 
 |         if (stream != 0) { | 
 |             stream->onDrainReady(); | 
 |         } | 
 |         return Void(); | 
 |     } | 
 |  | 
 |     Return<void> onError()  override { | 
 |         sp<StreamOutHalHidl> stream = mStream.promote(); | 
 |         if (stream != 0) { | 
 |             stream->onError(); | 
 |         } | 
 |         return Void(); | 
 |     } | 
 |  | 
 |   private: | 
 |     wp<StreamOutHalHidl> mStream; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | StreamOutHalHidl::StreamOutHalHidl(const sp<IStreamOut>& stream) | 
 |         : StreamHalHidl(stream.get()), mStream(stream), mEfGroup(nullptr), | 
 |           mGetPresentationPositionNotSupported(false), mPPosFromWriteObtained(0) { | 
 | } | 
 |  | 
 | StreamOutHalHidl::~StreamOutHalHidl() { | 
 |     if (mStream != 0) { | 
 |         if (mCallback.unsafe_get()) { | 
 |             processReturn("clearCallback", mStream->clearCallback()); | 
 |         } | 
 |         processReturn("close", mStream->close()); | 
 |     } | 
 |     mCallback.clear(); | 
 |     if (mEfGroup) { | 
 |         EventFlag::deleteEventFlag(&mEfGroup); | 
 |     } | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::getFrameSize(size_t *size) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("getFrameSize", mStream->getFrameSize(), size); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::getLatency(uint32_t *latency) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("getLatency", mStream->getLatency(), latency); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::setVolume(float left, float right) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("setVolume", mStream->setVolume(left, right)); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     *written = 0; | 
 |  | 
 |     if (bytes == 0 && !mDataMQ) { | 
 |         // Can't determine the size for the MQ buffer. Wait for a non-empty write request. | 
 |         ALOGW_IF(mCallback.unsafe_get(), "First call to async write with 0 bytes"); | 
 |         return OK; | 
 |     } | 
 |  | 
 |     status_t status; | 
 |     if (!mDataMQ && (status = prepareForWriting(bytes)) != OK) { | 
 |         return status; | 
 |     } | 
 |  | 
 |     const size_t availBytes = mDataMQ->availableToWrite(); | 
 |     if (bytes > availBytes) { bytes = availBytes; } | 
 |     if (!mDataMQ->write(static_cast<const uint8_t*>(buffer), bytes)) { | 
 |         ALOGW("data message queue write failed"); | 
 |     } | 
 |     mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)); | 
 |  | 
 |     // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422 | 
 |     uint32_t efState = 0; | 
 | retry: | 
 |     status_t ret = mEfGroup->wait( | 
 |             static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState, NS_PER_SEC); | 
 |     if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)) { | 
 |         WriteStatus writeStatus = { Result::NOT_INITIALIZED, 0, 0, { 0, 0 } }; | 
 |         mStatusMQ->read(&writeStatus); | 
 |         if (writeStatus.retval == Result::OK) { | 
 |             status = OK; | 
 |             *written = writeStatus.written; | 
 |             mPPosFromWriteFrames = writeStatus.frames; | 
 |             mPPosFromWriteTS.tv_sec = writeStatus.timeStamp.tvSec; | 
 |             mPPosFromWriteTS.tv_nsec = writeStatus.timeStamp.tvNSec; | 
 |             struct timespec timeNow; | 
 |             clock_gettime(CLOCK_MONOTONIC, &timeNow); | 
 |             mPPosFromWriteObtained = timeNow.tv_sec * 1000000 + timeNow.tv_nsec / 1000; | 
 |         } else { | 
 |             status = processReturn("write", writeStatus.retval); | 
 |         } | 
 |         return status; | 
 |     } | 
 |     if (ret == -EAGAIN) { | 
 |         // This normally retries no more than once. | 
 |         goto retry; | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::prepareForWriting(size_t bufferSize) { | 
 |     std::unique_ptr<DataMQ> tempDataMQ; | 
 |     std::unique_ptr<StatusMQ> tempStatusMQ; | 
 |     Result retval; | 
 |     Return<void> ret = mStream->prepareForWriting( | 
 |             1, bufferSize, ThreadPriority(mHalThreadPriority), | 
 |             [&](Result r, | 
 |                     const MQDescriptorSync<uint8_t>& dataMQ, | 
 |                     const MQDescriptorSync<WriteStatus>& statusMQ) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     tempDataMQ.reset(new DataMQ(dataMQ)); | 
 |                     tempStatusMQ.reset(new StatusMQ(statusMQ)); | 
 |                     if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { | 
 |                         EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); | 
 |                     } | 
 |                 } | 
 |             }); | 
 |     if (!ret.isOk() || retval != Result::OK) { | 
 |         return processReturn("prepareForWriting", ret, retval); | 
 |     } | 
 |     if (!tempDataMQ || !tempDataMQ->isValid() || !tempStatusMQ || !tempStatusMQ->isValid() | 
 |         || !mEfGroup) { | 
 |         ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing"); | 
 |         ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for writing is invalid"); | 
 |         ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing"); | 
 |         ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), | 
 |                 "Status message queue for writing is invalid"); | 
 |         ALOGE_IF(!mEfGroup, "Event flag creation for writing failed"); | 
 |         return NO_INIT; | 
 |     } | 
 |     mDataMQ = std::move(tempDataMQ); | 
 |     mStatusMQ = std::move(tempStatusMQ); | 
 |     return OK; | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     Result retval; | 
 |     Return<void> ret = mStream->getRenderPosition( | 
 |             [&](Result r, uint32_t d) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     *dspFrames = d; | 
 |                 } | 
 |             }); | 
 |     return processReturn("getRenderPosition", ret, retval); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     Result retval; | 
 |     Return<void> ret = mStream->getNextWriteTimestamp( | 
 |             [&](Result r, int64_t t) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     *timestamp = t; | 
 |                 } | 
 |             }); | 
 |     return processReturn("getRenderPosition", ret, retval); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::setCallback(wp<StreamOutHalInterfaceCallback> callback) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     status_t status = processReturn( | 
 |             "setCallback", mStream->setCallback(new StreamOutCallback(this))); | 
 |     if (status == OK) { | 
 |         mCallback = callback; | 
 |     } | 
 |     return status; | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     Return<void> ret = mStream->supportsPauseAndResume( | 
 |             [&](bool p, bool r) { | 
 |                 *supportsPause = p; | 
 |                 *supportsResume = r; | 
 |             }); | 
 |     return processReturn("supportsPauseAndResume", ret); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::pause() { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("pause", mStream->pause()); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::resume() { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("pause", mStream->resume()); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::drain(bool earlyNotify) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn( | 
 |             "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL)); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::flush() { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("pause", mStream->flush()); | 
 | } | 
 |  | 
 | status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     if (mGetPresentationPositionNotSupported) return INVALID_OPERATION; | 
 |     struct timespec timeNow; | 
 |     clock_gettime(CLOCK_MONOTONIC, &timeNow); | 
 |     uint64_t timeStampNow = timeNow.tv_sec * 1000000 + timeNow.tv_nsec / 1000; | 
 |     if (timeStampNow - mPPosFromWriteObtained <= 1000) { | 
 |         // No more than 1 ms passed since the last write, use cached result to avoid binder calls. | 
 |         *frames = mPPosFromWriteFrames; | 
 |         timestamp->tv_sec = mPPosFromWriteTS.tv_sec; | 
 |         timestamp->tv_nsec = mPPosFromWriteTS.tv_nsec; | 
 |         return OK; | 
 |     } | 
 |  | 
 |     Result retval; | 
 |     Return<void> ret = mStream->getPresentationPosition( | 
 |             [&](Result r, uint64_t hidlFrames, const TimeSpec& hidlTimeStamp) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     *frames = hidlFrames; | 
 |                     timestamp->tv_sec = hidlTimeStamp.tvSec; | 
 |                     timestamp->tv_nsec = hidlTimeStamp.tvNSec; | 
 |                 } | 
 |             }); | 
 |     if (ret.isOk() && retval == Result::NOT_SUPPORTED) { | 
 |         mGetPresentationPositionNotSupported = true; | 
 |     } | 
 |     return processReturn("getPresentationPosition", ret, retval); | 
 | } | 
 |  | 
 | void StreamOutHalHidl::onWriteReady() { | 
 |     sp<StreamOutHalInterfaceCallback> callback = mCallback.promote(); | 
 |     if (callback == 0) return; | 
 |     ALOGV("asyncCallback onWriteReady"); | 
 |     callback->onWriteReady(); | 
 | } | 
 |  | 
 | void StreamOutHalHidl::onDrainReady() { | 
 |     sp<StreamOutHalInterfaceCallback> callback = mCallback.promote(); | 
 |     if (callback == 0) return; | 
 |     ALOGV("asyncCallback onDrainReady"); | 
 |     callback->onDrainReady(); | 
 | } | 
 |  | 
 | void StreamOutHalHidl::onError() { | 
 |     sp<StreamOutHalInterfaceCallback> callback = mCallback.promote(); | 
 |     if (callback == 0) return; | 
 |     ALOGV("asyncCallback onError"); | 
 |     callback->onError(); | 
 | } | 
 |  | 
 |  | 
 | StreamInHalHidl::StreamInHalHidl(const sp<IStreamIn>& stream) | 
 |         : StreamHalHidl(stream.get()), mStream(stream), mEfGroup(nullptr) { | 
 | } | 
 |  | 
 | StreamInHalHidl::~StreamInHalHidl() { | 
 |     if (mStream != 0) { | 
 |         processReturn("close", mStream->close()); | 
 |     } | 
 |     if (mEfGroup) { | 
 |         EventFlag::deleteEventFlag(&mEfGroup); | 
 |     } | 
 | } | 
 |  | 
 | status_t StreamInHalHidl::getFrameSize(size_t *size) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("getFrameSize", mStream->getFrameSize(), size); | 
 | } | 
 |  | 
 | status_t StreamInHalHidl::setGain(float gain) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("setGain", mStream->setGain(gain)); | 
 | } | 
 |  | 
 | status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     *read = 0; | 
 |  | 
 |     if (bytes == 0 && !mDataMQ) { | 
 |         // Can't determine the size for the MQ buffer. Wait for a non-empty read request. | 
 |         return OK; | 
 |     } | 
 |  | 
 |     status_t status; | 
 |     if (!mDataMQ) { | 
 |         if ((status = prepareForReading(bytes)) != OK) return status; | 
 |         // Trigger the first read. | 
 |         mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)); | 
 |     } | 
 |  | 
 |     // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422 | 
 |     uint32_t efState = 0; | 
 | retry: | 
 |     status_t ret = mEfGroup->wait( | 
 |             static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState, NS_PER_SEC); | 
 |     if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)) { | 
 |         ReadStatus readStatus = { Result::NOT_INITIALIZED, 0 }; | 
 |         const size_t availToRead = mDataMQ->availableToRead(); | 
 |         if (bytes > availToRead) { bytes = availToRead; } | 
 |         mDataMQ->read(static_cast<uint8_t*>(buffer), bytes); | 
 |         mStatusMQ->read(&readStatus); | 
 |         mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)); | 
 |         if (readStatus.retval == Result::OK) { | 
 |             ALOGW_IF(availToRead != readStatus.read, | 
 |                     "HAL read report inconsistent: mq = %d, status = %d", | 
 |                     (int32_t)availToRead, (int32_t)readStatus.read); | 
 |             *read = readStatus.read; | 
 |         } else { | 
 |             status = processReturn("read", readStatus.retval); | 
 |         } | 
 |         return status; | 
 |     } | 
 |     if (ret == -EAGAIN) { | 
 |         // This normally retries no more than once. | 
 |         goto retry; | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | status_t StreamInHalHidl::prepareForReading(size_t bufferSize) { | 
 |     std::unique_ptr<DataMQ> tempDataMQ; | 
 |     std::unique_ptr<StatusMQ> tempStatusMQ; | 
 |     Result retval; | 
 |     Return<void> ret = mStream->prepareForReading( | 
 |             1, bufferSize, ThreadPriority(mHalThreadPriority), | 
 |             [&](Result r, | 
 |                     const MQDescriptorSync<uint8_t>& dataMQ, | 
 |                     const MQDescriptorSync<ReadStatus>& statusMQ) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     tempDataMQ.reset(new DataMQ(dataMQ)); | 
 |                     tempStatusMQ.reset(new StatusMQ(statusMQ)); | 
 |                     if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) { | 
 |                         EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); | 
 |                     } | 
 |                 } | 
 |             }); | 
 |     if (!ret.isOk() || retval != Result::OK) { | 
 |         return processReturn("prepareForReading", ret, retval); | 
 |     } | 
 |     if (!tempDataMQ || !tempDataMQ->isValid() || !tempStatusMQ || !tempStatusMQ->isValid() | 
 |         || !mEfGroup) { | 
 |         ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading"); | 
 |         ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for reading is invalid"); | 
 |         ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading"); | 
 |         ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(), | 
 |                 "Status message queue for reading is invalid"); | 
 |         ALOGE_IF(!mEfGroup, "Event flag creation for reading failed"); | 
 |         return NO_INIT; | 
 |     } | 
 |     mDataMQ = std::move(tempDataMQ); | 
 |     mStatusMQ = std::move(tempStatusMQ); | 
 |     return OK; | 
 | } | 
 |  | 
 | status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost); | 
 | } | 
 |  | 
 | status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) { | 
 |     if (mStream == 0) return NO_INIT; | 
 |     Result retval; | 
 |     Return<void> ret = mStream->getCapturePosition( | 
 |             [&](Result r, uint64_t hidlFrames, uint64_t hidlTime) { | 
 |                 retval = r; | 
 |                 if (retval == Result::OK) { | 
 |                     *frames = hidlFrames; | 
 |                     *time = hidlTime; | 
 |                 } | 
 |             }); | 
 |     return processReturn("getCapturePosition", ret, retval); | 
 | } | 
 |  | 
 | } // namespace android |