1/*
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>
23
24#include "A2dpAudioInterface.h"
25#include "audio/liba2dp.h"
26#include <hardware_legacy/power.h>
27
28
29namespace android_audio_legacy {
30
31static const char *sA2dpWakeLock = "A2dpOutputStream";
32#define MAX_WRITE_RETRIES  5
33
34// ----------------------------------------------------------------------------
35
36//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface()
37//{
38//    AudioHardwareInterface* hw = 0;
39//
40//    hw = AudioHardwareInterface::create();
41//    ALOGD("new A2dpAudioInterface(hw: %p)", hw);
42//    hw = new A2dpAudioInterface(hw);
43//    return hw;
44//}
45
46A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) :
47    mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true), mSuspended(false)
48{
49}
50
51A2dpAudioInterface::~A2dpAudioInterface()
52{
53    closeOutputStream((AudioStreamOut *)mOutput);
54    delete mHardwareInterface;
55}
56
57status_t A2dpAudioInterface::initCheck()
58{
59    if (mHardwareInterface == 0) return NO_INIT;
60    return mHardwareInterface->initCheck();
61}
62
63AudioStreamOut* A2dpAudioInterface::openOutputStream(
64        uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
65{
66    if (!audio_is_a2dp_out_device(devices)) {
67        ALOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
68        return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
69    }
70
71    status_t err = 0;
72
73    // only one output stream allowed
74    if (mOutput) {
75        if (status)
76            *status = -1;
77        return NULL;
78    }
79
80    // create new output stream
81    A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
82    if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
83        mOutput = out;
84        mOutput->setBluetoothEnabled(mBluetoothEnabled);
85        mOutput->setSuspended(mSuspended);
86    } else {
87        delete out;
88    }
89
90    if (status)
91        *status = err;
92    return mOutput;
93}
94
95void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) {
96    if (mOutput == 0 || mOutput != out) {
97        mHardwareInterface->closeOutputStream(out);
98    }
99    else {
100        delete mOutput;
101        mOutput = 0;
102    }
103}
104
105
106AudioStreamIn* A2dpAudioInterface::openInputStream(
107        uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
108        AudioSystem::audio_in_acoustics acoustics)
109{
110    return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
111}
112
113void A2dpAudioInterface::closeInputStream(AudioStreamIn* in)
114{
115    return mHardwareInterface->closeInputStream(in);
116}
117
118status_t A2dpAudioInterface::setMode(int mode)
119{
120    return mHardwareInterface->setMode(mode);
121}
122
123status_t A2dpAudioInterface::setMicMute(bool state)
124{
125    return mHardwareInterface->setMicMute(state);
126}
127
128status_t A2dpAudioInterface::getMicMute(bool* state)
129{
130    return mHardwareInterface->getMicMute(state);
131}
132
133status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs)
134{
135    AudioParameter param = AudioParameter(keyValuePairs);
136    String8 value;
137    String8 key;
138    status_t status = NO_ERROR;
139
140    ALOGV("setParameters() %s", keyValuePairs.string());
141
142    key = "bluetooth_enabled";
143    if (param.get(key, value) == NO_ERROR) {
144        mBluetoothEnabled = (value == "true");
145        if (mOutput) {
146            mOutput->setBluetoothEnabled(mBluetoothEnabled);
147        }
148        param.remove(key);
149    }
150    key = String8("A2dpSuspended");
151    if (param.get(key, value) == NO_ERROR) {
152        mSuspended = (value == "true");
153        if (mOutput) {
154            mOutput->setSuspended(mSuspended);
155        }
156        param.remove(key);
157    }
158
159    if (param.size()) {
160        status_t hwStatus = mHardwareInterface->setParameters(param.toString());
161        if (status == NO_ERROR) {
162            status = hwStatus;
163        }
164    }
165
166    return status;
167}
168
169String8 A2dpAudioInterface::getParameters(const String8& keys)
170{
171    AudioParameter param = AudioParameter(keys);
172    AudioParameter a2dpParam = AudioParameter();
173    String8 value;
174    String8 key;
175
176    key = "bluetooth_enabled";
177    if (param.get(key, value) == NO_ERROR) {
178        value = mBluetoothEnabled ? "true" : "false";
179        a2dpParam.add(key, value);
180        param.remove(key);
181    }
182    key = "A2dpSuspended";
183    if (param.get(key, value) == NO_ERROR) {
184        value = mSuspended ? "true" : "false";
185        a2dpParam.add(key, value);
186        param.remove(key);
187    }
188
189    String8 keyValuePairs  = a2dpParam.toString();
190
191    if (param.size()) {
192        if (keyValuePairs != "") {
193            keyValuePairs += ";";
194        }
195        keyValuePairs += mHardwareInterface->getParameters(param.toString());
196    }
197
198    ALOGV("getParameters() %s", keyValuePairs.string());
199    return keyValuePairs;
200}
201
202size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
203{
204    return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount);
205}
206
207status_t A2dpAudioInterface::setVoiceVolume(float v)
208{
209    return mHardwareInterface->setVoiceVolume(v);
210}
211
212status_t A2dpAudioInterface::setMasterVolume(float v)
213{
214    return mHardwareInterface->setMasterVolume(v);
215}
216
217status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
218{
219    return mHardwareInterface->dumpState(fd, args);
220}
221
222// ----------------------------------------------------------------------------
223
224A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
225    mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL),
226    // assume BT enabled to start, this is safe because its only the
227    // enabled->disabled transition we are worried about
228    mBluetoothEnabled(true), mDevice(0), mClosing(false), mSuspended(false)
229{
230    // use any address by default
231    strcpy(mA2dpAddress, "00:00:00:00:00:00");
232    init();
233}
234
235status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
236        uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
237{
238    int lFormat = pFormat ? *pFormat : 0;
239    uint32_t lChannels = pChannels ? *pChannels : 0;
240    uint32_t lRate = pRate ? *pRate : 0;
241
242    ALOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate);
243
244    // fix up defaults
245    if (lFormat == 0) lFormat = format();
246    if (lChannels == 0) lChannels = channels();
247    if (lRate == 0) lRate = sampleRate();
248
249    // check values
250    if ((lFormat != format()) ||
251            (lChannels != channels()) ||
252            (lRate != sampleRate())){
253        if (pFormat) *pFormat = format();
254        if (pChannels) *pChannels = channels();
255        if (pRate) *pRate = sampleRate();
256        return BAD_VALUE;
257    }
258
259    if (pFormat) *pFormat = lFormat;
260    if (pChannels) *pChannels = lChannels;
261    if (pRate) *pRate = lRate;
262
263    mDevice = device;
264    mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
265    return NO_ERROR;
266}
267
268A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
269{
270    ALOGV("A2dpAudioStreamOut destructor");
271    close();
272    ALOGV("A2dpAudioStreamOut destructor returning from close()");
273}
274
275ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
276{
277    status_t status = -1;
278    {
279        Mutex::Autolock lock(mLock);
280
281        size_t remaining = bytes;
282
283        if (!mBluetoothEnabled || mClosing || mSuspended) {
284            ALOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \
285                   mBluetoothEnabled %d, mClosing %d, mSuspended %d",
286                    mBluetoothEnabled, mClosing, mSuspended);
287            goto Error;
288        }
289
290        if (mStandby) {
291            acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
292            mStandby = false;
293            mLastWriteTime = systemTime();
294        }
295
296        status = init();
297        if (status < 0)
298            goto Error;
299
300        int retries = MAX_WRITE_RETRIES;
301        while (remaining > 0 && retries) {
302            status = a2dp_write(mData, buffer, remaining);
303            if (status < 0) {
304                ALOGE("a2dp_write failed err: %d\n", status);
305                goto Error;
306            }
307            if (status == 0) {
308                retries--;
309            }
310            remaining -= status;
311            buffer = (char *)buffer + status;
312        }
313
314        // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
315        // does no spin and starve other threads.
316        // NOTE: It is likely that the A2DP headset is being disconnected
317        nsecs_t now = systemTime();
318        if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
319            ALOGV("A2DP sink runs too fast");
320            usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
321        }
322        mLastWriteTime = now;
323        return bytes;
324
325    }
326Error:
327
328    standby();
329
330    // Simulate audio output timing in case of error
331    usleep(mBufferDurationUs);
332
333    return status;
334}
335
336status_t A2dpAudioInterface::A2dpAudioStreamOut::init()
337{
338    if (!mData) {
339        status_t status = a2dp_init(44100, 2, &mData);
340        if (status < 0) {
341            ALOGE("a2dp_init failed err: %d\n", status);
342            mData = NULL;
343            return status;
344        }
345        a2dp_set_sink(mData, mA2dpAddress);
346    }
347
348    return 0;
349}
350
351status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
352{
353    Mutex::Autolock lock(mLock);
354    return standby_l();
355}
356
357status_t A2dpAudioInterface::A2dpAudioStreamOut::standby_l()
358{
359    int result = NO_ERROR;
360
361    if (!mStandby) {
362        ALOGV_IF(mClosing || !mBluetoothEnabled, "Standby skip stop: closing %d enabled %d",
363                mClosing, mBluetoothEnabled);
364        if (!mClosing && mBluetoothEnabled) {
365            result = a2dp_stop(mData);
366        }
367        release_wake_lock(sA2dpWakeLock);
368        mStandby = true;
369    }
370
371    return result;
372}
373
374status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs)
375{
376    AudioParameter param = AudioParameter(keyValuePairs);
377    String8 value;
378    String8 key = String8("a2dp_sink_address");
379    status_t status = NO_ERROR;
380    int device;
381    ALOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string());
382
383    if (param.get(key, value) == NO_ERROR) {
384        if (value.length() != strlen("00:00:00:00:00:00")) {
385            status = BAD_VALUE;
386        } else {
387            setAddress(value.string());
388        }
389        param.remove(key);
390    }
391    key = String8("closing");
392    if (param.get(key, value) == NO_ERROR) {
393        mClosing = (value == "true");
394        if (mClosing) {
395            standby();
396        }
397        param.remove(key);
398    }
399    key = AudioParameter::keyRouting;
400    if (param.getInt(key, device) == NO_ERROR) {
401        if (audio_is_a2dp_out_device(device)) {
402            mDevice = device;
403            status = NO_ERROR;
404        } else {
405            status = BAD_VALUE;
406        }
407        param.remove(key);
408    }
409
410    if (param.size()) {
411        status = BAD_VALUE;
412    }
413    return status;
414}
415
416String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys)
417{
418    AudioParameter param = AudioParameter(keys);
419    String8 value;
420    String8 key = String8("a2dp_sink_address");
421
422    if (param.get(key, value) == NO_ERROR) {
423        value = mA2dpAddress;
424        param.add(key, value);
425    }
426    key = AudioParameter::keyRouting;
427    if (param.get(key, value) == NO_ERROR) {
428        param.addInt(key, (int)mDevice);
429    }
430
431    ALOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string());
432    return param.toString();
433}
434
435status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
436{
437    Mutex::Autolock lock(mLock);
438
439    if (strlen(address) != strlen("00:00:00:00:00:00"))
440        return -EINVAL;
441
442    strcpy(mA2dpAddress, address);
443    if (mData)
444        a2dp_set_sink(mData, mA2dpAddress);
445
446    return NO_ERROR;
447}
448
449status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled)
450{
451    ALOGD("setBluetoothEnabled %d", enabled);
452
453    Mutex::Autolock lock(mLock);
454
455    mBluetoothEnabled = enabled;
456    if (!enabled) {
457        return close_l();
458    }
459    return NO_ERROR;
460}
461
462status_t A2dpAudioInterface::A2dpAudioStreamOut::setSuspended(bool onOff)
463{
464    ALOGV("setSuspended %d", onOff);
465    mSuspended = onOff;
466    standby();
467    return NO_ERROR;
468}
469
470status_t A2dpAudioInterface::A2dpAudioStreamOut::close()
471{
472    Mutex::Autolock lock(mLock);
473    ALOGV("A2dpAudioStreamOut::close() calling close_l()");
474    return close_l();
475}
476
477status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l()
478{
479    standby_l();
480    if (mData) {
481        ALOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)");
482        a2dp_cleanup(mData);
483        mData = NULL;
484    }
485    return NO_ERROR;
486}
487
488status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args)
489{
490    return NO_ERROR;
491}
492
493status_t A2dpAudioInterface::A2dpAudioStreamOut::getRenderPosition(uint32_t *driverFrames)
494{
495    //TODO: enable when supported by driver
496    return INVALID_OPERATION;
497}
498
499}; // namespace android
500