/* * 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. */ #define LOG_TAG "AAudioServiceStreamBase" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include "binding/AAudioServiceMessage.h" #include "core/AudioGlobal.h" #include "utility/AudioClock.h" #include "AAudioEndpointManager.h" #include "AAudioService.h" #include "AAudioServiceEndpoint.h" #include "AAudioServiceStreamBase.h" using namespace android; // TODO just import names needed using namespace aaudio; // TODO just import names needed using content::AttributionSourceState; static const int64_t TIMEOUT_NANOS = 3LL * 1000 * 1000 * 1000; // If the stream is idle for more than `IDLE_TIMEOUT_NANOS`, the stream will be put into standby. static const int64_t IDLE_TIMEOUT_NANOS = 3e9; /** * Base class for streams in the service. * @return */ AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService) : mCommandThread("AACommand") , mAtomicStreamTimestamp() , mAudioService(audioService) { mMmapClient.attributionSource = AttributionSourceState(); } AAudioServiceStreamBase::~AAudioServiceStreamBase() { ALOGD("%s() called", __func__); // May not be set if open failed. if (mMetricsId.size() > 0) { mediametrics::LogItem(mMetricsId) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR) .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) .record(); } // If the stream is deleted when OPEN or in use then audio resources will leak. // This would indicate an internal error. So we want to find this ASAP. LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED), "service stream %p still open, state = %d", this, getState()); // Stop the command thread before destroying. stopCommandThread(); } std::string AAudioServiceStreamBase::dumpHeader() { return {" T Handle UId Port Run State Format Burst Chan Mask Capacity" " HwFormat HwChan HwRate"}; } std::string AAudioServiceStreamBase::dump() const { std::stringstream result; result << " 0x" << std::setfill('0') << std::setw(8) << std::hex << mHandle << std::dec << std::setfill(' ') ; result << std::setw(6) << mMmapClient.attributionSource.uid; result << std::setw(7) << mClientHandle; result << std::setw(4) << (isRunning() ? "yes" : " no"); result << std::setw(6) << getState(); result << std::setw(7) << getFormat(); result << std::setw(6) << mFramesPerBurst; result << std::setw(5) << getSamplesPerFrame(); result << std::setw(8) << std::hex << getChannelMask() << std::dec; result << std::setw(9) << getBufferCapacity(); result << std::setw(9) << getHardwareFormat(); result << std::setw(7) << getHardwareSamplesPerFrame(); result << std::setw(7) << getHardwareSampleRate(); return result.str(); } void AAudioServiceStreamBase::logOpen(aaudio_handle_t streamHandle) { // This is the first log sent from the AAudio Service for a stream. mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM) + std::to_string(streamHandle); audio_attributes_t attributes = AAudioServiceEndpoint::getAudioAttributesFrom(this); // Once this item is logged by the server, the client with the same PID, UID // can also log properties. mediametrics::LogItem(mMetricsId) .setPid(getOwnerProcessId()) .setUid(getOwnerUserId()) .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)getOwnerUserId()) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN) // the following are immutable .set(AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES, (int32_t)getBufferCapacity()) .set(AMEDIAMETRICS_PROP_BURSTFRAMES, (int32_t)getFramesPerBurst()) .set(AMEDIAMETRICS_PROP_CHANNELCOUNT, (int32_t)getSamplesPerFrame()) .set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(attributes.content_type).c_str()) .set(AMEDIAMETRICS_PROP_DIRECTION, AudioGlobal_convertDirectionToText(getDirection())) .set(AMEDIAMETRICS_PROP_ENCODING, toString(getFormat()).c_str()) .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, android::getFirstDeviceId(getDeviceIds())) .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEIDS, android::toString(getDeviceIds()).c_str()) .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)getSampleRate()) .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)getSessionId()) .set(AMEDIAMETRICS_PROP_SOURCE, toString(attributes.source).c_str()) .set(AMEDIAMETRICS_PROP_USAGE, toString(attributes.usage).c_str()) .record(); } aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request) { AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance(); aaudio_result_t result = AAUDIO_OK; mMmapClient.attributionSource = request.getAttributionSource(); // TODO b/182392769: use attribution source util mMmapClient.attributionSource.uid = VALUE_OR_FATAL( legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid())); mMmapClient.attributionSource.pid = VALUE_OR_FATAL( legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid())); // Limit scope of lock to avoid recursive lock in close(). { std::lock_guard lock(mUpMessageQueueLock); if (mUpMessageQueue != nullptr) { ALOGE("%s() called twice", __func__); return AAUDIO_ERROR_INVALID_STATE; } mUpMessageQueue = std::make_shared(); result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage), QUEUE_UP_CAPACITY_COMMANDS); if (result != AAUDIO_OK) { goto error; } // This is not protected by a lock because the stream cannot be // referenced until the service returns a handle to the client. // So only one thread can open a stream. mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, request); if (mServiceEndpoint == nullptr) { result = AAUDIO_ERROR_UNAVAILABLE; goto error; } // Save a weak pointer that we will use to access the endpoint. mServiceEndpointWeak = mServiceEndpoint; mFramesPerBurst = mServiceEndpoint->getFramesPerBurst(); copyFrom(*mServiceEndpoint); } // Make sure this object does not get deleted before the run() method // can protect it by making a strong pointer. mCommandQueue.startWaiting(); mThreadEnabled = true; incStrong(nullptr); // See run() method. result = mCommandThread.start(this); if (result != AAUDIO_OK) { decStrong(nullptr); // run() can't do it so we have to do it here. goto error; } return result; error: closeAndClear(); stopCommandThread(); return result; } aaudio_result_t AAudioServiceStreamBase::close() { aaudio_result_t result = sendCommand(CLOSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); if (result == AAUDIO_ERROR_ALREADY_CLOSED) { // AAUDIO_ERROR_ALREADY_CLOSED is not a really error but just indicate the stream has // already been closed. In that case, there is no need to close the stream once more. ALOGD("The stream(%d) is already closed", mHandle); return AAUDIO_OK; } stopCommandThread(); return result; } aaudio_result_t AAudioServiceStreamBase::close_l() { if (getState() == AAUDIO_STREAM_STATE_CLOSED) { return AAUDIO_ERROR_ALREADY_CLOSED; } // This will stop the stream, just in case it was not already stopped. stop_l(); return closeAndClear(); } aaudio_result_t AAudioServiceStreamBase::startDevice_l() { mClientHandle = AUDIO_PORT_HANDLE_NONE; sp endpoint = mServiceEndpointWeak.promote(); if (endpoint == nullptr) { ALOGE("%s() has no endpoint", __func__); return AAUDIO_ERROR_INVALID_STATE; } if (!endpoint->isConnected()) { ALOGE("%s() endpoint was already disconnected", __func__); return AAUDIO_ERROR_DISCONNECTED; } return endpoint->startStream(this, &mClientHandle); } /** * Start the flow of audio data. * * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete. */ aaudio_result_t AAudioServiceStreamBase::start() { return sendCommand(START, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); } aaudio_result_t AAudioServiceStreamBase::start_l() { const int64_t beginNs = AudioClock::getNanoseconds(); aaudio_result_t result = AAUDIO_OK; if (auto state = getState(); state == AAUDIO_STREAM_STATE_CLOSED || isDisconnected_l()) { ALOGW("%s() already CLOSED, returns INVALID_STATE, handle = %d", __func__, getHandle()); return AAUDIO_ERROR_INVALID_STATE; } if (mStandby) { ALOGW("%s() the stream is standby, return ERROR_STANDBY, " "expecting the client call exitStandby before start", __func__); return AAUDIO_ERROR_STANDBY; } mediametrics::Defer defer([&] { mediametrics::LogItem(mMetricsId) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START) .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) .record(); }); if (isRunning()) { return result; } setFlowing(false); setSuspended(false); // Start with fresh presentation timestamps. mAtomicStreamTimestamp.clear(); mClientHandle = AUDIO_PORT_HANDLE_NONE; result = startDevice_l(); if (result != AAUDIO_OK) goto error; // This should happen at the end of the start. sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED, static_cast(mClientHandle)); setState(AAUDIO_STREAM_STATE_STARTED); return result; error: disconnect_l(); return result; } aaudio_result_t AAudioServiceStreamBase::pause() { return sendCommand(PAUSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); } aaudio_result_t AAudioServiceStreamBase::pause_l() { aaudio_result_t result = AAUDIO_OK; if (!isRunning()) { return result; } const int64_t beginNs = AudioClock::getNanoseconds(); mediametrics::Defer defer([&] { mediametrics::LogItem(mMetricsId) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE) .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) .record(); }); setState(AAUDIO_STREAM_STATE_PAUSING); sp endpoint = mServiceEndpointWeak.promote(); if (endpoint == nullptr) { ALOGE("%s() has no endpoint", __func__); result = AAUDIO_ERROR_INVALID_STATE; // for MediaMetric tracking return result; } result = endpoint->stopStream(this, mClientHandle); if (result != AAUDIO_OK) { ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText()); disconnect_l(); // TODO should we return or pause Base first? } sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED); setState(AAUDIO_STREAM_STATE_PAUSED); return result; } aaudio_result_t AAudioServiceStreamBase::stop() { return sendCommand(STOP, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); } aaudio_result_t AAudioServiceStreamBase::stop_l() { aaudio_result_t result = AAUDIO_OK; if (!isRunning()) { ALOGW("%s() stream not running, returning early", __func__); return result; } const int64_t beginNs = AudioClock::getNanoseconds(); mediametrics::Defer defer([&] { mediametrics::LogItem(mMetricsId) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP) .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) .record(); }); setState(AAUDIO_STREAM_STATE_STOPPING); if (result != AAUDIO_OK) { disconnect_l(); return result; } sp endpoint = mServiceEndpointWeak.promote(); if (endpoint == nullptr) { ALOGE("%s() has no endpoint", __func__); result = AAUDIO_ERROR_INVALID_STATE; // for MediaMetric tracking return result; } // TODO wait for data to be played out result = endpoint->stopStream(this, mClientHandle); if (result != AAUDIO_OK) { ALOGE("%s() stopStream returned %d, %s", __func__, result, getTypeText()); disconnect_l(); // TODO what to do with result here? } sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED); setState(AAUDIO_STREAM_STATE_STOPPED); return result; } aaudio_result_t AAudioServiceStreamBase::flush() { return sendCommand(FLUSH, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); } aaudio_result_t AAudioServiceStreamBase::flush_l() { aaudio_result_t result = AAudio_isFlushAllowed(getState()); if (result != AAUDIO_OK) { return result; } const int64_t beginNs = AudioClock::getNanoseconds(); mediametrics::Defer defer([&] { mediametrics::LogItem(mMetricsId) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH) .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) .record(); }); // Data will get flushed when the client receives the FLUSHED event. sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED); setState(AAUDIO_STREAM_STATE_FLUSHED); return AAUDIO_OK; } // implement Runnable, periodically send timestamps to client and process commands from queue. // Enter standby mode if idle for a while. __attribute__((no_sanitize("integer"))) void AAudioServiceStreamBase::run() { ALOGD("%s() %s entering >>>>>>>>>>>>>> COMMANDS", __func__, getTypeText()); // Hold onto the ref counted stream until the end. android::sp holdStream(this); TimestampScheduler timestampScheduler; int64_t nextTimestampReportTime; int64_t nextDataReportTime; // When to try to enter standby. int64_t standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; // Balance the incStrong from when the thread was launched. holdStream->decStrong(nullptr); // Taking mLock while starting the thread. All the operation must be able to // run with holding the lock. std::scoped_lock _l(mLock); int32_t loopCount = 0; while (mThreadEnabled.load()) { loopCount++; int64_t timeoutNanos = -1; // wait forever if (isDisconnected_l() || isIdle_l()) { if (isStandbyImplemented() && !isStandby_l()) { // If not in standby mode, wait until standby time. timeoutNanos = standbyTime - AudioClock::getNanoseconds(); timeoutNanos = std::max(0, timeoutNanos); } // Otherwise, keep `timeoutNanos` as -1 to wait forever until next command. } else if (isRunning()) { timeoutNanos = std::min(nextTimestampReportTime, nextDataReportTime) - AudioClock::getNanoseconds(); timeoutNanos = std::max(0, timeoutNanos); } auto command = mCommandQueue.waitForCommand(timeoutNanos); if (!mThreadEnabled) { // Break the loop if the thread is disabled. break; } // Is it time to send timestamps? if (isRunning() && !isDisconnected_l()) { auto currentTimestamp = AudioClock::getNanoseconds(); if (currentTimestamp >= nextDataReportTime) { reportData_l(); nextDataReportTime = nextDataReportTime_l(); } if (currentTimestamp >= nextTimestampReportTime) { // It is time to update timestamp. if (sendCurrentTimestamp_l() != AAUDIO_OK) { ALOGE("Failed to send current timestamp, stop updating timestamp"); disconnect_l(); } nextTimestampReportTime = timestampScheduler.nextAbsoluteTime(); } } // Is it time to enter standby? if ((isIdle_l() || isDisconnected_l()) && isStandbyImplemented() && !isStandby_l() && (AudioClock::getNanoseconds() >= standbyTime)) { ALOGD("%s() call standby_l(), %d loops", __func__, loopCount); aaudio_result_t result = standby_l(); if (result != AAUDIO_OK) { ALOGW("Failed to enter standby, error = %d", result); // Try again later. standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; } } if (command != nullptr) { ALOGD("%s() got COMMAND opcode %d after %d loops", __func__, command->operationCode, loopCount); std::scoped_lock _commandLock(command->lock); switch (command->operationCode) { case START: command->result = start_l(); timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate()); timestampScheduler.start(AudioClock::getNanoseconds()); nextTimestampReportTime = timestampScheduler.nextAbsoluteTime(); nextDataReportTime = nextDataReportTime_l(); break; case PAUSE: command->result = pause_l(); standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; break; case STOP: command->result = stop_l(); standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; break; case FLUSH: command->result = flush_l(); break; case CLOSE: command->result = close_l(); break; case DISCONNECT: disconnect_l(); break; case REGISTER_AUDIO_THREAD: { auto param = (RegisterAudioThreadParam *) command->parameter.get(); command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT : registerAudioThread_l(param->mOwnerPid, param->mClientThreadId, param->mPriority); } break; case UNREGISTER_AUDIO_THREAD: { auto param = (UnregisterAudioThreadParam *) command->parameter.get(); command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT : unregisterAudioThread_l(param->mClientThreadId); } break; case GET_DESCRIPTION: { auto param = (GetDescriptionParam *) command->parameter.get(); command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT : getDescription_l(param->mParcelable); } break; case EXIT_STANDBY: { auto param = (ExitStandbyParam *) command->parameter.get(); command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT : exitStandby_l(param->mParcelable); standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; } break; case START_CLIENT: { auto param = (StartClientParam *) command->parameter.get(); command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT : startClient_l(param->mClient, param->mAttr, param->mClientHandle); } break; case STOP_CLIENT: { auto param = (StopClientParam *) command->parameter.get(); command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT : stopClient_l(param->mClientHandle); } break; default: ALOGE("Invalid command op code: %d", command->operationCode); break; } if (command->isWaitingForReply) { command->isWaitingForReply = false; command->conditionVariable.notify_one(); } } } ALOGD("%s() %s exiting after %d loops <<<<<<<<<<<<<< COMMANDS", __func__, getTypeText(), loopCount); } void AAudioServiceStreamBase::disconnect() { sendCommand(DISCONNECT); } void AAudioServiceStreamBase::disconnect_l() { if (!isDisconnected_l() && getState() != AAUDIO_STREAM_STATE_CLOSED) { mediametrics::LogItem(mMetricsId) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT) .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) .record(); sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED); setDisconnected_l(true); } } aaudio_result_t AAudioServiceStreamBase::registerAudioThread(pid_t clientThreadId, int priority) { const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review return sendCommand(REGISTER_AUDIO_THREAD, std::make_shared(ownerPid, clientThreadId, priority), true /*waitForReply*/, TIMEOUT_NANOS); } aaudio_result_t AAudioServiceStreamBase::registerAudioThread_l( pid_t ownerPid, pid_t clientThreadId, int priority) { aaudio_result_t result = AAUDIO_OK; if (getRegisteredThread() != AAudioServiceStreamBase::ILLEGAL_THREAD_ID) { ALOGE("AAudioService::registerAudioThread(), thread already registered"); result = AAUDIO_ERROR_INVALID_STATE; } else { setRegisteredThread(clientThreadId); int err = android::requestPriority(ownerPid, clientThreadId, priority, true /* isForApp */); if (err != 0) { ALOGE("AAudioService::registerAudioThread(%d) failed, errno = %d, priority = %d", clientThreadId, errno, priority); result = AAUDIO_ERROR_INTERNAL; } } return result; } aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread(pid_t clientThreadId) { return sendCommand(UNREGISTER_AUDIO_THREAD, std::make_shared(clientThreadId), true /*waitForReply*/, TIMEOUT_NANOS); } aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread_l(pid_t clientThreadId) { aaudio_result_t result = AAUDIO_OK; if (getRegisteredThread() != clientThreadId) { ALOGE("%s(), wrong thread", __func__); result = AAUDIO_ERROR_ILLEGAL_ARGUMENT; } else { setRegisteredThread(0); } return result; } void AAudioServiceStreamBase::setState(aaudio_stream_state_t state) { // CLOSED is a final state. if (mState != AAUDIO_STREAM_STATE_CLOSED) { mState = state; } else { ALOGW_IF(mState != state, "%s(%d) when already CLOSED", __func__, state); } } aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event, double dataDouble) { AAudioServiceMessage command; command.what = AAudioServiceMessage::code::EVENT; command.event.event = event; command.event.dataDouble = dataDouble; return writeUpMessageQueue(&command); } aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event, int64_t dataLong) { AAudioServiceMessage command; command.what = AAudioServiceMessage::code::EVENT; command.event.event = event; command.event.dataLong = dataLong; return writeUpMessageQueue(&command); } bool AAudioServiceStreamBase::isUpMessageQueueBusy() { std::lock_guard lock(mUpMessageQueueLock); if (mUpMessageQueue == nullptr) { ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); return true; } // Is it half full or more return mUpMessageQueue->getFractionalFullness() >= 0.5; } aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) { std::lock_guard lock(mUpMessageQueueLock); if (mUpMessageQueue == nullptr) { ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); return AAUDIO_ERROR_NULL; } int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1); if (count != 1) { ALOGW("%s(): Queue full. Did client stop? Suspending stream. what = %u, %s", __func__, static_cast(command->what), getTypeText()); setSuspended(true); return AAUDIO_ERROR_WOULD_BLOCK; } else { if (isSuspended()) { ALOGW("%s(): Queue no longer full. Un-suspending the stream.", __func__); setSuspended(false); } return AAUDIO_OK; } } aaudio_result_t AAudioServiceStreamBase::sendXRunCount(int32_t xRunCount) { return sendServiceEvent(AAUDIO_SERVICE_EVENT_XRUN, (int64_t) xRunCount); } aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp_l() { AAudioServiceMessage command; // It is not worth filling up the queue with timestamps. // That can cause the stream to get suspended. // So just drop the timestamp if the queue is getting full. if (isUpMessageQueueBusy()) { return AAUDIO_OK; } // Send a timestamp for the clock model. aaudio_result_t result = getFreeRunningPosition_l(&command.timestamp.position, &command.timestamp.timestamp); if (result == AAUDIO_OK) { ALOGV("%s() SERVICE %8lld at %lld", __func__, (long long) command.timestamp.position, (long long) command.timestamp.timestamp); command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE; result = writeUpMessageQueue(&command); if (result == AAUDIO_OK) { // Send a hardware timestamp for presentation time. result = getHardwareTimestamp_l(&command.timestamp.position, &command.timestamp.timestamp); if (result == AAUDIO_OK) { ALOGV("%s() HARDWARE %8lld at %lld", __func__, (long long) command.timestamp.position, (long long) command.timestamp.timestamp); command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE; result = writeUpMessageQueue(&command); } } } if (result == AAUDIO_ERROR_UNAVAILABLE) { // TODO review best error code result = AAUDIO_OK; // just not available yet, try again later } return result; } /** * Get an immutable description of the in-memory queues * used to communicate with the underlying HAL or Service. */ aaudio_result_t AAudioServiceStreamBase::getDescription(AudioEndpointParcelable &parcelable) { return sendCommand( GET_DESCRIPTION, std::make_shared(&parcelable), true /*waitForReply*/, TIMEOUT_NANOS); } aaudio_result_t AAudioServiceStreamBase::getDescription_l(AudioEndpointParcelable* parcelable) { { std::lock_guard lock(mUpMessageQueueLock); if (mUpMessageQueue == nullptr) { ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); return AAUDIO_ERROR_NULL; } // Gather information on the message queue. mUpMessageQueue->fillParcelable(parcelable, parcelable->mUpMessageQueueParcelable); } return getAudioDataDescription_l(parcelable); } aaudio_result_t AAudioServiceStreamBase::exitStandby(AudioEndpointParcelable *parcelable) { auto command = std::make_shared( EXIT_STANDBY, std::make_shared(parcelable), true /*waitForReply*/, TIMEOUT_NANOS); return mCommandQueue.sendCommand(command); } aaudio_result_t AAudioServiceStreamBase::sendStartClientCommand(const android::AudioClient &client, const audio_attributes_t *attr, audio_port_handle_t *clientHandle) { auto command = std::make_shared( START_CLIENT, std::make_shared(client, attr, clientHandle), true /*waitForReply*/, TIMEOUT_NANOS); return mCommandQueue.sendCommand(command); } aaudio_result_t AAudioServiceStreamBase::sendStopClientCommand(audio_port_handle_t clientHandle) { auto command = std::make_shared( STOP_CLIENT, std::make_shared(clientHandle), true /*waitForReply*/, TIMEOUT_NANOS); return mCommandQueue.sendCommand(command); } void AAudioServiceStreamBase::onVolumeChanged(float volume) { sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume); } aaudio_result_t AAudioServiceStreamBase::sendCommand(aaudio_command_opcode opCode, std::shared_ptr param, bool waitForReply, int64_t timeoutNanos) { return mCommandQueue.sendCommand(std::make_shared( opCode, param, waitForReply, timeoutNanos)); } aaudio_result_t AAudioServiceStreamBase::closeAndClear() { aaudio_result_t result = AAUDIO_OK; sp endpoint = mServiceEndpointWeak.promote(); if (endpoint == nullptr) { result = AAUDIO_ERROR_INVALID_STATE; } else { endpoint->unregisterStream(this); AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance(); endpointManager.closeEndpoint(endpoint); // AAudioService::closeStream() prevents two threads from closing at the same time. mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns. } setState(AAUDIO_STREAM_STATE_CLOSED); mediametrics::LogItem(mMetricsId) .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE) .record(); return result; } void AAudioServiceStreamBase::stopCommandThread() { bool threadEnabled = true; if (mThreadEnabled.compare_exchange_strong(threadEnabled, false)) { mCommandQueue.stopWaiting(); mCommandThread.stop(); } }