| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2016 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 |  | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 17 | #define LOG_TAG "AAudioServiceStreamBase" | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 18 | //#define LOG_NDEBUG 0 | 
|  | 19 | #include <utils/Log.h> | 
|  | 20 |  | 
| Phil Burk | a5222e2 | 2017-07-28 13:31:14 -0700 | [diff] [blame] | 21 | #include <iomanip> | 
|  | 22 | #include <iostream> | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 23 | #include <mutex> | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 24 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 25 | #include <media/MediaMetricsItem.h> | 
|  | 26 | #include <media/TypeConverter.h> | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 27 | #include <mediautils/SchedulingPolicyService.h> | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 28 |  | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 29 | #include "binding/AAudioServiceMessage.h" | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 30 | #include "core/AudioGlobal.h" | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 31 | #include "utility/AudioClock.h" | 
|  | 32 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 33 | #include "AAudioEndpointManager.h" | 
|  | 34 | #include "AAudioService.h" | 
|  | 35 | #include "AAudioServiceEndpoint.h" | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 36 | #include "AAudioServiceStreamBase.h" | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 37 |  | 
|  | 38 | using namespace android;  // TODO just import names needed | 
|  | 39 | using namespace aaudio;   // TODO just import names needed | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 40 |  | 
| Svet Ganov | 3376113 | 2021-05-13 22:51:08 +0000 | [diff] [blame] | 41 | using content::AttributionSourceState; | 
| Philip P. Moltmann | bda4575 | 2020-07-17 16:41:18 -0700 | [diff] [blame] | 42 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 43 | static const int64_t TIMEOUT_NANOS = 3LL * 1000 * 1000 * 1000; | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 44 | // If the stream is idle for more than `IDLE_TIMEOUT_NANOS`, the stream will be put into standby. | 
|  | 45 | static const int64_t IDLE_TIMEOUT_NANOS = 3e9; | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 46 |  | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 47 | /** | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 48 | * Base class for streams in the service. | 
|  | 49 | * @return | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 50 | */ | 
|  | 51 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 52 | AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService) | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 53 | : mCommandThread("AACommand") | 
| Phil Burk | a53ffa6 | 2018-10-10 16:21:37 -0700 | [diff] [blame] | 54 | , mAtomicStreamTimestamp() | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 55 | , mAudioService(audioService) { | 
| Svet Ganov | 3376113 | 2021-05-13 22:51:08 +0000 | [diff] [blame] | 56 | mMmapClient.attributionSource = AttributionSourceState(); | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 57 | } | 
|  | 58 |  | 
| Phil Burk | 5ed503c | 2017-02-01 09:38:15 -0800 | [diff] [blame] | 59 | AAudioServiceStreamBase::~AAudioServiceStreamBase() { | 
| Phil Burk | 8f4fe50 | 2020-07-15 23:54:50 +0000 | [diff] [blame] | 60 | ALOGD("%s() called", __func__); | 
|  | 61 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 62 | // May not be set if open failed. | 
|  | 63 | if (mMetricsId.size() > 0) { | 
|  | 64 | mediametrics::LogItem(mMetricsId) | 
|  | 65 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR) | 
|  | 66 | .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) | 
|  | 67 | .record(); | 
|  | 68 | } | 
|  | 69 |  | 
| Phil Burk | 5a26e66 | 2017-07-07 12:44:48 -0700 | [diff] [blame] | 70 | // If the stream is deleted when OPEN or in use then audio resources will leak. | 
|  | 71 | // This would indicate an internal error. So we want to find this ASAP. | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 72 | LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED | 
| Phil Burk | db46614 | 2021-04-16 16:50:00 +0000 | [diff] [blame] | 73 | || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED), | 
| Phil Burk | 8b4e05e | 2019-12-17 12:12:09 -0800 | [diff] [blame] | 74 | "service stream %p still open, state = %d", | 
|  | 75 | this, getState()); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 76 |  | 
|  | 77 | // Stop the command thread before destroying. | 
|  | 78 | if (mThreadEnabled) { | 
|  | 79 | mThreadEnabled = false; | 
|  | 80 | mCommandQueue.stopWaiting(); | 
|  | 81 | mCommandThread.stop(); | 
|  | 82 | } | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 83 | } | 
|  | 84 |  | 
| Phil Burk | a5222e2 | 2017-07-28 13:31:14 -0700 | [diff] [blame] | 85 | std::string AAudioServiceStreamBase::dumpHeader() { | 
| jiabin | a909409 | 2021-06-28 20:36:45 +0000 | [diff] [blame] | 86 | return std::string( | 
|  | 87 | "    T   Handle   UId   Port Run State Format Burst Chan Mask     Capacity"); | 
| Phil Burk | a5222e2 | 2017-07-28 13:31:14 -0700 | [diff] [blame] | 88 | } | 
|  | 89 |  | 
| Phil Burk | 4501b35 | 2017-06-29 18:12:36 -0700 | [diff] [blame] | 90 | std::string AAudioServiceStreamBase::dump() const { | 
|  | 91 | std::stringstream result; | 
|  | 92 |  | 
| Phil Burk | a5222e2 | 2017-07-28 13:31:14 -0700 | [diff] [blame] | 93 | result << "    0x" << std::setfill('0') << std::setw(8) << std::hex << mHandle | 
|  | 94 | << std::dec << std::setfill(' ') ; | 
| Svet Ganov | 3376113 | 2021-05-13 22:51:08 +0000 | [diff] [blame] | 95 | result << std::setw(6) << mMmapClient.attributionSource.uid; | 
| Phil Burk | bbd5286 | 2018-04-13 11:37:42 -0700 | [diff] [blame] | 96 | result << std::setw(7) << mClientHandle; | 
| Phil Burk | a5222e2 | 2017-07-28 13:31:14 -0700 | [diff] [blame] | 97 | result << std::setw(4) << (isRunning() ? "yes" : " no"); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 98 | result << std::setw(6) << getState(); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 99 | result << std::setw(7) << getFormat(); | 
| Phil Burk | a5222e2 | 2017-07-28 13:31:14 -0700 | [diff] [blame] | 100 | result << std::setw(6) << mFramesPerBurst; | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 101 | result << std::setw(5) << getSamplesPerFrame(); | 
| jiabin | a909409 | 2021-06-28 20:36:45 +0000 | [diff] [blame] | 102 | result << std::setw(8) << std::hex << getChannelMask() << std::dec; | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 103 | result << std::setw(9) << getBufferCapacity(); | 
| Phil Burk | 4501b35 | 2017-06-29 18:12:36 -0700 | [diff] [blame] | 104 |  | 
|  | 105 | return result.str(); | 
|  | 106 | } | 
|  | 107 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 108 | void AAudioServiceStreamBase::logOpen(aaudio_handle_t streamHandle) { | 
|  | 109 | // This is the first log sent from the AAudio Service for a stream. | 
|  | 110 | mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM) | 
|  | 111 | + std::to_string(streamHandle); | 
|  | 112 |  | 
|  | 113 | audio_attributes_t attributes = AAudioServiceEndpoint::getAudioAttributesFrom(this); | 
|  | 114 |  | 
|  | 115 | // Once this item is logged by the server, the client with the same PID, UID | 
|  | 116 | // can also log properties. | 
|  | 117 | mediametrics::LogItem(mMetricsId) | 
|  | 118 | .setPid(getOwnerProcessId()) | 
|  | 119 | .setUid(getOwnerUserId()) | 
| Andy Hung | d203eb6 | 2020-04-27 09:12:46 -0700 | [diff] [blame] | 120 | .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)getOwnerUserId()) | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 121 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_OPEN) | 
|  | 122 | // the following are immutable | 
|  | 123 | .set(AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES, (int32_t)getBufferCapacity()) | 
|  | 124 | .set(AMEDIAMETRICS_PROP_BURSTFRAMES, (int32_t)getFramesPerBurst()) | 
|  | 125 | .set(AMEDIAMETRICS_PROP_CHANNELCOUNT, (int32_t)getSamplesPerFrame()) | 
|  | 126 | .set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(attributes.content_type).c_str()) | 
|  | 127 | .set(AMEDIAMETRICS_PROP_DIRECTION, | 
|  | 128 | AudioGlobal_convertDirectionToText(getDirection())) | 
|  | 129 | .set(AMEDIAMETRICS_PROP_ENCODING, toString(getFormat()).c_str()) | 
|  | 130 | .set(AMEDIAMETRICS_PROP_ROUTEDDEVICEID, (int32_t)getDeviceId()) | 
|  | 131 | .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)getSampleRate()) | 
|  | 132 | .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)getSessionId()) | 
|  | 133 | .set(AMEDIAMETRICS_PROP_SOURCE, toString(attributes.source).c_str()) | 
|  | 134 | .set(AMEDIAMETRICS_PROP_USAGE, toString(attributes.usage).c_str()) | 
|  | 135 | .record(); | 
|  | 136 | } | 
|  | 137 |  | 
| Phil Burk | 15f97c9 | 2018-09-04 14:06:27 -0700 | [diff] [blame] | 138 | aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request) { | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 139 | AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance(); | 
|  | 140 | aaudio_result_t result = AAUDIO_OK; | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 141 |  | 
| Svet Ganov | 3376113 | 2021-05-13 22:51:08 +0000 | [diff] [blame] | 142 | mMmapClient.attributionSource = request.getAttributionSource(); | 
|  | 143 | // TODO b/182392769: use attribution source util | 
|  | 144 | mMmapClient.attributionSource.uid = VALUE_OR_FATAL( | 
| Philip P. Moltmann | bda4575 | 2020-07-17 16:41:18 -0700 | [diff] [blame] | 145 | legacy2aidl_uid_t_int32_t(IPCThreadState::self()->getCallingUid())); | 
| Svet Ganov | 3376113 | 2021-05-13 22:51:08 +0000 | [diff] [blame] | 146 | mMmapClient.attributionSource.pid = VALUE_OR_FATAL( | 
| Philip P. Moltmann | bda4575 | 2020-07-17 16:41:18 -0700 | [diff] [blame] | 147 | legacy2aidl_pid_t_int32_t(IPCThreadState::self()->getCallingPid())); | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 148 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 149 | // Limit scope of lock to avoid recursive lock in close(). | 
|  | 150 | { | 
|  | 151 | std::lock_guard<std::mutex> lock(mUpMessageQueueLock); | 
|  | 152 | if (mUpMessageQueue != nullptr) { | 
| Phil Burk | 19e990e | 2018-03-22 13:59:34 -0700 | [diff] [blame] | 153 | ALOGE("%s() called twice", __func__); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 154 | return AAUDIO_ERROR_INVALID_STATE; | 
|  | 155 | } | 
|  | 156 |  | 
| Phil Burk | 8f4fe50 | 2020-07-15 23:54:50 +0000 | [diff] [blame] | 157 | mUpMessageQueue = std::make_shared<SharedRingBuffer>(); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 158 | result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage), | 
|  | 159 | QUEUE_UP_CAPACITY_COMMANDS); | 
|  | 160 | if (result != AAUDIO_OK) { | 
|  | 161 | goto error; | 
|  | 162 | } | 
|  | 163 |  | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 164 | // This is not protected by a lock because the stream cannot be | 
|  | 165 | // referenced until the service returns a handle to the client. | 
|  | 166 | // So only one thread can open a stream. | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 167 | mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, | 
| Phil Burk | 15f97c9 | 2018-09-04 14:06:27 -0700 | [diff] [blame] | 168 | request); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 169 | if (mServiceEndpoint == nullptr) { | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 170 | result = AAUDIO_ERROR_UNAVAILABLE; | 
|  | 171 | goto error; | 
|  | 172 | } | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 173 | // Save a weak pointer that we will use to access the endpoint. | 
|  | 174 | mServiceEndpointWeak = mServiceEndpoint; | 
|  | 175 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 176 | mFramesPerBurst = mServiceEndpoint->getFramesPerBurst(); | 
|  | 177 | copyFrom(*mServiceEndpoint); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 178 | } | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 179 |  | 
|  | 180 | // Make sure this object does not get deleted before the run() method | 
|  | 181 | // can protect it by making a strong pointer. | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 182 | mCommandQueue.startWaiting(); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 183 | mThreadEnabled = true; | 
|  | 184 | incStrong(nullptr); // See run() method. | 
|  | 185 | result = mCommandThread.start(this); | 
|  | 186 | if (result != AAUDIO_OK) { | 
|  | 187 | decStrong(nullptr); // run() can't do it so we have to do it here. | 
|  | 188 | goto error; | 
|  | 189 | } | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 190 | return result; | 
|  | 191 |  | 
|  | 192 | error: | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 193 | closeAndClear(); | 
|  | 194 | mThreadEnabled = false; | 
|  | 195 | mCommandQueue.stopWaiting(); | 
|  | 196 | mCommandThread.stop(); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 197 | return result; | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 198 | } | 
| Phil Burk | dec33ab | 2017-01-17 14:48:16 -0800 | [diff] [blame] | 199 |  | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 200 | aaudio_result_t AAudioServiceStreamBase::close() { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 201 | aaudio_result_t result = sendCommand(CLOSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 202 |  | 
|  | 203 | // Stop the command thread as the stream is closed. | 
|  | 204 | mThreadEnabled = false; | 
|  | 205 | mCommandQueue.stopWaiting(); | 
|  | 206 | mCommandThread.stop(); | 
|  | 207 |  | 
|  | 208 | return result; | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 209 | } | 
|  | 210 |  | 
|  | 211 | aaudio_result_t AAudioServiceStreamBase::close_l() { | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 212 | if (getState() == AAUDIO_STREAM_STATE_CLOSED) { | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 213 | return AAUDIO_OK; | 
|  | 214 | } | 
|  | 215 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 216 | // This will stop the stream, just in case it was not already stopped. | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 217 | stop_l(); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 218 |  | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 219 | return closeAndClear(); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 220 | } | 
|  | 221 |  | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 222 | aaudio_result_t AAudioServiceStreamBase::startDevice() { | 
|  | 223 | mClientHandle = AUDIO_PORT_HANDLE_NONE; | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 224 | sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote(); | 
|  | 225 | if (endpoint == nullptr) { | 
|  | 226 | ALOGE("%s() has no endpoint", __func__); | 
|  | 227 | return AAUDIO_ERROR_INVALID_STATE; | 
|  | 228 | } | 
|  | 229 | return endpoint->startStream(this, &mClientHandle); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 230 | } | 
|  | 231 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 232 | /** | 
|  | 233 | * Start the flow of audio data. | 
|  | 234 | * | 
|  | 235 | * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete. | 
|  | 236 | */ | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 237 | aaudio_result_t AAudioServiceStreamBase::start() { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 238 | return sendCommand(START, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 239 | } | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 240 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 241 | aaudio_result_t AAudioServiceStreamBase::start_l() { | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 242 | const int64_t beginNs = AudioClock::getNanoseconds(); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 243 | aaudio_result_t result = AAUDIO_OK; | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 244 |  | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 245 | if (auto state = getState(); | 
| Phil Burk | db46614 | 2021-04-16 16:50:00 +0000 | [diff] [blame] | 246 | state == AAUDIO_STREAM_STATE_CLOSED || isDisconnected_l()) { | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 247 | ALOGW("%s() already CLOSED, returns INVALID_STATE, handle = %d", | 
|  | 248 | __func__, getHandle()); | 
|  | 249 | return AAUDIO_ERROR_INVALID_STATE; | 
|  | 250 | } | 
|  | 251 |  | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 252 | if (mStandby) { | 
|  | 253 | ALOGW("%s() the stream is standby, return ERROR_STANDBY, " | 
|  | 254 | "expecting the client call exitStandby before start", __func__); | 
|  | 255 | return AAUDIO_ERROR_STANDBY; | 
|  | 256 | } | 
|  | 257 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 258 | mediametrics::Defer defer([&] { | 
|  | 259 | mediametrics::LogItem(mMetricsId) | 
|  | 260 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START) | 
| Andy Hung | ea84038 | 2020-05-05 21:50:17 -0700 | [diff] [blame] | 261 | .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 262 | .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) | 
|  | 263 | .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) | 
|  | 264 | .record(); }); | 
|  | 265 |  | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 266 | if (isRunning()) { | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 267 | return result; | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 268 | } | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 269 |  | 
| Phil Burk | 2329638 | 2017-11-20 15:45:11 -0800 | [diff] [blame] | 270 | setFlowing(false); | 
| Phil Burk | 762365c | 2018-12-10 16:02:16 -0800 | [diff] [blame] | 271 | setSuspended(false); | 
| Phil Burk | 2329638 | 2017-11-20 15:45:11 -0800 | [diff] [blame] | 272 |  | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 273 | // Start with fresh presentation timestamps. | 
| Phil Burk | a53ffa6 | 2018-10-10 16:21:37 -0700 | [diff] [blame] | 274 | mAtomicStreamTimestamp.clear(); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 275 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 276 | mClientHandle = AUDIO_PORT_HANDLE_NONE; | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 277 | result = startDevice(); | 
|  | 278 | if (result != AAUDIO_OK) goto error; | 
|  | 279 |  | 
|  | 280 | // This should happen at the end of the start. | 
|  | 281 | sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED); | 
|  | 282 | setState(AAUDIO_STREAM_STATE_STARTED); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 283 |  | 
|  | 284 | return result; | 
|  | 285 |  | 
|  | 286 | error: | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 287 | disconnect_l(); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 288 | return result; | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 289 | } | 
|  | 290 |  | 
|  | 291 | aaudio_result_t AAudioServiceStreamBase::pause() { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 292 | return sendCommand(PAUSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 293 | } | 
|  | 294 |  | 
|  | 295 | aaudio_result_t AAudioServiceStreamBase::pause_l() { | 
| Phil Burk | 11e8d33 | 2017-05-24 09:59:02 -0700 | [diff] [blame] | 296 | aaudio_result_t result = AAUDIO_OK; | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 297 | if (!isRunning()) { | 
|  | 298 | return result; | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 299 | } | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 300 | const int64_t beginNs = AudioClock::getNanoseconds(); | 
| Phil Burk | 73af62a | 2017-10-26 12:11:47 -0700 | [diff] [blame] | 301 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 302 | mediametrics::Defer defer([&] { | 
|  | 303 | mediametrics::LogItem(mMetricsId) | 
|  | 304 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE) | 
| Andy Hung | ea84038 | 2020-05-05 21:50:17 -0700 | [diff] [blame] | 305 | .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 306 | .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) | 
|  | 307 | .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) | 
|  | 308 | .record(); }); | 
|  | 309 |  | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 310 | sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote(); | 
|  | 311 | if (endpoint == nullptr) { | 
|  | 312 | ALOGE("%s() has no endpoint", __func__); | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 313 | result =  AAUDIO_ERROR_INVALID_STATE; // for MediaMetric tracking | 
|  | 314 | return result; | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 315 | } | 
|  | 316 | result = endpoint->stopStream(this, mClientHandle); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 317 | if (result != AAUDIO_OK) { | 
| Phil Burk | 19e990e | 2018-03-22 13:59:34 -0700 | [diff] [blame] | 318 | ALOGE("%s() mServiceEndpoint returned %d, %s", __func__, result, getTypeText()); | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 319 | disconnect_l(); // TODO should we return or pause Base first? | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 320 | } | 
|  | 321 |  | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 322 | sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 323 | setState(AAUDIO_STREAM_STATE_PAUSED); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 324 | return result; | 
|  | 325 | } | 
|  | 326 |  | 
| Phil Burk | 71f35bb | 2017-04-13 16:05:07 -0700 | [diff] [blame] | 327 | aaudio_result_t AAudioServiceStreamBase::stop() { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 328 | return sendCommand(STOP, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 329 | } | 
|  | 330 |  | 
|  | 331 | aaudio_result_t AAudioServiceStreamBase::stop_l() { | 
| Phil Burk | 11e8d33 | 2017-05-24 09:59:02 -0700 | [diff] [blame] | 332 | aaudio_result_t result = AAUDIO_OK; | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 333 | if (!isRunning()) { | 
|  | 334 | return result; | 
| Phil Burk | 71f35bb | 2017-04-13 16:05:07 -0700 | [diff] [blame] | 335 | } | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 336 | const int64_t beginNs = AudioClock::getNanoseconds(); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 337 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 338 | mediametrics::Defer defer([&] { | 
|  | 339 | mediametrics::LogItem(mMetricsId) | 
|  | 340 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP) | 
| Andy Hung | ea84038 | 2020-05-05 21:50:17 -0700 | [diff] [blame] | 341 | .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 342 | .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) | 
|  | 343 | .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) | 
|  | 344 | .record(); }); | 
|  | 345 |  | 
| Phil Burk | 83fb844 | 2017-10-05 16:55:17 -0700 | [diff] [blame] | 346 | setState(AAUDIO_STREAM_STATE_STOPPING); | 
|  | 347 |  | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 348 | if (result != AAUDIO_OK) { | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 349 | disconnect_l(); | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 350 | return result; | 
|  | 351 | } | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 352 |  | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 353 | sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote(); | 
|  | 354 | if (endpoint == nullptr) { | 
|  | 355 | ALOGE("%s() has no endpoint", __func__); | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 356 | result =  AAUDIO_ERROR_INVALID_STATE; // for MediaMetric tracking | 
|  | 357 | return result; | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 358 | } | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 359 | // TODO wait for data to be played out | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 360 | result = endpoint->stopStream(this, mClientHandle); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 361 | if (result != AAUDIO_OK) { | 
| Phil Burk | 6e2770e | 2018-05-01 13:03:52 -0700 | [diff] [blame] | 362 | ALOGE("%s() stopStream returned %d, %s", __func__, result, getTypeText()); | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 363 | disconnect_l(); | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 364 | // TODO what to do with result here? | 
|  | 365 | } | 
|  | 366 |  | 
| Eric Laurent | cb4dae2 | 2017-07-01 19:39:32 -0700 | [diff] [blame] | 367 | sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 368 | setState(AAUDIO_STREAM_STATE_STOPPED); | 
| Phil Burk | 71f35bb | 2017-04-13 16:05:07 -0700 | [diff] [blame] | 369 | return result; | 
|  | 370 | } | 
|  | 371 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 372 | aaudio_result_t AAudioServiceStreamBase::flush() { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 373 | return sendCommand(FLUSH, nullptr, true /*waitForReply*/, TIMEOUT_NANOS); | 
| Phil Burk | 98d6d92 | 2017-07-06 11:52:45 -0700 | [diff] [blame] | 374 | } | 
|  | 375 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 376 | aaudio_result_t AAudioServiceStreamBase::flush_l() { | 
| Phil Burk | 5cc83c3 | 2017-11-28 15:43:18 -0800 | [diff] [blame] | 377 | aaudio_result_t result = AAudio_isFlushAllowed(getState()); | 
|  | 378 | if (result != AAUDIO_OK) { | 
|  | 379 | return result; | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 380 | } | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 381 | const int64_t beginNs = AudioClock::getNanoseconds(); | 
| Phil Burk | 5cc83c3 | 2017-11-28 15:43:18 -0800 | [diff] [blame] | 382 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 383 | mediametrics::Defer defer([&] { | 
|  | 384 | mediametrics::LogItem(mMetricsId) | 
|  | 385 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH) | 
| Andy Hung | ea84038 | 2020-05-05 21:50:17 -0700 | [diff] [blame] | 386 | .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs)) | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 387 | .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) | 
|  | 388 | .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result) | 
|  | 389 | .record(); }); | 
|  | 390 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 391 | // Data will get flushed when the client receives the FLUSHED event. | 
| Phil Burk | 71f35bb | 2017-04-13 16:05:07 -0700 | [diff] [blame] | 392 | sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED); | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 393 | setState(AAUDIO_STREAM_STATE_FLUSHED); | 
| Phil Burk | 71f35bb | 2017-04-13 16:05:07 -0700 | [diff] [blame] | 394 | return AAUDIO_OK; | 
|  | 395 | } | 
|  | 396 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 397 | // implement Runnable, periodically send timestamps to client and process commands from queue. | 
| Phil Burk | a53ffa6 | 2018-10-10 16:21:37 -0700 | [diff] [blame] | 398 | __attribute__((no_sanitize("integer"))) | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 399 | void AAudioServiceStreamBase::run() { | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 400 | ALOGD("%s() %s entering >>>>>>>>>>>>>> COMMANDS", __func__, getTypeText()); | 
| Phil Burk | 3d20194 | 2021-04-08 23:27:04 +0000 | [diff] [blame] | 401 | // Hold onto the ref counted stream until the end. | 
|  | 402 | android::sp<AAudioServiceStreamBase> holdStream(this); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 403 | TimestampScheduler timestampScheduler; | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 404 | int64_t nextTime; | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 405 | int64_t standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; | 
| Phil Burk | 3d20194 | 2021-04-08 23:27:04 +0000 | [diff] [blame] | 406 | // Balance the incStrong from when the thread was launched. | 
|  | 407 | holdStream->decStrong(nullptr); | 
|  | 408 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 409 | // Taking mLock while starting the thread. All the operation must be able to | 
|  | 410 | // run with holding the lock. | 
|  | 411 | std::scoped_lock<std::mutex> _l(mLock); | 
|  | 412 |  | 
| Phil Burk | a53ffa6 | 2018-10-10 16:21:37 -0700 | [diff] [blame] | 413 | int32_t loopCount = 0; | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 414 | while (mThreadEnabled.load()) { | 
| Phil Burk | a53ffa6 | 2018-10-10 16:21:37 -0700 | [diff] [blame] | 415 | loopCount++; | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 416 | int64_t timeoutNanos = -1; | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 417 | if (isRunning() || (isIdle_l() && !isStandby_l())) { | 
|  | 418 | timeoutNanos = (isRunning() ? nextTime : standbyTime) - AudioClock::getNanoseconds(); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 419 | timeoutNanos = std::max<int64_t>(0, timeoutNanos); | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | auto command = mCommandQueue.waitForCommand(timeoutNanos); | 
|  | 423 | if (!mThreadEnabled) { | 
|  | 424 | // Break the loop if the thread is disabled. | 
|  | 425 | break; | 
|  | 426 | } | 
|  | 427 |  | 
|  | 428 | if (isRunning() && AudioClock::getNanoseconds() >= nextTime) { | 
|  | 429 | // It is time to update timestamp. | 
|  | 430 | if (sendCurrentTimestamp_l() != AAUDIO_OK) { | 
|  | 431 | ALOGE("Failed to send current timestamp, stop updating timestamp"); | 
|  | 432 | disconnect_l(); | 
|  | 433 | } else { | 
|  | 434 | nextTime = timestampScheduler.nextAbsoluteTime(); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 435 | } | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 436 | } | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 437 | if (isIdle_l() && AudioClock::getNanoseconds() >= standbyTime) { | 
| Jason Lin | fb53f96 | 2022-11-22 13:39:49 +0800 | [diff] [blame] | 438 | aaudio_result_t result = standby_l(); | 
|  | 439 | if (result != AAUDIO_OK) { | 
|  | 440 | // If standby failed because of the function is not implemented, there is no | 
|  | 441 | // need to retry. Otherwise, retry standby later. | 
|  | 442 | ALOGW("Failed to enter standby, error=%d", result); | 
|  | 443 | standbyTime = result == AAUDIO_ERROR_UNIMPLEMENTED | 
|  | 444 | ? std::numeric_limits<int64_t>::max() | 
|  | 445 | : AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; | 
|  | 446 | } | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 447 | } | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 448 |  | 
|  | 449 | if (command != nullptr) { | 
|  | 450 | std::scoped_lock<std::mutex> _commandLock(command->lock); | 
|  | 451 | switch (command->operationCode) { | 
|  | 452 | case START: | 
|  | 453 | command->result = start_l(); | 
|  | 454 | timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate()); | 
|  | 455 | timestampScheduler.start(AudioClock::getNanoseconds()); | 
|  | 456 | nextTime = timestampScheduler.nextAbsoluteTime(); | 
|  | 457 | break; | 
|  | 458 | case PAUSE: | 
|  | 459 | command->result = pause_l(); | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 460 | standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 461 | break; | 
|  | 462 | case STOP: | 
|  | 463 | command->result = stop_l(); | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 464 | standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 465 | break; | 
|  | 466 | case FLUSH: | 
|  | 467 | command->result = flush_l(); | 
|  | 468 | break; | 
|  | 469 | case CLOSE: | 
|  | 470 | command->result = close_l(); | 
|  | 471 | break; | 
|  | 472 | case DISCONNECT: | 
|  | 473 | disconnect_l(); | 
|  | 474 | break; | 
|  | 475 | case REGISTER_AUDIO_THREAD: { | 
|  | 476 | RegisterAudioThreadParam *param = | 
|  | 477 | (RegisterAudioThreadParam *) command->parameter.get(); | 
|  | 478 | command->result = | 
|  | 479 | param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT | 
|  | 480 | : registerAudioThread_l(param->mOwnerPid, | 
|  | 481 | param->mClientThreadId, | 
|  | 482 | param->mPriority); | 
|  | 483 | } | 
|  | 484 | break; | 
|  | 485 | case UNREGISTER_AUDIO_THREAD: { | 
|  | 486 | UnregisterAudioThreadParam *param = | 
|  | 487 | (UnregisterAudioThreadParam *) command->parameter.get(); | 
|  | 488 | command->result = | 
|  | 489 | param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT | 
|  | 490 | : unregisterAudioThread_l(param->mClientThreadId); | 
|  | 491 | } | 
|  | 492 | break; | 
|  | 493 | case GET_DESCRIPTION: { | 
|  | 494 | GetDescriptionParam *param = (GetDescriptionParam *) command->parameter.get(); | 
|  | 495 | command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT | 
|  | 496 | : getDescription_l(param->mParcelable); | 
|  | 497 | } | 
|  | 498 | break; | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 499 | case EXIT_STANDBY: { | 
|  | 500 | ExitStandbyParam *param = (ExitStandbyParam *) command->parameter.get(); | 
|  | 501 | command->result = param == nullptr ? AAUDIO_ERROR_ILLEGAL_ARGUMENT | 
|  | 502 | : exitStandby_l(param->mParcelable); | 
|  | 503 | standbyTime = AudioClock::getNanoseconds() + IDLE_TIMEOUT_NANOS; | 
|  | 504 | } break; | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 505 | default: | 
|  | 506 | ALOGE("Invalid command op code: %d", command->operationCode); | 
|  | 507 | break; | 
|  | 508 | } | 
|  | 509 | if (command->isWaitingForReply) { | 
|  | 510 | command->isWaitingForReply = false; | 
|  | 511 | command->conditionVariable.notify_one(); | 
|  | 512 | } | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 513 | } | 
|  | 514 | } | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 515 | ALOGD("%s() %s exiting after %d loops <<<<<<<<<<<<<< COMMANDS", | 
| Phil Burk | a53ffa6 | 2018-10-10 16:21:37 -0700 | [diff] [blame] | 516 | __func__, getTypeText(), loopCount); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 517 | } | 
|  | 518 |  | 
| Phil Burk | 5ef003b | 2017-06-30 11:43:37 -0700 | [diff] [blame] | 519 | void AAudioServiceStreamBase::disconnect() { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 520 | sendCommand(DISCONNECT); | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 521 | } | 
|  | 522 |  | 
|  | 523 | void AAudioServiceStreamBase::disconnect_l() { | 
| Phil Burk | db46614 | 2021-04-16 16:50:00 +0000 | [diff] [blame] | 524 | if (!isDisconnected_l() && getState() != AAUDIO_STREAM_STATE_CLOSED) { | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 525 |  | 
| Phil Burk | a987670 | 2020-04-20 18:16:15 -0700 | [diff] [blame] | 526 | mediametrics::LogItem(mMetricsId) | 
|  | 527 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT) | 
|  | 528 | .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState())) | 
|  | 529 | .record(); | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 530 |  | 
| Phil Burk | 5ef003b | 2017-06-30 11:43:37 -0700 | [diff] [blame] | 531 | sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED); | 
| Phil Burk | db46614 | 2021-04-16 16:50:00 +0000 | [diff] [blame] | 532 | setDisconnected_l(true); | 
| Phil Burk | 5ef003b | 2017-06-30 11:43:37 -0700 | [diff] [blame] | 533 | } | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 534 | } | 
|  | 535 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 536 | aaudio_result_t AAudioServiceStreamBase::registerAudioThread(pid_t clientThreadId, int priority) { | 
|  | 537 | const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 538 | return sendCommand(REGISTER_AUDIO_THREAD, | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 539 | std::make_shared<RegisterAudioThreadParam>(ownerPid, clientThreadId, priority), | 
|  | 540 | true /*waitForReply*/, | 
|  | 541 | TIMEOUT_NANOS); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 542 | } | 
|  | 543 |  | 
|  | 544 | aaudio_result_t AAudioServiceStreamBase::registerAudioThread_l( | 
|  | 545 | pid_t ownerPid, pid_t clientThreadId, int priority) { | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 546 | aaudio_result_t result = AAUDIO_OK; | 
|  | 547 | if (getRegisteredThread() != AAudioServiceStreamBase::ILLEGAL_THREAD_ID) { | 
|  | 548 | ALOGE("AAudioService::registerAudioThread(), thread already registered"); | 
|  | 549 | result = AAUDIO_ERROR_INVALID_STATE; | 
|  | 550 | } else { | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 551 | setRegisteredThread(clientThreadId); | 
|  | 552 | int err = android::requestPriority(ownerPid, clientThreadId, | 
|  | 553 | priority, true /* isForApp */); | 
|  | 554 | if (err != 0) { | 
|  | 555 | ALOGE("AAudioService::registerAudioThread(%d) failed, errno = %d, priority = %d", | 
|  | 556 | clientThreadId, errno, priority); | 
|  | 557 | result = AAUDIO_ERROR_INTERNAL; | 
|  | 558 | } | 
|  | 559 | } | 
|  | 560 | return result; | 
|  | 561 | } | 
|  | 562 |  | 
|  | 563 | aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread(pid_t clientThreadId) { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 564 | return sendCommand(UNREGISTER_AUDIO_THREAD, | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 565 | std::make_shared<UnregisterAudioThreadParam>(clientThreadId), | 
|  | 566 | true /*waitForReply*/, | 
|  | 567 | TIMEOUT_NANOS); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 568 | } | 
|  | 569 |  | 
|  | 570 | aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread_l(pid_t clientThreadId) { | 
| Phil Burk | 7ebbc11 | 2020-05-13 15:55:17 -0700 | [diff] [blame] | 571 | aaudio_result_t result = AAUDIO_OK; | 
|  | 572 | if (getRegisteredThread() != clientThreadId) { | 
|  | 573 | ALOGE("%s(), wrong thread", __func__); | 
|  | 574 | result = AAUDIO_ERROR_ILLEGAL_ARGUMENT; | 
|  | 575 | } else { | 
|  | 576 | setRegisteredThread(0); | 
|  | 577 | } | 
|  | 578 | return result; | 
|  | 579 | } | 
|  | 580 |  | 
|  | 581 | void AAudioServiceStreamBase::setState(aaudio_stream_state_t state) { | 
|  | 582 | // CLOSED is a final state. | 
|  | 583 | if (mState != AAUDIO_STREAM_STATE_CLOSED) { | 
|  | 584 | mState = state; | 
|  | 585 | } else { | 
|  | 586 | ALOGW_IF(mState != state, "%s(%d) when already CLOSED", __func__, state); | 
|  | 587 | } | 
|  | 588 | } | 
|  | 589 |  | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 590 | aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event, | 
| Phil Burk | 2329638 | 2017-11-20 15:45:11 -0800 | [diff] [blame] | 591 | double  dataDouble) { | 
| Phil Burk | 5ed503c | 2017-02-01 09:38:15 -0800 | [diff] [blame] | 592 | AAudioServiceMessage command; | 
|  | 593 | command.what = AAudioServiceMessage::code::EVENT; | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 594 | command.event.event = event; | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 595 | command.event.dataDouble = dataDouble; | 
| Phil Burk | 2329638 | 2017-11-20 15:45:11 -0800 | [diff] [blame] | 596 | return writeUpMessageQueue(&command); | 
|  | 597 | } | 
|  | 598 |  | 
|  | 599 | aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event, | 
|  | 600 | int64_t dataLong) { | 
|  | 601 | AAudioServiceMessage command; | 
|  | 602 | command.what = AAudioServiceMessage::code::EVENT; | 
|  | 603 | command.event.event = event; | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 604 | command.event.dataLong = dataLong; | 
|  | 605 | return writeUpMessageQueue(&command); | 
|  | 606 | } | 
|  | 607 |  | 
| Phil Burk | f878a8d | 2019-03-29 17:23:00 -0700 | [diff] [blame] | 608 | bool AAudioServiceStreamBase::isUpMessageQueueBusy() { | 
|  | 609 | std::lock_guard<std::mutex> lock(mUpMessageQueueLock); | 
|  | 610 | if (mUpMessageQueue == nullptr) { | 
|  | 611 | ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); | 
|  | 612 | return true; | 
|  | 613 | } | 
| Phil Burk | f878a8d | 2019-03-29 17:23:00 -0700 | [diff] [blame] | 614 | // Is it half full or more | 
| Phil Burk | 8f4fe50 | 2020-07-15 23:54:50 +0000 | [diff] [blame] | 615 | return mUpMessageQueue->getFractionalFullness() >= 0.5; | 
| Phil Burk | f878a8d | 2019-03-29 17:23:00 -0700 | [diff] [blame] | 616 | } | 
|  | 617 |  | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 618 | aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) { | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 619 | std::lock_guard<std::mutex> lock(mUpMessageQueueLock); | 
| Phil Burk | 71f35bb | 2017-04-13 16:05:07 -0700 | [diff] [blame] | 620 | if (mUpMessageQueue == nullptr) { | 
| Phil Burk | 19e990e | 2018-03-22 13:59:34 -0700 | [diff] [blame] | 621 | ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); | 
| Phil Burk | 71f35bb | 2017-04-13 16:05:07 -0700 | [diff] [blame] | 622 | return AAUDIO_ERROR_NULL; | 
|  | 623 | } | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 624 | int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1); | 
|  | 625 | if (count != 1) { | 
| Phil Burk | 762365c | 2018-12-10 16:02:16 -0800 | [diff] [blame] | 626 | ALOGW("%s(): Queue full. Did client stop? Suspending stream. what = %u, %s", | 
|  | 627 | __func__, command->what, getTypeText()); | 
|  | 628 | setSuspended(true); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 629 | return AAUDIO_ERROR_WOULD_BLOCK; | 
|  | 630 | } else { | 
|  | 631 | return AAUDIO_OK; | 
|  | 632 | } | 
|  | 633 | } | 
|  | 634 |  | 
| Phil Burk | 2329638 | 2017-11-20 15:45:11 -0800 | [diff] [blame] | 635 | aaudio_result_t AAudioServiceStreamBase::sendXRunCount(int32_t xRunCount) { | 
|  | 636 | return sendServiceEvent(AAUDIO_SERVICE_EVENT_XRUN, (int64_t) xRunCount); | 
|  | 637 | } | 
|  | 638 |  | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 639 | aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp_l() { | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 640 | AAudioServiceMessage command; | 
| Phil Burk | f878a8d | 2019-03-29 17:23:00 -0700 | [diff] [blame] | 641 | // It is not worth filling up the queue with timestamps. | 
|  | 642 | // That can cause the stream to get suspended. | 
|  | 643 | // So just drop the timestamp if the queue is getting full. | 
|  | 644 | if (isUpMessageQueueBusy()) { | 
|  | 645 | return AAUDIO_OK; | 
|  | 646 | } | 
|  | 647 |  | 
| Phil Burk | 97350f9 | 2017-07-21 15:59:44 -0700 | [diff] [blame] | 648 | // Send a timestamp for the clock model. | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 649 | aaudio_result_t result = getFreeRunningPosition_l(&command.timestamp.position, | 
|  | 650 | &command.timestamp.timestamp); | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 651 | if (result == AAUDIO_OK) { | 
| Phil Burk | 19e990e | 2018-03-22 13:59:34 -0700 | [diff] [blame] | 652 | ALOGV("%s() SERVICE  %8lld at %lld", __func__, | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 653 | (long long) command.timestamp.position, | 
|  | 654 | (long long) command.timestamp.timestamp); | 
| Phil Burk | 97350f9 | 2017-07-21 15:59:44 -0700 | [diff] [blame] | 655 | command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE; | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 656 | result = writeUpMessageQueue(&command); | 
| Phil Burk | 97350f9 | 2017-07-21 15:59:44 -0700 | [diff] [blame] | 657 |  | 
|  | 658 | if (result == AAUDIO_OK) { | 
|  | 659 | // Send a hardware timestamp for presentation time. | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 660 | result = getHardwareTimestamp_l(&command.timestamp.position, | 
|  | 661 | &command.timestamp.timestamp); | 
| Phil Burk | 97350f9 | 2017-07-21 15:59:44 -0700 | [diff] [blame] | 662 | if (result == AAUDIO_OK) { | 
| Phil Burk | 19e990e | 2018-03-22 13:59:34 -0700 | [diff] [blame] | 663 | ALOGV("%s() HARDWARE %8lld at %lld", __func__, | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 664 | (long long) command.timestamp.position, | 
|  | 665 | (long long) command.timestamp.timestamp); | 
| Phil Burk | 97350f9 | 2017-07-21 15:59:44 -0700 | [diff] [blame] | 666 | command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE; | 
|  | 667 | result = writeUpMessageQueue(&command); | 
|  | 668 | } | 
|  | 669 | } | 
|  | 670 | } | 
|  | 671 |  | 
| Phil Burk | bcc3674 | 2017-08-31 17:24:51 -0700 | [diff] [blame] | 672 | if (result == AAUDIO_ERROR_UNAVAILABLE) { // TODO review best error code | 
| Phil Burk | 940083c | 2017-07-17 17:00:02 -0700 | [diff] [blame] | 673 | result = AAUDIO_OK; // just not available yet, try again later | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 674 | } | 
|  | 675 | return result; | 
| Phil Burk | 2355edb | 2016-12-26 13:54:02 -0800 | [diff] [blame] | 676 | } | 
|  | 677 |  | 
| Phil Burk | c0c70e3 | 2017-02-09 13:18:38 -0800 | [diff] [blame] | 678 | /** | 
|  | 679 | * Get an immutable description of the in-memory queues | 
|  | 680 | * used to communicate with the underlying HAL or Service. | 
|  | 681 | */ | 
|  | 682 | aaudio_result_t AAudioServiceStreamBase::getDescription(AudioEndpointParcelable &parcelable) { | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 683 | return sendCommand( | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 684 | GET_DESCRIPTION, | 
|  | 685 | std::make_shared<GetDescriptionParam>(&parcelable), | 
|  | 686 | true /*waitForReply*/, | 
|  | 687 | TIMEOUT_NANOS); | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 688 | } | 
|  | 689 |  | 
|  | 690 | aaudio_result_t AAudioServiceStreamBase::getDescription_l(AudioEndpointParcelable* parcelable) { | 
| Phil Burk | 523b304 | 2017-09-13 13:03:08 -0700 | [diff] [blame] | 691 | { | 
|  | 692 | std::lock_guard<std::mutex> lock(mUpMessageQueueLock); | 
|  | 693 | if (mUpMessageQueue == nullptr) { | 
| Phil Burk | 19e990e | 2018-03-22 13:59:34 -0700 | [diff] [blame] | 694 | ALOGE("%s(): mUpMessageQueue null! - stream not open", __func__); | 
| Phil Burk | 523b304 | 2017-09-13 13:03:08 -0700 | [diff] [blame] | 695 | return AAUDIO_ERROR_NULL; | 
|  | 696 | } | 
|  | 697 | // Gather information on the message queue. | 
|  | 698 | mUpMessageQueue->fillParcelable(parcelable, | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 699 | parcelable->mUpMessageQueueParcelable); | 
| Phil Burk | 523b304 | 2017-09-13 13:03:08 -0700 | [diff] [blame] | 700 | } | 
| jiabin | 2a59462 | 2021-10-14 00:32:25 +0000 | [diff] [blame] | 701 | return getAudioDataDescription_l(parcelable); | 
| Phil Burk | 11e8d33 | 2017-05-24 09:59:02 -0700 | [diff] [blame] | 702 | } | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 703 |  | 
| jiabin | f7f0615 | 2021-11-22 18:10:14 +0000 | [diff] [blame] | 704 | aaudio_result_t AAudioServiceStreamBase::exitStandby(AudioEndpointParcelable *parcelable) { | 
|  | 705 | auto command = std::make_shared<AAudioCommand>( | 
|  | 706 | EXIT_STANDBY, | 
|  | 707 | std::make_shared<ExitStandbyParam>(parcelable), | 
|  | 708 | true /*waitForReply*/, | 
|  | 709 | TIMEOUT_NANOS); | 
|  | 710 | return mCommandQueue.sendCommand(command); | 
|  | 711 | } | 
|  | 712 |  | 
| Phil Burk | 39f02dd | 2017-08-04 09:13:31 -0700 | [diff] [blame] | 713 | void AAudioServiceStreamBase::onVolumeChanged(float volume) { | 
|  | 714 | sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume); | 
|  | 715 | } | 
| jiabin | ba75f21 | 2021-12-07 20:06:30 +0000 | [diff] [blame] | 716 |  | 
|  | 717 | aaudio_result_t AAudioServiceStreamBase::sendCommand(aaudio_command_opcode opCode, | 
|  | 718 | std::shared_ptr<AAudioCommandParam> param, | 
|  | 719 | bool waitForReply, | 
|  | 720 | int64_t timeoutNanos) { | 
|  | 721 | return mCommandQueue.sendCommand(std::make_shared<AAudioCommand>( | 
|  | 722 | opCode, param, waitForReply, timeoutNanos)); | 
|  | 723 | } | 
|  | 724 |  | 
|  | 725 | aaudio_result_t AAudioServiceStreamBase::closeAndClear() { | 
|  | 726 | aaudio_result_t result = AAUDIO_OK; | 
|  | 727 | sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote(); | 
|  | 728 | if (endpoint == nullptr) { | 
|  | 729 | result = AAUDIO_ERROR_INVALID_STATE; | 
|  | 730 | } else { | 
|  | 731 | endpoint->unregisterStream(this); | 
|  | 732 | AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance(); | 
|  | 733 | endpointManager.closeEndpoint(endpoint); | 
|  | 734 |  | 
|  | 735 | // AAudioService::closeStream() prevents two threads from closing at the same time. | 
|  | 736 | mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns. | 
|  | 737 | } | 
|  | 738 |  | 
|  | 739 | setState(AAUDIO_STREAM_STATE_CLOSED); | 
|  | 740 |  | 
|  | 741 | mediametrics::LogItem(mMetricsId) | 
|  | 742 | .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE) | 
|  | 743 | .record(); | 
|  | 744 | return result; | 
|  | 745 | } |