blob: 50708ba726641d5549c83863eecdb690fddd94f2 [file] [log] [blame]
Dima Zavinf0121592011-04-19 16:33:12 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <math.h>
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "A2dpAudioInterface"
21#include <utils/Log.h>
22#include <utils/String8.h>
Daniel Erataccf5b42015-08-05 10:38:37 -060023#include <utils/Timers.h>
Dima Zavinf0121592011-04-19 16:33:12 -070024
25#include "A2dpAudioInterface.h"
26#include "audio/liba2dp.h"
27#include <hardware_legacy/power.h>
28
Dima Zavine81531e2011-04-19 16:53:42 -070029
30namespace android_audio_legacy {
Dima Zavinf0121592011-04-19 16:33:12 -070031
32static const char *sA2dpWakeLock = "A2dpOutputStream";
33#define MAX_WRITE_RETRIES 5
34
35// ----------------------------------------------------------------------------
36
37//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface()
38//{
39// AudioHardwareInterface* hw = 0;
40//
41// hw = AudioHardwareInterface::create();
Steve Blockb381b932011-12-20 16:25:34 +000042// ALOGD("new A2dpAudioInterface(hw: %p)", hw);
Dima Zavinf0121592011-04-19 16:33:12 -070043// hw = new A2dpAudioInterface(hw);
44// return hw;
45//}
46
47A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) :
48 mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false)
49{
50}
51
52A2dpAudioInterface::~A2dpAudioInterface()
53{
54 closeOutputStream((AudioStreamOut *)mOutput);
55 delete mHardwareInterface;
56}
57
58status_t A2dpAudioInterface::initCheck()
59{
60 if (mHardwareInterface == 0) return NO_INIT;
61 return mHardwareInterface->initCheck();
62}
63
64AudioStreamOut* A2dpAudioInterface::openOutputStream(
65 uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
66{
Mike Lockwood33bf1b02014-05-21 09:28:05 -070067 if (!audio_is_a2dp_out_device(devices)) {
Steve Block6a705182011-10-20 11:56:19 +010068 ALOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
Dima Zavinf0121592011-04-19 16:33:12 -070069 return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
70 }
71
72 status_t err = 0;
73
74 // only one output stream allowed
75 if (mOutput) {
76 if (status)
77 *status = -1;
78 return NULL;
79 }
80
81 // create new output stream
82 A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
83 if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
84 mOutput = out;
85 mOutput->setBluetoothEnabled(mBluetoothEnabled);
86 mOutput->setSuspended(mSuspended);
87 } else {
88 delete out;
89 }
90
91 if (status)
92 *status = err;
93 return mOutput;
94}
95
96void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) {
97 if (mOutput == 0 || mOutput != out) {
98 mHardwareInterface->closeOutputStream(out);
99 }
100 else {
101 delete mOutput;
102 mOutput = 0;
103 }
104}
105
106
107AudioStreamIn* A2dpAudioInterface::openInputStream(
108 uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
109 AudioSystem::audio_in_acoustics acoustics)
110{
111 return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
112}
113
114void A2dpAudioInterface::closeInputStream(AudioStreamIn* in)
115{
116 return mHardwareInterface->closeInputStream(in);
117}
118
119status_t A2dpAudioInterface::setMode(int mode)
120{
121 return mHardwareInterface->setMode(mode);
122}
123
124status_t A2dpAudioInterface::setMicMute(bool state)
125{
126 return mHardwareInterface->setMicMute(state);
127}
128
129status_t A2dpAudioInterface::getMicMute(bool* state)
130{
131 return mHardwareInterface->getMicMute(state);
132}
133
134status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs)
135{
136 AudioParameter param = AudioParameter(keyValuePairs);
137 String8 value;
138 String8 key;
139 status_t status = NO_ERROR;
140
Steve Block6a705182011-10-20 11:56:19 +0100141 ALOGV("setParameters() %s", keyValuePairs.string());
Dima Zavinf0121592011-04-19 16:33:12 -0700142
143 key = "bluetooth_enabled";
144 if (param.get(key, value) == NO_ERROR) {
145 mBluetoothEnabled = (value == "true");
146 if (mOutput) {
147 mOutput->setBluetoothEnabled(mBluetoothEnabled);
148 }
149 param.remove(key);
150 }
151 key = String8("A2dpSuspended");
152 if (param.get(key, value) == NO_ERROR) {
153 mSuspended = (value == "true");
154 if (mOutput) {
155 mOutput->setSuspended(mSuspended);
156 }
157 param.remove(key);
158 }
159
160 if (param.size()) {
161 status_t hwStatus = mHardwareInterface->setParameters(param.toString());
162 if (status == NO_ERROR) {
163 status = hwStatus;
164 }
165 }
166
167 return status;
168}
169
170String8 A2dpAudioInterface::getParameters(const String8& keys)
171{
172 AudioParameter param = AudioParameter(keys);
173 AudioParameter a2dpParam = AudioParameter();
174 String8 value;
175 String8 key;
176
177 key = "bluetooth_enabled";
178 if (param.get(key, value) == NO_ERROR) {
179 value = mBluetoothEnabled ? "true" : "false";
180 a2dpParam.add(key, value);
181 param.remove(key);
182 }
183 key = "A2dpSuspended";
184 if (param.get(key, value) == NO_ERROR) {
185 value = mSuspended ? "true" : "false";
186 a2dpParam.add(key, value);
187 param.remove(key);
188 }
189
190 String8 keyValuePairs = a2dpParam.toString();
191
192 if (param.size()) {
193 if (keyValuePairs != "") {
194 keyValuePairs += ";";
195 }
196 keyValuePairs += mHardwareInterface->getParameters(param.toString());
197 }
198
Steve Block6a705182011-10-20 11:56:19 +0100199 ALOGV("getParameters() %s", keyValuePairs.string());
Dima Zavinf0121592011-04-19 16:33:12 -0700200 return keyValuePairs;
201}
202
203size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
204{
205 return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount);
206}
207
208status_t A2dpAudioInterface::setVoiceVolume(float v)
209{
210 return mHardwareInterface->setVoiceVolume(v);
211}
212
213status_t A2dpAudioInterface::setMasterVolume(float v)
214{
215 return mHardwareInterface->setMasterVolume(v);
216}
217
218status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
219{
220 return mHardwareInterface->dumpState(fd, args);
221}
222
223// ----------------------------------------------------------------------------
224
225A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
226 mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL),
227 // assume BT enabled to start, this is safe because its only the
228 // enabled->disabled transition we are worried about
229 mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false)
230{
231 // use any address by default
232 strcpy(mA2dpAddress, "00:00:00:00:00:00");
233 init();
234}
235
236status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
237 uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
238{
239 int lFormat = pFormat ? *pFormat : 0;
240 uint32_t lChannels = pChannels ? *pChannels : 0;
241 uint32_t lRate = pRate ? *pRate : 0;
242
Steve Blockb381b932011-12-20 16:25:34 +0000243 ALOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate);
Dima Zavinf0121592011-04-19 16:33:12 -0700244
245 // fix up defaults
246 if (lFormat == 0) lFormat = format();
247 if (lChannels == 0) lChannels = channels();
248 if (lRate == 0) lRate = sampleRate();
249
250 // check values
251 if ((lFormat != format()) ||
252 (lChannels != channels()) ||
253 (lRate != sampleRate())){
254 if (pFormat) *pFormat = format();
255 if (pChannels) *pChannels = channels();
256 if (pRate) *pRate = sampleRate();
257 return BAD_VALUE;
258 }
259
260 if (pFormat) *pFormat = lFormat;
261 if (pChannels) *pChannels = lChannels;
262 if (pRate) *pRate = lRate;
263
264 mDevice = device;
265 mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
266 return NO_ERROR;
267}
268
269A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
270{
Steve Block6a705182011-10-20 11:56:19 +0100271 ALOGV("A2dpAudioStreamOut destructor");
Dima Zavinf0121592011-04-19 16:33:12 -0700272 close();
Steve Block6a705182011-10-20 11:56:19 +0100273 ALOGV("A2dpAudioStreamOut destructor returning from close()");
Dima Zavinf0121592011-04-19 16:33:12 -0700274}
275
276ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
277{
278 status_t status = -1;
279 {
280 Mutex::Autolock lock(mLock);
281
282 size_t remaining = bytes;
283
284 if (!mBluetoothEnabled || mClosing || mSuspended) {
Steve Block6a705182011-10-20 11:56:19 +0100285 ALOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
Dima Zavinf0121592011-04-19 16:33:12 -0700286 mBluetoothEnabled %d, mClosing %d, mSuspended %d",
287 mBluetoothEnabled, mClosing, mSuspended);
288 goto Error;
289 }
290
291 if (mStandby) {
292 acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
293 mStandby = false;
294 mLastWriteTime = systemTime();
295 }
296
297 status = init();
298 if (status < 0)
299 goto Error;
300
301 int retries = MAX_WRITE_RETRIES;
302 while (remaining > 0 && retries) {
303 status = a2dp_write(mData, buffer, remaining);
304 if (status < 0) {
Steve Block5efbd422012-01-08 10:18:02 +0000305 ALOGE("a2dp_write failed err: %d\n", status);
Dima Zavinf0121592011-04-19 16:33:12 -0700306 goto Error;
307 }
308 if (status == 0) {
309 retries--;
310 }
311 remaining -= status;
312 buffer = (char *)buffer + status;
313 }
314
315 // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
316 // does no spin and starve other threads.
317 // NOTE: It is likely that the A2DP headset is being disconnected
318 nsecs_t now = systemTime();
319 if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
Steve Block6a705182011-10-20 11:56:19 +0100320 ALOGV("A2DP sink runs too fast");
Dima Zavinf0121592011-04-19 16:33:12 -0700321 usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
322 }
323 mLastWriteTime = now;
324 return bytes;
325
326 }
327Error:
328
329 standby();
330
331 // Simulate audio output timing in case of error
332 usleep(mBufferDurationUs);
333
334 return status;
335}
336
337status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
338{
339 if (!mData) {
340 status_t status = a2dp_init(44100, 2, &mData);
341 if (status < 0) {
Steve Block5efbd422012-01-08 10:18:02 +0000342 ALOGE("a2dp_init failed err: %d\n", status);
Dima Zavinf0121592011-04-19 16:33:12 -0700343 mData = NULL;
344 return status;
345 }
346 a2dp_set_sink(mData, mA2dpAddress);
347 }
348
349 return 0;
350}
351
352status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
353{
354 Mutex::Autolock lock(mLock);
355 return standby_l();
356}
357
358status_t A2dpAudioInterface::A2dpAudioStreamOut::standby_l()
359{
360 int result = NO_ERROR;
361
362 if (!mStandby) {
Steve Block6a705182011-10-20 11:56:19 +0100363 ALOGV_IF(mClosing || !mBluetoothEnabled, "Standby skip stop: closing %d enabled %d",
Dima Zavinf0121592011-04-19 16:33:12 -0700364 mClosing, mBluetoothEnabled);
365 if (!mClosing && mBluetoothEnabled) {
366 result = a2dp_stop(mData);
367 }
368 release_wake_lock(sA2dpWakeLock);
369 mStandby = true;
370 }
371
372 return result;
373}
374
375status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs)
376{
377 AudioParameter param = AudioParameter(keyValuePairs);
378 String8 value;
379 String8 key = String8("a2dp_sink_address");
380 status_t status = NO_ERROR;
381 int device;
Steve Block6a705182011-10-20 11:56:19 +0100382 ALOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string());
Dima Zavinf0121592011-04-19 16:33:12 -0700383
384 if (param.get(key, value) == NO_ERROR) {
385 if (value.length() != strlen("00:00:00:00:00:00")) {
386 status = BAD_VALUE;
387 } else {
388 setAddress(value.string());
389 }
390 param.remove(key);
391 }
392 key = String8("closing");
393 if (param.get(key, value) == NO_ERROR) {
394 mClosing = (value == "true");
395 if (mClosing) {
396 standby();
397 }
398 param.remove(key);
399 }
400 key = AudioParameter::keyRouting;
401 if (param.getInt(key, device) == NO_ERROR) {
Mike Lockwood33bf1b02014-05-21 09:28:05 -0700402 if (audio_is_a2dp_out_device(device)) {
Dima Zavinf0121592011-04-19 16:33:12 -0700403 mDevice = device;
404 status = NO_ERROR;
405 } else {
406 status = BAD_VALUE;
407 }
408 param.remove(key);
409 }
410
411 if (param.size()) {
412 status = BAD_VALUE;
413 }
414 return status;
415}
416
417String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys)
418{
419 AudioParameter param = AudioParameter(keys);
420 String8 value;
421 String8 key = String8("a2dp_sink_address");
422
423 if (param.get(key, value) == NO_ERROR) {
424 value = mA2dpAddress;
425 param.add(key, value);
426 }
427 key = AudioParameter::keyRouting;
428 if (param.get(key, value) == NO_ERROR) {
429 param.addInt(key, (int)mDevice);
430 }
431
Steve Block6a705182011-10-20 11:56:19 +0100432 ALOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string());
Dima Zavinf0121592011-04-19 16:33:12 -0700433 return param.toString();
434}
435
436status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
437{
438 Mutex::Autolock lock(mLock);
439
440 if (strlen(address) != strlen("00:00:00:00:00:00"))
441 return -EINVAL;
442
443 strcpy(mA2dpAddress, address);
444 if (mData)
445 a2dp_set_sink(mData, mA2dpAddress);
446
447 return NO_ERROR;
448}
449
450status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled)
451{
Steve Blockb381b932011-12-20 16:25:34 +0000452 ALOGD("setBluetoothEnabled %d", enabled);
Dima Zavinf0121592011-04-19 16:33:12 -0700453
454 Mutex::Autolock lock(mLock);
455
456 mBluetoothEnabled = enabled;
457 if (!enabled) {
458 return close_l();
459 }
460 return NO_ERROR;
461}
462
463status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff)
464{
Steve Block6a705182011-10-20 11:56:19 +0100465 ALOGV("setSuspended %d", onOff);
Dima Zavinf0121592011-04-19 16:33:12 -0700466 mSuspended = onOff;
467 standby();
468 return NO_ERROR;
469}
470
471status_t A2dpAudioInterface::A2dpAudioStreamOut::close()
472{
473 Mutex::Autolock lock(mLock);
Steve Block6a705182011-10-20 11:56:19 +0100474 ALOGV("A2dpAudioStreamOut::close() calling close_l()");
Dima Zavinf0121592011-04-19 16:33:12 -0700475 return close_l();
476}
477
478status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l()
479{
480 standby_l();
481 if (mData) {
Steve Block6a705182011-10-20 11:56:19 +0100482 ALOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)");
Dima Zavinf0121592011-04-19 16:33:12 -0700483 a2dp_cleanup(mData);
484 mData = NULL;
485 }
486 return NO_ERROR;
487}
488
489status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args)
490{
491 return NO_ERROR;
492}
493
494status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames)
495{
496 //TODO: enable when supported by driver
497 return INVALID_OPERATION;
498}
499
500}; // namespace android