summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean McNeil <sean.mcneil@windriver.com>2009-12-02 23:06:34 +0700
committerSean McNeil <sean.mcneil@windriver.com>2009-12-02 23:06:34 +0700
commitb68de1db25598a54313ce4e7faf47d7af1c9c844 (patch)
treef7832f3651308724c16baf5f6d42def395016958
parent64ff6838637d2285b836ef0d8121bd6d6316e964 (diff)
downloadalsa_sound-b68de1db25598a54313ce4e7faf47d7af1c9c844.tar.gz
Eclair ALSA version including new ALSA module mechanism.
-rw-r--r--ALSAControl.cpp217
-rw-r--r--ALSAMixer.cpp432
-rw-r--r--ALSAStreamOps.cpp291
-rw-r--r--Android.mk70
-rw-r--r--AudioHardwareALSA.cpp1530
-rw-r--r--AudioHardwareALSA.h525
-rw-r--r--AudioPolicyManagerALSA.cpp1854
-rw-r--r--AudioPolicyManagerALSA.h201
-rw-r--r--AudioStreamInALSA.cpp155
-rw-r--r--AudioStreamOutALSA.cpp169
-rw-r--r--acoustics_default.cpp136
-rw-r--r--alsa_default.cpp535
12 files changed, 4370 insertions, 1745 deletions
diff --git a/ALSAControl.cpp b/ALSAControl.cpp
new file mode 100644
index 0000000..65fd86f
--- /dev/null
+++ b/ALSAControl.cpp
@@ -0,0 +1,217 @@
+/* ALSAControl.cpp
+ **
+ ** Copyright 2008-2009 Wind River Systems
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "ALSAControl"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <cutils/properties.h>
+#include <media/AudioRecord.h>
+#include <hardware_legacy/power.h>
+
+#include "AudioHardwareALSA.h"
+
+namespace android
+{
+
+ALSAControl::ALSAControl(const char *device)
+{
+ snd_ctl_open(&mHandle, device, 0);
+}
+
+ALSAControl::~ALSAControl()
+{
+ if (mHandle) snd_ctl_close(mHandle);
+}
+
+status_t ALSAControl::get(const char *name, unsigned int &value, int index)
+{
+ if (!mHandle) {
+ LOGE("Control not initialized");
+ return NO_INIT;
+ }
+
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_value_t *control;
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, name);
+ snd_ctl_elem_info_set_id(info, id);
+
+ int ret = snd_ctl_elem_info(mHandle, info);
+ if (ret < 0) {
+ LOGE("Control '%s' cannot get element info: %d", name, ret);
+ return BAD_VALUE;
+ }
+
+ int count = snd_ctl_elem_info_get_count(info);
+ if (index >= count) {
+ LOGE("Control '%s' index is out of range (%d >= %d)", name, index, count);
+ return BAD_VALUE;
+ }
+
+ snd_ctl_elem_info_get_id(info, id);
+ snd_ctl_elem_value_set_id(control, id);
+
+ ret = snd_ctl_elem_read(mHandle, control);
+ if (ret < 0) {
+ LOGE("Control '%s' cannot read element value: %d", name, ret);
+ return BAD_VALUE;
+ }
+
+ snd_ctl_elem_type_t type = snd_ctl_elem_info_get_type(info);
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ value = snd_ctl_elem_value_get_boolean(control, index);
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ value = snd_ctl_elem_value_get_integer(control, index);
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ value = snd_ctl_elem_value_get_integer64(control, index);
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ value = snd_ctl_elem_value_get_enumerated(control, index);
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ value = snd_ctl_elem_value_get_byte(control, index);
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t ALSAControl::set(const char *name, unsigned int value, int index)
+{
+ if (!mHandle) {
+ LOGE("Control not initialized");
+ return NO_INIT;
+ }
+
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_info_t *info;
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_info_alloca(&info);
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, name);
+ snd_ctl_elem_info_set_id(info, id);
+
+ int ret = snd_ctl_elem_info(mHandle, info);
+ if (ret < 0) {
+ LOGE("Control '%s' cannot get element info: %d", name, ret);
+ return BAD_VALUE;
+ }
+
+ int count = snd_ctl_elem_info_get_count(info);
+ if (index >= count) {
+ LOGE("Control '%s' index is out of range (%d >= %d)", name, index, count);
+ return BAD_VALUE;
+ }
+
+ if (index == -1)
+ index = 0; // Range over all of them
+ else
+ count = index + 1; // Just do the one specified
+
+ snd_ctl_elem_type_t type = snd_ctl_elem_info_get_type(info);
+
+ snd_ctl_elem_value_t *control;
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_info_get_id(info, id);
+ snd_ctl_elem_value_set_id(control, id);
+
+ for (int i = index; i < count; i++)
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ snd_ctl_elem_value_set_boolean(control, i, value);
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ snd_ctl_elem_value_set_integer(control, i, value);
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ snd_ctl_elem_value_set_integer64(control, i, value);
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ snd_ctl_elem_value_set_enumerated(control, i, value);
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ snd_ctl_elem_value_set_byte(control, i, value);
+ break;
+ default:
+ break;
+ }
+
+ ret = snd_ctl_elem_write(mHandle, control);
+ return (ret < 0) ? BAD_VALUE : NO_ERROR;
+}
+
+status_t ALSAControl::set(const char *name, const char *value)
+{
+ if (!mHandle) {
+ LOGE("Control not initialized");
+ return NO_INIT;
+ }
+
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_info_t *info;
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_info_alloca(&info);
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_name(id, name);
+ snd_ctl_elem_info_set_id(info, id);
+
+ int ret = snd_ctl_elem_info(mHandle, info);
+ if (ret < 0) {
+ LOGE("Control '%s' cannot get element info: %d", name, ret);
+ return BAD_VALUE;
+ }
+
+ int items = snd_ctl_elem_info_get_items(info);
+ for (int i = 0; i < items; i++) {
+ snd_ctl_elem_info_set_item(info, i);
+ ret = snd_ctl_elem_info(mHandle, info);
+ if (ret < 0) continue;
+ if (strcmp(value, snd_ctl_elem_info_get_item_name(info)) == 0)
+ return set(name, i, -1);
+ }
+
+ LOGE("Control '%s' has no enumerated value of '%s'", name, value);
+
+ return BAD_VALUE;
+}
+
+}; // namespace android
diff --git a/ALSAMixer.cpp b/ALSAMixer.cpp
new file mode 100644
index 0000000..883edf1
--- /dev/null
+++ b/ALSAMixer.cpp
@@ -0,0 +1,432 @@
+/* ALSAMixer.cpp
+ **
+ ** Copyright 2008-2009 Wind River Systems
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "AudioHardwareALSA"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <cutils/properties.h>
+#include <media/AudioRecord.h>
+#include <hardware_legacy/power.h>
+
+#include "AudioHardwareALSA.h"
+
+#define SND_MIXER_VOL_RANGE_MIN (0)
+#define SND_MIXER_VOL_RANGE_MAX (100)
+
+#define ALSA_NAME_MAX 128
+
+#define ALSA_STRCAT(x,y) \
+ if (strlen(x) + strlen(y) < ALSA_NAME_MAX) \
+ strcat(x, y);
+
+namespace android
+{
+
+// ----------------------------------------------------------------------------
+
+struct mixer_info_t;
+
+struct alsa_properties_t
+{
+ const AudioSystem::audio_devices device;
+ const char *propName;
+ const char *propDefault;
+ mixer_info_t *mInfo;
+};
+
+#define ALSA_PROP(dev, name, out, in) \
+ {\
+ {dev, "alsa.mixer.playback." name, out, NULL},\
+ {dev, "alsa.mixer.capture." name, in, NULL}\
+ }
+
+static alsa_properties_t
+mixerMasterProp[SND_PCM_STREAM_LAST+1] =
+ ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture");
+
+static alsa_properties_t
+mixerProp[][SND_PCM_STREAM_LAST+1] = {
+ ALSA_PROP(AudioSystem::DEVICE_OUT_EARPIECE, "earpiece", "Earpiece", "Capture"),
+ ALSA_PROP(AudioSystem::DEVICE_OUT_SPEAKER, "speaker", "Speaker", ""),
+ ALSA_PROP(AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset", "Headphone", "Capture"),
+ ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth.sco", "Bluetooth", "Bluetooth Capture"),
+ ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth.a2dp", "Bluetooth A2DP", "Bluetooth A2DP Capture"),
+ ALSA_PROP(AudioSystem::DEVICE_OUT_FM_HEADPHONE, "fm", "FM", ""),
+ ALSA_PROP(static_cast<AudioSystem::audio_devices>(0), "", NULL, NULL)
+};
+
+struct mixer_info_t
+{
+ mixer_info_t() :
+ elem(0),
+ min(SND_MIXER_VOL_RANGE_MIN),
+ max(SND_MIXER_VOL_RANGE_MAX),
+ mute(false)
+ {
+ }
+
+ snd_mixer_elem_t *elem;
+ long min;
+ long max;
+ long volume;
+ bool mute;
+ char name[ALSA_NAME_MAX];
+};
+
+static int initMixer (snd_mixer_t **mixer, const char *name)
+{
+ int err;
+
+ if ((err = snd_mixer_open(mixer, 0)) < 0) {
+ LOGE("Unable to open mixer: %s", snd_strerror(err));
+ return err;
+ }
+
+ if ((err = snd_mixer_attach(*mixer, name)) < 0) {
+ LOGW("Unable to attach mixer to device %s: %s",
+ name, snd_strerror(err));
+
+ if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
+ LOGE("Unable to attach mixer to device default: %s",
+ snd_strerror(err));
+
+ snd_mixer_close (*mixer);
+ *mixer = NULL;
+ return err;
+ }
+ }
+
+ if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
+ LOGE("Unable to register mixer elements: %s", snd_strerror(err));
+ snd_mixer_close (*mixer);
+ *mixer = NULL;
+ return err;
+ }
+
+ // Get the mixer controls from the kernel
+ if ((err = snd_mixer_load(*mixer)) < 0) {
+ LOGE("Unable to load mixer elements: %s", snd_strerror(err));
+ snd_mixer_close (*mixer);
+ *mixer = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+typedef int (*hasVolume_t)(snd_mixer_elem_t*);
+
+static const hasVolume_t hasVolume[] = {
+ snd_mixer_selem_has_playback_volume,
+ snd_mixer_selem_has_capture_volume
+};
+
+typedef int (*getVolumeRange_t)(snd_mixer_elem_t*, long int*, long int*);
+
+static const getVolumeRange_t getVolumeRange[] = {
+ snd_mixer_selem_get_playback_volume_range,
+ snd_mixer_selem_get_capture_volume_range
+};
+
+typedef int (*setVolume_t)(snd_mixer_elem_t*, long int);
+
+static const setVolume_t setVol[] = {
+ snd_mixer_selem_set_playback_volume_all,
+ snd_mixer_selem_set_capture_volume_all
+};
+
+ALSAMixer::ALSAMixer()
+{
+ int err;
+
+ initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidOut");
+ initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidIn");
+
+ snd_mixer_selem_id_t *sid;
+ snd_mixer_selem_id_alloca(&sid);
+
+ for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
+
+ mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;
+
+ property_get (mixerMasterProp[i].propName,
+ info->name,
+ mixerMasterProp[i].propDefault);
+
+ for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
+ elem;
+ elem = snd_mixer_elem_next(elem)) {
+
+ if (!snd_mixer_selem_is_active(elem))
+ continue;
+
+ snd_mixer_selem_get_id(elem, sid);
+
+ // Find PCM playback volume control element.
+ const char *elementName = snd_mixer_selem_id_get_name(sid);
+
+ if (info->elem == NULL &&
+ strcmp(elementName, info->name) == 0 &&
+ hasVolume[i] (elem)) {
+
+ info->elem = elem;
+ getVolumeRange[i] (elem, &info->min, &info->max);
+ info->volume = info->max;
+ setVol[i] (elem, info->volume);
+ if (i == SND_PCM_STREAM_PLAYBACK &&
+ snd_mixer_selem_has_playback_switch (elem))
+ snd_mixer_selem_set_playback_switch_all (elem, 1);
+ break;
+ }
+ }
+
+ LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");
+
+ for (int j = 0; mixerProp[j][i].device; j++) {
+
+ mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;
+
+ property_get (mixerProp[j][i].propName,
+ info->name,
+ mixerProp[j][i].propDefault);
+
+ for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
+ elem;
+ elem = snd_mixer_elem_next(elem)) {
+
+ if (!snd_mixer_selem_is_active(elem))
+ continue;
+
+ snd_mixer_selem_get_id(elem, sid);
+
+ // Find PCM playback volume control element.
+ const char *elementName = snd_mixer_selem_id_get_name(sid);
+
+ if (info->elem == NULL &&
+ strcmp(elementName, info->name) == 0 &&
+ hasVolume[i] (elem)) {
+
+ info->elem = elem;
+ getVolumeRange[i] (elem, &info->min, &info->max);
+ info->volume = info->max;
+ setVol[i] (elem, info->volume);
+ if (i == SND_PCM_STREAM_PLAYBACK &&
+ snd_mixer_selem_has_playback_switch (elem))
+ snd_mixer_selem_set_playback_switch_all (elem, 1);
+ break;
+ }
+ }
+ LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
+ }
+ }
+ LOGV("mixer initialized.");
+}
+
+ALSAMixer::~ALSAMixer()
+{
+ for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
+ if (mMixer[i]) snd_mixer_close (mMixer[i]);
+ if (mixerMasterProp[i].mInfo) {
+ delete mixerMasterProp[i].mInfo;
+ mixerMasterProp[i].mInfo = NULL;
+ }
+ for (int j = 0; mixerProp[j][i].device; j++) {
+ if (mixerProp[j][i].mInfo) {
+ delete mixerProp[j][i].mInfo;
+ mixerProp[j][i].mInfo = NULL;
+ }
+ }
+ }
+ LOGV("mixer destroyed.");
+}
+
+status_t ALSAMixer::setMasterVolume(float volume)
+{
+ mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_PLAYBACK].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ long minVol = info->min;
+ long maxVol = info->max;
+
+ // Make sure volume is between bounds.
+ long vol = minVol + volume * (maxVol - minVol);
+ if (vol > maxVol) vol = maxVol;
+ if (vol < minVol) vol = minVol;
+
+ info->volume = vol;
+ snd_mixer_selem_set_playback_volume_all (info->elem, vol);
+
+ return NO_ERROR;
+}
+
+status_t ALSAMixer::setMasterGain(float gain)
+{
+ mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_CAPTURE].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ long minVol = info->min;
+ long maxVol = info->max;
+
+ // Make sure volume is between bounds.
+ long vol = minVol + gain * (maxVol - minVol);
+ if (vol > maxVol) vol = maxVol;
+ if (vol < minVol) vol = minVol;
+
+ info->volume = vol;
+ snd_mixer_selem_set_capture_volume_all (info->elem, vol);
+
+ return NO_ERROR;
+}
+
+status_t ALSAMixer::setVolume(uint32_t device, float left, float right)
+{
+ for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
+ if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
+
+ mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ long minVol = info->min;
+ long maxVol = info->max;
+
+ // Make sure volume is between bounds.
+ long vol = minVol + left * (maxVol - minVol);
+ if (vol > maxVol) vol = maxVol;
+ if (vol < minVol) vol = minVol;
+
+ info->volume = vol;
+ snd_mixer_selem_set_playback_volume_all (info->elem, vol);
+ }
+
+ return NO_ERROR;
+}
+
+status_t ALSAMixer::setGain(uint32_t device, float gain)
+{
+ for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
+ if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
+
+ mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ long minVol = info->min;
+ long maxVol = info->max;
+
+ // Make sure volume is between bounds.
+ long vol = minVol + gain * (maxVol - minVol);
+ if (vol > maxVol) vol = maxVol;
+ if (vol < minVol) vol = minVol;
+
+ info->volume = vol;
+ snd_mixer_selem_set_capture_volume_all (info->elem, vol);
+ }
+
+ return NO_ERROR;
+}
+
+status_t ALSAMixer::setCaptureMuteState(uint32_t device, bool state)
+{
+ for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
+ if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
+
+ mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ if (snd_mixer_selem_has_capture_switch (info->elem)) {
+
+ int err = snd_mixer_selem_set_capture_switch_all (info->elem, static_cast<int>(!state));
+ if (err < 0) {
+ LOGE("Unable to %s capture mixer switch %s",
+ state ? "enable" : "disable", info->name);
+ return INVALID_OPERATION;
+ }
+ }
+
+ info->mute = state;
+ }
+
+ return NO_ERROR;
+}
+
+status_t ALSAMixer::getCaptureMuteState(uint32_t device, bool *state)
+{
+ if (!state) return BAD_VALUE;
+
+ for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].device; j++)
+ if (mixerProp[j][SND_PCM_STREAM_CAPTURE].device & device) {
+
+ mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ *state = info->mute;
+ return NO_ERROR;
+ }
+
+ return BAD_VALUE;
+}
+
+status_t ALSAMixer::setPlaybackMuteState(uint32_t device, bool state)
+{
+ for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
+ if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
+
+ mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ if (snd_mixer_selem_has_playback_switch (info->elem)) {
+
+ int err = snd_mixer_selem_set_playback_switch_all (info->elem, static_cast<int>(!state));
+ if (err < 0) {
+ LOGE("Unable to %s playback mixer switch %s",
+ state ? "enable" : "disable", info->name);
+ return INVALID_OPERATION;
+ }
+ }
+
+ info->mute = state;
+ }
+
+ return NO_ERROR;
+}
+
+status_t ALSAMixer::getPlaybackMuteState(uint32_t device, bool *state)
+{
+ if (!state) return BAD_VALUE;
+
+ for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].device; j++)
+ if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].device & device) {
+
+ mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
+ if (!info || !info->elem) return INVALID_OPERATION;
+
+ *state = info->mute;
+ return NO_ERROR;
+ }
+
+ return BAD_VALUE;
+}
+
+}; // namespace android
diff --git a/ALSAStreamOps.cpp b/ALSAStreamOps.cpp
new file mode 100644
index 0000000..0d14971
--- /dev/null
+++ b/ALSAStreamOps.cpp
@@ -0,0 +1,291 @@
+/* ALSAStreamOps.cpp
+ **
+ ** Copyright 2008-2009 Wind River Systems
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "AudioHardwareALSA"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <cutils/properties.h>
+#include <media/AudioRecord.h>
+#include <hardware_legacy/power.h>
+
+#include "AudioHardwareALSA.h"
+
+namespace android
+{
+
+// ----------------------------------------------------------------------------
+
+ALSAStreamOps::ALSAStreamOps(AudioHardwareALSA *parent, alsa_handle_t *handle) :
+ mParent(parent),
+ mHandle(handle),
+ mPowerLock(false)
+{
+}
+
+ALSAStreamOps::~ALSAStreamOps()
+{
+ AutoMutex lock(mLock);
+
+ close();
+}
+
+// use emulated popcount optimization
+// http://www.df.lth.se/~john_e/gems/gem002d.html
+static inline uint32_t popCount(uint32_t u)
+{
+ u = ((u&0x55555555) + ((u>>1)&0x55555555));
+ u = ((u&0x33333333) + ((u>>2)&0x33333333));
+ u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
+ u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
+ u = ( u&0x0000ffff) + (u>>16);
+ return u;
+}
+
+acoustic_device_t *ALSAStreamOps::acoustics()
+{
+ return mParent->mAcousticDevice;
+}
+
+ALSAMixer *ALSAStreamOps::mixer()
+{
+ return mParent->mMixer;
+}
+
+status_t ALSAStreamOps::set(int *format,
+ uint32_t *channels,
+ uint32_t *rate)
+{
+ if (channels && *channels != 0) {
+ if (mHandle->channels != popCount(*channels))
+ return BAD_VALUE;
+ } else if (channels) {
+ *channels = 0;
+ if (mHandle->devices & AudioSystem::DEVICE_OUT_ALL)
+ switch(mHandle->channels) {
+ case 4:
+ *channels |= AudioSystem::CHANNEL_OUT_BACK_LEFT;
+ *channels |= AudioSystem::CHANNEL_OUT_BACK_RIGHT;
+ // Fall through...
+ default:
+ case 2:
+ *channels |= AudioSystem::CHANNEL_OUT_FRONT_RIGHT;
+ // Fall through...
+ case 1:
+ *channels |= AudioSystem::CHANNEL_OUT_FRONT_LEFT;
+ break;
+ }
+ else
+ switch(mHandle->channels) {
+ default:
+ case 2:
+ *channels |= AudioSystem::CHANNEL_IN_RIGHT;
+ // Fall through...
+ case 1:
+ *channels |= AudioSystem::CHANNEL_IN_LEFT;
+ break;
+ }
+ }
+
+ if (rate && *rate > 0) {
+ if (mHandle->sampleRate != *rate)
+ return BAD_VALUE;
+ } else if (rate)
+ *rate = mHandle->sampleRate;
+
+ snd_pcm_format_t iformat = mHandle->format;
+
+ if (format) {
+ switch(*format) {
+ case AudioSystem::FORMAT_DEFAULT:
+ break;
+
+ case AudioSystem::PCM_16_BIT:
+ iformat = SND_PCM_FORMAT_S16_LE;
+ break;
+
+ case AudioSystem::PCM_8_BIT:
+ iformat = SND_PCM_FORMAT_S8;
+ break;
+
+ default:
+ LOGE("Unknown PCM format %i. Forcing default", *format);
+ break;
+ }
+
+ if (mHandle->format != iformat)
+ return BAD_VALUE;
+
+ switch(iformat) {
+ default:
+ case SND_PCM_FORMAT_S16_LE:
+ *format = AudioSystem::PCM_16_BIT;
+ break;
+ case SND_PCM_FORMAT_S8:
+ *format = AudioSystem::PCM_8_BIT;
+ break;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t ALSAStreamOps::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ status_t status = NO_ERROR;
+ int device;
+ LOGV("setParameters() %s", keyValuePairs.string());
+
+ if (param.getInt(key, device) == NO_ERROR) {
+ mParent->mALSADevice->route(mHandle, (uint32_t)device, mParent->mode());
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+String8 ALSAStreamOps::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mHandle->curDev);
+ }
+
+ LOGV("getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
+uint32_t ALSAStreamOps::sampleRate() const
+{
+ return mHandle->sampleRate;
+}
+
+//
+// Return the number of bytes (not frames)
+//
+size_t ALSAStreamOps::bufferSize() const
+{
+ snd_pcm_uframes_t bufferSize = mHandle->bufferSize;
+ snd_pcm_uframes_t periodSize;
+
+ snd_pcm_get_params(mHandle->handle, &bufferSize, &periodSize);
+
+ size_t bytes = static_cast<size_t>(snd_pcm_frames_to_bytes(mHandle->handle, bufferSize));
+
+ // Not sure when this happened, but unfortunately it now
+ // appears that the bufferSize must be reported as a
+ // power of 2. This might be for OSS compatibility.
+ for (size_t i = 1; (bytes & ~i) != 0; i<<=1)
+ bytes &= ~i;
+
+ return bytes;
+}
+
+int ALSAStreamOps::format() const
+{
+ int pcmFormatBitWidth;
+ int audioSystemFormat;
+
+ snd_pcm_format_t ALSAFormat = mHandle->format;
+
+ pcmFormatBitWidth = snd_pcm_format_physical_width(ALSAFormat);
+ switch(pcmFormatBitWidth) {
+ case 8:
+ audioSystemFormat = AudioSystem::PCM_8_BIT;
+ break;
+
+ default:
+ LOG_FATAL("Unknown AudioSystem bit width %i!", pcmFormatBitWidth);
+
+ case 16:
+ audioSystemFormat = AudioSystem::PCM_16_BIT;
+ break;
+ }
+
+ return audioSystemFormat;
+}
+
+uint32_t ALSAStreamOps::channels() const
+{
+ unsigned int count = mHandle->channels;
+ uint32_t channels = 0;
+
+ if (mHandle->curDev & AudioSystem::DEVICE_OUT_ALL)
+ switch(count) {
+ case 4:
+ channels |= AudioSystem::CHANNEL_OUT_BACK_LEFT;
+ channels |= AudioSystem::CHANNEL_OUT_BACK_RIGHT;
+ // Fall through...
+ default:
+ case 2:
+ channels |= AudioSystem::CHANNEL_OUT_FRONT_RIGHT;
+ // Fall through...
+ case 1:
+ channels |= AudioSystem::CHANNEL_OUT_FRONT_LEFT;
+ break;
+ }
+ else
+ switch(count) {
+ default:
+ case 2:
+ channels |= AudioSystem::CHANNEL_IN_RIGHT;
+ // Fall through...
+ case 1:
+ channels |= AudioSystem::CHANNEL_IN_LEFT;
+ break;
+ }
+
+ return channels;
+}
+
+void ALSAStreamOps::close()
+{
+ mParent->mALSADevice->close(mHandle);
+}
+
+//
+// Set playback or capture PCM device. It's possible to support audio output
+// or input from multiple devices by using the ALSA plugins, but this is
+// not supported for simplicity.
+//
+// The AudioHardwareALSA API does not allow one to set the input routing.
+//
+// If the "routes" value does not map to a valid device, the default playback
+// device is used.
+//
+status_t ALSAStreamOps::open(int mode)
+{
+ return mParent->mALSADevice->open(mHandle, mHandle->curDev, mode);
+}
+
+} // namespace android
diff --git a/Android.mk b/Android.mk
index 9a171dc..5eee4a1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -11,42 +11,88 @@ ifeq ($(strip $(BOARD_USES_ALSA_AUDIO)),true)
LOCAL_ARM_MODE := arm
LOCAL_CFLAGS := -D_POSIX_SOURCE
- LOCAL_WHOLE_STATIC_LIBRARIES := libasound
-
- ifneq ($(ALSA_DEFAULT_SAMPLE_RATE),)
- LOCAL_CFLAGS += -DALSA_DEFAULT_SAMPLE_RATE=$(ALSA_DEFAULT_SAMPLE_RATE)
- endif
-
- ifeq ($(strip $(BOARD_HAVE_FM_ROUTING)),true)
- LOCAL_CFLAGS += -DFM_ROUTE_SUPPORT
- endif
LOCAL_C_INCLUDES += external/alsa-lib/include
- LOCAL_SRC_FILES := AudioHardwareALSA.cpp
+ LOCAL_SRC_FILES := \
+ AudioHardwareALSA.cpp \
+ AudioStreamOutALSA.cpp \
+ AudioStreamInALSA.cpp \
+ ALSAStreamOps.cpp \
+ ALSAMixer.cpp \
+ ALSAControl.cpp
LOCAL_MODULE := libaudio
LOCAL_STATIC_LIBRARIES += libaudiointerface
LOCAL_SHARED_LIBRARIES := \
+ libasound \
libcutils \
libutils \
libmedia \
libhardware \
libhardware_legacy \
- libdl \
libc
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_SHARED_LIBRARIES += liba2dp
+endif
+
+ include $(BUILD_SHARED_LIBRARY)
+
+# This is the ALSA audio policy manager
+
+ include $(CLEAR_VARS)
+
+ LOCAL_CFLAGS := -D_POSIX_SOURCE
+
+ LOCAL_SRC_FILES := AudioPolicyManagerALSA.cpp
+
+ LOCAL_MODULE := libaudiopolicy
+
+ LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libmedia
+
include $(BUILD_SHARED_LIBRARY)
+# This is the default ALSA module which behaves closely like the original
+
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
- LOCAL_CFLAGS := -D_POSIX_SOURCE
+ LOCAL_CFLAGS := -D_POSIX_SOURCE -Wno-multichar
+
+ifneq ($(ALSA_DEFAULT_SAMPLE_RATE),)
+ LOCAL_CFLAGS += -DALSA_DEFAULT_SAMPLE_RATE=$(ALSA_DEFAULT_SAMPLE_RATE)
+endif
+
+ LOCAL_C_INCLUDES += external/alsa-lib/include
+
+ LOCAL_SRC_FILES:= alsa_default.cpp
+
+ LOCAL_SHARED_LIBRARIES := \
+ libasound \
+ liblog
+
+ LOCAL_MODULE:= alsa.default
+
+ include $(BUILD_SHARED_LIBRARY)
+
+# This is the default Acoustics module which is essentially a stub
+
+ include $(CLEAR_VARS)
+
+ LOCAL_PRELINK_MODULE := false
+
+ LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+
+ LOCAL_CFLAGS := -D_POSIX_SOURCE -Wno-multichar
LOCAL_C_INCLUDES += external/alsa-lib/include
diff --git a/AudioHardwareALSA.cpp b/AudioHardwareALSA.cpp
index 684ee63..46dfd4e 100644
--- a/AudioHardwareALSA.cpp
+++ b/AudioHardwareALSA.cpp
@@ -33,61 +33,21 @@
#include "AudioHardwareALSA.h"
-#undef DISABLE_HARWARE_RESAMPLING
-
-#ifndef ALSA_DEFAULT_SAMPLE_RATE
-#define ALSA_DEFAULT_SAMPLE_RATE 44100 // in Hz
-#endif
-
-#define SND_MIXER_VOL_RANGE_MIN (0)
-#define SND_MIXER_VOL_RANGE_MAX (100)
-
-#define ALSA_NAME_MAX 128
-
-#define ALSA_STRCAT(x,y) \
- if (strlen(x) + strlen(y) < ALSA_NAME_MAX) \
- strcat(x, y);
-
extern "C"
{
//
- // Make sure this prototype is consistent with what's in
- // external/libasound/alsa-lib-1.0.16/src/pcm/pcm_null.c!
- //
- extern int snd_pcm_null_open(snd_pcm_t **pcmp,
- const char *name,
- snd_pcm_stream_t stream,
- int mode);
-
- //
// Function for dlsym() to look up for creating a new AudioHardwareInterface.
//
android::AudioHardwareInterface *createAudioHardware(void) {
- return new android::AudioHardwareALSA();
+ return android::AudioHardwareALSA::create();
}
} // extern "C"
namespace android
{
-typedef AudioSystem::audio_routes audio_routes;
-
-#define ROUTE_ALL AudioSystem::ROUTE_ALL
-#define ROUTE_EARPIECE AudioSystem::ROUTE_EARPIECE
-#define ROUTE_SPEAKER AudioSystem::ROUTE_SPEAKER
-#define ROUTE_BLUETOOTH_SCO AudioSystem::ROUTE_BLUETOOTH_SCO
-#define ROUTE_HEADSET AudioSystem::ROUTE_HEADSET
-#define ROUTE_BLUETOOTH_A2DP AudioSystem::ROUTE_BLUETOOTH_A2DP
-#ifdef FM_ROUTE_SUPPORT
-#define ROUTE_FM AudioSystem::ROUTE_FM
-#endif
-
// ----------------------------------------------------------------------------
-static const int DEFAULT_SAMPLE_RATE = ALSA_DEFAULT_SAMPLE_RATE;
-
-static const char _nullALSADeviceName[] = "NULL_Device";
-
static void ALSAErrorHandler(const char *file,
int line,
const char *function,
@@ -107,108 +67,57 @@ static void ALSAErrorHandler(const char *file,
va_end(arg);
}
-// ----------------------------------------------------------------------------
-
-/* The following table(s) need to match in order of the route bits
- */
-static const char *deviceSuffix[] = {
- /* ROUTE_EARPIECE */ "_Earpiece",
- /* ROUTE_SPEAKER */ "_Speaker",
- /* ROUTE_BLUETOOTH_SCO */ "_Bluetooth",
- /* ROUTE_HEADSET */ "_Headset",
- /* ROUTE_BLUETOOTH_A2DP */ "_Bluetooth-A2DP",
-#ifdef FM_ROUTE_SUPPORT
- /* ROUTE_FM */ "_FM",
-#endif
-};
-
-static const int deviceSuffixLen = (sizeof(deviceSuffix) / sizeof(char *));
-
-struct mixer_info_t;
-
-struct alsa_properties_t
-{
- const audio_routes routes;
- const char *propName;
- const char *propDefault;
- mixer_info_t *mInfo;
-};
-
-static alsa_properties_t
-mixerMasterProp[SND_PCM_STREAM_LAST+1] = {
- { ROUTE_ALL, "alsa.mixer.playback.master", "PCM", NULL},
- { ROUTE_ALL, "alsa.mixer.capture.master", "Capture", NULL}
-};
-
-static alsa_properties_t
-mixerProp[][SND_PCM_STREAM_LAST+1] = {
- {
- {ROUTE_EARPIECE, "alsa.mixer.playback.earpiece", "Earpiece", NULL},
- {ROUTE_EARPIECE, "alsa.mixer.capture.earpiece", "Capture", NULL}
- },
- {
- {ROUTE_SPEAKER, "alsa.mixer.playback.speaker", "Speaker", NULL},
- {ROUTE_SPEAKER, "alsa.mixer.capture.speaker", "", NULL}
- },
- {
- {ROUTE_BLUETOOTH_SCO, "alsa.mixer.playback.bluetooth.sco", "Bluetooth", NULL},
- {ROUTE_BLUETOOTH_SCO, "alsa.mixer.capture.bluetooth.sco", "Bluetooth Capture", NULL}
- },
- {
- {ROUTE_HEADSET, "alsa.mixer.playback.headset", "Headphone", NULL},
- {ROUTE_HEADSET, "alsa.mixer.capture.headset", "Capture", NULL}
- },
- {
- {ROUTE_BLUETOOTH_A2DP, "alsa.mixer.playback.bluetooth.a2dp", "Bluetooth A2DP", NULL},
- {ROUTE_BLUETOOTH_A2DP, "alsa.mixer.capture.bluetooth.a2dp", "Bluetooth A2DP Capture", NULL}
- },
-#ifdef FM_ROUTE_SUPPORT
- {
- {ROUTE_FM, "alsa.mixer.playback.fm", "FM", NULL},
- {ROUTE_FM, "alsa.mixer.capture.fm", "", NULL}
- },
-#endif
- {
- {static_cast<audio_routes>(0), NULL, NULL, NULL},
- {static_cast<audio_routes>(0), NULL, NULL, NULL}
- }
-};
-
-// ----------------------------------------------------------------------------
+AudioHardwareInterface *AudioHardwareALSA::create() {
+ return new AudioHardwareALSA();
+}
AudioHardwareALSA::AudioHardwareALSA() :
- mOutput(0),
- mInput(0),
+ mALSADevice(0),
mAcousticDevice(0)
{
snd_lib_error_set_handler(&ALSAErrorHandler);
mMixer = new ALSAMixer;
hw_module_t *module;
- int err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
+ int err = hw_get_module(ALSA_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
- err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
+ err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);
if (err == 0) {
+ mALSADevice = (alsa_device_t *)device;
+ mALSADevice->init(mALSADevice, mDeviceList);
+ } else
+ LOGE("ALSA Module could not be opened!!!");
+ } else
+ LOGE("ALSA Module not found!!!");
+
+ err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&module);
+
+ if (err == 0) {
+ hw_device_t* device;
+ err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
+ if (err == 0)
mAcousticDevice = (acoustic_device_t *)device;
- }
+ else
+ LOGE("Acoustics Module not found.");
}
}
AudioHardwareALSA::~AudioHardwareALSA()
{
- if (mOutput) delete mOutput;
- if (mInput) delete mInput;
if (mMixer) delete mMixer;
+ if (mALSADevice)
+ mALSADevice->common.close(&mALSADevice->common);
if (mAcousticDevice)
mAcousticDevice->common.close(&mAcousticDevice->common);
}
status_t AudioHardwareALSA::initCheck()
{
- if (mMixer && mMixer->isValid())
+ if (mALSADevice && mMixer && mMixer->isValid())
return NO_ERROR;
else
return NO_INIT;
@@ -218,7 +127,7 @@ status_t AudioHardwareALSA::setVoiceVolume(float volume)
{
// The voice volume is used by the VOICE_CALL audio stream.
if (mMixer)
- return mMixer->setVolume(ROUTE_EARPIECE, volume);
+ return mMixer->setVolume(AudioSystem::DEVICE_OUT_EARPIECE, volume, volume);
else
return INVALID_OPERATION;
}
@@ -231,1363 +140,128 @@ status_t AudioHardwareALSA::setMasterVolume(float volume)
return INVALID_OPERATION;
}
-AudioStreamOut *
-AudioHardwareALSA::openOutputStream(int format,
- int channelCount,
- uint32_t sampleRate,
- status_t *status)
-{
- AutoMutex lock(mLock);
-
- // only one output stream allowed
- if (mOutput) {
- *status = ALREADY_EXISTS;
- return 0;
- }
-
- AudioStreamOutALSA *out = new AudioStreamOutALSA(this);
-
- *status = out->set(format, channelCount, sampleRate);
-
- if (*status == NO_ERROR) {
- mOutput = out;
- // Some information is expected to be available immediately after
- // the device is open.
- *status = mOutput->setDevice(mMode, mRoutes[mMode]);
- }
- else {
- delete out;
- }
-
- return mOutput;
-}
-
-AudioStreamIn *
-AudioHardwareALSA::openInputStream(int inputSource,
- int format,
- int channelCount,
- uint32_t sampleRate,
- status_t *status,
- AudioSystem::audio_in_acoustics acoustics)
-{
- // check for valid input source
- if ((inputSource < AudioRecord::DEFAULT_INPUT) ||
- (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) {
- return 0;
- }
-
- AutoMutex lock(mLock);
-
- // only one input stream allowed
- if (mInput) {
- *status = ALREADY_EXISTS;
- return 0;
- }
-
- AudioStreamInALSA *in = new AudioStreamInALSA(this, acoustics);
-
- *status = in->set(format, channelCount, sampleRate);
- if (*status == NO_ERROR) {
- mInput = in;
- // Some information is expected to be available immediately after
- // the device is open.
- *status = mInput->setDevice(mMode, mRoutes[mMode]);
- }
- else {
- delete in;
- }
-
- return mInput;
-}
-
-status_t AudioHardwareALSA::doRouting()
-{
- AutoMutex lock(mLock);
-
- if (!mOutput)
- return NO_INIT;
-
- return mOutput->setDevice(mMode, mRoutes[mMode]);
-}
-
-status_t AudioHardwareALSA::setMicMute(bool state)
-{
- if (mMixer)
- return mMixer->setCaptureMuteState(ROUTE_EARPIECE, state);
-
- return NO_INIT;
-}
-
-status_t AudioHardwareALSA::getMicMute(bool *state)
-{
- if (mMixer)
- return mMixer->getCaptureMuteState(ROUTE_EARPIECE, state);
-
- return NO_ERROR;
-}
-
-status_t AudioHardwareALSA::dump(int fd, const Vector<String16>& args)
-{
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-ALSAStreamOps::ALSAStreamOps(AudioHardwareALSA *parent) :
- mParent(parent),
- mHandle(0),
- mHardwareParams(0),
- mSoftwareParams(0),
- mMode(-1),
- mDevice(0),
- mPowerLock(false)
-{
- if (snd_pcm_hw_params_malloc(&mHardwareParams) < 0) {
- LOG_ALWAYS_FATAL("Failed to allocate ALSA hardware parameters!");
- }
-
- if (snd_pcm_sw_params_malloc(&mSoftwareParams) < 0) {
- LOG_ALWAYS_FATAL("Failed to allocate ALSA software parameters!");
- }
-}
-
-ALSAStreamOps::~ALSAStreamOps()
-{
- AutoMutex lock(mLock);
-
- close();
-
- if (mHardwareParams)
- snd_pcm_hw_params_free(mHardwareParams);
-
- if (mSoftwareParams)
- snd_pcm_sw_params_free(mSoftwareParams);
-}
-
-status_t ALSAStreamOps::set(int format,
- int channels,
- uint32_t rate)
-{
- if (channels > 0)
- mDefaults->channels = channels;
-
- if (rate > 0)
- mDefaults->sampleRate = rate;
-
- switch(format) {
- // format == 0
- case AudioSystem::DEFAULT:
- break;
-
- case AudioSystem::PCM_16_BIT:
- mDefaults->format = SND_PCM_FORMAT_S16_LE;
- break;
-
- case AudioSystem::PCM_8_BIT:
- mDefaults->format = SND_PCM_FORMAT_S8;
- break;
-
- default:
- LOGE("Unknown PCM format %i. Forcing default", format);
- break;
- }
-
- return NO_ERROR;
-}
-
-uint32_t ALSAStreamOps::sampleRate() const
-{
- unsigned int rate;
- int err;
-
- if (!mHandle)
- return mDefaults->sampleRate;
-
- return snd_pcm_hw_params_get_rate(mHardwareParams, &rate, 0) < 0
- ? 0 : static_cast<uint32_t>(rate);
-}
-
-status_t ALSAStreamOps::sampleRate(uint32_t rate)
-{
- const char *stream;
- unsigned int requestedRate;
- int err;
-
- if (!mHandle)
- return NO_INIT;
-
- stream = streamName();
- requestedRate = rate;
- err = snd_pcm_hw_params_set_rate_near(mHandle,
- mHardwareParams,
- &requestedRate,
- 0);
-
- if (err < 0) {
- LOGE("Unable to set %s sample rate to %u: %s",
- stream, rate, snd_strerror(err));
- return BAD_VALUE;
- }
- if (requestedRate != rate) {
- // Some devices have a fixed sample rate, and can not be changed.
- // This may cause resampling problems; i.e. PCM playback will be too
- // slow or fast.
- LOGW("Requested rate (%u HZ) does not match actual rate (%u HZ)",
- rate, requestedRate);
- }
- else {
- LOGV("Set %s sample rate to %u HZ", stream, requestedRate);
- }
- return NO_ERROR;
-}
-
-//
-// Return the number of bytes (not frames)
-//
-size_t ALSAStreamOps::bufferSize() const
+status_t AudioHardwareALSA::setMode(int mode)
{
- if (!mHandle)
- return NO_INIT;
-
- snd_pcm_uframes_t bufferSize = 0;
- snd_pcm_uframes_t periodSize = 0;
- int err;
-
- err = snd_pcm_get_params(mHandle, &bufferSize, &periodSize);
-
- if (err < 0)
- return -1;
-
- size_t bytes = static_cast<size_t>(snd_pcm_frames_to_bytes(mHandle, bufferSize));
-
- // Not sure when this happened, but unfortunately it now
- // appears that the bufferSize must be reported as a
- // power of 2. This might be the fault of 3rd party
- // users.
- for (size_t i = 1; (bytes & ~i) != 0; i<<=1)
- bytes &= ~i;
-
- return bytes;
-}
-
-int ALSAStreamOps::format() const
-{
- snd_pcm_format_t ALSAFormat;
- int pcmFormatBitWidth;
- int audioSystemFormat;
-
- if (!mHandle)
- return -1;
-
- if (snd_pcm_hw_params_get_format(mHardwareParams, &ALSAFormat) < 0) {
- return -1;
- }
-
- pcmFormatBitWidth = snd_pcm_format_physical_width(ALSAFormat);
- audioSystemFormat = AudioSystem::DEFAULT;
- switch(pcmFormatBitWidth) {
- case 8:
- audioSystemFormat = AudioSystem::PCM_8_BIT;
- break;
-
- case 16:
- audioSystemFormat = AudioSystem::PCM_16_BIT;
- break;
-
- default:
- LOG_FATAL("Unknown AudioSystem bit width %i!", pcmFormatBitWidth);
- }
-
- return audioSystemFormat;
-}
-
-int ALSAStreamOps::channelCount() const
-{
- if (!mHandle)
- return mDefaults->channels;
-
- unsigned int val;
- int err;
-
- err = snd_pcm_hw_params_get_channels(mHardwareParams, &val);
- if (err < 0) {
- LOGE("Unable to get device channel count: %s",
- snd_strerror(err));
- return mDefaults->channels;
- }
-
- return val;
-}
-
-status_t ALSAStreamOps::channelCount(int channels) {
- int err;
-
- if (!mHandle)
- return NO_INIT;
-
- err = snd_pcm_hw_params_set_channels(mHandle, mHardwareParams, channels);
- if (err < 0) {
- LOGE("Unable to set channel count to %i: %s",
- channels, snd_strerror(err));
- return BAD_VALUE;
- }
-
- LOGV("Using %i %s for %s.",
- channels, channels == 1 ? "channel" : "channels", streamName());
+ status_t status = NO_ERROR;
- return NO_ERROR;
-}
-
-status_t ALSAStreamOps::open(int mode, uint32_t device)
-{
- const char *stream = streamName();
- const char *devName = deviceName(mode, device);
-
- int err;
-
- for(;;) {
- // The PCM stream is opened in blocking mode, per ALSA defaults. The
- // AudioFlinger seems to assume blocking mode too, so asynchronous mode
- // should not be used.
- err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
- if (err == 0) break;
-
- // See if there is a less specific name we can try.
- // Note: We are changing the contents of a const char * here.
- char *tail = strrchr(devName, '_');
- if (!tail) break;
- *tail = 0;
- }
-
- if (err < 0) {
- // None of the Android defined audio devices exist. Open a generic one.
- devName = "default";
- err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
- if (err < 0) {
- // Last resort is the NULL device (i.e. the bit bucket).
- devName = _nullALSADeviceName;
- err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
- }
- }
-
- mMode = mode;
- mDevice = device;
-
- LOGI("Initialized ALSA %s device %s", stream, devName);
- return err;
-}
-
-void ALSAStreamOps::close()
-{
- snd_pcm_t *handle = mHandle;
- mHandle = NULL;
-
- if (handle)
- snd_pcm_close(handle);
-}
-
-status_t ALSAStreamOps::setSoftwareParams()
-{
- if (!mHandle)
- return NO_INIT;
-
- int err;
-
- // Get the current software parameters
- err = snd_pcm_sw_params_current(mHandle, mSoftwareParams);
- if (err < 0) {
- LOGE("Unable to get software parameters: %s", snd_strerror(err));
- return NO_INIT;
- }
-
- snd_pcm_uframes_t bufferSize = 0;
- snd_pcm_uframes_t periodSize = 0;
- snd_pcm_uframes_t startThreshold, stopThreshold;
+ if (mode != mMode) {
+ status = AudioHardwareBase::setMode(mode);
- // Configure ALSA to start the transfer when the buffer is almost full.
- snd_pcm_get_params(mHandle, &bufferSize, &periodSize);
-
- if (mDefaults->direction == SND_PCM_STREAM_PLAYBACK) {
- // For playback, configure ALSA to start the transfer when the
- // buffer is full.
- startThreshold = bufferSize - periodSize;
- stopThreshold = bufferSize;
- }
- else {
- // For recording, configure ALSA to start the transfer on the
- // first frame.
- startThreshold = 1;
- stopThreshold = bufferSize;
-}
-
- err = snd_pcm_sw_params_set_start_threshold(mHandle,
- mSoftwareParams,
- startThreshold);
- if (err < 0) {
- LOGE("Unable to set start threshold to %lu frames: %s",
- startThreshold, snd_strerror(err));
- return NO_INIT;
- }
-
- err = snd_pcm_sw_params_set_stop_threshold(mHandle,
- mSoftwareParams,
- stopThreshold);
- if (err < 0) {
- LOGE("Unable to set stop threshold to %lu frames: %s",
- stopThreshold, snd_strerror(err));
- return NO_INIT;
- }
-
- // Allow the transfer to start when at least periodSize samples can be
- // processed.
- err = snd_pcm_sw_params_set_avail_min(mHandle,
- mSoftwareParams,
- periodSize);
- if (err < 0) {
- LOGE("Unable to configure available minimum to %lu: %s",
- periodSize, snd_strerror(err));
- return NO_INIT;
- }
-
- // Commit the software parameters back to the device.
- err = snd_pcm_sw_params(mHandle, mSoftwareParams);
- if (err < 0) {
- LOGE("Unable to configure software parameters: %s",
- snd_strerror(err));
- return NO_INIT;
- }
-
- return NO_ERROR;
-}
-
-status_t ALSAStreamOps::setPCMFormat(snd_pcm_format_t format)
-{
- const char *formatDesc;
- const char *formatName;
- bool validFormat;
- int err;
-
- // snd_pcm_format_description() and snd_pcm_format_name() do not perform
- // proper bounds checking.
- validFormat = (static_cast<int>(format) > SND_PCM_FORMAT_UNKNOWN) &&
- (static_cast<int>(format) <= SND_PCM_FORMAT_LAST);
- formatDesc = validFormat ?
- snd_pcm_format_description(format) : "Invalid Format";
- formatName = validFormat ?
- snd_pcm_format_name(format) : "UNKNOWN";
-
- err = snd_pcm_hw_params_set_format(mHandle, mHardwareParams, format);
- if (err < 0) {
- LOGE("Unable to configure PCM format %s (%s): %s",
- formatName, formatDesc, snd_strerror(err));
- return NO_INIT;
- }
-
- LOGV("Set %s PCM format to %s (%s)", streamName(), formatName, formatDesc);
- return NO_ERROR;
-}
-
-status_t ALSAStreamOps::setHardwareResample(bool resample)
-{
- int err;
-
- err = snd_pcm_hw_params_set_rate_resample(mHandle,
- mHardwareParams,
- static_cast<int>(resample));
- if (err < 0) {
- LOGE("Unable to %s hardware resampling: %s",
- resample ? "enable" : "disable",
- snd_strerror(err));
- return NO_INIT;
- }
- return NO_ERROR;
-}
-
-const char *ALSAStreamOps::streamName()
-{
- // Don't use snd_pcm_stream(mHandle), as the PCM stream may not be
- // opened yet. In such case, snd_pcm_stream() will abort().
- return snd_pcm_stream_name(mDefaults->direction);
-}
-
-//
-// Set playback or capture PCM device. It's possible to support audio output
-// or input from multiple devices by using the ALSA plugins, but this is
-// not supported for simplicity.
-//
-// The AudioHardwareALSA API does not allow one to set the input routing.
-//
-// If the "routes" value does not map to a valid device, the default playback
-// device is used.
-//
-status_t ALSAStreamOps::setDevice(int mode, uint32_t device)
-{
- // Close off previously opened device.
- // It would be nice to determine if the underlying device actually
- // changes, but we might be manipulating mixer settings (see asound.conf).
- //
- close();
-
- const char *stream = streamName();
-
- status_t status = open (mode, device);
- int err;
-
- if (status != NO_ERROR)
- return status;
-
- err = snd_pcm_hw_params_any(mHandle, mHardwareParams);
- if (err < 0) {
- LOGE("Unable to configure hardware: %s", snd_strerror(err));
- return NO_INIT;
- }
-
- // Set the interleaved read and write format.
- err = snd_pcm_hw_params_set_access(mHandle, mHardwareParams,
- SND_PCM_ACCESS_RW_INTERLEAVED);
- if (err < 0) {
- LOGE("Unable to configure PCM read/write format: %s",
- snd_strerror(err));
- return NO_INIT;
- }
-
- status = setPCMFormat(mDefaults->format);
-
- //
- // Some devices do not have the default two channels. Force an error to
- // prevent AudioMixer from crashing and taking the whole system down.
- //
- // Note that some devices will return an -EINVAL if the channel count
- // is queried before it has been set. i.e. calling channelCount()
- // before channelCount(channels) may return -EINVAL.
- //
- status = channelCount(mDefaults->channels);
- if (status != NO_ERROR)
- return status;
-
- // Don't check for failure; some devices do not support the default
- // sample rate.
- sampleRate(mDefaults->sampleRate);
-
-#ifdef DISABLE_HARWARE_RESAMPLING
- // Disable hardware resampling.
- status = setHardwareResample(false);
- if (status != NO_ERROR)
- return status;
-#endif
-
- snd_pcm_uframes_t bufferSize = mDefaults->bufferSize;
-
- // Make sure we have at least the size we originally wanted
- err = snd_pcm_hw_params_set_buffer_size(mHandle, mHardwareParams, bufferSize);
- if (err < 0) {
- LOGE("Unable to set buffer size to %d: %s",
- (int)bufferSize, snd_strerror(err));
- return NO_INIT;
- }
-
- unsigned int latency = mDefaults->latency;
-
- // Setup buffers for latency
- err = snd_pcm_hw_params_set_buffer_time_near (mHandle, mHardwareParams,
- &latency, NULL);
- if (err < 0) {
- /* That didn't work, set the period instead */
- unsigned int periodTime = latency / 4;
- err = snd_pcm_hw_params_set_period_time_near (mHandle, mHardwareParams,
- &periodTime, NULL);
- if (err < 0) {
- LOGE("Unable to set the period time for latency: %s", snd_strerror(err));
- return NO_INIT;
- }
- snd_pcm_uframes_t periodSize;
- err = snd_pcm_hw_params_get_period_size (mHardwareParams, &periodSize, NULL);
- if (err < 0) {
- LOGE("Unable to get the period size for latency: %s", snd_strerror(err));
- return NO_INIT;
- }
- bufferSize = periodSize * 4;
- if (bufferSize < mDefaults->bufferSize)
- bufferSize = mDefaults->bufferSize;
- err = snd_pcm_hw_params_set_buffer_size_near (mHandle, mHardwareParams, &bufferSize);
- if (err < 0) {
- LOGE("Unable to set the buffer size for latency: %s", snd_strerror(err));
- return NO_INIT;
- }
- } else {
- // OK, we got buffer time near what we expect. See what that did for bufferSize.
- err = snd_pcm_hw_params_get_buffer_size (mHardwareParams, &bufferSize);
- if (err < 0) {
- LOGE("Unable to get the buffer size for latency: %s", snd_strerror(err));
- return NO_INIT;
- }
- // Does set_buffer_time_near change the passed value? It should.
- err = snd_pcm_hw_params_get_buffer_time (mHardwareParams, &latency, NULL);
- if (err < 0) {
- LOGE("Unable to get the buffer time for latency: %s", snd_strerror(err));
- return NO_INIT;
- }
- unsigned int periodTime = latency / 4;
- err = snd_pcm_hw_params_set_period_time_near (mHandle, mHardwareParams,
- &periodTime, NULL);
- if (err < 0) {
- LOGE("Unable to set the period time for latency: %s", snd_strerror(err));
- return NO_INIT;
+ if (status == NO_ERROR) {
+ // take care of mode change.
+ for(ALSAHandleList::iterator it = mDeviceList.begin();
+ it != mDeviceList.end(); ++it) {
+ status = mALSADevice->route(&(*it), it->curDev, mode);
+ if (status != NO_ERROR)
+ break;
+ }
}
}
- LOGV("Buffer size: %d", (int)bufferSize);
- LOGV("Latency: %d", (int)latency);
-
- mDefaults->bufferSize = bufferSize;
- mDefaults->latency = latency;
-
- // Commit the hardware parameters back to the device.
- err = snd_pcm_hw_params(mHandle, mHardwareParams);
- if (err < 0) {
- LOGE("Unable to set hardware parameters: %s", snd_strerror(err));
- return NO_INIT;
- }
-
- status = setSoftwareParams();
-
return status;
}
-const char *ALSAStreamOps::deviceName(int mode, uint32_t device)
-{
- static char devString[ALSA_NAME_MAX];
- int hasDevExt = 0;
-
- strcpy (devString, mDefaults->devicePrefix);
-
- for (int dev=0; device; dev++)
- if (device & (1 << dev)) {
- /* Don't go past the end of our list */
- if (dev >= deviceSuffixLen)
- break;
- ALSA_STRCAT (devString, deviceSuffix[dev]);
- device &= ~(1 << dev);
- hasDevExt = 1;
- }
-
- if (hasDevExt)
- switch (mode) {
- case AudioSystem::MODE_NORMAL:
- ALSA_STRCAT (devString, "_normal");
- break;
- case AudioSystem::MODE_RINGTONE:
- ALSA_STRCAT (devString, "_ringtone");
- break;
- case AudioSystem::MODE_IN_CALL:
- ALSA_STRCAT (devString, "_incall");
- break;
- };
-
- return devString;
-}
-
-// ----------------------------------------------------------------------------
-
-AudioStreamOutALSA::AudioStreamOutALSA(AudioHardwareALSA *parent) :
- ALSAStreamOps(parent)
-{
- static StreamDefaults _defaults = {
- devicePrefix : "AndroidPlayback",
- direction : SND_PCM_STREAM_PLAYBACK,
- format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
- channels : 2,
- sampleRate : DEFAULT_SAMPLE_RATE,
- latency : 200000, // Desired Delay in usec
- bufferSize : DEFAULT_SAMPLE_RATE / 5, // Desired Number of samples
- };
-
- setStreamDefaults(&_defaults);
-
- snd_pcm_uframes_t bufferSize = mDefaults->bufferSize;
-
- // See comment in bufferSize() method.
- for (size_t i = 1; (bufferSize & ~i) != 0; i<<=1)
- bufferSize &= ~i;
-
- mDefaults->bufferSize = bufferSize;
-}
-
-AudioStreamOutALSA::~AudioStreamOutALSA()
-{
- standby();
- mParent->mOutput = NULL;
-}
-
-int AudioStreamOutALSA::channelCount() const
-{
- int c = ALSAStreamOps::channelCount();
-
- // AudioMixer will seg fault if it doesn't have two channels.
- LOGW_IF(c != 2,
- "AudioMixer expects two channels, but only %i found!", c);
- return c;
-}
-
-status_t AudioStreamOutALSA::setVolume(float volume)
-{
- if (!mParent->mMixer || !mDevice)
- return NO_INIT;
-
- return mParent->mMixer->setVolume (mDevice, volume);
-}
-
-ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
-{
- snd_pcm_sframes_t n;
- size_t sent = 0;
- status_t err;
-
- AutoMutex lock(mLock);
-
- if (!mPowerLock) {
- acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
- mPowerLock = true;
- }
-
- if (!mHandle)
- ALSAStreamOps::setDevice(mMode, mDevice);
-
- do {
- n = snd_pcm_writei(mHandle,
- (char *)buffer + sent,
- snd_pcm_bytes_to_frames(mHandle, bytes));
- if (n == -EBADFD) {
- // Somehow the stream is in a bad state. The driver probably
- // has a bug and snd_pcm_recover() doesn't seem to handle this.
- ALSAStreamOps::setDevice(mMode, mDevice);
- }
- else if (n < 0) {
- if (mHandle) {
- // snd_pcm_recover() will return 0 if successful in recovering from
- // an error, or -errno if the error was unrecoverable.
- n = snd_pcm_recover(mHandle, n, 1);
- if (n)
- return static_cast<ssize_t>(n);
- }
- }
- else
- sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle, n));
-
- } while (mHandle && sent < bytes);
-
- return sent;
-}
-
-status_t AudioStreamOutALSA::dump(int fd, const Vector<String16>& args)
-{
- return NO_ERROR;
-}
-
-status_t AudioStreamOutALSA::setDevice(int mode, uint32_t newDevice)
+AudioStreamOut *
+AudioHardwareALSA::openOutputStream(uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status)
{
AutoMutex lock(mLock);
- return ALSAStreamOps::setDevice(mode, newDevice);
-}
-
-status_t AudioStreamOutALSA::standby()
-{
- AutoMutex lock(mLock);
+ LOGD("openOutputStream called for devices: 0x%08x", devices);
- if (mHandle) {
- snd_pcm_drain (mHandle);
- close();
- }
+ status_t err = BAD_VALUE;
+ AudioStreamOutALSA *out = 0;
- if (mPowerLock) {
- release_wake_lock ("AudioOutLock");
- mPowerLock = false;
+ if (devices & (devices - 1)) {
+ if (status) *status = err;
+ LOGD("openOutputStream called with bad devices");
+ return out;
}
- return NO_ERROR;
-}
-
-#define USEC_TO_MSEC(x) ((x + 999) / 1000)
-
-uint32_t AudioStreamOutALSA::latency() const
-{
- // Android wants latency in milliseconds.
- return USEC_TO_MSEC (mDefaults->latency);
-}
-
-// ----------------------------------------------------------------------------
-
-AudioStreamInALSA::AudioStreamInALSA(AudioHardwareALSA *parent,
- AudioSystem::audio_in_acoustics acoustics) :
- ALSAStreamOps(parent),
- mAcoustics(acoustics)
-{
- static StreamDefaults _defaults = {
- devicePrefix : "AndroidRecord",
- direction : SND_PCM_STREAM_CAPTURE,
- format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
- channels : 1,
- sampleRate : AudioRecord::DEFAULT_SAMPLE_RATE,
- latency : 250000, // Desired Delay in usec
- bufferSize : 2048, // Desired Number of samples
- };
-
- setStreamDefaults(&_defaults);
-}
-
-AudioStreamInALSA::~AudioStreamInALSA()
-{
- standby();
- mParent->mInput = NULL;
-}
-
-status_t AudioStreamInALSA::setGain(float gain)
-{
- if (mParent->mMixer)
- return mParent->mMixer->setMasterGain (gain);
- else
- return NO_INIT;
-}
-
-ssize_t AudioStreamInALSA::read(void *buffer, ssize_t bytes)
-{
- snd_pcm_sframes_t n, frames = snd_pcm_bytes_to_frames(mHandle, bytes);
- status_t err;
-
- AutoMutex lock(mLock);
-
- if (!mPowerLock) {
- acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock");
- mPowerLock = true;
- }
-
- if (!mHandle)
- ALSAStreamOps::setDevice(mMode, mDevice);
-
- n = snd_pcm_readi(mHandle, buffer, frames);
- if (n < frames) {
- if (mHandle) {
- if (n < 0)
- n = snd_pcm_recover(mHandle, n, 0);
- else
- n = snd_pcm_prepare(mHandle);
+ // Find the appropriate alsa device
+ for(ALSAHandleList::iterator it = mDeviceList.begin();
+ it != mDeviceList.end(); ++it)
+ if (it->devices & devices) {
+ err = mALSADevice->open(&(*it), devices, mode());
+ if (err) break;
+ out = new AudioStreamOutALSA(this, &(*it));
+ err = out->set(format, channels, sampleRate);
+ break;
}
- return static_cast<ssize_t>(n);
- }
- if (mParent->mAcousticDevice &&
- mParent->mAcousticDevice->filter) {
- n = mParent->mAcousticDevice->filter(mHandle, buffer, frames);
- if (n < 0)
- return static_cast<ssize_t>(n);
- }
-
- return static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle, n));
+ if (status) *status = err;
+ return out;
}
-status_t AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
-{
- return NO_ERROR;
-}
-
-status_t AudioStreamInALSA::setDevice(int mode, uint32_t newDevice)
+void
+AudioHardwareALSA::closeOutputStream(AudioStreamOut* out)
{
AutoMutex lock(mLock);
-
- status_t status = ALSAStreamOps::setDevice(mode, newDevice);
-
- if (status == NO_ERROR && mParent->mAcousticDevice)
- status = mParent->mAcousticDevice->set_acoustics(mHandle, mAcoustics);
-
- return status;
+ delete out;
}
-status_t AudioStreamInALSA::standby()
+AudioStreamIn *
+AudioHardwareALSA::openInputStream(uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status,
+ AudioSystem::audio_in_acoustics acoustics)
{
AutoMutex lock(mLock);
- close();
-
- if (mPowerLock) {
- release_wake_lock ("AudioInLock");
- mPowerLock = false;
- }
-
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-struct mixer_info_t
-{
- mixer_info_t() :
- elem(0),
- min(SND_MIXER_VOL_RANGE_MIN),
- max(SND_MIXER_VOL_RANGE_MAX),
- mute(false)
- {
- }
-
- snd_mixer_elem_t *elem;
- long min;
- long max;
- long volume;
- bool mute;
- char name[ALSA_NAME_MAX];
-};
-
-static int initMixer (snd_mixer_t **mixer, const char *name)
-{
- int err;
-
- if ((err = snd_mixer_open(mixer, 0)) < 0) {
- LOGE("Unable to open mixer: %s", snd_strerror(err));
- return err;
- }
-
- if ((err = snd_mixer_attach(*mixer, name)) < 0) {
- LOGE("Unable to attach mixer to device %s: %s",
- name, snd_strerror(err));
-
- if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
- LOGE("Unable to attach mixer to device default: %s",
- snd_strerror(err));
-
- snd_mixer_close (*mixer);
- *mixer = NULL;
- return err;
- }
- }
-
- if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
- LOGE("Unable to register mixer elements: %s", snd_strerror(err));
- snd_mixer_close (*mixer);
- *mixer = NULL;
- return err;
- }
-
- // Get the mixer controls from the kernel
- if ((err = snd_mixer_load(*mixer)) < 0) {
- LOGE("Unable to load mixer elements: %s", snd_strerror(err));
- snd_mixer_close (*mixer);
- *mixer = NULL;
- return err;
- }
-
- return 0;
-}
-
-typedef int (*hasVolume_t)(snd_mixer_elem_t*);
-
-static const hasVolume_t hasVolume[] = {
- snd_mixer_selem_has_playback_volume,
- snd_mixer_selem_has_capture_volume
-};
-
-typedef int (*getVolumeRange_t)(snd_mixer_elem_t*, long int*, long int*);
-
-static const getVolumeRange_t getVolumeRange[] = {
- snd_mixer_selem_get_playback_volume_range,
- snd_mixer_selem_get_capture_volume_range
-};
-
-typedef int (*setVolume_t)(snd_mixer_elem_t*, long int);
-
-static const setVolume_t setVol[] = {
- snd_mixer_selem_set_playback_volume_all,
- snd_mixer_selem_set_capture_volume_all
-};
-
-ALSAMixer::ALSAMixer()
-{
- int err;
-
- initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidPlayback");
- initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidRecord");
-
- snd_mixer_selem_id_t *sid;
- snd_mixer_selem_id_alloca(&sid);
-
- for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
+ status_t err = BAD_VALUE;
+ AudioStreamInALSA *in = 0;
- mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;
-
- property_get (mixerMasterProp[i].propName,
- info->name,
- mixerMasterProp[i].propDefault);
-
- for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
- elem;
- elem = snd_mixer_elem_next(elem)) {
-
- if (!snd_mixer_selem_is_active(elem))
- continue;
-
- snd_mixer_selem_get_id(elem, sid);
-
- // Find PCM playback volume control element.
- const char *elementName = snd_mixer_selem_id_get_name(sid);
-
- if (info->elem == NULL &&
- strcmp(elementName, info->name) == 0 &&
- hasVolume[i] (elem)) {
-
- info->elem = elem;
- getVolumeRange[i] (elem, &info->min, &info->max);
- info->volume = info->max;
- setVol[i] (elem, info->volume);
- if (i == SND_PCM_STREAM_PLAYBACK &&
- snd_mixer_selem_has_playback_switch (elem))
- snd_mixer_selem_set_playback_switch_all (elem, 1);
- break;
- }
- }
-
- LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");
-
- for (int j = 0; mixerProp[j][i].routes; j++) {
-
- mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;
-
- property_get (mixerProp[j][i].propName,
- info->name,
- mixerProp[j][i].propDefault);
-
- for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
- elem;
- elem = snd_mixer_elem_next(elem)) {
-
- if (!snd_mixer_selem_is_active(elem))
- continue;
-
- snd_mixer_selem_get_id(elem, sid);
-
- // Find PCM playback volume control element.
- const char *elementName = snd_mixer_selem_id_get_name(sid);
-
- if (info->elem == NULL &&
- strcmp(elementName, info->name) == 0 &&
- hasVolume[i] (elem)) {
-
- info->elem = elem;
- getVolumeRange[i] (elem, &info->min, &info->max);
- info->volume = info->max;
- setVol[i] (elem, info->volume);
- if (i == SND_PCM_STREAM_PLAYBACK &&
- snd_mixer_selem_has_playback_switch (elem))
- snd_mixer_selem_set_playback_switch_all (elem, 1);
- break;
- }
- }
- LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
- }
+ if (devices & (devices - 1)) {
+ if (status) *status = err;
+ return in;
}
- LOGV("mixer initialized.");
-}
-
-ALSAMixer::~ALSAMixer()
-{
- for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
- if (mMixer[i]) snd_mixer_close (mMixer[i]);
- if (mixerMasterProp[i].mInfo) {
- delete mixerMasterProp[i].mInfo;
- mixerMasterProp[i].mInfo = NULL;
- }
- for (int j = 0; mixerProp[j][i].routes; j++) {
- if (mixerProp[j][i].mInfo) {
- delete mixerProp[j][i].mInfo;
- mixerProp[j][i].mInfo = NULL;
- }
- }
- }
- LOGV("mixer destroyed.");
-}
-
-status_t ALSAMixer::setMasterVolume(float volume)
-{
- mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_PLAYBACK].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- long minVol = info->min;
- long maxVol = info->max;
-
- // Make sure volume is between bounds.
- long vol = minVol + volume * (maxVol - minVol);
- if (vol > maxVol) vol = maxVol;
- if (vol < minVol) vol = minVol;
- info->volume = vol;
- snd_mixer_selem_set_playback_volume_all (info->elem, vol);
-
- return NO_ERROR;
-}
-
-status_t ALSAMixer::setMasterGain(float gain)
-{
- mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_CAPTURE].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- long minVol = info->min;
- long maxVol = info->max;
-
- // Make sure volume is between bounds.
- long vol = minVol + gain * (maxVol - minVol);
- if (vol > maxVol) vol = maxVol;
- if (vol < minVol) vol = minVol;
-
- info->volume = vol;
- snd_mixer_selem_set_capture_volume_all (info->elem, vol);
-
- return NO_ERROR;
-}
-
-status_t ALSAMixer::setVolume(uint32_t device, float volume)
-{
- for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes; j++)
- if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes & device) {
-
- mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- long minVol = info->min;
- long maxVol = info->max;
-
- // Make sure volume is between bounds.
- long vol = minVol + volume * (maxVol - minVol);
- if (vol > maxVol) vol = maxVol;
- if (vol < minVol) vol = minVol;
-
- info->volume = vol;
- snd_mixer_selem_set_playback_volume_all (info->elem, vol);
- }
-
- return NO_ERROR;
-}
-
-status_t ALSAMixer::setGain(uint32_t device, float gain)
-{
- for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].routes; j++)
- if (mixerProp[j][SND_PCM_STREAM_CAPTURE].routes & device) {
-
- mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- long minVol = info->min;
- long maxVol = info->max;
-
- // Make sure volume is between bounds.
- long vol = minVol + gain * (maxVol - minVol);
- if (vol > maxVol) vol = maxVol;
- if (vol < minVol) vol = minVol;
-
- info->volume = vol;
- snd_mixer_selem_set_capture_volume_all (info->elem, vol);
+ // Find the appropriate alsa device
+ for(ALSAHandleList::iterator it = mDeviceList.begin();
+ it != mDeviceList.end(); ++it)
+ if (it->devices & devices) {
+ err = mALSADevice->open(&(*it), devices, mode());
+ if (err) break;
+ in = new AudioStreamInALSA(this, &(*it), acoustics);
+ err = in->set(format, channels, sampleRate);
+ break;
}
- return NO_ERROR;
+ if (status) *status = err;
+ return in;
}
-status_t ALSAMixer::setCaptureMuteState(uint32_t device, bool state)
+void
+AudioHardwareALSA::closeInputStream(AudioStreamIn* in)
{
- for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].routes; j++)
- if (mixerProp[j][SND_PCM_STREAM_CAPTURE].routes & device) {
-
- mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- if (snd_mixer_selem_has_capture_switch (info->elem)) {
-
- int err = snd_mixer_selem_set_capture_switch_all (info->elem, static_cast<int>(!state));
- if (err < 0) {
- LOGE("Unable to %s capture mixer switch %s",
- state ? "enable" : "disable", info->name);
- return INVALID_OPERATION;
- }
- }
-
- info->mute = state;
- }
-
- return NO_ERROR;
+ AutoMutex lock(mLock);
+ delete in;
}
-status_t ALSAMixer::getCaptureMuteState(uint32_t device, bool *state)
+status_t AudioHardwareALSA::setMicMute(bool state)
{
- if (!state) return BAD_VALUE;
-
- for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].routes; j++)
- if (mixerProp[j][SND_PCM_STREAM_CAPTURE].routes & device) {
-
- mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- *state = info->mute;
- return NO_ERROR;
- }
+ if (mMixer)
+ return mMixer->setCaptureMuteState(AudioSystem::DEVICE_OUT_EARPIECE, state);
- return BAD_VALUE;
+ return NO_INIT;
}
-status_t ALSAMixer::setPlaybackMuteState(uint32_t device, bool state)
+status_t AudioHardwareALSA::getMicMute(bool *state)
{
- for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes; j++)
- if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes & device) {
-
- mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- if (snd_mixer_selem_has_playback_switch (info->elem)) {
-
- int err = snd_mixer_selem_set_playback_switch_all (info->elem, static_cast<int>(!state));
- if (err < 0) {
- LOGE("Unable to %s playback mixer switch %s",
- state ? "enable" : "disable", info->name);
- return INVALID_OPERATION;
- }
- }
-
- info->mute = state;
- }
+ if (mMixer)
+ return mMixer->getCaptureMuteState(AudioSystem::DEVICE_OUT_EARPIECE, state);
return NO_ERROR;
}
-status_t ALSAMixer::getPlaybackMuteState(uint32_t device, bool *state)
-{
- if (!state) return BAD_VALUE;
-
- for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes; j++)
- if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes & device) {
-
- mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
- if (!info || !info->elem) return INVALID_OPERATION;
-
- *state = info->mute;
- return NO_ERROR;
- }
-
- return BAD_VALUE;
-}
-
-// ----------------------------------------------------------------------------
-
-ALSAControl::ALSAControl(const char *device)
-{
- snd_ctl_open(&mHandle, device, 0);
-}
-
-ALSAControl::~ALSAControl()
-{
- if (mHandle) snd_ctl_close(mHandle);
-}
-
-status_t ALSAControl::get(const char *name, unsigned int &value, int index)
+status_t AudioHardwareALSA::dump(int fd, const Vector<String16>& args)
{
- if (!mHandle) return NO_INIT;
-
- snd_ctl_elem_id_t *id;
- snd_ctl_elem_info_t *info;
- snd_ctl_elem_value_t *control;
-
- snd_ctl_elem_id_alloca(&id);
- snd_ctl_elem_info_alloca(&info);
- snd_ctl_elem_value_alloca(&control);
-
- snd_ctl_elem_id_set_name(id, name);
- snd_ctl_elem_info_set_id(info, id);
-
- int ret = snd_ctl_elem_info(mHandle, info);
- if (ret < 0) return BAD_VALUE;
-
- snd_ctl_elem_info_get_id(info, id);
- snd_ctl_elem_type_t type = snd_ctl_elem_info_get_type(info);
- unsigned int count = snd_ctl_elem_info_get_count(info);
- if ((unsigned int)index >= count) return BAD_VALUE;
-
- snd_ctl_elem_value_set_id(control, id);
-
- ret = snd_ctl_elem_read(mHandle, control);
- if (ret < 0) return BAD_VALUE;
-
- switch (type) {
- case SND_CTL_ELEM_TYPE_BOOLEAN:
- value = snd_ctl_elem_value_get_boolean(control, index);
- break;
- case SND_CTL_ELEM_TYPE_INTEGER:
- value = snd_ctl_elem_value_get_integer(control, index);
- break;
- case SND_CTL_ELEM_TYPE_INTEGER64:
- value = snd_ctl_elem_value_get_integer64(control, index);
- break;
- case SND_CTL_ELEM_TYPE_ENUMERATED:
- value = snd_ctl_elem_value_get_enumerated(control, index);
- break;
- case SND_CTL_ELEM_TYPE_BYTES:
- value = snd_ctl_elem_value_get_byte(control, index);
- break;
- default:
- return BAD_VALUE;
- }
-
return NO_ERROR;
}
-status_t ALSAControl::set(const char *name, unsigned int value, int index)
-{
- if (!mHandle) return NO_INIT;
-
- snd_ctl_elem_id_t *id;
- snd_ctl_elem_info_t *info;
- snd_ctl_elem_value_t *control;
-
- snd_ctl_elem_id_alloca(&id);
- snd_ctl_elem_info_alloca(&info);
- snd_ctl_elem_value_alloca(&control);
-
- snd_ctl_elem_id_set_name(id, name);
- snd_ctl_elem_info_set_id(info, id);
-
- int ret = snd_ctl_elem_info(mHandle, info);
- if (ret < 0) return BAD_VALUE;
-
- snd_ctl_elem_info_get_id(info, id);
- snd_ctl_elem_type_t type = snd_ctl_elem_info_get_type(info);
- unsigned int count = snd_ctl_elem_info_get_count(info);
- if ((unsigned int)index >= count) return BAD_VALUE;
-
- if (index == -1)
- index = 0; // Range over all of them
- else
- count = index + 1; // Just do the one specified
-
- snd_ctl_elem_value_set_id(control, id);
-
- for (unsigned int i = index; i < count; i++)
- switch (type) {
- case SND_CTL_ELEM_TYPE_BOOLEAN:
- snd_ctl_elem_value_set_boolean(control, i, value);
- break;
- case SND_CTL_ELEM_TYPE_INTEGER:
- snd_ctl_elem_value_set_integer(control, i, value);
- break;
- case SND_CTL_ELEM_TYPE_INTEGER64:
- snd_ctl_elem_value_set_integer64(control, i, value);
- break;
- case SND_CTL_ELEM_TYPE_ENUMERATED:
- snd_ctl_elem_value_set_enumerated(control, i, value);
- break;
- case SND_CTL_ELEM_TYPE_BYTES:
- snd_ctl_elem_value_set_byte(control, i, value);
- break;
- default:
- break;
- }
-
- ret = snd_ctl_elem_write(mHandle, control);
- return (ret < 0) ? BAD_VALUE : NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
+} // namespace android
diff --git a/AudioHardwareALSA.h b/AudioHardwareALSA.h
index bae5bc7..d2a9bec 100644
--- a/AudioHardwareALSA.h
+++ b/AudioHardwareALSA.h
@@ -18,6 +18,7 @@
#ifndef ANDROID_AUDIO_HARDWARE_ALSA_H
#define ANDROID_AUDIO_HARDWARE_ALSA_H
+#include <utils/List.h>
#include <hardware_legacy/AudioHardwareBase.h>
#include <alsa/asoundlib.h>
@@ -26,289 +27,325 @@
namespace android
{
- class AudioHardwareALSA;
- /**
- * The id of acoustics module
- */
-#define ACOUSTICS_HARDWARE_MODULE_ID "acoustics"
-#define ACOUSTICS_HARDWARE_NAME "name"
-
- struct acoustic_device_t {
- hw_device_t common;
-
- /**
- * Set the provided acoustics for a particular ALSA pcm device.
- *
- * Returns: 0 on succes, error code on failure.
- */
- status_t (*set_acoustics)(snd_pcm_t *, AudioSystem::audio_in_acoustics);
-
- /**
- * Read callback with PCM data so that filtering may be applied.
- *
- * Returns: frames filtered on success, error code on failure.
- */
- ssize_t (*filter)(snd_pcm_t *, void *, ssize_t);
- };
-
- // ----------------------------------------------------------------------------
-
- class ALSAMixer
- {
- public:
- ALSAMixer();
- virtual ~ALSAMixer();
+class AudioHardwareALSA;
- bool isValid() { return !!mMixer[SND_PCM_STREAM_PLAYBACK]; }
- status_t setMasterVolume(float volume);
- status_t setMasterGain(float gain);
+/**
+ * The id of ALSA module
+ */
+#define ALSA_HARDWARE_MODULE_ID "alsa"
+#define ALSA_HARDWARE_NAME "alsa"
+
+struct alsa_device_t;
+
+struct alsa_handle_t {
+ alsa_device_t * module;
+ uint32_t devices;
+ uint32_t curDev;
+ int curMode;
+ snd_pcm_t * handle;
+ snd_pcm_format_t format;
+ uint32_t channels;
+ uint32_t sampleRate;
+ unsigned int latency; // Delay in usec
+ unsigned int bufferSize; // Size of sample buffer
+ void * modPrivate;
+};
- status_t setVolume(uint32_t device, float volume);
- status_t setGain(uint32_t device, float gain);
+typedef List<alsa_handle_t> ALSAHandleList;
- status_t setCaptureMuteState(uint32_t device, bool state);
- status_t getCaptureMuteState(uint32_t device, bool *state);
- status_t setPlaybackMuteState(uint32_t device, bool state);
- status_t getPlaybackMuteState(uint32_t device, bool *state);
+struct alsa_device_t {
+ hw_device_t common;
- private:
- snd_mixer_t *mMixer[SND_PCM_STREAM_LAST+1];
- };
+ status_t (*init)(alsa_device_t *, ALSAHandleList &);
+ status_t (*open)(alsa_handle_t *, uint32_t, int);
+ status_t (*close)(alsa_handle_t *);
+ status_t (*route)(alsa_handle_t *, uint32_t, int);
+};
- class ALSAControl
- {
- public:
- ALSAControl(const char *device = "default");
- virtual ~ALSAControl();
+/**
+ * The id of acoustics module
+ */
+#define ACOUSTICS_HARDWARE_MODULE_ID "acoustics"
+#define ACOUSTICS_HARDWARE_NAME "acoustics"
- status_t get(const char *name, unsigned int &value, int index = 0);
- status_t set(const char *name, unsigned int value, int index = -1);
+struct acoustic_device_t {
+ hw_device_t common;
- private:
- snd_ctl_t *mHandle;
- };
+ // Required methods...
+ status_t (*use_handle)(acoustic_device_t *, alsa_handle_t *);
+ status_t (*cleanup)(acoustic_device_t *);
- class ALSAStreamOps
- {
- protected:
- friend class AudioStreamOutALSA;
- friend class AudioStreamInALSA;
-
- struct StreamDefaults
- {
- const char * devicePrefix;
- snd_pcm_stream_t direction; // playback or capture
- snd_pcm_format_t format;
- int channels;
- uint32_t sampleRate;
- unsigned int latency; // Delay in usec
- unsigned int bufferSize; // Size of sample buffer
- };
-
- ALSAStreamOps(AudioHardwareALSA *parent);
- virtual ~ALSAStreamOps();
-
- status_t set(int format,
- int channels,
- uint32_t rate);
- virtual uint32_t sampleRate() const;
- status_t sampleRate(uint32_t rate);
- virtual size_t bufferSize() const;
- virtual int format() const;
- virtual int channelCount() const;
- status_t channelCount(int channels);
-
- status_t open(int mode, uint32_t device);
- void close();
- status_t setSoftwareParams();
- status_t setPCMFormat(snd_pcm_format_t format);
- status_t setHardwareResample(bool resample);
-
- status_t setDevice(int mode, uint32_t device);
-
- const char *streamName();
- const char *deviceName(int mode, uint32_t device);
-
- void setStreamDefaults(StreamDefaults *dev) {
- mDefaults = dev;
- }
-
- private:
- AudioHardwareALSA *mParent;
- snd_pcm_t *mHandle;
- snd_pcm_hw_params_t *mHardwareParams;
- snd_pcm_sw_params_t *mSoftwareParams;
- int mMode;
- uint32_t mDevice;
-
- StreamDefaults *mDefaults;
-
- Mutex mLock;
- bool mPowerLock;
+ status_t (*set_params)(acoustic_device_t *, AudioSystem::audio_in_acoustics, void *);
+
+ // Optional methods...
+ ssize_t (*read)(acoustic_device_t *, void *, size_t);
+ ssize_t (*write)(acoustic_device_t *, const void *, size_t);
+ status_t (*recover)(acoustic_device_t *, int);
+
+ void * modPrivate;
+};
+
+// ----------------------------------------------------------------------------
+
+class ALSAMixer
+{
+public:
+ ALSAMixer();
+ virtual ~ALSAMixer();
+
+ bool isValid() { return !!mMixer[SND_PCM_STREAM_PLAYBACK]; }
+ status_t setMasterVolume(float volume);
+ status_t setMasterGain(float gain);
+
+ status_t setVolume(uint32_t device, float left, float right);
+ status_t setGain(uint32_t device, float gain);
+
+ status_t setCaptureMuteState(uint32_t device, bool state);
+ status_t getCaptureMuteState(uint32_t device, bool *state);
+ status_t setPlaybackMuteState(uint32_t device, bool state);
+ status_t getPlaybackMuteState(uint32_t device, bool *state);
+
+private:
+ snd_mixer_t * mMixer[SND_PCM_STREAM_LAST+1];
};
- // ----------------------------------------------------------------------------
+class ALSAControl
+{
+public:
+ ALSAControl(const char *device = "hw:00");
+ virtual ~ALSAControl();
+
+ status_t get(const char *name, unsigned int &value, int index = 0);
+ status_t set(const char *name, unsigned int value, int index = -1);
+
+ status_t set(const char *name, const char *);
+
+private:
+ snd_ctl_t * mHandle;
+};
+
+class ALSAStreamOps
+{
+public:
+ ALSAStreamOps(AudioHardwareALSA *parent, alsa_handle_t *handle);
+ virtual ~ALSAStreamOps();
- class AudioStreamOutALSA : public AudioStreamOut, public ALSAStreamOps
+ status_t set(int *format, uint32_t *channels, uint32_t *rate);
+
+ status_t setParameters(const String8& keyValuePairs);
+ String8 getParameters(const String8& keys);
+
+ uint32_t sampleRate() const;
+ size_t bufferSize() const;
+ int format() const;
+ uint32_t channels() const;
+
+ status_t open(int mode);
+ void close();
+
+protected:
+ friend class AudioHardwareALSA;
+
+ acoustic_device_t *acoustics();
+ ALSAMixer *mixer();
+
+ AudioHardwareALSA * mParent;
+ alsa_handle_t * mHandle;
+
+ Mutex mLock;
+ bool mPowerLock;
+};
+
+// ----------------------------------------------------------------------------
+
+class AudioStreamOutALSA : public AudioStreamOut, public ALSAStreamOps
+{
+public:
+ AudioStreamOutALSA(AudioHardwareALSA *parent, alsa_handle_t *handle);
+ virtual ~AudioStreamOutALSA();
+
+ virtual uint32_t sampleRate() const
{
- public:
- AudioStreamOutALSA(AudioHardwareALSA *parent);
- virtual ~AudioStreamOutALSA();
+ return ALSAStreamOps::sampleRate();
+ }
- status_t set(int format = 0,
- int channelCount = 0,
- uint32_t sampleRate = 0) {
- return ALSAStreamOps::set(format, channelCount, sampleRate);
- }
+ virtual size_t bufferSize() const
+ {
+ return ALSAStreamOps::bufferSize();
+ }
- virtual uint32_t sampleRate() const
- {
- return ALSAStreamOps::sampleRate();
- }
+ virtual uint32_t channels() const;
- virtual size_t bufferSize() const
- {
- return ALSAStreamOps::bufferSize();
- }
+ virtual int format() const
+ {
+ return ALSAStreamOps::format();
+ }
- virtual int channelCount() const;
+ virtual uint32_t latency() const;
- virtual int format() const
- {
- return ALSAStreamOps::format();
- }
+ virtual ssize_t write(const void *buffer, size_t bytes);
+ virtual status_t dump(int fd, const Vector<String16>& args);
- virtual uint32_t latency() const;
+ status_t setVolume(float left, float right);
- virtual ssize_t write(const void *buffer, size_t bytes);
- virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t standby();
- status_t setVolume(float volume);
+ virtual status_t setParameters(const String8& keyValuePairs) {
+ return ALSAStreamOps::setParameters(keyValuePairs);
+ }
- virtual status_t standby();
+ virtual String8 getParameters(const String8& keys) {
+ return ALSAStreamOps::getParameters(keys);
+ }
- protected:
- friend class AudioHardwareALSA;
+ status_t open(int mode);
+ status_t close();
+};
- status_t setDevice(int mode, uint32_t newDevice);
- };
+class AudioStreamInALSA : public AudioStreamIn, public ALSAStreamOps
+{
+public:
+ AudioStreamInALSA(AudioHardwareALSA *parent,
+ alsa_handle_t *handle,
+ AudioSystem::audio_in_acoustics audio_acoustics);
+ virtual ~AudioStreamInALSA();
- class AudioStreamInALSA : public AudioStreamIn, public ALSAStreamOps
+ virtual uint32_t sampleRate() const
{
- public:
- AudioStreamInALSA(AudioHardwareALSA *parent,
- AudioSystem::audio_in_acoustics acoustics);
- virtual ~AudioStreamInALSA();
+ return ALSAStreamOps::sampleRate();
+ }
- status_t set(int format = 0,
- int channelCount = 0,
- uint32_t sampleRate = 0) {
- return ALSAStreamOps::set(format, channelCount, sampleRate);
- }
+ virtual size_t bufferSize() const
+ {
+ return ALSAStreamOps::bufferSize();
+ }
- virtual uint32_t sampleRate() {
- return ALSAStreamOps::sampleRate();
- }
+ virtual uint32_t channels() const
+ {
+ return ALSAStreamOps::channels();
+ }
+
+ virtual int format() const
+ {
+ return ALSAStreamOps::format();
+ }
- virtual size_t bufferSize() const
- {
- return ALSAStreamOps::bufferSize();
- }
+ virtual ssize_t read(void* buffer, ssize_t bytes);
+ virtual status_t dump(int fd, const Vector<String16>& args);
- virtual int channelCount() const
- {
- return ALSAStreamOps::channelCount();
- }
+ virtual status_t setGain(float gain);
- virtual int format() const
- {
- return ALSAStreamOps::format();
- }
+ virtual status_t standby();
- virtual ssize_t read(void* buffer, ssize_t bytes);
- virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs)
+ {
+ return ALSAStreamOps::setParameters(keyValuePairs);
+ }
- virtual status_t setGain(float gain);
+ virtual String8 getParameters(const String8& keys)
+ {
+ return ALSAStreamOps::getParameters(keys);
+ }
- virtual status_t standby();
+ status_t setAcousticParams(void* params);
- protected:
- friend class AudioHardwareALSA;
+ status_t open(int mode);
+ status_t close();
- status_t setDevice(int mode, uint32_t newDevice);
+private:
+ AudioSystem::audio_in_acoustics mAcoustics;
+};
- private:
- AudioSystem::audio_in_acoustics mAcoustics;
- };
+class AudioHardwareALSA : public AudioHardwareBase
+{
+public:
+ AudioHardwareALSA();
+ virtual ~AudioHardwareALSA();
- class AudioHardwareALSA : public AudioHardwareBase
+ /**
+ * check to see if the audio hardware interface has been initialized.
+ * return status based on values defined in include/utils/Errors.h
+ */
+ virtual status_t initCheck();
+
+ /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */
+ virtual status_t setVoiceVolume(float volume);
+
+ /**
+ * set the audio volume for all audio activities other than voice call.
+ * Range between 0.0 and 1.0. If any value other than NO_ERROR is returned,
+ * the software mixer will emulate this capability.
+ */
+ virtual status_t setMasterVolume(float volume);
+
+ /**
+ * setMode is called when the audio mode changes. NORMAL mode is for
+ * standard audio playback, RINGTONE when a ringtone is playing, and IN_CALL
+ * when a call is in progress.
+ */
+ virtual status_t setMode(int mode);
+
+ // mic mute
+ virtual status_t setMicMute(bool state);
+ virtual status_t getMicMute(bool* state);
+
+ // set/get global audio parameters
+ //virtual status_t setParameters(const String8& keyValuePairs);
+ //virtual String8 getParameters(const String8& keys);
+
+ // Returns audio input buffer size according to parameters passed or 0 if one of the
+ // parameters is not supported
+ //virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channels);
+
+ /** This method creates and opens the audio hardware output stream */
+ virtual AudioStreamOut* openOutputStream(
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
+ status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
+
+ /** This method creates and opens the audio hardware input stream */
+ virtual AudioStreamIn* openInputStream(
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status,
+ AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
+
+ /**This method dumps the state of the audio hardware */
+ //virtual status_t dumpState(int fd, const Vector<String16>& args);
+
+ static AudioHardwareInterface* create();
+
+ int mode()
{
- public:
- AudioHardwareALSA();
- virtual ~AudioHardwareALSA();
-
- /**
- * check to see if the audio hardware interface has been initialized.
- * return status based on values defined in include/utils/Errors.h
- */
- virtual status_t initCheck();
-
- /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */
- virtual status_t setVoiceVolume(float volume);
-
- /**
- * set the audio volume for all audio activities other than voice call.
- * Range between 0.0 and 1.0. If any value other than NO_ERROR is returned,
- * the software mixer will emulate this capability.
- */
- virtual status_t setMasterVolume(float volume);
-
- // mic mute
- virtual status_t setMicMute(bool state);
- virtual status_t getMicMute(bool* state);
-
- /** This method creates and opens the audio hardware output stream */
- virtual AudioStreamOut* openOutputStream(
- int format=0,
- int channelCount=0,
- uint32_t sampleRate=0,
- status_t *status=0);
-
- /** This method creates and opens the audio hardware input stream */
- virtual AudioStreamIn* openInputStream(
- int inputSource,
- int format,
- int channelCount,
- uint32_t sampleRate,
- status_t *status,
- AudioSystem::audio_in_acoustics acoustics);
-
- protected:
- /**
- * doRouting actually initiates the routing. A call to setRouting
- * or setMode may result in a routing change. The generic logic calls
- * doRouting when required. If the device has any special requirements these
- * methods can be overriden.
- */
- virtual status_t doRouting();
-
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- friend class AudioStreamOutALSA;
- friend class AudioStreamInALSA;
-
- ALSAMixer *mMixer;
- AudioStreamOutALSA *mOutput;
- AudioStreamInALSA *mInput;
-
- acoustic_device_t *mAcousticDevice;
-
- private:
- Mutex mLock;
- };
-
- // ----------------------------------------------------------------------------
+ return mMode;
+ }
+
+protected:
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ friend class AudioStreamOutALSA;
+ friend class AudioStreamInALSA;
+ friend class ALSAStreamOps;
+
+ ALSAMixer * mMixer;
+
+ alsa_device_t * mALSADevice;
+ acoustic_device_t * mAcousticDevice;
+
+ ALSAHandleList mDeviceList;
+
+private:
+ Mutex mLock;
+};
+
+// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_AUDIO_HARDWARE_ALSA_H
diff --git a/AudioPolicyManagerALSA.cpp b/AudioPolicyManagerALSA.cpp
new file mode 100644
index 0000000..b7808c9
--- /dev/null
+++ b/AudioPolicyManagerALSA.cpp
@@ -0,0 +1,1854 @@
+/*
+ * Copyright (C) 2009 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 "AudioPolicyManagerALSA"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include "AudioPolicyManagerALSA.h"
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyInterface implementation
+// ----------------------------------------------------------------------------
+
+
+status_t AudioPolicyManagerALSA::setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address)
+{
+
+ LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
+
+ // connect/disconnect only 1 device at a time
+ if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
+
+ if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
+ LOGE("setDeviceConnectionState() invalid address: %s", device_address);
+ return BAD_VALUE;
+ }
+
+ // handle output devices
+ if (AudioSystem::isOutputDevice(device)) {
+
+#ifndef WITH_A2DP
+ if (AudioSystem::isA2dpDevice(device)) {
+ LOGE("setDeviceConnectionState() invalid device: %x", device);
+ return BAD_VALUE;
+ }
+#endif
+
+ switch (state)
+ {
+ // handle output device connection
+ case AudioSystem::DEVICE_STATE_AVAILABLE:
+ if (mAvailableOutputDevices & device) {
+ LOGW("setDeviceConnectionState() device already connected: %x", device);
+ return INVALID_OPERATION;
+ }
+ LOGW_IF((getOutputForDevice((uint32_t)device) != 0), "setDeviceConnectionState(): output using unconnected device %x", device);
+
+ LOGV("setDeviceConnectionState() connecting device %x", device);
+
+ // register new device as available
+ mAvailableOutputDevices |= device;
+
+#ifdef WITH_A2DP
+ // handle A2DP device connection
+ if (AudioSystem::isA2dpDevice(device)) {
+ // when an A2DP device is connected, open an A2DP and a duplicated output
+ LOGV("opening A2DP output for device %s", device_address);
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = device;
+ mA2dpOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+ if (mA2dpOutput) {
+ // add A2DP output descriptor
+ mOutputs.add(mA2dpOutput, outputDesc);
+ // set initial stream volume for A2DP device
+ applyStreamVolumes(mA2dpOutput, device);
+ mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);
+ if (mDuplicatedOutput != 0) {
+ // If both A2DP and duplicated outputs are open, send device address to A2DP hardware
+ // interface
+ AudioParameter param;
+ param.add(String8("a2dp_sink_address"), String8(device_address));
+ mpClientInterface->setParameters(mA2dpOutput, param.toString());
+ mA2dpDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
+
+ // add duplicated output descriptor
+ AudioOutputDescriptor *dupOutputDesc = new AudioOutputDescriptor();
+ dupOutputDesc->mOutput1 = mOutputs.valueFor(mHardwareOutput);
+ dupOutputDesc->mOutput2 = mOutputs.valueFor(mA2dpOutput);
+ dupOutputDesc->mSamplingRate = outputDesc->mSamplingRate;
+ dupOutputDesc->mFormat = outputDesc->mFormat;
+ dupOutputDesc->mChannels = outputDesc->mChannels;
+ dupOutputDesc->mLatency = outputDesc->mLatency;
+ mOutputs.add(mDuplicatedOutput, dupOutputDesc);
+ applyStreamVolumes(mDuplicatedOutput, device);
+ } else {
+ LOGW("getOutput() could not open duplicated output for %d and %d",
+ mHardwareOutput, mA2dpOutput);
+ mAvailableOutputDevices &= ~device;
+ delete outputDesc;
+ return NO_INIT;
+ }
+ } else {
+ LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device);
+ mAvailableOutputDevices &= ~device;
+ delete outputDesc;
+ return NO_INIT;
+ }
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+
+ if (mA2dpDeviceAddress == mScoDeviceAddress) {
+ // It is normal to suspend twice if we are both in call,
+ // and have the hardware audio output routed to BT SCO
+ if (mPhoneState != AudioSystem::MODE_NORMAL) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)hwOutputDesc->device())) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ }
+
+ // move streams pertaining to STRATEGY_MEDIA to the newly opened A2DP output
+ if (getDeviceForStrategy(STRATEGY_MEDIA) & device) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_MEDIA) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mA2dpOutput);
+ outputDesc->mRefCount[i] = hwOutputDesc->mRefCount[i];
+ hwOutputDesc->mRefCount[i] = 0;
+ }
+ }
+
+ }
+ // move streams pertaining to STRATEGY_DTMF to the newly opened A2DP output
+ if (getDeviceForStrategy(STRATEGY_DTMF) & device) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_DTMF) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mA2dpOutput);
+ outputDesc->mRefCount[i] = hwOutputDesc->mRefCount[i];
+ hwOutputDesc->mRefCount[i] = 0;
+ }
+ }
+
+ }
+ // move streams pertaining to STRATEGY_SONIFICATION to the newly opened duplicated output
+ if (getDeviceForStrategy(STRATEGY_SONIFICATION) & device) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_SONIFICATION) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mDuplicatedOutput);
+ outputDesc->mRefCount[i] =
+ hwOutputDesc->mRefCount[i];
+ mOutputs.valueFor(mDuplicatedOutput)->mRefCount[i] =
+ hwOutputDesc->mRefCount[i];
+ }
+ }
+ }
+ } else
+#endif
+ // handle wired and SCO device connection (accessed via hardware output)
+ {
+
+ uint32_t newDevice = 0;
+ if (AudioSystem::isBluetoothScoDevice(device)) {
+ LOGV("setDeviceConnectionState() BT SCO device, address %s", device_address);
+ // keep track of SCO device address
+ mScoDeviceAddress = String8(device_address, MAX_DEVICE_ADDRESS_LEN);
+ // if in call and connecting SCO device, check if we must reroute hardware output
+ if (mPhoneState == AudioSystem::MODE_IN_CALL &&
+ getDeviceForStrategy(STRATEGY_PHONE) == device) {
+ newDevice = device;
+ } else if (mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_DTMF) &&
+ getDeviceForStrategy(STRATEGY_DTMF) == device) {
+ newDevice = device;
+ }
+ if ((mA2dpDeviceAddress == mScoDeviceAddress) &&
+ (mPhoneState != AudioSystem::MODE_NORMAL)) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ } else if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET ||
+ device == AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) {
+ LOGV("setDeviceConnectionState() wired headset device");
+ // if connecting a wired headset, we check the following by order of priority
+ // to request a routing change if necessary:
+ // 1: we are in call or the strategy phone is active on the hardware output:
+ // use device for strategy phone
+ // 2: the strategy sonification is active on the hardware output:
+ // use device for strategy sonification
+ // 3: the strategy media is active on the hardware output:
+ // use device for strategy media
+ // 4: the strategy DTMF is active on the hardware output:
+ // use device for strategy DTMF
+ if (getDeviceForStrategy(STRATEGY_PHONE) == device &&
+ (mPhoneState == AudioSystem::MODE_IN_CALL ||
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_PHONE))) {
+ newDevice = device;
+ } else if ((getDeviceForStrategy(STRATEGY_SONIFICATION) & device) &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_SONIFICATION)){
+ newDevice = getDeviceForStrategy(STRATEGY_SONIFICATION);
+ } else if ((getDeviceForStrategy(STRATEGY_MEDIA) == device) &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA)){
+ newDevice = device;
+ } else if (getDeviceForStrategy(STRATEGY_DTMF) == device &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_DTMF)) {
+ newDevice = device;
+ }
+ } else if (device == AudioSystem::DEVICE_OUT_TTY) {
+ LOGV("setDeviceConnectionState() tty device");
+ // if connecting a wired headset, we check the following by order of priority
+ // to request a routing change if necessary:
+ // 1: we are in call or the strategy phone is active on the hardware output:
+ // use device for strategy phone
+ if (getDeviceForStrategy(STRATEGY_PHONE) == device &&
+ (mPhoneState == AudioSystem::MODE_IN_CALL ||
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_PHONE))) {
+ newDevice = device;
+ }
+ } else if (device == AudioSystem::DEVICE_OUT_FM_SPEAKER ||
+ device == AudioSystem::DEVICE_OUT_FM_HEADPHONE) {
+ LOGV("setDeviceConnectionState() no mic headphone device");
+ // if connecting a wired headset, we check the following by order of priority
+ // to request a routing change if necessary:
+ // 1: the strategy sonification is active on the hardware output:
+ // use device for strategy sonification
+ // 2: the strategy media is active on the hardware output:
+ // use device for strategy media
+ if ((getDeviceForStrategy(STRATEGY_SONIFICATION) & device) &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_SONIFICATION)){
+ newDevice = getDeviceForStrategy(STRATEGY_SONIFICATION);
+ } else if ((getDeviceForStrategy(STRATEGY_MEDIA) == device) &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA)){
+ newDevice = device;
+ }
+ }
+
+ // request routing change if necessary
+ setOutputDevice(mHardwareOutput, newDevice);
+ }
+ break;
+ // handle output device disconnection
+ case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
+ if (!(mAvailableOutputDevices & device)) {
+ LOGW("setDeviceConnectionState() device not connected: %x", device);
+ return INVALID_OPERATION;
+ }
+
+ uint32_t newDevice = 0;
+ // get usage of disconnected device by all strategies
+ bool wasUsedForMedia = (getDeviceForStrategy(STRATEGY_MEDIA) & device) != 0;
+ bool wasUsedForSonification = (getDeviceForStrategy(STRATEGY_SONIFICATION) & device) != 0;
+ bool wasUsedforPhone = (getDeviceForStrategy(STRATEGY_PHONE) & device) != 0;
+ bool wasUsedforDtmf = (getDeviceForStrategy(STRATEGY_DTMF) & device) != 0;
+ LOGV("setDeviceConnectionState() disconnecting device %x used by media %d, sonification %d, phone %d",
+ device, wasUsedForMedia, wasUsedForSonification, wasUsedforPhone);
+ // remove device from available output devices
+ mAvailableOutputDevices &= ~device;
+
+#ifdef WITH_A2DP
+ // handle A2DP device disconnection
+ if (AudioSystem::isA2dpDevice(device)) {
+ if (mA2dpOutput == 0 || mDuplicatedOutput == 0) {
+ LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
+ mAvailableOutputDevices |= device;
+ return INVALID_OPERATION;
+ }
+
+ if (mA2dpDeviceAddress != device_address) {
+ LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
+ mAvailableOutputDevices |= device;
+ return INVALID_OPERATION;
+ }
+
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+ AudioOutputDescriptor *a2dpOutputDesc = mOutputs.valueFor(mA2dpOutput);
+
+ // mute media during 2 seconds to avoid outputing sound on hardware output while music stream
+ // is switched from A2DP output and before music is paused by music application
+ setStrategyMute(STRATEGY_MEDIA, true, mHardwareOutput);
+ setStrategyMute(STRATEGY_MEDIA, false, mHardwareOutput, 2000);
+
+ // If the A2DP device was used by DTMF strategy, move all streams pertaining to DTMF strategy to
+ // hardware output
+ if (wasUsedforDtmf) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_DTMF) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
+ hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,
+ a2dpOutputDesc->mRefCount[i]);
+ }
+ }
+ if (a2dpOutputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
+ newDevice = getDeviceForStrategy(STRATEGY_DTMF);
+ }
+ }
+
+ // If the A2DP device was used by media strategy, move all streams pertaining to media strategy to
+ // hardware output
+ if (wasUsedForMedia) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_MEDIA) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
+ hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,
+ a2dpOutputDesc->mRefCount[i]);
+ }
+ }
+ if (a2dpOutputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
+ newDevice = getDeviceForStrategy(STRATEGY_MEDIA);
+ }
+ }
+
+ // If the A2DP device was used by sonification strategy, move all streams pertaining to
+ // sonification strategy to hardware output.
+ // Note that newDevice is overwritten here giving sonification strategy a higher priority than
+ // media strategy.
+ if (wasUsedForSonification) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_SONIFICATION) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
+ }
+ }
+ if (a2dpOutputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
+ newDevice = getDeviceForStrategy(STRATEGY_SONIFICATION);
+ }
+ }
+
+ // close A2DP and duplicated outputs
+ AudioParameter param;
+ param.add(String8("closing"), String8("true"));
+ mpClientInterface->setParameters(mA2dpOutput, param.toString());
+
+ LOGW("setDeviceConnectionState() closing A2DP and duplicated output!");
+ mpClientInterface->closeOutput(mDuplicatedOutput);
+ delete mOutputs.valueFor(mDuplicatedOutput);
+ mOutputs.removeItem(mDuplicatedOutput);
+ mDuplicatedOutput = 0;
+ mpClientInterface->closeOutput(mA2dpOutput);
+ delete mOutputs.valueFor(mA2dpOutput);
+ mOutputs.removeItem(mA2dpOutput);
+ mA2dpOutput = 0;
+ } else
+#endif
+ {
+ if (AudioSystem::isBluetoothScoDevice(device)) {
+ // handle SCO device disconnection
+ if (wasUsedforPhone &&
+ mPhoneState == AudioSystem::MODE_IN_CALL) {
+ // if in call, find new suitable device for phone strategy
+ newDevice = getDeviceForStrategy(STRATEGY_PHONE);
+ } else if (wasUsedforDtmf &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_DTMF)) {
+ newDevice = getDeviceForStrategy(STRATEGY_DTMF);
+ }
+ if ((mA2dpDeviceAddress == mScoDeviceAddress) &&
+ (mPhoneState != AudioSystem::MODE_NORMAL)) {
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+ } else if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET ||
+ device == AudioSystem::DEVICE_OUT_WIRED_HEADPHONE) {
+ // if disconnecting a wired headset, we check the following by order of priority
+ // to request a routing change if necessary:
+ // 1: we are in call or the strategy phone is active on the hardware output:
+ // use device for strategy phone
+ // 2: the strategy sonification is active on the hardware output:
+ // use device for strategy sonification
+ // 3: the strategy media is active on the hardware output:
+ // use device for strategy media
+ // 4: the strategy DTMF is active on the hardware output:
+ // use device for strategy DTMF
+ if (wasUsedforPhone &&
+ (mPhoneState == AudioSystem::MODE_IN_CALL ||
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_PHONE))) {
+ newDevice = getDeviceForStrategy(STRATEGY_PHONE);
+ } else if (wasUsedForSonification &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_SONIFICATION)){
+ newDevice = getDeviceForStrategy(STRATEGY_SONIFICATION);
+ } else if (wasUsedForMedia &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA)){
+ newDevice = getDeviceForStrategy(STRATEGY_MEDIA);
+ } else if (wasUsedforDtmf &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_DTMF)){
+ newDevice = getDeviceForStrategy(STRATEGY_DTMF);
+ }
+ } else if (device == AudioSystem::DEVICE_OUT_TTY) {
+ LOGV("setDeviceConnectionState() tty device");
+ if (wasUsedforPhone &&
+ (mPhoneState == AudioSystem::MODE_IN_CALL ||
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_PHONE))) {
+ newDevice = getDeviceForStrategy(STRATEGY_PHONE);
+ }
+ } else if (device == AudioSystem::DEVICE_OUT_FM_SPEAKER ||
+ device == AudioSystem::DEVICE_OUT_FM_HEADPHONE) {
+ LOGV("setDeviceConnectionState() no mic headphone device");
+ if (wasUsedForSonification &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_SONIFICATION)){
+ newDevice = getDeviceForStrategy(STRATEGY_SONIFICATION);
+ } else if (wasUsedForMedia &&
+ mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA)){
+ newDevice = getDeviceForStrategy(STRATEGY_MEDIA);
+ }
+ }
+ }
+ // request routing change if necessary
+ setOutputDevice(mHardwareOutput, newDevice);
+
+ // clear A2DP and SCO device address if necessary
+#ifdef WITH_A2DP
+ if (AudioSystem::isA2dpDevice(device)) {
+ mA2dpDeviceAddress = "";
+ }
+#endif
+ if (AudioSystem::isBluetoothScoDevice(device)) {
+ mScoDeviceAddress = "";
+ }
+ } break;
+
+ default:
+ LOGE("setDeviceConnectionState() invalid state: %x", state);
+ return BAD_VALUE;
+ }
+
+ if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
+ device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
+ } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
+ device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
+ device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
+ device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ } else {
+ return NO_ERROR;
+ }
+ }
+ // handle input devices
+ if (AudioSystem::isInputDevice(device)) {
+
+ switch (state)
+ {
+ // handle input device connection
+ case AudioSystem::DEVICE_STATE_AVAILABLE: {
+ if (mAvailableInputDevices & device) {
+ LOGW("setDeviceConnectionState() device already connected: %d", device);
+ return INVALID_OPERATION;
+ }
+ mAvailableInputDevices |= device;
+ }
+ break;
+
+ // handle input device disconnection
+ case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
+ if (!(mAvailableInputDevices & device)) {
+ LOGW("setDeviceConnectionState() device not connected: %d", device);
+ return INVALID_OPERATION;
+ }
+ mAvailableInputDevices &= ~device;
+ } break;
+
+ default:
+ LOGE("setDeviceConnectionState() invalid state: %x", state);
+ return BAD_VALUE;
+ }
+
+ audio_io_handle_t activeInput = getActiveInput();
+ if (activeInput != 0) {
+ AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
+ uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
+ if (newDevice != inputDesc->mDevice) {
+ LOGV("setDeviceConnectionState() changing device from %x to %x for input %d",
+ inputDesc->mDevice, newDevice, activeInput);
+ inputDesc->mDevice = newDevice;
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
+ mpClientInterface->setParameters(activeInput, param.toString());
+ }
+ }
+
+ return NO_ERROR;
+ }
+
+ LOGW("setDeviceConnectionState() invalid device: %x", device);
+ return BAD_VALUE;
+}
+
+AudioSystem::device_connection_state AudioPolicyManagerALSA::getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ String8 address = String8(device_address);
+ if (AudioSystem::isOutputDevice(device)) {
+ if (device & mAvailableOutputDevices) {
+#ifdef WITH_A2DP
+ if (AudioSystem::isA2dpDevice(device) &&
+ address != "" && mA2dpDeviceAddress != address) {
+ return state;
+ }
+#endif
+ if (AudioSystem::isBluetoothScoDevice(device) &&
+ address != "" && mScoDeviceAddress != address) {
+ return state;
+ }
+ state = AudioSystem::DEVICE_STATE_AVAILABLE;
+ }
+ } else if (AudioSystem::isInputDevice(device)) {
+ if (device & mAvailableInputDevices) {
+ state = AudioSystem::DEVICE_STATE_AVAILABLE;
+ }
+ }
+
+ return state;
+}
+
+void AudioPolicyManagerALSA::setPhoneState(int state)
+{
+ LOGV("setPhoneState() state %d", state);
+ uint32_t newDevice = 0;
+ if (state < 0 || state >= AudioSystem::NUM_MODES) {
+ LOGW("setPhoneState() invalid state %d", state);
+ return;
+ }
+
+ if (state == mPhoneState ) {
+ LOGW("setPhoneState() setting same state %d", state);
+ return;
+ }
+
+ // if leaving call state, handle special case of active streams
+ // pertaining to sonification strategy see handleIncallSonification()
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ LOGV("setPhoneState() in call state management: new state is %d", state);
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ handleIncallSonification(stream, false, true);
+ }
+ }
+
+ // store previous phone state for management of sonification strategy below
+ int oldState = mPhoneState;
+ uint32_t oldDtmfDevice = getDeviceForStrategy(STRATEGY_DTMF);
+ uint32_t oldSonificationDevice = getDeviceForStrategy(STRATEGY_SONIFICATION) & ~AudioSystem::DEVICE_OUT_SPEAKER;
+ mPhoneState = state;
+ bool force = false;
+ // check if a routing change is required for hardware output in the following
+ // order of priority:
+ // 1: a stream pertaining to sonification strategy is active
+ // 2: new state is incall
+ // 3: a stream pertaining to media strategy is active
+ // 4: a stream pertaining to DTMF strategy is active
+ if (mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_SONIFICATION)) {
+ newDevice = getDeviceForStrategy(STRATEGY_SONIFICATION);
+ } else if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ newDevice = getDeviceForStrategy(STRATEGY_PHONE);
+ // force routing command to audio hardware when starting call
+ // even if no device change is needed
+ force = true;
+ } else if (mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA)) {
+ newDevice = getDeviceForStrategy(STRATEGY_MEDIA);
+ } else if (mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_DTMF)) {
+ newDevice = getDeviceForStrategy(STRATEGY_DTMF);
+ }
+
+
+ if (mA2dpOutput != 0) {
+ // If entering or exiting in call state, switch DTMF streams to/from A2DP output
+ // if necessary
+ uint32_t newDtmfDevice = getDeviceForStrategy(STRATEGY_DTMF);
+ uint32_t newSonificationDevice = getDeviceForStrategy(STRATEGY_SONIFICATION) & ~AudioSystem::DEVICE_OUT_SPEAKER;
+ if (state == AudioSystem::MODE_IN_CALL) { // entering in call mode
+ // move DTMF streams from A2DP output to hardware output if necessary
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)oldDtmfDevice) &&
+ !AudioSystem::isA2dpDevice((AudioSystem::audio_devices)newDtmfDevice)) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_DTMF) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
+ int refCount = mOutputs.valueFor(mA2dpOutput)->mRefCount[i];
+ mOutputs.valueFor(mHardwareOutput)->changeRefCount((AudioSystem::stream_type)i,
+ refCount);
+ mOutputs.valueFor(mA2dpOutput)->changeRefCount((AudioSystem::stream_type)i,-refCount);
+ }
+ }
+ if (newDevice == 0 && mOutputs.valueFor(mA2dpOutput)->isUsedByStrategy(STRATEGY_DTMF)) {
+ newDevice = newDtmfDevice;
+ }
+ }
+ // move SONIFICATION streams from duplicated output to hardware output if necessary
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)oldSonificationDevice) &&
+ !AudioSystem::isA2dpDevice((AudioSystem::audio_devices)newSonificationDevice)) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_SONIFICATION) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mHardwareOutput);
+ int refCount = mOutputs.valueFor(mDuplicatedOutput)->mRefCount[i];
+ mOutputs.valueFor(mHardwareOutput)->changeRefCount((AudioSystem::stream_type)i,
+ refCount);
+ mOutputs.valueFor(mDuplicatedOutput)->changeRefCount((AudioSystem::stream_type)i,-refCount);
+ }
+ }
+ }
+ } else { // exiting in call mode
+ // move DTMF streams from hardware output to A2DP output if necessary
+ if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)oldDtmfDevice) &&
+ AudioSystem::isA2dpDevice((AudioSystem::audio_devices)newDtmfDevice)) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_DTMF) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mA2dpOutput);
+ int refCount = mOutputs.valueFor(mHardwareOutput)->mRefCount[i];
+ mOutputs.valueFor(mA2dpOutput)->changeRefCount((AudioSystem::stream_type)i, refCount);
+ mOutputs.valueFor(mHardwareOutput)->changeRefCount((AudioSystem::stream_type)i, -refCount);
+ }
+ }
+ }
+ // move SONIFICATION streams from hardware output to A2DP output if necessary
+ if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)oldSonificationDevice) &&
+ AudioSystem::isA2dpDevice((AudioSystem::audio_devices)newSonificationDevice)) {
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == STRATEGY_SONIFICATION) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, mDuplicatedOutput);
+ int refCount = mOutputs.valueFor(mHardwareOutput)->mRefCount[i];
+ mOutputs.valueFor(mDuplicatedOutput)->changeRefCount((AudioSystem::stream_type)i, refCount);
+ mOutputs.valueFor(mHardwareOutput)->changeRefCount((AudioSystem::stream_type)i, -refCount);
+ }
+ }
+ }
+ }
+ // suspend A2DP output if SCO device address is the same as A2DP device address.
+ // no need to check that a SCO device is actually connected as mScoDeviceAddress == ""
+ // if none is connected and the test below will fail.
+ if (mA2dpDeviceAddress == mScoDeviceAddress) {
+ if (oldState == AudioSystem::MODE_NORMAL) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ } else if (state == AudioSystem::MODE_NORMAL) {
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+ }
+ }
+
+ // force routing command to audio hardware when ending call
+ // even if no device change is needed
+ if (oldState == AudioSystem::MODE_IN_CALL) {
+ if (newDevice == 0) {
+ newDevice = mOutputs.valueFor(mHardwareOutput)->device();
+ }
+ force = true;
+ }
+ // change routing is necessary
+ setOutputDevice(mHardwareOutput, newDevice, force);
+
+ // if entering in call state, handle special case of active streams
+ // pertaining to sonification strategy see handleIncallSonification()
+ if (state == AudioSystem::MODE_IN_CALL) {
+ LOGV("setPhoneState() in call state management: new state is %d", state);
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ handleIncallSonification(stream, true, true);
+ }
+ }
+}
+
+void AudioPolicyManagerALSA::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ LOGV("setRingerMode() mode %x, mask %x", mode, mask);
+
+ mRingerMode = mode;
+}
+
+void AudioPolicyManagerALSA::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+{
+ LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
+
+ switch(usage) {
+ case AudioSystem::FOR_COMMUNICATION:
+ if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO &&
+ config != AudioSystem::FORCE_NONE) {
+ LOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config);
+ return;
+ }
+ mForceUse[usage] = config;
+ // update hardware output routing immediately if in call, or if there is an active
+ // VOICE_CALL stream, as would be the case with an application that uses this stream
+ // for it to behave like in a telephony app (e.g. voicemail app that plays audio files
+ // streamed or downloaded to the device)
+ if ((mPhoneState == AudioSystem::MODE_IN_CALL) ||
+ (mOutputs.valueFor(mHardwareOutput)->isUsedByStream(AudioSystem::VOICE_CALL))) {
+ uint32_t device = getDeviceForStrategy(STRATEGY_PHONE);
+ setOutputDevice(mHardwareOutput, device);
+ }
+ break;
+ case AudioSystem::FOR_MEDIA:
+ if (config != AudioSystem::FORCE_HEADPHONES && config != AudioSystem::FORCE_BT_A2DP &&
+ config != AudioSystem::FORCE_WIRED_ACCESSORY && config != AudioSystem::FORCE_NONE) {
+ LOGW("setForceUse() invalid config %d for FOR_MEDIA", config);
+ return;
+ }
+ mForceUse[usage] = config;
+ break;
+ case AudioSystem::FOR_RECORD:
+ if (config != AudioSystem::FORCE_BT_SCO && config != AudioSystem::FORCE_WIRED_ACCESSORY &&
+ config != AudioSystem::FORCE_NONE) {
+ LOGW("setForceUse() invalid config %d for FOR_RECORD", config);
+ return;
+ }
+ mForceUse[usage] = config;
+ break;
+ default:
+ LOGW("setForceUse() invalid usage %d", usage);
+ break;
+ }
+}
+
+AudioSystem::forced_config AudioPolicyManagerALSA::getForceUse(AudioSystem::force_use usage)
+{
+ return mForceUse[usage];
+}
+
+void AudioPolicyManagerALSA::setSystemProperty(const char* property, const char* value)
+{
+ LOGV("setSystemProperty() property %s, value %s", property, value);
+ if (strcmp(property, "ro.camera.sound.forced") == 0) {
+ if (atoi(value)) {
+ LOGV("ENFORCED_AUDIBLE cannot be muted");
+ mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
+ } else {
+ LOGV("ENFORCED_AUDIBLE can be muted");
+ mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;
+ }
+ }
+}
+
+audio_io_handle_t AudioPolicyManagerALSA::getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags)
+{
+ audio_io_handle_t output = 0;
+ uint32_t latency = 0;
+ routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+ uint32_t device = getDeviceForStrategy(strategy);
+ LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
+
+
+ // open a direct output if:
+ // 1 a direct output is explicitely requested
+ // 2 the audio format is compressed
+ if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+ (format !=0 && !AudioSystem::isLinearPCM(format))) {
+
+ LOGV("getOutput() opening direct output device %x", device);
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = device;
+ outputDesc->mSamplingRate = samplingRate;
+ outputDesc->mFormat = format;
+ outputDesc->mChannels = channels;
+ outputDesc->mLatency = 0;
+ outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
+ outputDesc->mRefCount[stream] = 1;
+ output = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+
+ // only accept an output with the requeted parameters
+ if ((samplingRate != 0 && samplingRate != outputDesc->mSamplingRate) ||
+ (format != 0 && format != outputDesc->mFormat) ||
+ (channels != 0 && channels != outputDesc->mChannels)) {
+ LOGV("getOutput() failed opening direct output: samplingRate %d, format %d, channels %d",
+ samplingRate, format, channels);
+ mpClientInterface->closeOutput(output);
+ delete outputDesc;
+ return 0;
+ }
+ mOutputs.add(output, outputDesc);
+ return output;
+ }
+
+ if (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO &&
+ channels != AudioSystem::CHANNEL_OUT_STEREO) {
+ return 0;
+ }
+ // open a non direct output
+
+ // get which output is suitable for the specified stream. The actual routing change will happen
+ // when startOutput() will be called
+ uint32_t device2 = device & ~AudioSystem::DEVICE_OUT_SPEAKER;
+ if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) {
+#ifdef WITH_A2DP
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device2)) {
+ // if playing on 2 devices among which one is A2DP, use duplicated output
+ LOGV("getOutput() using duplicated output");
+ LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device in multiple %x selected but A2DP output not opened", device);
+ output = mDuplicatedOutput;
+ } else
+#endif
+ {
+ // if playing on 2 devices among which none is A2DP, use hardware output
+ output = mHardwareOutput;
+ }
+ LOGV("getOutput() using output %d for 2 devices %x", output, device);
+ } else {
+#ifdef WITH_A2DP
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device2)) {
+ // if playing on A2DP device, use a2dp output
+ LOGW_IF((mA2dpOutput == 0), "getOutput() A2DP device %x selected but A2DP output not opened", device);
+ output = mA2dpOutput;
+ } else
+#endif
+ {
+ // if playing on not A2DP device, use hardware output
+ output = mHardwareOutput;
+ }
+ }
+
+
+ LOGW_IF((output ==0), "getOutput() could not find output for stream %d, samplingRate %d, format %d, channels %x, flags %x",
+ stream, samplingRate, format, channels, flags);
+
+ return output;
+}
+
+status_t AudioPolicyManagerALSA::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ LOGV("startOutput() output %d, stream %d", output, stream);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("startOutput() unknow output %d", output);
+ return BAD_VALUE;
+ }
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+ uint32_t device = getDeviceForStrategy(strategy);
+
+ if (!outputDesc->isUsedByStrategy(strategy)) {
+ // if the stream started is the first active stream in its strategy, check if routing change
+ // must be done on hardware output
+ uint32_t newDevice = 0;
+ if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) {
+#ifdef WITH_A2DP
+ uint32_t device2 = device & ~AudioSystem::DEVICE_OUT_SPEAKER;
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device2)) {
+ // if one device is A2DP, selected the second device for hardware output
+ device &= ~device2;
+ } else
+#endif
+ {
+ // we only support speaker + headset and speaker + headphone combinations on hardware output.
+ // other combinations will leave device = 0 and no routing will happen.
+ if (device != (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET) &&
+ device != (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) {
+ device = AudioSystem::DEVICE_OUT_SPEAKER;
+ }
+ }
+ }
+
+ // By order of priority
+ // 1 apply routing for phone strategy in any case
+ // 2 apply routing for notification strategy if no stream pertaining to
+ // phone strategies is playing
+ // 3 apply routing for media strategy is not incall and neither phone nor sonification
+ // strategies is active.
+ // 4 apply routing for DTMF strategy if no stream pertaining to
+ // neither phone, sonification nor media strategy is playing
+ if (strategy == STRATEGY_PHONE) {
+ newDevice = device;
+ } else if (!mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_PHONE)) {
+ if (strategy == STRATEGY_SONIFICATION) {
+ newDevice = device;
+ } else if (!mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_SONIFICATION)) {
+ if (strategy == STRATEGY_MEDIA) {
+ newDevice = device;
+ } else if (!mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(STRATEGY_MEDIA)) {
+ // strategy == STRATEGY_DTMF
+ newDevice = device;
+ }
+ }
+ }
+
+ // TODO: maybe mute stream is selected device was refused
+ setOutputDevice(mHardwareOutput, newDevice);
+ }
+
+ // incremenent usage count for this stream on the requested output:
+ // NOTE that the usage count is the same for duplicated output and hardware output which is
+ // necassary for a correct control of hardware output routing by startOutput() and stopOutput()
+ outputDesc->changeRefCount(stream, 1);
+
+ // handle special case for sonification while in call
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ handleIncallSonification(stream, true, false);
+ }
+
+ // apply volume rules for current stream and device if necessary
+ checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, outputDesc->device());
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerALSA::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ LOGV("stopOutput() output %d, stream %d", output, stream);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("stopOutput() unknow output %d", output);
+ return BAD_VALUE;
+ }
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
+
+ // handle special case for sonification while in call
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ handleIncallSonification(stream, false, false);
+ }
+
+ if (outputDesc->isUsedByStrategy(strategy)) {
+ // decrement usage count of this stream on the output
+ outputDesc->changeRefCount(stream, -1);
+ if (!outputDesc->isUsedByStrategy(strategy)) {
+ // if the stream is the last of its strategy to use this output, change routing
+ // in the following order or priority:
+ // PHONE > SONIFICATION > MEDIA > DTMF
+ uint32_t newDevice = 0;
+ if (outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
+ newDevice = getDeviceForStrategy(STRATEGY_PHONE);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
+ newDevice = getDeviceForStrategy(STRATEGY_SONIFICATION);
+ } else if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ newDevice = getDeviceForStrategy(STRATEGY_PHONE);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
+ newDevice = getDeviceForStrategy(STRATEGY_MEDIA);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
+ newDevice = getDeviceForStrategy(STRATEGY_DTMF);
+ }
+
+ // apply routing change if necessary.
+ // insert a delay of 2 times the audio hardware latency to ensure PCM
+ // buffers in audio flinger and audio hardware are emptied before the
+ // routing change is executed.
+ setOutputDevice(mHardwareOutput, newDevice, false, mOutputs.valueFor(mHardwareOutput)->mLatency*2);
+ }
+ // store time at which the last music track was stopped - see computeVolume()
+ if (stream == AudioSystem::MUSIC) {
+ mMusicStopTime = systemTime();
+ }
+ return NO_ERROR;
+ } else {
+ LOGW("stopOutput() refcount is already 0 for output %d", output);
+ return INVALID_OPERATION;
+ }
+}
+
+void AudioPolicyManagerALSA::releaseOutput(audio_io_handle_t output)
+{
+ LOGV("releaseOutput() %d", output);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("releaseOutput() releasing unknown output %d", output);
+ return;
+ }
+ if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
+ mpClientInterface->closeOutput(output);
+ delete mOutputs.valueAt(index);
+ mOutputs.removeItem(output);
+ }
+}
+
+audio_io_handle_t AudioPolicyManagerALSA::getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ audio_io_handle_t input = 0;
+ uint32_t device = getDeviceForInputSource(inputSource);
+
+ LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics);
+
+ if (device == 0) {
+ return 0;
+ }
+
+ // adapt channel selection to input source
+ switch(inputSource) {
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ channels = AudioSystem::CHANNEL_IN_VOICE_UPLINK;
+ break;
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ channels = AudioSystem::CHANNEL_IN_VOICE_DNLINK;
+ break;
+ case AUDIO_SOURCE_VOICE_CALL:
+ channels = (AudioSystem::CHANNEL_IN_VOICE_UPLINK | AudioSystem::CHANNEL_IN_VOICE_DNLINK);
+ break;
+ default:
+ break;
+ }
+
+ AudioInputDescriptor *inputDesc = new AudioInputDescriptor();
+
+ inputDesc->mInputSource = inputSource;
+ inputDesc->mDevice = device;
+ inputDesc->mSamplingRate = samplingRate;
+ inputDesc->mFormat = format;
+ inputDesc->mChannels = channels;
+ inputDesc->mAcoustics = acoustics;
+ inputDesc->mRefCount = 0;
+ input = mpClientInterface->openInput(&inputDesc->mDevice,
+ &inputDesc->mSamplingRate,
+ &inputDesc->mFormat,
+ &inputDesc->mChannels,
+ inputDesc->mAcoustics);
+
+ // only accept input with the exact requested set of parameters
+ if ((samplingRate != inputDesc->mSamplingRate) ||
+ (format != inputDesc->mFormat) ||
+ (channels != inputDesc->mChannels)) {
+ LOGV("getOutput() failed opening input: samplingRate %d, format %d, channels %d",
+ samplingRate, format, channels);
+ mpClientInterface->closeInput(input);
+ delete inputDesc;
+ return 0;
+ }
+ mInputs.add(input, inputDesc);
+ return input;
+}
+
+status_t AudioPolicyManagerALSA::startInput(audio_io_handle_t input)
+{
+ LOGV("startInput() input %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("startInput() unknow input %d", input);
+ return BAD_VALUE;
+ }
+ AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+ // refuse 2 active AudioRecord clients at the same time
+ if (getActiveInput() != 0) {
+ LOGW("startInput() input %d failed: other input already started", input);
+ return INVALID_OPERATION;
+ }
+
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice);
+ mpClientInterface->setParameters(input, param.toString());
+
+ inputDesc->mRefCount = 1;
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerALSA::stopInput(audio_io_handle_t input)
+{
+ LOGV("stopInput() input %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("stopInput() unknow input %d", input);
+ return BAD_VALUE;
+ }
+ AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+ if (inputDesc->mRefCount == 0) {
+ LOGW("stopInput() input %d already stopped", input);
+ return INVALID_OPERATION;
+ } else {
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), 0);
+ mpClientInterface->setParameters(input, param.toString());
+ inputDesc->mRefCount = 0;
+ return NO_ERROR;
+ }
+}
+
+void AudioPolicyManagerALSA::releaseInput(audio_io_handle_t input)
+{
+ LOGV("releaseInput() %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("releaseInput() releasing unknown input %d", input);
+ return;
+ }
+ mpClientInterface->closeInput(input);
+ delete mInputs.valueAt(index);
+ mInputs.removeItem(input);
+ LOGV("releaseInput() exit");
+}
+
+
+
+void AudioPolicyManagerALSA::initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax)
+{
+ LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
+ if (indexMin < 0 || indexMin >= indexMax) {
+ LOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax);
+ return;
+ }
+ mStreams[stream].mIndexMin = indexMin;
+ mStreams[stream].mIndexMax = indexMax;
+}
+
+status_t AudioPolicyManagerALSA::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+
+ if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
+ return BAD_VALUE;
+ }
+
+ LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
+ mStreams[stream].mIndexCur = index;
+
+ // compute and apply stream volume on all outputs according to connected device
+ status_t status = NO_ERROR;
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device());
+ if (volStatus != NO_ERROR) {
+ status = volStatus;
+ }
+ }
+ return status;
+}
+
+status_t AudioPolicyManagerALSA::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+{
+ if (index == 0) {
+ return BAD_VALUE;
+ }
+ LOGV("getStreamVolumeIndex() stream %d", stream);
+ *index = mStreams[stream].mIndexCur;
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerALSA::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Hardware Output: %d\n", mHardwareOutput);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " A2DP Output: %d\n", mA2dpOutput);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Duplicated Output: %d\n", mDuplicatedOutput);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Output devices: %08x\n", mAvailableOutputDevices);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Input devices: %08x\n", mAvailableInputDevices);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " A2DP device address: %s\n", mA2dpDeviceAddress.string());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Ringer mode: %d\n", mRingerMode);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Force use for communications %d\n", mForceUse[AudioSystem::FOR_COMMUNICATION]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AudioSystem::FOR_MEDIA]);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AudioSystem::FOR_RECORD]);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ snprintf(buffer, SIZE, "\nOutputs dump:\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i));
+ write(fd, buffer, strlen(buffer));
+ mOutputs.valueAt(i)->dump(fd);
+ }
+
+ snprintf(buffer, SIZE, "\nInputs dump:\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i));
+ write(fd, buffer, strlen(buffer));
+ mInputs.valueAt(i)->dump(fd);
+ }
+
+ snprintf(buffer, SIZE, "\nStreams dump:\n");
+ write(fd, buffer, strlen(buffer));
+ snprintf(buffer, SIZE, " Stream Index Min Index Max Index Cur Mute Count Can be muted\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ snprintf(buffer, SIZE, " %02d", i);
+ mStreams[i].dump(buffer + 3, SIZE);
+ write(fd, buffer, strlen(buffer));
+ }
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// AudioPolicyManagerALSA
+// ----------------------------------------------------------------------------
+
+// --- class factory
+
+
+extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
+{
+ return new AudioPolicyManagerALSA(clientInterface);
+}
+
+extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
+{
+ delete interface;
+}
+
+AudioPolicyManagerALSA::AudioPolicyManagerALSA(AudioPolicyClientInterface *clientInterface)
+: mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0)
+{
+ mpClientInterface = clientInterface;
+
+ for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
+ mForceUse[i] = AudioSystem::FORCE_NONE;
+ }
+
+ // devices available by default are speaker, ear piece and microphone
+ mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
+ AudioSystem::DEVICE_OUT_SPEAKER;
+ mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+
+ mA2dpDeviceAddress = String8("");
+ mScoDeviceAddress = String8("");
+
+ // open hardware output
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
+ mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+
+ if (mHardwareOutput == 0) {
+ LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
+ outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+ } else {
+ mOutputs.add(mHardwareOutput, outputDesc);
+ setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
+ }
+
+ mA2dpOutput = 0;
+ mDuplicatedOutput = 0;
+}
+
+AudioPolicyManagerALSA::~AudioPolicyManagerALSA()
+{
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ mpClientInterface->closeOutput(mOutputs.keyAt(i));
+ delete mOutputs.valueAt(i);
+ }
+ mOutputs.clear();
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ mpClientInterface->closeInput(mInputs.keyAt(i));
+ delete mInputs.valueAt(i);
+ }
+ mInputs.clear();
+}
+
+// ---
+
+audio_io_handle_t AudioPolicyManagerALSA::getOutputForDevice(uint32_t device)
+{
+ audio_io_handle_t output = 0;
+ uint32_t lDevice;
+
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ lDevice = mOutputs.valueAt(i)->device();
+ LOGV("getOutputForDevice() output %d devices %x", mOutputs.keyAt(i), lDevice);
+
+ // We are only considering outputs connected to a mixer here => exclude direct outputs
+ if ((lDevice == device) &&
+ !(mOutputs.valueAt(i)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
+ output = mOutputs.keyAt(i);
+ LOGV("getOutputForDevice() found output %d for device %x", output, device);
+ break;
+ }
+ }
+ return output;
+}
+
+AudioPolicyManagerALSA::routing_strategy AudioPolicyManagerALSA::getStrategy(AudioSystem::stream_type stream)
+{
+ // stream to strategy mapping
+ switch (stream) {
+ case AudioSystem::VOICE_CALL:
+ case AudioSystem::BLUETOOTH_SCO:
+ return STRATEGY_PHONE;
+ case AudioSystem::RING:
+ case AudioSystem::NOTIFICATION:
+ case AudioSystem::ALARM:
+ case AudioSystem::ENFORCED_AUDIBLE:
+ return STRATEGY_SONIFICATION;
+ case AudioSystem::DTMF:
+ return STRATEGY_DTMF;
+ default:
+ LOGE("unknown stream type");
+ case AudioSystem::SYSTEM:
+ // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
+ // while key clicks are played produces a poor result
+ case AudioSystem::TTS:
+ case AudioSystem::MUSIC:
+ return STRATEGY_MEDIA;
+ }
+}
+
+uint32_t AudioPolicyManagerALSA::getDeviceForStrategy(routing_strategy strategy)
+{
+ uint32_t device = 0;
+
+ switch (strategy) {
+ case STRATEGY_DTMF:
+ if (mPhoneState != AudioSystem::MODE_IN_CALL) {
+ // when off call, DTMF strategy follows the same rules as MEDIA strategy
+ device = getDeviceForStrategy(STRATEGY_MEDIA);
+ break;
+ }
+ // when in call, DTMF and PHONE strategies follow the same rules
+ // FALL THROUGH
+
+ case STRATEGY_PHONE:
+ // for phone strategy, we first consider the forced use and then the available devices by order
+ // of priority
+ switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
+ case AudioSystem::FORCE_BT_SCO:
+ if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ if (device) break;
+ }
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
+ if (device) break;
+ // if SCO device is requested but no SCO device is available, fall back to default case
+ // FALL THROUGH
+
+ default: // FORCE_NONE
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_TTY;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() earpiece device not found");
+ }
+ break;
+
+ case AudioSystem::FORCE_SPEAKER:
+ if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ if (device) break;
+ }
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_FM_SPEAKER;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() speaker device not found");
+ }
+ break;
+ }
+ break;
+
+ case STRATEGY_SONIFICATION:
+
+ // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
+ // handleIncallSonification().
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ device = getDeviceForStrategy(STRATEGY_PHONE);
+ break;
+ }
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() speaker device not found");
+ }
+ // The second device used for sonification is the same as the device used by media strategy
+ // FALL THROUGH
+
+ case STRATEGY_MEDIA: {
+ uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_FM_HEADPHONE;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_FM_SPEAKER;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+ if (device2 == 0) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() speaker device not found");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
+ device |= device2;
+ // Do not play media stream if in call and the requested device would change the hardware
+ // output routing
+ if (mPhoneState == AudioSystem::MODE_IN_CALL &&
+ !AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device) &&
+ device != getDeviceForStrategy(STRATEGY_PHONE)) {
+ device = 0;
+ LOGV("getDeviceForStrategy() incompatible media and phone devices");
+ }
+ } break;
+
+ default:
+ LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
+ break;
+ }
+
+ LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
+ return device;
+}
+
+void AudioPolicyManagerALSA::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
+{
+ LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
+ if (mOutputs.indexOfKey(output) < 0) {
+ LOGW("setOutputDevice() unknown output %d", output);
+ return;
+ }
+#ifdef WITH_A2DP
+ if (output == mHardwareOutput) {
+ // clear A2DP devices from device bit field here so that the caller does not have to
+ // do it in case of multiple device selections
+ uint32_t device2 = device & ~AudioSystem::DEVICE_OUT_SPEAKER;
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device2)) {
+ LOGV("setOutputDevice() removing A2DP device");
+ device &= ~device2;
+ }
+ } else if (output == mA2dpOutput) {
+ // clear hardware devices from device bit field here so that the caller does not have to
+ // do it in case of multiple device selections (the second device is always DEVICE_OUT_SPEAKER)
+ // in this case
+ device &= ~AudioSystem::DEVICE_OUT_SPEAKER;
+ }
+#endif
+
+ // doing this check here allows the caller to call setOutputDevice() without conditions
+ if (device == 0) return;
+
+ uint32_t oldDevice = (uint32_t)mOutputs.valueFor(output)->device();
+ // Do not change the routing if the requested device is the same as current device. Doing this check
+ // here allows the caller to call setOutputDevice() without conditions
+ if (device == oldDevice && !force) {
+ LOGV("setOutputDevice() setting same device %x for output %d", device, output);
+ return;
+ }
+
+ mOutputs.valueFor(output)->mDevice = device;
+ // mute media streams if both speaker and headset are selected
+ if (device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET) ||
+ device == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) {
+ setStrategyMute(STRATEGY_MEDIA, true, output);
+ // wait for the PCM output buffers to empty before proceeding with the rest of the command
+ usleep(mOutputs.valueFor(output)->mLatency*2*1000);
+ }
+ // suspend A2D output if SCO device is selected
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
+ if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ }
+ // do the routing
+ AudioParameter param = AudioParameter();
+ param.addInt(String8(AudioParameter::keyRouting), (int)device);
+ mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs);
+ // update stream volumes according to new device
+ applyStreamVolumes(output, device, delayMs);
+
+ // if disconnecting SCO device, restore A2DP output
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)oldDevice)) {
+ if (mA2dpOutput && mScoDeviceAddress == mA2dpDeviceAddress) {
+ LOGV("restore A2DP output");
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+ }
+ // if changing from a combined headset + speaker route, unmute media streams
+ if (oldDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET) ||
+ oldDevice == (AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) {
+ setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
+ }
+}
+
+uint32_t AudioPolicyManagerALSA::getDeviceForInputSource(int inputSource)
+{
+ uint32_t device;
+
+ switch(inputSource) {
+ case AUDIO_SOURCE_DEFAULT:
+ case AUDIO_SOURCE_MIC:
+ case AUDIO_SOURCE_VOICE_RECOGNITION:
+ if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
+ mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
+ device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ } else if (mAvailableInputDevices & AudioSystem::DEVICE_IN_WIRED_HEADSET) {
+ device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
+ } else {
+ device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+ }
+ break;
+ case AUDIO_SOURCE_CAMCORDER:
+ device = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+ break;
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ case AUDIO_SOURCE_VOICE_CALL:
+ device = AudioSystem::DEVICE_IN_VOICE_CALL;
+ break;
+ default:
+ LOGW("getInput() invalid input source %d", inputSource);
+ device = 0;
+ break;
+ }
+ return device;
+}
+
+audio_io_handle_t AudioPolicyManagerALSA::getActiveInput()
+{
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ if (mInputs.valueAt(i)->mRefCount > 0) {
+ return mInputs.keyAt(i);
+ }
+ }
+ return 0;
+}
+
+float AudioPolicyManagerALSA::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device)
+{
+ float volume = 1.0;
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ StreamDescriptor &streamDesc = mStreams[stream];
+
+ // Force max volume if stream cannot be muted
+ if (!streamDesc.mCanBeMuted) index = streamDesc.mIndexMax;
+
+ if (device == 0) {
+ device = outputDesc->device();
+ }
+
+ int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
+ volume = AudioSystem::linearToLog(volInt);
+
+ // if a heaset is connected, apply the following rules to ring tones and notifications
+ // to avoid sound level bursts in user's ears:
+ // - always attenuate ring tones and notifications volume by 6dB
+ // - if music is playing, always limit the volume to current music volume,
+ // with a minimum threshold at -36dB so that notification is always perceived.
+ if ((device &
+ (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
+ AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ AudioSystem::DEVICE_OUT_WIRED_HEADSET |
+ AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) &&
+ (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION)) {
+ volume *= SONIFICATION_HEADSET_VOLUME_FACTOR;
+ // when the phone is ringing we must consider that music could have been paused just before
+ // by the music application and behave as if music was active if the last music track was
+ // just stopped
+ if (outputDesc->isUsedByStream(AudioSystem::MUSIC) ||
+ ((mPhoneState == AudioSystem::MODE_RINGTONE) &&
+ (systemTime() - mMusicStopTime < seconds(SONIFICATION_HEADSET_MUSIC_DELAY)))) {
+ float musicVol = computeVolume(AudioSystem::MUSIC, mStreams[AudioSystem::MUSIC].mIndexCur, output, device);
+ float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
+ if (volume > minVol) {
+ volume = minVol;
+ LOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol);
+ }
+ }
+ }
+
+ return volume;
+}
+
+status_t AudioPolicyManagerALSA::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
+{
+
+ // do not change actual stream volume if the stream is muted
+ if (mStreams[stream].mMuteCount != 0) {
+ LOGV("checkAndSetVolume() stream %d muted count %d", stream, mStreams[stream].mMuteCount);
+ return NO_ERROR;
+ }
+
+ // do not change in call volume if bluetooth is connected and vice versa
+ if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
+ (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
+ LOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
+ stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
+ return INVALID_OPERATION;
+ }
+
+ float volume = computeVolume(stream, index, output, device || force);
+ // do not set volume if the float value did not change
+ if (volume != mOutputs.valueFor(output)->mCurVolume[stream]) {
+ mOutputs.valueFor(output)->mCurVolume[stream] = volume;
+ LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
+ if (stream == AudioSystem::VOICE_CALL ||
+ stream == AudioSystem::DTMF ||
+ stream == AudioSystem::BLUETOOTH_SCO) {
+ float voiceVolume = -1.0;
+ // offset value to reflect actual hardware volume that never reaches 0
+ // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
+ volume = 0.01 + 0.99 * volume;
+ if (stream == AudioSystem::VOICE_CALL) {
+ voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
+ } else if (stream == AudioSystem::BLUETOOTH_SCO) {
+ voiceVolume = 1.0;
+ }
+ if (voiceVolume >= 0 && output == mHardwareOutput) {
+ mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
+ }
+ }
+ mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
+ }
+
+ return NO_ERROR;
+}
+
+void AudioPolicyManagerALSA::applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs)
+{
+ LOGV("applyStreamVolumes() for output %d and device %x", output, device);
+
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ checkAndSetVolume(stream, mStreams[stream].mIndexCur, output, device, delayMs);
+ }
+}
+
+void AudioPolicyManagerALSA::setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs)
+{
+ LOGV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ if (getStrategy((AudioSystem::stream_type)stream) == strategy) {
+ setStreamMute(stream, on, output, delayMs);
+ }
+ }
+}
+
+void AudioPolicyManagerALSA::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs)
+{
+ StreamDescriptor &streamDesc = mStreams[stream];
+ uint32_t device = mOutputs.valueFor(output)->mDevice;
+
+ LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, streamDesc.mMuteCount);
+
+ if (on) {
+ if (streamDesc.mMuteCount == 0) {
+ if (streamDesc.mCanBeMuted) {
+ checkAndSetVolume(stream, 0, output, device, delayMs);
+ }
+ }
+ // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
+ streamDesc.mMuteCount++;
+ } else {
+ if (streamDesc.mMuteCount == 0) {
+ LOGW("setStreamMute() unmuting non muted stream!");
+ return;
+ }
+ if (--streamDesc.mMuteCount == 0) {
+ checkAndSetVolume(stream, streamDesc.mIndexCur, output, device, delayMs);
+ }
+ }
+}
+
+void AudioPolicyManagerALSA::handleIncallSonification(int stream, bool starting, bool stateChange)
+{
+ // if the stream pertains to sonification strategy and we are in call we must
+ // mute the stream if it is low visibility. If it is high visibility, we must play a tone
+ // in the device used for phone strategy and play the tone if the selected device does not
+ // interfere with the device used for phone strategy
+ // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as
+ // many times as there are active tracks on the output
+
+ if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput);
+ LOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
+ stream, starting, outputDesc->mDevice, stateChange);
+ if (outputDesc->isUsedByStream((AudioSystem::stream_type)stream)) {
+ int muteCount = 1;
+ if (stateChange) {
+ muteCount = outputDesc->mRefCount[stream];
+ }
+ if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) {
+ LOGV("handleIncallSonification() low visibility, muteCount %d", muteCount);
+ for (int i = 0; i < muteCount; i++) {
+ setStreamMute(stream, starting, mHardwareOutput);
+ }
+ } else {
+ LOGV("handleIncallSonification() high visibility ");
+ if (outputDesc->mDevice & getDeviceForStrategy(STRATEGY_PHONE)) {
+ LOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount);
+ for (int i = 0; i < muteCount; i++) {
+ setStreamMute(stream, starting, mHardwareOutput);
+ }
+ }
+ if (starting) {
+ mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
+ } else {
+ mpClientInterface->stopTone();
+ }
+ }
+ }
+ }
+}
+
+
+// --- AudioOutputDescriptor class implementation
+
+AudioPolicyManagerALSA::AudioOutputDescriptor::AudioOutputDescriptor()
+ : mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
+ mFlags((AudioSystem::output_flags)0), mDevice(0), mOutput1(0), mOutput2(0)
+{
+ // clear usage count for all stream types
+ for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ mRefCount[i] = 0;
+ mCurVolume[i] = -1.0;
+ }
+}
+
+uint32_t AudioPolicyManagerALSA::AudioOutputDescriptor::device()
+{
+ uint32_t device = 0;
+ if (isDuplicated()) {
+ device = mOutput1->mDevice | mOutput2->mDevice;
+ } else {
+ device = mDevice;
+ }
+ return device;
+}
+
+void AudioPolicyManagerALSA::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
+{
+ // forward usage count change to attached outputs
+ if (isDuplicated()) {
+ mOutput1->changeRefCount(stream, delta);
+ mOutput2->changeRefCount(stream, delta);
+ }
+ if ((delta + (int)mRefCount[stream]) < 0) {
+ LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
+ mRefCount[stream] = 0;
+ return;
+ }
+ mRefCount[stream] += delta;
+ LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
+}
+
+bool AudioPolicyManagerALSA::AudioOutputDescriptor::isUsedByStrategy(routing_strategy strategy)
+{
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (AudioPolicyManagerALSA::getStrategy((AudioSystem::stream_type)i) == strategy &&
+ isUsedByStream((AudioSystem::stream_type)i)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+status_t AudioPolicyManagerALSA::AudioOutputDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Format: %d\n", mFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Latency: %d\n", mLatency);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Flags %08x\n", mFlags);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Stream volume refCount\n");
+ result.append(buffer);
+ for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ snprintf(buffer, SIZE, " %02d %.03f %d\n", i, mCurVolume[i], mRefCount[i]);
+ result.append(buffer);
+ }
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+// --- AudioInputDescriptor class implementation
+
+AudioPolicyManagerALSA::AudioInputDescriptor::AudioInputDescriptor()
+ : mSamplingRate(0), mFormat(0), mChannels(0),
+ mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
+{
+}
+
+status_t AudioPolicyManagerALSA::AudioInputDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Format: %d\n", mFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Channels: %08x\n", mChannels);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Acoustics %08x\n", mAcoustics);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Devices %08x\n", mDevice);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+// --- StreamDescriptor class implementation
+
+void AudioPolicyManagerALSA::StreamDescriptor::dump(char* buffer, size_t size)
+{
+ snprintf(buffer, size, " %02d %02d %02d %02d %d\n",
+ mIndexMin,
+ mIndexMax,
+ mIndexCur,
+ mMuteCount,
+ mCanBeMuted);
+}
+
+
+}; // namespace android
diff --git a/AudioPolicyManagerALSA.h b/AudioPolicyManagerALSA.h
new file mode 100644
index 0000000..b31747c
--- /dev/null
+++ b/AudioPolicyManagerALSA.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Timers.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <hardware_legacy/AudioPolicyInterface.h>
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define MAX_DEVICE_ADDRESS_LEN 20
+// Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB
+#define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5
+// Min volume for STRATEGY_SONIFICATION streams when limited by music volume: -36dB
+#define SONIFICATION_HEADSET_VOLUME_MIN 0.016
+// Time in seconds during which we consider that music is still active after a music
+// track was stopped - see computeVolume()
+#define SONIFICATION_HEADSET_MUSIC_DELAY 5
+class AudioPolicyManagerALSA: public AudioPolicyInterface
+{
+
+public:
+ AudioPolicyManagerALSA(AudioPolicyClientInterface *clientInterface);
+ virtual ~AudioPolicyManagerALSA();
+
+ // AudioPolicyInterface
+ virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address);
+ virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address);
+ virtual void setPhoneState(int state);
+ virtual void setRingerMode(uint32_t mode, uint32_t mask);
+ virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
+ virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
+ virtual void setSystemProperty(const char* property, const char* value);
+ virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags);
+ virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual void releaseOutput(audio_io_handle_t output);
+ virtual audio_io_handle_t getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics);
+ // indicates to the audio policy manager that the input starts being used.
+ virtual status_t startInput(audio_io_handle_t input);
+ // indicates to the audio policy manager that the input stops being used.
+ virtual status_t stopInput(audio_io_handle_t input);
+ virtual void releaseInput(audio_io_handle_t input);
+ virtual void initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax);
+ virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
+ virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
+
+ virtual status_t dump(int fd);
+
+private:
+
+ enum routing_strategy {
+ STRATEGY_MEDIA,
+ STRATEGY_PHONE,
+ STRATEGY_SONIFICATION,
+ STRATEGY_DTMF,
+ NUM_STRATEGIES
+ };
+
+ // descriptor for audio outputs. Used to maintain current configuration of each opened audio output
+ // and keep track of the usage of this output by each audio stream type.
+ class AudioOutputDescriptor
+ {
+ public:
+ AudioOutputDescriptor();
+
+ status_t dump(int fd);
+
+ uint32_t device();
+ void changeRefCount(AudioSystem::stream_type, int delta);
+ bool isUsedByStrategy(routing_strategy strategy);
+ bool isUsedByStream(AudioSystem::stream_type stream) { return mRefCount[stream] > 0 ? true : false; }
+ bool isDuplicated() { return (mDevice == 0); } // by convention mDevice is 0 for duplicated outputs
+
+ uint32_t mSamplingRate; //
+ uint32_t mFormat; //
+ uint32_t mChannels; // output configuration
+ uint32_t mLatency; //
+ AudioSystem::output_flags mFlags; //
+ uint32_t mDevice; // current device this output is routed to
+ uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output
+ AudioOutputDescriptor *mOutput1; // used by duplicated outputs: first output
+ AudioOutputDescriptor *mOutput2; // used by duplicated outputs: second output
+ float mCurVolume[AudioSystem::NUM_STREAM_TYPES]; // current stream volume
+ };
+
+ // descriptor for audio inputs. Used to maintain current configuration of each opened audio input
+ // and keep track of the usage of this input.
+ class AudioInputDescriptor
+ {
+ public:
+ AudioInputDescriptor();
+
+ status_t dump(int fd);
+
+ uint32_t mSamplingRate; //
+ uint32_t mFormat; // input configuration
+ uint32_t mChannels; //
+ AudioSystem::audio_in_acoustics mAcoustics; //
+ uint32_t mDevice; // current device this input is routed to
+ uint32_t mRefCount; // number of AudioRecord clients using this output
+ int mInputSource; // input source selected by application (mediarecorder.h)
+ };
+
+ // stream descriptor used for volume control
+ class StreamDescriptor
+ {
+ public:
+ StreamDescriptor()
+ : mIndexMin(0), mIndexMax(1), mIndexCur(1), mMuteCount(0), mCanBeMuted(true) {}
+
+ void dump(char* buffer, size_t size);
+
+ int mIndexMin; // min volume index
+ int mIndexMax; // max volume index
+ int mIndexCur; // current volume index
+ int mMuteCount; // mute request counter
+ bool mCanBeMuted; // true is the stream can be muted
+ };
+
+ // return the strategy corresponding to a given stream type
+ static routing_strategy getStrategy(AudioSystem::stream_type stream);
+ // return the output handle of an output routed to the specified device, 0 if no output
+ // is routed to the device
+ audio_io_handle_t getOutputForDevice(uint32_t device);
+ // return appropriate device for streams handled by the specified strategy according to current
+ // phone state, connected devices...
+ uint32_t getDeviceForStrategy(routing_strategy strategy);
+ // change the route of the specified output
+ void setOutputDevice(audio_io_handle_t output, uint32_t device, bool force = false, int delayMs = 0);
+ // select input device corresponding to requested audio source
+ uint32_t getDeviceForInputSource(int inputSource);
+ // return io handle of active input or 0 if no input is active
+ audio_io_handle_t getActiveInput();
+ // compute the actual volume for a given stream according to the requested index and a particular
+ // device
+ float computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device);
+ // check that volume change is permitted, compute and send new volume to audio hardware
+ status_t checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs = 0, bool force = false);
+ // apply all stream volumes to the specified output and device
+ void applyStreamVolumes(audio_io_handle_t output, uint32_t device, int delayMs = 0);
+ // Mute or unmute all streams handled by the specified strategy on the specified output
+ void setStrategyMute(routing_strategy strategy, bool on, audio_io_handle_t output, int delayMs = 0);
+ // Mute or unmute the stream on the specified output
+ void setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs = 0);
+ // handle special cases for sonification strategy while in call: mute streams or replace by
+ // a special tone in the device used for communication
+ void handleIncallSonification(int stream, bool starting, bool stateChange);
+
+ AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
+ audio_io_handle_t mHardwareOutput; // hardware output handler
+ audio_io_handle_t mA2dpOutput; // A2DP output handler
+ audio_io_handle_t mDuplicatedOutput; // duplicated output handler: outputs to hardware and A2DP.
+
+ KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; // list of output descriptors
+ KeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors
+ uint32_t mAvailableOutputDevices; // bit field of all available output devices
+ uint32_t mAvailableInputDevices; // bit field of all available input devices
+ int mPhoneState; // current phone state
+ uint32_t mRingerMode; // current ringer mode
+ AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE]; // current forced use configuration
+
+ StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES]; // stream descriptors for volume control
+ String8 mA2dpDeviceAddress; // A2DP device MAC address
+ String8 mScoDeviceAddress; // SCO device MAC address
+ nsecs_t mMusicStopTime; // time when last music stream was stopped
+};
+
+};
diff --git a/AudioStreamInALSA.cpp b/AudioStreamInALSA.cpp
new file mode 100644
index 0000000..f645357
--- /dev/null
+++ b/AudioStreamInALSA.cpp
@@ -0,0 +1,155 @@
+/* AudioStreamInALSA.cpp
+ **
+ ** Copyright 2008-2009 Wind River Systems
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "AudioHardwareALSA"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <cutils/properties.h>
+#include <media/AudioRecord.h>
+#include <hardware_legacy/power.h>
+
+#include "AudioHardwareALSA.h"
+
+namespace android
+{
+
+AudioStreamInALSA::AudioStreamInALSA(AudioHardwareALSA *parent,
+ alsa_handle_t *handle,
+ AudioSystem::audio_in_acoustics audio_acoustics) :
+ ALSAStreamOps(parent, handle),
+ mAcoustics(audio_acoustics)
+{
+ acoustic_device_t *aDev = acoustics();
+
+ if (aDev) aDev->set_params(aDev, mAcoustics, NULL);
+}
+
+AudioStreamInALSA::~AudioStreamInALSA()
+{
+ close();
+}
+
+status_t AudioStreamInALSA::setGain(float gain)
+{
+ return mixer() ? mixer()->setMasterGain(gain) : (status_t)NO_INIT;
+}
+
+ssize_t AudioStreamInALSA::read(void *buffer, ssize_t bytes)
+{
+ AutoMutex lock(mLock);
+
+ if (!mPowerLock) {
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock");
+ mPowerLock = true;
+ }
+
+ acoustic_device_t *aDev = acoustics();
+
+ // If there is an acoustics module read method, then it overrides this
+ // implementation (unlike AudioStreamOutALSA write).
+ if (aDev && aDev->read)
+ return aDev->read(aDev, buffer, bytes);
+
+ snd_pcm_sframes_t n, frames = snd_pcm_bytes_to_frames(mHandle->handle, bytes);
+ status_t err;
+
+ do {
+ n = snd_pcm_readi(mHandle->handle, buffer, frames);
+ if (n < frames) {
+ if (mHandle->handle) {
+ if (n < 0) {
+ n = snd_pcm_recover(mHandle->handle, n, 0);
+
+ if (aDev && aDev->recover) aDev->recover(aDev, n);
+ } else
+ n = snd_pcm_prepare(mHandle->handle);
+ }
+ return static_cast<ssize_t>(n);
+ }
+ } while (n == -EAGAIN);
+
+ return static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n));
+}
+
+status_t AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
+{
+ return NO_ERROR;
+}
+
+status_t AudioStreamInALSA::open(int mode)
+{
+ AutoMutex lock(mLock);
+
+ status_t status = ALSAStreamOps::open(mode);
+
+ acoustic_device_t *aDev = acoustics();
+
+ if (status == NO_ERROR && aDev)
+ status = aDev->use_handle(aDev, mHandle);
+
+ return status;
+}
+
+status_t AudioStreamInALSA::close()
+{
+ AutoMutex lock(mLock);
+
+ acoustic_device_t *aDev = acoustics();
+
+ if (mHandle && aDev) aDev->cleanup(aDev);
+
+ close();
+
+ if (mPowerLock) {
+ release_wake_lock ("AudioInLock");
+ mPowerLock = false;
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioStreamInALSA::standby()
+{
+ AutoMutex lock(mLock);
+
+ if (mPowerLock) {
+ release_wake_lock ("AudioInLock");
+ mPowerLock = false;
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioStreamInALSA::setAcousticParams(void *params)
+{
+ AutoMutex lock(mLock);
+
+ acoustic_device_t *aDev = acoustics();
+
+ return aDev ? aDev->set_params(aDev, mAcoustics, params) : (status_t)NO_ERROR;
+}
+
+} // namespace android
diff --git a/AudioStreamOutALSA.cpp b/AudioStreamOutALSA.cpp
new file mode 100644
index 0000000..b042bfb
--- /dev/null
+++ b/AudioStreamOutALSA.cpp
@@ -0,0 +1,169 @@
+/* AudioStreamOutALSA.cpp
+ **
+ ** Copyright 2008-2009 Wind River Systems
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#define LOG_TAG "AudioHardwareALSA"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <cutils/properties.h>
+#include <media/AudioRecord.h>
+#include <hardware_legacy/power.h>
+
+#include "AudioHardwareALSA.h"
+
+#ifndef ALSA_DEFAULT_SAMPLE_RATE
+#define ALSA_DEFAULT_SAMPLE_RATE 44100 // in Hz
+#endif
+
+namespace android
+{
+
+// ----------------------------------------------------------------------------
+
+static const int DEFAULT_SAMPLE_RATE = ALSA_DEFAULT_SAMPLE_RATE;
+
+// ----------------------------------------------------------------------------
+
+AudioStreamOutALSA::AudioStreamOutALSA(AudioHardwareALSA *parent, alsa_handle_t *handle) :
+ ALSAStreamOps(parent, handle)
+{
+}
+
+AudioStreamOutALSA::~AudioStreamOutALSA()
+{
+ close();
+}
+
+uint32_t AudioStreamOutALSA::channels() const
+{
+ int c = ALSAStreamOps::channels();
+ return c;
+}
+
+status_t AudioStreamOutALSA::setVolume(float left, float right)
+{
+ return mixer()->setVolume (mHandle->curDev, left, right);
+}
+
+ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
+{
+ AutoMutex lock(mLock);
+
+ if (!mPowerLock) {
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
+ mPowerLock = true;
+ }
+
+ acoustic_device_t *aDev = acoustics();
+
+ // For output, we will pass the data on to the acoustics module, but the actual
+ // data is expected to be sent to the audio device directly as well.
+ if (aDev && aDev->write)
+ aDev->write(aDev, buffer, bytes);
+
+ snd_pcm_sframes_t n;
+ size_t sent = 0;
+ status_t err;
+
+ do {
+ n = snd_pcm_writei(mHandle->handle,
+ (char *)buffer + sent,
+ snd_pcm_bytes_to_frames(mHandle->handle, bytes));
+ if (n == -EBADFD) {
+ // Somehow the stream is in a bad state. The driver probably
+ // has a bug and snd_pcm_recover() doesn't seem to handle this.
+ mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode);
+
+ if (aDev && aDev->recover) aDev->recover(aDev, n);
+ }
+ else if (n < 0) {
+ if (mHandle->handle) {
+ // snd_pcm_recover() will return 0 if successful in recovering from
+ // an error, or -errno if the error was unrecoverable.
+ n = snd_pcm_recover(mHandle->handle, n, 1);
+
+ if (aDev && aDev->recover) aDev->recover(aDev, n);
+
+ if (n) return static_cast<ssize_t>(n);
+ }
+ }
+ else
+ sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n));
+
+ } while (mHandle->handle && sent < bytes);
+
+ return sent;
+}
+
+status_t AudioStreamOutALSA::dump(int fd, const Vector<String16>& args)
+{
+ return NO_ERROR;
+}
+
+status_t AudioStreamOutALSA::open(int mode)
+{
+ AutoMutex lock(mLock);
+
+ return ALSAStreamOps::open(mode);
+}
+
+status_t AudioStreamOutALSA::close()
+{
+ AutoMutex lock(mLock);
+
+ snd_pcm_drain (mHandle->handle);
+ close();
+
+ if (mPowerLock) {
+ release_wake_lock ("AudioOutLock");
+ mPowerLock = false;
+ }
+
+ return NO_ERROR;
+}
+
+status_t AudioStreamOutALSA::standby()
+{
+ AutoMutex lock(mLock);
+
+ snd_pcm_drain (mHandle->handle);
+
+ if (mPowerLock) {
+ release_wake_lock ("AudioOutLock");
+ mPowerLock = false;
+ }
+
+ return NO_ERROR;
+}
+
+#define USEC_TO_MSEC(x) ((x + 999) / 1000)
+
+uint32_t AudioStreamOutALSA::latency() const
+{
+ // Android wants latency in milliseconds.
+ return USEC_TO_MSEC (mHandle->latency);
+}
+
+} // namespace android
diff --git a/acoustics_default.cpp b/acoustics_default.cpp
index 91a169d..6280979 100644
--- a/acoustics_default.cpp
+++ b/acoustics_default.cpp
@@ -15,71 +15,85 @@
** limitations under the License.
*/
-
-#define LOG_TAG "AudioHardwareALSA"
+#define LOG_TAG "AcousticsModule"
#include <utils/Log.h>
#include "AudioHardwareALSA.h"
namespace android
{
- static int s_device_open(const hw_module_t*, const char*, hw_device_t**);
- static int s_device_close (hw_device_t*);
- static status_t s_set_acoustics (snd_pcm_t *, AudioSystem::audio_in_acoustics);
- static ssize_t s_filter (snd_pcm_t *, void *, ssize_t);
-
- static hw_module_methods_t s_module_methods = {
- open: s_device_open
- };
-
- extern "C" const hw_module_t HAL_MODULE_INFO_SYM = {
- tag: HARDWARE_MODULE_TAG,
- version_major: 1,
- version_minor: 0,
- id: ACOUSTICS_HARDWARE_MODULE_ID,
- name: "ALSA acoustics module",
- author: "Wind River",
- methods: &s_module_methods,
- reserved: {}
- };
-
- static int s_device_open(const hw_module_t* module,
- const char* name,
- hw_device_t** device)
- {
- acoustic_device_t *dev;
- dev = (acoustic_device_t *)malloc(sizeof(*dev));
- if (! dev) return -ENOMEM;
-
- memset(dev, 0, sizeof(*dev));
-
- /* initialize the procs */
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = 0;
- dev->common.module = (hw_module_t *)module;
- dev->common.close = s_device_close;
- dev->set_acoustics = s_set_acoustics;
- dev->filter = s_filter;
-
- *device = &dev->common;
- return 0;
- }
-
- static int s_device_close (hw_device_t* device)
- {
- free(device);
- return 0;
- }
-
- static status_t s_set_acoustics (snd_pcm_t *handle, AudioSystem::audio_in_acoustics acoustics)
- {
- LOGD("Acoustics set_acoustics stub called with %d.", (int)acoustics);
- return NO_ERROR;
- }
-
- static ssize_t s_filter (snd_pcm_t *handle, void *buffer, ssize_t frames)
- {
- // Default acoustics doesn't apply any filtering
- return frames;
- }
+
+static int s_device_open(const hw_module_t*, const char*, hw_device_t**);
+static int s_device_close(hw_device_t*);
+
+static status_t s_use_handle(acoustic_device_t *, alsa_handle_t *);
+static status_t s_cleanup(acoustic_device_t *);
+static status_t s_set_params(acoustic_device_t *,
+ AudioSystem::audio_in_acoustics, void *params);
+
+static hw_module_methods_t s_module_methods = {
+ open : s_device_open
+};
+
+extern "C" const hw_module_t HAL_MODULE_INFO_SYM = {
+ tag : HARDWARE_MODULE_TAG,
+ version_major : 1,
+ version_minor : 0,
+ id : ACOUSTICS_HARDWARE_MODULE_ID,
+ name : "ALSA acoustics module",
+ author : "Wind River",
+ methods : &s_module_methods,
+ dso : 0,
+ reserved : { 0, },
+};
+
+static int s_device_open(const hw_module_t* module, const char* name,
+ hw_device_t** device)
+{
+ acoustic_device_t *dev;
+ dev = (acoustic_device_t *) malloc(sizeof(*dev));
+ if (!dev) return -ENOMEM;
+
+ memset(dev, 0, sizeof(*dev));
+
+ /* initialize the procs */
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = 0;
+ dev->common.module = (hw_module_t *) module;
+ dev->common.close = s_device_close;
+
+ // Required methods...
+ dev->use_handle = s_use_handle;
+ dev->cleanup = s_cleanup;
+ dev->set_params = s_set_params;
+
+ // read, write, and recover are optional methods...
+
+ *device = &dev->common;
+ return 0;
+}
+
+static int s_device_close(hw_device_t* device)
+{
+ free(device);
+ return 0;
+}
+
+static status_t s_use_handle(acoustic_device_t *dev, alsa_handle_t *h)
+{
+ return NO_ERROR;
+}
+
+static status_t s_cleanup(acoustic_device_t *dev)
+{
+ LOGD("Acoustics close stub called.");
+ return NO_ERROR;
+}
+
+static status_t s_set_params(acoustic_device_t *dev,
+ AudioSystem::audio_in_acoustics acoustics, void *params)
+{
+ LOGD("Acoustics set_params stub called with %d.", (int)acoustics);
+ return NO_ERROR;
+}
}
diff --git a/alsa_default.cpp b/alsa_default.cpp
new file mode 100644
index 0000000..0e8e71e
--- /dev/null
+++ b/alsa_default.cpp
@@ -0,0 +1,535 @@
+/* alsa_default.cpp
+ **
+ ** Copyright 2009 Wind River Systems
+ **
+ ** 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 "ALSAModule"
+#include <utils/Log.h>
+
+#include "AudioHardwareALSA.h"
+#include <media/AudioRecord.h>
+
+#undef DISABLE_HARWARE_RESAMPLING
+
+#define ALSA_NAME_MAX 128
+
+#define ALSA_STRCAT(x,y) \
+ if (strlen(x) + strlen(y) < ALSA_NAME_MAX) \
+ strcat(x, y);
+
+#ifndef ALSA_DEFAULT_SAMPLE_RATE
+#define ALSA_DEFAULT_SAMPLE_RATE 44100 // in Hz
+#endif
+
+namespace android
+{
+
+static int s_device_open(const hw_module_t*, const char*, hw_device_t**);
+static int s_device_close(hw_device_t*);
+static status_t s_init(alsa_device_t *, ALSAHandleList &);
+static status_t s_open(alsa_handle_t *, uint32_t, int);
+static status_t s_close(alsa_handle_t *);
+static status_t s_route(alsa_handle_t *, uint32_t, int);
+
+static hw_module_methods_t s_module_methods = {
+ open : s_device_open
+};
+
+extern "C" const hw_module_t HAL_MODULE_INFO_SYM = {
+ tag : HARDWARE_MODULE_TAG,
+ version_major : 1,
+ version_minor : 0,
+ id : ALSA_HARDWARE_MODULE_ID,
+ name : "ALSA module",
+ author : "Wind River",
+ methods : &s_module_methods,
+ dso : 0,
+ reserved : { 0, },
+};
+
+static int s_device_open(const hw_module_t* module, const char* name,
+ hw_device_t** device)
+{
+ alsa_device_t *dev;
+ dev = (alsa_device_t *) malloc(sizeof(*dev));
+ if (!dev) return -ENOMEM;
+
+ memset(dev, 0, sizeof(*dev));
+
+ /* initialize the procs */
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = 0;
+ dev->common.module = (hw_module_t *) module;
+ dev->common.close = s_device_close;
+ dev->init = s_init;
+ dev->open = s_open;
+ dev->close = s_close;
+ dev->route = s_route;
+
+ *device = &dev->common;
+ return 0;
+}
+
+static int s_device_close(hw_device_t* device)
+{
+ free(device);
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+
+static const int DEFAULT_SAMPLE_RATE = ALSA_DEFAULT_SAMPLE_RATE;
+
+static const char *devicePrefix[SND_PCM_STREAM_LAST + 1] = {
+ /* SND_PCM_STREAM_PLAYBACK : */"AndroidOut",
+ /* SND_PCM_STREAM_CAPTURE : */"AndroidIn",
+};
+
+static alsa_handle_t _defaultsOut = {
+ module : 0,
+ devices : AudioSystem::DEVICE_OUT_ALL,
+ curDev : 0,
+ curMode : 0,
+ handle : 0,
+ format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
+ channels : 2,
+ sampleRate : DEFAULT_SAMPLE_RATE,
+ latency : 200000, // Desired Delay in usec
+ bufferSize : DEFAULT_SAMPLE_RATE / 5, // Desired Number of samples
+ modPrivate : 0,
+};
+
+static alsa_handle_t _defaultsIn = {
+ module : 0,
+ devices : AudioSystem::DEVICE_IN_ALL,
+ curDev : 0,
+ curMode : 0,
+ handle : 0,
+ format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
+ channels : 1,
+ sampleRate : AudioRecord::DEFAULT_SAMPLE_RATE,
+ latency : 250000, // Desired Delay in usec
+ bufferSize : 2048, // Desired Number of samples
+ modPrivate : 0,
+};
+
+struct device_suffix_t {
+ const AudioSystem::audio_devices device;
+ const char *suffix;
+};
+
+/* The following table(s) need to match in order of the route bits
+ */
+static const device_suffix_t deviceSuffix[] = {
+ {AudioSystem::DEVICE_OUT_EARPIECE, "_Earpiece"},
+ {AudioSystem::DEVICE_OUT_SPEAKER, "_Speaker"},
+ {AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "_Bluetooth"},
+ {AudioSystem::DEVICE_OUT_WIRED_HEADSET, "_Headset"},
+ {AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "_Bluetooth-A2DP"},
+ {AudioSystem::DEVICE_OUT_FM_HEADPHONE, "_FM"},
+};
+
+static const int deviceSuffixLen = (sizeof(deviceSuffix)
+ / sizeof(device_suffix_t));
+
+// ----------------------------------------------------------------------------
+
+const char *deviceName(alsa_handle_t *handle, uint32_t device, int mode)
+{
+ static char devString[ALSA_NAME_MAX];
+ int hasDevExt = 0;
+
+ strcpy(devString, devicePrefix[0]);
+
+ for (int dev = 0; device && dev < deviceSuffixLen; dev++)
+ if (device & deviceSuffix[dev].device) {
+ ALSA_STRCAT (devString, deviceSuffix[dev].suffix);
+ device &= ~deviceSuffix[dev].device;
+ hasDevExt = 1;
+ }
+
+ if (hasDevExt) switch (mode) {
+ case AudioSystem::MODE_NORMAL:
+ ALSA_STRCAT (devString, "_normal")
+ ;
+ break;
+ case AudioSystem::MODE_RINGTONE:
+ ALSA_STRCAT (devString, "_ringtone")
+ ;
+ break;
+ case AudioSystem::MODE_IN_CALL:
+ ALSA_STRCAT (devString, "_incall")
+ ;
+ break;
+ };
+
+ return devString;
+}
+
+snd_pcm_stream_t direction(alsa_handle_t *handle)
+{
+ return (handle->devices & AudioSystem::DEVICE_OUT_ALL) ? SND_PCM_STREAM_PLAYBACK
+ : SND_PCM_STREAM_CAPTURE;
+}
+
+const char *streamName(alsa_handle_t *handle)
+{
+ return snd_pcm_stream_name(direction(handle));
+}
+
+status_t setHardwareParams(alsa_handle_t *handle)
+{
+ snd_pcm_hw_params_t *hardwareParams;
+ status_t err;
+
+ snd_pcm_uframes_t bufferSize = handle->bufferSize;
+ unsigned int requestedRate = handle->sampleRate;
+ unsigned int latency = handle->latency;
+
+ // snd_pcm_format_description() and snd_pcm_format_name() do not perform
+ // proper bounds checking.
+ bool validFormat = (static_cast<int> (handle->format)
+ > SND_PCM_FORMAT_UNKNOWN) && (static_cast<int> (handle->format)
+ <= SND_PCM_FORMAT_LAST);
+ const char *formatDesc = validFormat ? snd_pcm_format_description(
+ handle->format) : "Invalid Format";
+ const char *formatName = validFormat ? snd_pcm_format_name(handle->format)
+ : "UNKNOWN";
+
+ if (snd_pcm_hw_params_malloc(&hardwareParams) < 0) {
+ LOG_ALWAYS_FATAL("Failed to allocate ALSA hardware parameters!");
+ return NO_INIT;
+ }
+
+ err = snd_pcm_hw_params_any(handle->handle, hardwareParams);
+ if (err < 0) {
+ LOGE("Unable to configure hardware: %s", snd_strerror(err));
+ goto done;
+ }
+
+ // Set the interleaved read and write format.
+ err = snd_pcm_hw_params_set_access(handle->handle, hardwareParams,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (err < 0) {
+ LOGE("Unable to configure PCM read/write format: %s",
+ snd_strerror(err));
+ goto done;
+ }
+
+ err = snd_pcm_hw_params_set_format(handle->handle, hardwareParams,
+ handle->format);
+ if (err < 0) {
+ LOGE("Unable to configure PCM format %s (%s): %s",
+ formatName, formatDesc, snd_strerror(err));
+ goto done;
+ }
+
+ LOGV("Set %s PCM format to %s (%s)", streamName(), formatName, formatDesc);
+
+ err = snd_pcm_hw_params_set_channels(handle->handle, hardwareParams,
+ handle->channels);
+ if (err < 0) {
+ LOGE("Unable to set channel count to %i: %s",
+ handle->channels, snd_strerror(err));
+ goto done;
+ }
+
+ LOGV("Using %i %s for %s.", handle->channels,
+ handle->channels == 1 ? "channel" : "channels", streamName());
+
+ err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams,
+ &requestedRate, 0);
+
+ if (err < 0)
+ LOGE("Unable to set %s sample rate to %u: %s",
+ streamName(handle), handle->sampleRate, snd_strerror(err));
+ else if (requestedRate != handle->sampleRate)
+ // Some devices have a fixed sample rate, and can not be changed.
+ // This may cause resampling problems; i.e. PCM playback will be too
+ // slow or fast.
+ LOGW("Requested rate (%u HZ) does not match actual rate (%u HZ)",
+ handle->sampleRate, requestedRate);
+ else
+ LOGV("Set %s sample rate to %u HZ", stream, requestedRate);
+
+#ifdef DISABLE_HARWARE_RESAMPLING
+ // Disable hardware re-sampling.
+ err = snd_pcm_hw_params_set_rate_resample(handle->handle,
+ hardwareParams,
+ static_cast<int>(resample));
+ if (err < 0) {
+ LOGE("Unable to %s hardware resampling: %s",
+ resample ? "enable" : "disable",
+ snd_strerror(err));
+ goto done;
+ }
+#endif
+
+ // Make sure we have at least the size we originally wanted
+ err = snd_pcm_hw_params_set_buffer_size(handle->handle, hardwareParams,
+ bufferSize);
+ if (err < 0) {
+ LOGE("Unable to set buffer size to %d: %s",
+ (int)bufferSize, snd_strerror(err));
+ goto done;
+ }
+
+ // Setup buffers for latency
+ err = snd_pcm_hw_params_set_buffer_time_near(handle->handle,
+ hardwareParams, &latency, NULL);
+ if (err < 0) {
+ /* That didn't work, set the period instead */
+ unsigned int periodTime = latency / 4;
+ err = snd_pcm_hw_params_set_period_time_near(handle->handle,
+ hardwareParams, &periodTime, NULL);
+ if (err < 0) {
+ LOGE("Unable to set the period time for latency: %s", snd_strerror(err));
+ goto done;
+ }
+ snd_pcm_uframes_t periodSize;
+ err = snd_pcm_hw_params_get_period_size(hardwareParams, &periodSize,
+ NULL);
+ if (err < 0) {
+ LOGE("Unable to get the period size for latency: %s", snd_strerror(err));
+ goto done;
+ }
+ bufferSize = periodSize * 4;
+ if (bufferSize < handle->bufferSize) bufferSize = handle->bufferSize;
+ err = snd_pcm_hw_params_set_buffer_size_near(handle->handle,
+ hardwareParams, &bufferSize);
+ if (err < 0) {
+ LOGE("Unable to set the buffer size for latency: %s", snd_strerror(err));
+ goto done;
+ }
+ } else {
+ // OK, we got buffer time near what we expect. See what that did for bufferSize.
+ err = snd_pcm_hw_params_get_buffer_size(hardwareParams, &bufferSize);
+ if (err < 0) {
+ LOGE("Unable to get the buffer size for latency: %s", snd_strerror(err));
+ goto done;
+ }
+ // Does set_buffer_time_near change the passed value? It should.
+ err = snd_pcm_hw_params_get_buffer_time(hardwareParams, &latency, NULL);
+ if (err < 0) {
+ LOGE("Unable to get the buffer time for latency: %s", snd_strerror(err));
+ goto done;
+ }
+ unsigned int periodTime = latency / 4;
+ err = snd_pcm_hw_params_set_period_time_near(handle->handle,
+ hardwareParams, &periodTime, NULL);
+ if (err < 0) {
+ LOGE("Unable to set the period time for latency: %s", snd_strerror(err));
+ goto done;
+ }
+ }
+
+ LOGV("Buffer size: %d", (int)bufferSize);
+ LOGV("Latency: %d", (int)latency);
+
+ handle->bufferSize = bufferSize;
+ handle->latency = latency;
+
+ // Commit the hardware parameters back to the device.
+ err = snd_pcm_hw_params(handle->handle, hardwareParams);
+ if (err < 0) LOGE("Unable to set hardware parameters: %s", snd_strerror(err));
+
+ done:
+ snd_pcm_hw_params_free(hardwareParams);
+
+ return err;
+}
+
+status_t setSoftwareParams(alsa_handle_t *handle)
+{
+ snd_pcm_sw_params_t * softwareParams;
+ int err;
+
+ snd_pcm_uframes_t bufferSize = 0;
+ snd_pcm_uframes_t periodSize = 0;
+ snd_pcm_uframes_t startThreshold, stopThreshold;
+
+ if (snd_pcm_sw_params_malloc(&softwareParams) < 0) {
+ LOG_ALWAYS_FATAL("Failed to allocate ALSA software parameters!");
+ return NO_INIT;
+ }
+
+ // Get the current software parameters
+ err = snd_pcm_sw_params_current(handle->handle, softwareParams);
+ if (err < 0) {
+ LOGE("Unable to get software parameters: %s", snd_strerror(err));
+ goto done;
+ }
+
+ // Configure ALSA to start the transfer when the buffer is almost full.
+ snd_pcm_get_params(handle->handle, &bufferSize, &periodSize);
+
+ if (handle->devices & AudioSystem::DEVICE_OUT_ALL) {
+ // For playback, configure ALSA to start the transfer when the
+ // buffer is full.
+ startThreshold = bufferSize - 1;
+ stopThreshold = bufferSize;
+ } else {
+ // For recording, configure ALSA to start the transfer on the
+ // first frame.
+ startThreshold = 1;
+ stopThreshold = bufferSize;
+ }
+
+ err = snd_pcm_sw_params_set_start_threshold(handle->handle, softwareParams,
+ startThreshold);
+ if (err < 0) {
+ LOGE("Unable to set start threshold to %lu frames: %s",
+ startThreshold, snd_strerror(err));
+ goto done;
+ }
+
+ err = snd_pcm_sw_params_set_stop_threshold(handle->handle, softwareParams,
+ stopThreshold);
+ if (err < 0) {
+ LOGE("Unable to set stop threshold to %lu frames: %s",
+ stopThreshold, snd_strerror(err));
+ goto done;
+ }
+
+ // Allow the transfer to start when at least periodSize samples can be
+ // processed.
+ err = snd_pcm_sw_params_set_avail_min(handle->handle, softwareParams,
+ periodSize);
+ if (err < 0) {
+ LOGE("Unable to configure available minimum to %lu: %s",
+ periodSize, snd_strerror(err));
+ goto done;
+ }
+
+ // Commit the software parameters back to the device.
+ err = snd_pcm_sw_params(handle->handle, softwareParams);
+ if (err < 0) LOGE("Unable to configure software parameters: %s",
+ snd_strerror(err));
+
+ done:
+ snd_pcm_sw_params_free(softwareParams);
+
+ return err;
+}
+
+// ----------------------------------------------------------------------------
+
+static status_t s_init(alsa_device_t *module, ALSAHandleList &list)
+{
+ list.clear();
+
+ snd_pcm_uframes_t bufferSize = _defaultsOut.bufferSize;
+
+ for (size_t i = 1; (bufferSize & ~i) != 0; i <<= 1)
+ bufferSize &= ~i;
+
+ _defaultsOut.module = module;
+ _defaultsOut.bufferSize = bufferSize;
+
+ list.push_back(_defaultsOut);
+
+ bufferSize = _defaultsIn.bufferSize;
+
+ for (size_t i = 1; (bufferSize & ~i) != 0; i <<= 1)
+ bufferSize &= ~i;
+
+ _defaultsIn.module = module;
+ _defaultsIn.bufferSize = bufferSize;
+
+ list.push_back(_defaultsIn);
+
+ return NO_ERROR;
+}
+
+static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode)
+{
+ // Close off previously opened device.
+ // It would be nice to determine if the underlying device actually
+ // changes, but we might be recovering from an error or manipulating
+ // mixer settings (see asound.conf).
+ //
+ s_close(handle);
+
+ LOGD("open called for devices %08x in mode %d...", devices, mode);
+
+ const char *stream = streamName(handle);
+ const char *devName = deviceName(handle, devices, mode);
+
+ int err;
+
+ for (;;) {
+ // The PCM stream is opened in blocking mode, per ALSA defaults. The
+ // AudioFlinger seems to assume blocking mode too, so asynchronous mode
+ // should not be used.
+ err = snd_pcm_open(&handle->handle, devName, direction(handle),
+ SND_PCM_ASYNC);
+ if (err == 0) break;
+
+ // See if there is a less specific name we can try.
+ // Note: We are changing the contents of a const char * here.
+ char *tail = strrchr(devName, '_');
+ if (!tail) break;
+ *tail = 0;
+ }
+
+ if (err < 0) {
+ // None of the Android defined audio devices exist. Open a generic one.
+ devName = "default";
+ err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
+ }
+
+ if (err < 0) {
+ LOGE("Failed to Initialize any ALSA %s device: %s",
+ stream, strerror(err));
+ return NO_INIT;
+ }
+
+ err = setHardwareParams(handle);
+
+ if (err == NO_ERROR) err = setSoftwareParams(handle);
+
+ LOGI("Initialized ALSA %s device %s", stream, devName);
+
+ handle->curDev = devices;
+ handle->curMode = mode;
+
+ return err;
+}
+
+static status_t s_close(alsa_handle_t *handle)
+{
+ status_t err = NO_ERROR;
+ snd_pcm_t *h = handle->handle;
+ handle->handle = 0;
+ handle->curDev = 0;
+ handle->curMode = 0;
+ if (h) {
+ snd_pcm_drain(h);
+ err = snd_pcm_close(h);
+ }
+
+ return err;
+}
+
+static status_t s_route(alsa_handle_t *handle, uint32_t devices, int mode)
+{
+ LOGD("route called for devices %08x in mode %d...", devices, mode);
+
+ if (handle->handle && handle->curDev == devices && handle->curMode == mode) return NO_ERROR;
+
+ return s_open(handle, devices, mode);
+}
+
+}