summaryrefslogtreecommitdiffstats
path: root/libaudio
diff options
context:
space:
mode:
authorJeong-Seok Yang <jseok.yang@samsung.com>2010-08-25 18:32:24 -0700
committerEd Heyl <edheyl@google.com>2010-08-25 18:52:19 -0700
commitec456383c58adf2d4c4818438a703e5a2ca949b5 (patch)
tree6d7b1ddc2610c37e911917dad5be7ebdebc6c6ad /libaudio
parent1206fad9881e5bca592fcd0fbdafc9d7f195d539 (diff)
downloaddevice_samsung_crespo-ec456383c58adf2d4c4818438a703e5a2ca949b5.zip
device_samsung_crespo-ec456383c58adf2d4c4818438a703e5a2ca949b5.tar.gz
device_samsung_crespo-ec456383c58adf2d4c4818438a703e5a2ca949b5.tar.bz2
S5PC11X: crespo: add alsa-lib, alsa-utils, libaudio, libcamera
Change-Id: I4a6ee248b407c67682eb8884d1176f4807288c7c
Diffstat (limited to 'libaudio')
-rw-r--r--libaudio/Android.mk82
-rw-r--r--libaudio/AudioHardwareALSA.cpp2361
-rw-r--r--libaudio/AudioHardwareALSA.h411
-rw-r--r--libaudio/AudioPolicyManager.cpp1865
-rw-r--r--libaudio/AudioPolicyManager.h218
-rw-r--r--libaudio/AudioPolicyManagerBase.cpp2121
-rw-r--r--libaudio/NOTICE191
7 files changed, 7249 insertions, 0 deletions
diff --git a/libaudio/Android.mk b/libaudio/Android.mk
new file mode 100644
index 0000000..efe16f2
--- /dev/null
+++ b/libaudio/Android.mk
@@ -0,0 +1,82 @@
+# hardware/libaudio-alsa/Android.mk
+#
+# Copyright 2008 Wind River Systems
+#
+
+ifeq ($(filter-out s5pc110 s5pc100 s5p6440,$(TARGET_BOARD_PLATFORM)),)
+ifeq ($(BOARD_USES_GENERIC_AUDIO),false)
+
+ LOCAL_PATH := $(call my-dir)
+
+ include $(CLEAR_VARS)
+
+ 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
+
+# Samsung Feature
+ifeq ($(TARGET_BOARD_PLATFORM),s5pc110)
+ LOCAL_CFLAGS += -DSLSI_S5PC110
+
+# Samsung Driver Feature
+# LOCAL_CFLAGS += -DSEC_SWP_SOUND -DSEC_IPC -DPOWER_GATING -DSYNCHRONIZE_CP -DBT_NR_EC_ONOFF
+ LOCAL_CFLAGS += -DSEC_SWP_SOUND -DSEC_IPC
+ LOCAL_CFLAGS += -DTURN_ON_DEVICE_ONLY_USE
+endif
+
+ifeq ($(TARGET_BOARD_PLATFORM),s5pc100)
+ LOCAL_CFLAGS += -DSLSI_S5PC100
+endif
+
+ LOCAL_C_INCLUDES += external/alsa-lib/include
+
+ LOCAL_SRC_FILES := AudioHardwareALSA.cpp
+
+ LOCAL_MODULE := libaudio
+
+ LOCAL_STATIC_LIBRARIES += libaudiointerface
+
+ LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libmedia \
+ libhardware_legacy \
+ libdl \
+ libc
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_SHARED_LIBRARIES += liba2dp
+endif
+
+ifneq ($(NO_IPC_ALSA_RILD),true)
+ LOCAL_SHARED_LIBRARIES += libsecril-client
+ LOCAL_CFLAGS += -DIPC_ALSA_RILD
+endif
+ include $(BUILD_SHARED_LIBRARY)
+
+# To build audiopolicy library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AudioPolicyManager.cpp \
+ AudioPolicyManagerBase.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libmedia
+
+LOCAL_MODULE:= libaudiopolicy
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_CFLAGS += -DWITH_A2DP
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
+endif
diff --git a/libaudio/AudioHardwareALSA.cpp b/libaudio/AudioHardwareALSA.cpp
new file mode 100644
index 0000000..bb6a8fc
--- /dev/null
+++ b/libaudio/AudioHardwareALSA.cpp
@@ -0,0 +1,2361 @@
+/* AudioHardwareALSA.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 <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.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 <alsa/asoundlib.h>
+#include "AudioHardwareALSA.h"
+
+#if defined SEC_IPC
+// sangsu fix : headers for IPC
+#include <telephony/ril.h>
+#endif
+#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);
+
+#define PERIOD_PLAYBACK 4
+#define PERIOD_CAPTURE 4
+#define PLAYBACK 0
+#define CAPTURE 1
+
+// If you want to dump PCM data, activate this feature
+//#define PCM_INPUT_DUMP
+//#define PCM_OUTPUT_DUMP
+
+#ifdef PCM_INPUT_DUMP
+#define PCM_INPUT_DUMP_PATH "/data/Read_PCM_Dump.dat"
+FILE *fpInput = NULL ;
+#endif
+
+#ifdef PCM_OUTPUT_DUMP
+#define PCM_OUTPUT_DUMP_PATH "/data/Write_PCM_Dump.dat"
+FILE *fpOutput = NULL ;
+#endif
+
+extern "C"
+{
+ extern int ffs(int i);
+
+ //
+ // 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();
+ }
+} // extern "C"
+
+namespace android
+{
+
+#if 0
+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
+#elif defined SEC_SWP_SOUND
+typedef AudioSystem::audio_devices audio_routes;
+#define ROUTE_ALL AudioSystem::DEVICE_OUT_ALL
+#define ROUTE_EARPIECE AudioSystem::DEVICE_OUT_EARPIECE
+#define ROUTE_HEADSET AudioSystem::DEVICE_OUT_WIRED_HEADSET
+#define ROUTE_HEADPHONE AudioSystem::DEVICE_OUT_WIRED_HEADPHONE
+#define ROUTE_SPEAKER AudioSystem::DEVICE_OUT_SPEAKER
+#define ROUTE_BLUETOOTH_SCO AudioSystem::DEVICE_OUT_BLUETOOTH_SCO
+#define ROUTE_BLUETOOTH_SCO_HEADSET AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET
+#define ROUTE_BLUETOOTH_SCO_CARKIT AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+#define ROUTE_BLUETOOTH_A2DP AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP
+#define ROUTE_BLUETOOTH_A2DP_HEADPHONES AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+#define ROUTE_BLUETOOTH_A2DP_SPEAKER AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+#else
+typedef AudioSystem::audio_devices audio_routes;
+#define ROUTE_ALL AudioSystem::DEVICE_OUT_ALL
+#define ROUTE_EARPIECE AudioSystem::DEVICE_OUT_EARPIECE
+#define ROUTE_SPEAKER AudioSystem::DEVICE_OUT_SPEAKER
+#define ROUTE_BLUETOOTH_SCO AudioSystem::DEVICE_OUT_BLUETOOTH_SCO
+#define ROUTE_HEADSET AudioSystem::DEVICE_OUT_WIRED_HEADSET
+#define ROUTE_BLUETOOTH_A2DP AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP
+#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,
+ int err,
+ const char *fmt,
+ ...)
+{
+ char buf[BUFSIZ];
+ va_list arg;
+ int l;
+
+ va_start(arg, fmt);
+ l = snprintf(buf, BUFSIZ, "%s:%i:(%s) ", file, line, function);
+ vsnprintf(buf + l, BUFSIZ - l, fmt, arg);
+ buf[BUFSIZ-1] = '\0';
+ LOGE("ALSALib %s.", buf);
+ va_end(arg);
+}
+
+// ----------------------------------------------------------------------------
+
+/* The following table(s) need to match in order of the route bits
+ */
+#if defined SEC_SWP_SOUND
+static const char *deviceSuffix[] = {
+ // output devices
+ /* ROUTE_EARPIECE */ "_Earpiece",
+ /* ROUTE_SPEAKER */ "_Speaker",
+ /* ROUTE_HEADSET */ "_Headset",
+ /* ROUTE_HEADPHONE */ "_Headset",
+ /* ROUTE_BLUETOOTH_SCO */ "_Bluetooth",
+ /* ROUTE_BLUETOOTH_SCO_HEADSET */ "_Bluetooth",
+ /* ROUTE_BLUETOOTH_SCO_CARKIT */ "_Bluetooth", //"_Bluetooth_Carkit"
+ /* ROUTE_BLUETOOTH_A2DP */ "_Bluetooth", //"_Bluetooth-A2DP"
+ /* ROUTE_BLUETOOTH_A2DP_HEADPHONES */ "_Bluetooth", //"_Bluetooth-A2DP_HeadPhone"
+ /* ROUTE_BLUETOOTH_A2DP_SPEAKER */ "_Bluetooth", // "_Bluetooth-A2DP_Speaker"
+ /* ROUTE_AUX_DIGITAL */ "_AuxDigital",
+ /* ROUTE_TV_OUT */ "_TvOut",
+ /* ROUTE_AUX_DIGITAL */ "_ExtraDockSpeaker",
+ /* ROUTE_NULL */ "_Null",
+ /* ROUTE_NULL */ "_Null",
+ /* ROUTE_DEFAULT */ "_OutDefault",
+
+ // input devices
+ /* ROUTE_COMMUNICATION */ "_Communication",
+ /* ROUTE_AMBIENT */ "_Ambient",
+ /* ROUTE_BUILTIN_MIC */ "_Speaker",
+ /* ROUTE_BLUETOOTH_SCO_HEADSET */ "_Bluetooth",
+ /* ROUTE_WIRED_HEADSET */ "_Headset",
+ /* ROUTE_AUX_DIGITAL */ "_AuxDigital",
+ /* ROUTE_VOICE_CALL */ "_VoiceCall",
+ /* ROUTE_BACK_MIC */ "_BackMic",
+ /* ROUTE_IN_DEFAULT */ "_InDefault",
+};
+#else
+static const char *deviceSuffix[] = {
+ /* ROUTE_EARPIECE */ "_Earpiece",
+ /* ROUTE_SPEAKER */ "_Speaker",
+ /* ROUTE_BLUETOOTH_SCO */ "_Bluetooth",
+ /* ROUTE_HEADSET */ "_Headset",
+ /* ROUTE_BLUETOOTH_A2DP */ "_Bluetooth-A2DP",
+};
+#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 masterPlaybackProp = {
+ ROUTE_ALL, "alsa.mixer.playback.master", "PCM", NULL
+};
+
+static alsa_properties_t masterCaptureProp = {
+ ROUTE_ALL, "alsa.mixer.capture.master", "Capture", NULL
+};
+
+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}
+ },
+ {
+ {static_cast<audio_routes>(0), NULL, NULL, NULL},
+ {static_cast<audio_routes>(0), NULL, NULL, NULL}
+ }
+};
+
+// ----------------------------------------------------------------------------
+
+AudioHardwareALSA::AudioHardwareALSA() :
+ mOutput(0),
+ mInput(0)
+#if defined SEC_IPC
+ ,mIPC(0) //for IPC
+#endif
+#if defined TURN_ON_DEVICE_ONLY_USE
+ ,mActivatedInputDevice(false)
+#endif
+#if defined SYNCHRONIZE_CP
+ ,mActivatedCP(false)
+#endif
+
+{
+ snd_lib_error_set_handler(&ALSAErrorHandler);
+ mMixer = new ALSAMixer;
+#if defined SEC_IPC
+ mIPC = new AudioHardwareIPC; // IPC init
+#endif
+}
+
+AudioHardwareALSA::~AudioHardwareALSA()
+{
+ if (mOutput) delete mOutput;
+ if (mInput) delete mInput;
+ if (mMixer) delete mMixer;
+#if defined SEC_IPC
+ if (mIPC) delete mIPC; // for IPC
+#endif
+}
+
+status_t AudioHardwareALSA::initCheck()
+{
+ if (mMixer && mMixer->isValid())
+ return NO_ERROR;
+ else
+ return NO_INIT;
+}
+
+status_t AudioHardwareALSA::standby()
+{
+ if (mOutput)
+ return mOutput->standby();
+
+ return NO_ERROR;
+}
+
+status_t AudioHardwareALSA::setVoiceVolume(float volume)
+{
+ LOGI("### setVoiceVolume");
+#if defined SEC_IPC
+ // sangsu fix : transmic volume level IPC to modem
+ if (AudioSystem::MODE_IN_CALL == mMode)
+ {
+ uint32_t routes = mRoutes[mMode];
+
+ LOGI("### route(%d) call volume(%f)", routes, volume);
+ switch (routes){
+ case AudioSystem::ROUTE_EARPIECE:
+ case AudioSystem::ROUTE_HEADPHONE: // Use receive path with 3 pole headset.
+ LOGI("### earpiece call volume");
+ mIPC->transmitVolumeIPC(OEM_SOUND_TYPE_VOICE, volume);
+ break;
+
+ case AudioSystem::ROUTE_SPEAKER:
+ LOGI("### speaker call volume");
+ mIPC->transmitVolumeIPC(OEM_SOUND_TYPE_SPEAKER, volume);
+ break;
+
+ case AudioSystem::ROUTE_BLUETOOTH_SCO:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ case AudioSystem::ROUTE_BLUETOOTH_A2DP:
+ LOGI("### bluetooth call volume");
+ mIPC->transmitVolumeIPC(OEM_SOUND_TYPE_BTVOICE, volume);
+ break;
+
+ case AudioSystem::ROUTE_HEADSET:
+ LOGI("### headset call volume");
+ mIPC->transmitVolumeIPC(OEM_SOUND_TYPE_HEADSET, volume);
+ break;
+
+ default:
+ LOGE("### Call volume setting error!!!0x%08x \n", routes);
+ break;
+ }
+ }
+ // sangsu fix end
+#endif
+
+ // The voice volume is used by the VOICE_CALL audio stream.
+ if (mMixer)
+ return mMixer->setVolume(ROUTE_EARPIECE, volume);
+ else
+ return INVALID_OPERATION;
+}
+
+status_t AudioHardwareALSA::setMasterVolume(float volume)
+{
+ if (mMixer)
+ return mMixer->setMasterVolume(volume);
+ else
+ return INVALID_OPERATION;
+}
+
+#if defined TURN_ON_DEVICE_ONLY_USE
+int AudioHardwareALSA::setMicStatus(int on)
+{
+ LOGI("[%s], on=%d", __func__, on);
+ ALSAControl *mALSAControl = new ALSAControl();
+ status_t ret = mALSAControl->set("Mic Status", on);
+ delete mALSAControl;
+ return NO_ERROR;
+}
+#endif
+
+
+#if 0
+AudioStreamOut *
+AudioHardwareALSA::AudioStreamOut* openOutputStream(
+ int format=0,
+ int channelCount=0,
+ uint32_t sampleRate=0,
+ status_t *status=0)
+#else
+AudioStreamOut *
+AudioHardwareALSA::openOutputStream(
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status)
+#endif
+{
+ AutoMutex lock(mLock);
+
+ // only one output stream allowed
+ if (mOutput) {
+ *status = ALREADY_EXISTS;
+ return 0;
+ }
+
+ LOGV("[[[[[[[[\n%s - format = %d, channels = %d, sampleRate = %d, devices = %d]]]]]]]]\n", __func__, *format, *channels, *sampleRate,devices);
+
+ AudioStreamOutALSA *out = new AudioStreamOutALSA(this);
+
+ *status = out->set(format, channels, sampleRate);
+
+#ifdef PCM_OUTPUT_DUMP
+ if(fpOutput == NULL)
+ {
+ fpOutput = fopen(PCM_OUTPUT_DUMP_PATH, "w");
+ if (fpOutput == NULL)
+ LOGE("fpOutput File Open Error!!");
+ }
+#endif
+
+ if (*status == NO_ERROR) {
+ mOutput = out;
+ // Some information is expected to be available immediately after
+ // the device is open.
+ /* Tushar - Sets the current device output here - we may set device here */
+ //uint32_t routes = mRoutes[mMode];
+ //mOutput->setDevice(mMode, routes);
+ LOGI("%s] Setting ALSA device.", __func__);
+ mOutput->setDevice(mMode, devices, PLAYBACK); /* tushar - Enable all devices as of now */
+ }
+ else {
+ delete out;
+ }
+
+ return mOutput;
+}
+
+void
+AudioHardwareALSA::closeOutputStream(AudioStreamOut* out)
+{
+ /* TODO:Tushar: May lead to segmentation fault - check*/
+ //delete out;
+ AutoMutex lock(mLock);
+
+#ifdef PCM_OUTPUT_DUMP
+ fclose(fpOutput);
+ fpOutput = NULL;
+#endif
+
+ if (mOutput == 0 || mOutput != out) {
+ LOGW("Attempt to close invalid output stream");
+ }
+ else {
+ delete mOutput;
+ mOutput = 0;
+ }
+
+}
+
+#if 0
+AudioStreamIn *
+AudioHardwareALSA::openInputStream(int format,
+ int channelCount,
+ uint32_t sampleRate,
+ status_t *status,
+ AudioSystem::audio_in_acoustics acoustics)
+#else
+AudioStreamIn*
+AudioHardwareALSA::openInputStream(
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
+ status_t *status,
+ AudioSystem::audio_in_acoustics acoustics)
+#endif
+{
+ AutoMutex lock(mLock);
+
+ // only one input stream allowed
+ if (mInput) {
+ *status = ALREADY_EXISTS;
+ return 0;
+ }
+
+ AudioStreamInALSA *in = new AudioStreamInALSA(this);
+
+ *status = in->set(format, channels, sampleRate);
+ if (*status == NO_ERROR) {
+ mInput = in;
+ // Some information is expected to be available immediately after
+ // the device is open.
+ //uint32_t routes = mRoutes[mMode];
+ //mInput->setDevice(mMode, routes);
+ mInput->setDevice(mMode, devices, CAPTURE); /* Tushar - as per modified arch */
+#if defined TURN_ON_DEVICE_ONLY_USE
+ mActivatedInputDevice = true;
+ setMicStatus(1);
+
+#ifdef PCM_INPUT_DUMP
+ if(fpInput == NULL)
+ {
+ fpInput = fopen(PCM_INPUT_DUMP_PATH, "w");
+ if (fpInput == NULL)
+ LOGE("fpInput File Open Error!!");
+ }
+#endif
+
+#endif
+ return mInput;
+ }
+ else {
+ delete in;
+ }
+ return mInput;
+}
+
+void
+AudioHardwareALSA::closeInputStream(AudioStreamIn* in)
+{
+ /* TODO:Tushar: May lead to segmentation fault - check*/
+ //delete in;
+ AutoMutex lock(mLock);
+
+ if (mInput == 0 || mInput != in) {
+ LOGW("Attempt to close invalid input stream");
+ }
+ else {
+ delete mInput;
+ mInput = 0;
+#ifdef PCM_INPUT_DUMP
+ fclose(fpInput);
+ fpInput = NULL;
+#endif
+#if defined TURN_ON_DEVICE_ONLY_USE
+ mActivatedInputDevice = false;
+ setMicStatus(0);
+#endif
+
+ }
+}
+
+#if defined SEC_SWP_SOUND
+status_t AudioHardwareALSA::doRouting(uint32_t device)
+{
+ uint32_t routes;
+ status_t ret;
+
+ AutoMutex lock(mLock);
+ int mode = mMode; // Prevent to changing mode on setup sequence.
+
+ if (mOutput) {
+ routes = device;
+ //routes = 0; /* Tushar - temp implementation */
+
+ // Setup sound path for CP clocking
+
+#if defined SEC_IPC
+
+ if (AudioSystem::MODE_IN_CALL == mode)
+ {
+
+ LOGI("### incall mode route (%d)", routes);
+ switch(routes){
+ case AudioSystem::ROUTE_EARPIECE:
+ LOGI("### incall mode earpiece route");
+ mIPC->transmitAudioPathIPC(OEM_SOUND_AUDIO_PATH_HANDSET);
+ break;
+
+ case AudioSystem::ROUTE_SPEAKER:
+ LOGI("### incall mode speaker route");
+ mIPC->transmitAudioPathIPC(OEM_SOUND_AUDIO_PATH_SPEAKER);
+ break;
+
+ case AudioSystem::ROUTE_BLUETOOTH_SCO:
+ case AudioSystem::ROUTE_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::ROUTE_BLUETOOTH_SCO_CARKIT:
+#if defined BT_NR_EC_ONOFF
+ if(mBluetoothECOff)
+ {
+ LOGI("### incall mode bluetooth EC OFF route");
+ mIPC->transmitAudioPathIPC(OEM_SOUND_AUDIO_PATH_BT_NSEC_OFF);
+ }
+ else
+ {
+#endif
+ LOGI("### incall mode bluetooth route");
+ mIPC->transmitAudioPathIPC(OEM_SOUND_AUDIO_PATH_BLUETOOTH);
+#if defined BT_NR_EC_ONOFF
+ }
+#endif
+ break;
+
+ case AudioSystem::ROUTE_HEADSET :
+ case AudioSystem::ROUTE_HEADPHONE :
+ LOGI("### incall mode headset route");
+ mIPC->transmitAudioPathIPC(OEM_SOUND_AUDIO_PATH_HEADSET);
+ break;
+
+ case AudioSystem::ROUTE_BLUETOOTH_A2DP:
+ LOGI("### incall mode bluetooth route");
+ mIPC->transmitAudioPathIPC(OEM_SOUND_AUDIO_PATH_BLUETOOTH);
+ break;
+
+ default:
+ LOGE("### incall mode Error!! route = [%d]", routes);
+ break;
+ }
+ }
+#endif// end of #if defined SEC_IPC
+
+ ret = mOutput->setDevice(mode, routes, PLAYBACK);
+
+#if defined SEC_IPC
+ if (AudioSystem::MODE_IN_CALL == mode)
+ {
+#if defined SYNCHRONIZE_CP
+ if(!mActivatedCP)
+ {
+ mIPC->transmitClock_IPC(OEM_SOUND_CLOCK_START);
+ mActivatedCP = true;
+ }
+#endif
+ }
+
+ if (AudioSystem::MODE_NORMAL== mode) // Call stop.
+ {
+#if defined SYNCHRONIZE_CP
+ if(mActivatedCP)
+ mActivatedCP = false;
+#endif
+
+ }
+#endif // end of #if defined SEC_IPC
+
+#ifndef SYNCHRONIZE_CP
+ ret = mOutput->setDevice(mode, routes, PLAYBACK);
+#endif
+ return ret;
+ }
+
+ return NO_INIT;
+}
+
+#else
+/** This function is no more used */
+status_t AudioHardwareALSA::doRouting()
+{
+ uint32_t routes;
+ AutoMutex lock(mLock);
+
+ LOGD("Inside AudioHardwareALSA::doRouting \n");
+ if (mOutput) {
+ //routes = mRoutes[mMode];
+ routes = 0; /* Tushar - temp implementation */
+ return mOutput->setDevice(mMode, routes, PLAYBACK);
+ }
+ return NO_INIT;
+
+}
+#endif
+
+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() :
+ mHandle(0),
+ mHardwareParams(0),
+ mSoftwareParams(0),
+ mMode(-1),
+ mDevice(0)
+{
+ 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 *pformat,
+ uint32_t *pchannels,
+ uint32_t *prate)
+{
+ int lformat = pformat ? *pformat : 0;
+ unsigned int lchannels = pchannels ? *pchannels : 0;
+ unsigned int lrate = prate ? *prate : 0;
+
+
+ LOGD("ALSAStreamOps - input - format = %d, channels = %d, rate = %d\n", lformat, lchannels, lrate);
+ LOGD("ALSAStreamOps - default - format = %d, channels = %d, rate = %d\n", mDefaults->format, mDefaults->channels, mDefaults->sampleRate);
+
+ if(lformat == 0) lformat = getAndroidFormat(mDefaults->format);//format();
+ if(lchannels == 0) lchannels = getAndroidChannels(mDefaults->channels);// channelCount();
+ if(lrate == 0) lrate = mDefaults->sampleRate;
+
+ if((lformat != getAndroidFormat(mDefaults->format)) ||
+ (lchannels != getAndroidChannels(mDefaults->channels)) ||
+ (lrate != mDefaults->sampleRate)){
+ if(pformat) *pformat = getAndroidFormat(mDefaults->format);
+ if(pchannels) *pchannels = getAndroidChannels(mDefaults->channels);
+ if(prate) *prate = mDefaults->sampleRate;
+ return BAD_VALUE;
+ }
+
+ if(pformat) *pformat = getAndroidFormat(mDefaults->format);
+ if(pchannels) *pchannels = getAndroidChannels(mDefaults->channels);
+ if(prate) *prate = mDefaults->sampleRate;
+
+ return NO_ERROR;
+}
+
+
+uint32_t ALSAStreamOps::sampleRate() const
+{
+ unsigned int rate;
+ int err;
+
+ if (! mHandle)
+ return NO_INIT;
+
+ 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 {
+ LOGD("Set %s sample rate to %u HZ", stream, requestedRate);
+ }
+ return NO_ERROR;
+}
+
+//
+// Return the number of bytes (not frames)
+//
+size_t ALSAStreamOps::bufferSize() const
+{
+ int err;
+
+ if (!mHandle)
+ return -1;
+
+ snd_pcm_uframes_t bufferSize = 0;
+ snd_pcm_uframes_t periodSize = 0;
+
+ err = snd_pcm_get_params(mHandle, &bufferSize, &periodSize);
+
+ if (err < 0)
+ return -1;
+
+ return static_cast<size_t>(snd_pcm_frames_to_bytes(mHandle, bufferSize));
+}
+
+int ALSAStreamOps::getAndroidFormat(snd_pcm_format_t format)
+{
+ int pcmFormatBitWidth;
+ int audioSystemFormat;
+
+ pcmFormatBitWidth = snd_pcm_format_physical_width(format);
+ 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::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;
+}
+
+uint32_t ALSAStreamOps::getAndroidChannels(int channels)
+{
+ int AudioSystemChannels = AudioSystem::DEFAULT;
+
+ switch(channels){
+ case 1:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_FRONT_RIGHT;
+ break;
+ case 2:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ break;
+ case 4:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_QUAD;
+ break;
+ case 6:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_5POINT1;
+ break;
+ defualt:
+ LOGE("FATAL: AudioSystem does not support %d channels.", channels);
+ }
+ return AudioSystemChannels;
+}
+
+int ALSAStreamOps::channelCount() const
+{
+ unsigned int val;
+ int err;
+
+ int AudioSystemChannels;
+
+ if (!mHandle)
+ return -1;
+
+ err = snd_pcm_hw_params_get_channels(mHardwareParams, &val);
+ if (err < 0) {
+ LOGE("Unable to get device channel count: %s",
+ snd_strerror(err));
+ return -1;
+ }
+
+ AudioSystemChannels = AudioSystem::DEFAULT;
+
+ switch(val){
+ case 1:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_FRONT_RIGHT;
+ break;
+ case 2:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ break;
+ case 4:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_QUAD;
+ break;
+ case 6:
+ AudioSystemChannels = AudioSystem::CHANNEL_OUT_5POINT1;
+ break;
+ defualt:
+ LOGE("FATAL: AudioSystem does not support %d channels.", val);
+ }
+
+
+ return AudioSystemChannels;
+}
+
+status_t ALSAStreamOps::channelCount(int channels) {
+ int err;
+
+ if (!mHandle)
+ return NO_INIT;
+
+ // if(channels == 1) channels = 2; //Kamat: This is a fix added to avoid audioflinger crash (current audio driver does not support mono). Please check and modify suitably later.
+
+ 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;
+ }
+
+ LOGD("Using %i %s for %s.",
+ channels, channels == 1 ? "channel" : "channels", streamName());
+
+ return NO_ERROR;
+}
+
+status_t ALSAStreamOps::open(int mode, uint32_t device)
+{
+ const char *stream = streamName();
+ const char *devName = deviceName(mode, device);
+
+ int err;
+
+ LOGI("Try to open ALSA %s device %s", stream, devName);
+
+ 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 = "hw:00,1"; // 090507 SMDKC110 Froyo
+
+ 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);
+ mMode = -1;
+ mDevice = 0;
+ }
+}
+
+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;
+
+ // Configure ALSA to start the transfer when the buffer is almost full.
+ snd_pcm_get_params(mHandle, &bufferSize, &periodSize);
+ LOGE("bufferSize %d, periodSize %d\n", bufferSize, periodSize);
+
+ if (mDefaults->direction == SND_PCM_STREAM_PLAYBACK) {
+ // For playback, configure ALSA to start the transfer when the
+ // buffer is almost full.
+ startThreshold = (bufferSize / periodSize) * periodSize;
+ //startThreshold = 1;
+ }
+ else {
+ // For recording, configure ALSA to start the transfer on the
+ // first frame.
+ startThreshold = 1;
+ }
+
+ 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;
+ }
+
+ // Stop the transfer when the buffer is full.
+ err = snd_pcm_sw_params_set_stop_threshold(mHandle,
+ mSoftwareParams,
+ bufferSize);
+ if (err < 0) {
+ LOGE("Unable to set stop threshold to %lu frames: %s",
+ bufferSize, 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;
+ }
+
+ LOGD("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, uint audio_mode)
+{
+ // 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();
+
+
+ LOGD("\n------------------------>>>>>> ALSA OPEN mode %d,device %d \n",mode,device);
+
+ status_t status = open (mode, device);
+ int err;
+ unsigned int period_val;
+
+ 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;
+ }
+
+ status = setPCMFormat(mDefaults->format);
+
+ // 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;
+ }
+
+ //
+ // 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);
+
+ // Disable hardware resampling.
+ status = setHardwareResample(false);
+ if (status != NO_ERROR)
+ return status;
+
+ snd_pcm_uframes_t bufferSize = mDefaults->bufferSize;
+
+ unsigned int latency = mDefaults->latency;
+
+ // 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;
+ }
+
+ // Setup buffers for latency
+ err = snd_pcm_hw_params_set_buffer_time_near (mHandle, mHardwareParams,
+ &latency, NULL);
+ if(audio_mode == PLAYBACK) {
+ period_val = PERIOD_PLAYBACK;
+ if(snd_pcm_hw_params_set_periods(mHandle, mHardwareParams, period_val, 0) < 0)
+ LOGE("Fail to set period size %d for playback", period_val);
+ }
+ else
+ period_val = PERIOD_CAPTURE;
+
+ if (err < 0) {
+ /* That didn't work, set the period instead */
+ unsigned int periodTime = latency / period_val;
+ 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 * period_val;
+ 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 / period_val;
+ 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;
+ }
+ }
+
+ LOGD("Buffer size: %d", (int)bufferSize);
+ LOGD("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 dev;
+ int hasDevExt = 0;
+
+ strcpy (devString, mDefaults->devicePrefix);
+
+ for (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) :
+ mParent(parent),
+ mPowerLock(false)
+{
+ 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 : 250000, // Desired Delay in usec
+ bufferSize : 4096, // Desired Number of samples
+};
+
+ setStreamDefaults(&_defaults);
+}
+
+AudioStreamOutALSA::~AudioStreamOutALSA()
+{
+ standby();
+ mParent->mOutput = NULL;
+}
+
+//int AudioStreamOutALSA::channelCount() const
+uint32_t AudioStreamOutALSA::channels() const
+{
+ uint32_t c = ALSAStreamOps::channelCount();
+
+ // AudioMixer will seg fault if it doesn't have two channels.
+ LOGW_IF(c != AudioSystem::CHANNEL_OUT_STEREO,
+ "AudioMixer expects two channels, but only %i found!", c);
+ return c;
+}
+
+/* New arch */
+status_t AudioStreamOutALSA::setVolume(float left, float right)
+{
+ if (! mParent->mMixer || ! mDevice)
+ return NO_INIT;
+
+ /** Tushar - Need to decide on the volume value
+ * that we pass onto the mixer. */
+ return mParent->mMixer->setVolume (mDevice, (left + right)/2);
+}
+
+status_t AudioStreamOutALSA::setVolume(float volume)
+{
+ if (! mParent->mMixer || ! mDevice)
+ return NO_INIT;
+
+ return mParent->mMixer->setVolume (mDevice, volume);
+}
+
+/* New Arch */
+status_t AudioStreamOutALSA::setParameters(const String8& keyValuePairs)
+{
+#if defined SLSI_S5PC110
+ AudioParameter param = AudioParameter(keyValuePairs);
+ status_t status = NO_ERROR;
+ int device;
+ int value;
+ LOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string());
+
+ if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR)
+ {
+ mDevice = device;
+
+ if (mParent->mInput) mParent->mInput->mDevice = device;
+ mParent->mRoutes[mParent->mMode] = mDevice;
+ mParent->doRouting(mDevice);
+
+ param.remove(String8(AudioParameter::keyRouting));
+ }
+ else if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR)
+ {
+ mParent->mOutput->mDefaults->sampleRate = value;
+ mParent->doRouting(mDevice);
+ param.remove(String8(AudioParameter::keySamplingRate));
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+#else
+ /* TODO: Implement as per new arch */
+
+ LOGD("AudioStreamOutAlsa::setParameters... %s \n\n",keyValuePairs.string());
+ if (! mParent->mOutput )//|| ! mMode)
+ return NO_INIT;
+
+
+ int device = keyValuePairs.string()[keyValuePairs.length()-1] - 48 -1 ; //easy conversion frm ascii to int and then to required number
+ LOGV("\n\n-------->> ALSA SET PARAMS device %d \n\n",(1<<device));
+ mParent->mOutput->setDevice(mMode, 1<<device, PLAYBACK);
+ return NO_ERROR;
+#endif
+}
+String8 AudioStreamOutALSA::getParameters(const String8& keys)
+{
+#if defined SLSI_S5PC110
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGD("AudioStreamOutALSA::getParameters() %s", param.toString().string());
+ return param.toString();
+#else
+ /* TODO: Implement as per new arch */
+ return keys;
+#endif
+}
+
+status_t AudioStreamOutALSA::getRenderPosition(uint32_t *dspFrames)
+{
+
+ //TODO: enable when supported by driver
+ return INVALID_OPERATION;
+}
+
+#if 1 // Fix for underrun error
+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) {
+ ALSAStreamOps::setDevice(mMode, mDevice, PLAYBACK);
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
+ mPowerLock = true;
+ }
+ // if (isStandby())
+ // return 0;
+
+#ifdef PCM_OUTPUT_DUMP
+ fwrite(buffer, bytes, 1, fpOutput);
+ LOGD("Output PCM dumped!!");
+#endif
+ if (!mHandle){
+ LOGD("Calling setDevice from write @..%d.\n",__LINE__);
+ ALSAStreamOps::setDevice(mMode, mDevice, PLAYBACK);
+ }
+ do {
+ // write correct number of bytes per attempt
+ n = snd_pcm_writei(mHandle,
+ (char *)buffer + sent,
+ snd_pcm_bytes_to_frames(mHandle, bytes-sent));
+ if (n == -EBADFD) {
+ LOGD("Calling setDevice.. pcm_write returned error @..%d.\n",__LINE__);
+ // 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, PLAYBACK);
+ }
+ 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.
+ // We can make silent bit on as we are now handling the under-run and there will not be any data loss due to under-run
+ 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);
+ //LOGI("Request Bytes=%d, Actual Written=%d",bytes,sent);
+ return sent;
+}
+#else
+ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
+{
+ snd_pcm_sframes_t n;
+ status_t err;
+
+ AutoMutex lock(mLock);
+#if 0
+ if (isStandby())
+ return 0;
+#endif
+ if (!mPowerLock) {
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioLock");
+ ALSAStreamOps::setDevice(mMode, mDevice,PLAYBACK);
+ mPowerLock = true;
+ }
+
+ n = snd_pcm_writei(mHandle,
+ buffer,
+ snd_pcm_bytes_to_frames(mHandle, bytes));
+ if (n < 0 && mHandle) {
+ // snd_pcm_recover() will return 0 if successful in recovering from
+ // an error, or -errno if the error was unrecoverable.
+ //device driver sometimes does not recover -vladi
+ n = snd_pcm_recover(mHandle, n, 0);
+ if(n < 0) //if recover fails
+ ALSAStreamOps::setDevice(mMode, mDevice, PLAYBACK);
+ }
+
+ return static_cast<ssize_t>(n);
+}
+#endif
+
+
+
+status_t AudioStreamOutALSA::dump(int fd, const Vector<String16>& args)
+{
+ return NO_ERROR;
+}
+
+status_t AudioStreamOutALSA::setDevice(int mode, uint32_t newDevice, uint32_t audio_mode)
+{
+ AutoMutex lock(mLock);
+
+ return ALSAStreamOps::setDevice(mode, newDevice, audio_mode);
+}
+
+status_t AudioStreamOutALSA::standby()
+{
+ AutoMutex lock(mLock);
+ LOGD("Inside AudioStreamOutALSA::standby\n");
+ if (mHandle)
+ snd_pcm_drain (mHandle);
+
+ if (mPowerLock) {
+ if(!mParent->mActivatedInputDevice){ // Let PCM device alive on activating input stream.
+ snd_pcm_close(mHandle);
+ mHandle = NULL;
+#if 1 // Fix for underrun error
+ release_wake_lock ("AudioOutLock");
+#else
+ release_wake_lock ("AudioLock");
+#endif
+ mPowerLock = false;
+ }
+ }
+// close(); //Don't call this as this function will reset the mode also
+ return NO_ERROR;
+}
+
+bool AudioStreamOutALSA::isStandby()
+{
+ return (!mHandle);
+}
+
+#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) :
+ mParent(parent),
+ mPowerLock(false)
+{
+ 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 : 4096, // Desired Number of samples
+ };
+
+ setStreamDefaults(&_defaults);
+}
+
+AudioStreamInALSA::~AudioStreamInALSA()
+{
+ if (mPowerLock) {
+ snd_pcm_close(mHandle);
+ mHandle = NULL;
+ release_wake_lock ("AudioInLock");
+ mPowerLock = false;
+ }
+ 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;
+ status_t err;
+
+ AutoMutex lock(mLock);
+
+ if (!mPowerLock) {
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock");
+
+#ifdef PCM_INPUT_DUMP
+ fwrite(buffer, readBytes, 1, fpInput);
+ LOGD("Input PCM dumped!!");
+#endif
+
+ LOGD("Calling setDevice from read@..%d.\n",__LINE__);
+ ALSAStreamOps::setDevice(mMode, mDevice,CAPTURE);
+ mPowerLock = true;
+ }
+ n = snd_pcm_readi(mHandle,
+ buffer,
+ snd_pcm_bytes_to_frames(mHandle, bytes));
+ if (n < 0 && mHandle) {
+ n = snd_pcm_recover(mHandle, n, 0);
+ }
+
+#ifdef PCM_INPUT_DUMP
+ fwrite(buffer, bytes, 1, fpInput);
+ LOGD("Input PCM dumped!!");
+#endif
+
+ return static_cast<ssize_t>(n);
+}
+
+status_t AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
+{
+ return NO_ERROR;
+}
+
+status_t AudioStreamInALSA::setDevice(int mode, uint32_t newDevice, uint32_t audio_mode)
+{
+ AutoMutex lock(mLock);
+
+ return ALSAStreamOps::setDevice(mode, newDevice, audio_mode);
+}
+
+status_t AudioStreamInALSA::standby()
+{
+ AutoMutex lock(mLock);
+
+ LOGD("Entering AudioStreamInALSA::standby\n");
+ if (mPowerLock) {
+ mParent->mActivatedInputDevice = false;
+ snd_pcm_close(mHandle);
+ mHandle = NULL;
+ release_wake_lock ("AudioInLock");
+ mPowerLock = false;
+ }
+
+ return NO_ERROR;
+}
+
+/* New Arch */
+status_t AudioStreamInALSA::setParameters(const String8& keyValuePairs)
+{
+#if defined SLSI_S5PC110
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ status_t status = NO_ERROR;
+ int device;
+ LOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string());
+
+ if (param.getInt(key, device) == NO_ERROR) {
+ mDevice = device;
+ if(mDevice != 0)
+ setDevice(mMode, mDevice, CAPTURE);
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+#else
+ /* TODO: Implement as per new arch */
+
+ if (! mParent->mInput )//|| ! mMode)
+ return NO_INIT;
+
+ // yman.seo use setDevice temp.
+ int device = keyValuePairs.string()[keyValuePairs.length()-1] - 48 -1 ; //easy conversion frm ascii to int and then to required number
+ LOGD("\n\n-------->> ALSA AudioStreamIn SET PARAMS device %d \n\n",(1<<device));
+ if(mParent->mActivatedInputDevice )//Recording stopped with Alarm ,then don't call setDevice() of record, just return
+ return mParent->mInput->setDevice(mMode, 1<<device, CAPTURE);
+
+ return NO_ERROR;
+// yman.seo return NO_ERROR;
+#endif
+}
+
+String8 AudioStreamInALSA::getParameters(const String8& keys)
+{
+#if defined SLSI_S5PC110
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGD("AudioStreamInALSA::getParameters() %s", param.toString().string());
+ return param.toString();
+#else
+ /* TODO: Implement as per new arch */
+ return keys;
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+
+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++) {
+
+ 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 (hasVolume[i] (elem))
+ LOGD ("Mixer: element name: '%s'", elementName);
+
+ 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;
+ }
+ }
+
+ LOGD ("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;
+ }
+ }
+ LOGD ("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
+ }
+ }
+ LOGD("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;
+ }
+ }
+ }
+ LOGD("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);
+ }
+
+ return NO_ERROR;
+}
+
+status_t ALSAMixer::setCaptureMuteState(uint32_t device, bool state)
+{
+ 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;
+}
+
+status_t ALSAMixer::getCaptureMuteState(uint32_t device, 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;
+ }
+
+ return BAD_VALUE;
+}
+
+status_t ALSAMixer::setPlaybackMuteState(uint32_t device, bool state)
+{
+
+ LOGE("\n set playback mute device %d, state %d \n", device,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;
+ }
+
+ 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)
+{
+ 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_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) 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_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) 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;
+}
+
+// ----------------------------------------------------------------------------
+
+#if defined SEC_IPC
+AudioHardwareIPC::AudioHardwareIPC() :
+ mClient(NULL)
+{
+LOGD("### %s", __func__);
+ int err = 0;
+
+ mClient = OpenClient_RILD();
+ if (mClient == NULL){
+ LOGE("[*] OpenClient_RILD() error\n");
+ err = 1;
+ }
+
+ if (RegisterRequestCompleteHandler(mClient, RIL_REQUEST_OEM_HOOK_RAW,
+ onRawReqComplete) != RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] RegisterRequestCompleteHandler() error\n");
+ err = 1;
+ }
+
+ if (RegisterUnsolicitedHandler(mClient, 11004, onUnsol) !=
+ RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] RegisterUnsolicitedHandler() error\n");
+ err = 1;
+ }
+
+ if (!err) LOGD("Success Initializing IPC");
+ else LOGE("Failed Initializing IPC");
+}
+
+AudioHardwareIPC::~AudioHardwareIPC()
+{
+ LOGD("### %s", __func__);
+ if (RegisterRequestCompleteHandler(mClient, RIL_REQUEST_OEM_HOOK_RAW, NULL) != RIL_CLIENT_ERR_SUCCESS)
+ LOGE("RegisterRequestCompleteHandler(NULL) error\n");
+
+ if (RegisterUnsolicitedHandler(mClient, 11004, NULL) != RIL_CLIENT_ERR_SUCCESS)
+ LOGE("RegisterUnsolicitedHandler(NULL) error\n");
+
+ if (Disconnect_RILD(mClient) != RIL_CLIENT_ERR_SUCCESS)
+ LOGE("[*] Disconnect_RILD() error\n");
+
+ if (CloseClient_RILD(mClient) != RIL_CLIENT_ERR_SUCCESS)
+ LOGE("[*] CloseClient_RILD() error\n");
+ mClient = NULL;
+}
+
+status_t AudioHardwareIPC::transmitVolumeIPC(uint32_t type, float volume)
+{
+ int ret = 0;
+ uint32_t level = (uint32_t)(volume * 5);
+
+ if (isConnected_RILD(mClient) == 0) {
+ if (Connect_RILD(mClient) != RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] Connect_RILD() error\n");
+ return INVALID_OPERATION;
+ }
+ }
+
+ memset(data, 0, 100);
+ data[0] = OEM_FUNCTION_ID_SOUND;
+ data[1] = OEM_SOUND_SET_VOLUME_CTRL;
+ data[2] = 0x00; // data length
+ data[3] = 0x06; // data length
+ data[4] = type; // volume type
+ data[5] = level; // volume level
+
+ ret = InvokeOemRequestHookRaw(mClient, data, 6); //sizeof(data));
+ if (ret != RIL_CLIENT_ERR_AGAIN && ret != RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] InvokeOemRequestHookRaw() error ret = %d\n", ret);
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioHardwareIPC::transmitAudioPathIPC(uint32_t path)
+{
+ int ret = 0;
+
+ LOGI("### %s %d ", __func__, path);
+ if (isConnected_RILD(mClient) == 0) {
+ if (Connect_RILD(mClient) != RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] Connect_RILD() error\n");
+ return INVALID_OPERATION;
+ }
+ }
+
+ memset(data, 0, 100);
+ data[0] = OEM_FUNCTION_ID_SOUND;
+ data[1] = OEM_SOUND_SET_AUDIO_PATH_CTRL;
+ data[2] = 0x00; // data length
+ data[3] = 0x05; // data length
+ data[4] = path; // audio path
+
+ ret = InvokeOemRequestHookRaw(mClient, data, 5); //sizeof(data));
+ if (ret != RIL_CLIENT_ERR_AGAIN && ret != RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] InvokeOemRequestHookRaw() error ret = %d\n", ret);
+ return INVALID_OPERATION;
+ }
+ return NO_ERROR;
+}
+
+
+#if defined SYNCHRONIZE_CP
+status_t AudioHardwareIPC::transmitClock_IPC(uint32_t condition)
+{
+ int ret = 0;
+
+ LOGV("### %s %d ", __func__, condition);
+
+ if (isConnected_RILD(mClient) == 0) {
+ if (Connect_RILD(mClient) != RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] Connect_RILD() error\n");
+ return INVALID_OPERATION;
+ }
+ }
+
+ memset(data, 0, 100);
+ data[0] = OEM_FUNCTION_ID_SOUND;
+ data[1] = OEM_SOUND_SET_CLOCK_CTRL;
+ data[2] = 0x00; // data length
+ data[3] = 0x05; // data length
+ data[4] = condition;
+
+ ret = InvokeOemRequestHookRaw(mClient, data, 5); //sizeof(data));
+
+ if (ret != RIL_CLIENT_ERR_AGAIN && ret != RIL_CLIENT_ERR_SUCCESS){
+ LOGE("[*] InvokeOemRequestHookRaw() error ret = %d\n", ret);
+ return INVALID_OPERATION;
+ }
+
+ return NO_ERROR;
+}
+#endif
+
+static int onRawReqComplete(HRilClient client, const void *data, size_t datalen)
+{
+ LOGV("[*] %s(): datalen(%d)\n", __FUNCTION__, datalen);
+ return 0;
+}
+static int onUnsol(HRilClient client, const void *data, size_t datalen)
+{
+ int a;
+ a = ((int *)data)[0];
+ LOGV("%s(): a(%d)\n", __FUNCTION__, a);
+
+ return 0;
+}
+#endif //SEC_IPC
+}; // namespace android
+
diff --git a/libaudio/AudioHardwareALSA.h b/libaudio/AudioHardwareALSA.h
new file mode 100644
index 0000000..690b4db
--- /dev/null
+++ b/libaudio/AudioHardwareALSA.h
@@ -0,0 +1,411 @@
+/* AudioHardwareALSA.h
+ **
+ ** Copyright 2008, 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.
+ */
+
+#ifndef ANDROID_AUDIO_HARDWARE_ALSA_H
+#define ANDROID_AUDIO_HARDWARE_ALSA_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <alsa/asoundlib.h>
+
+#include <hardware_legacy/AudioHardwareBase.h>
+
+#if defined SEC_IPC
+#include <hardware/hardware.h>
+
+// sangsu fix : headers for IPC
+#include "../libsecril-client/secril-client.h"
+
+// sangsu fix : defines for IPC
+#define OEM_FUNCTION_ID_SOUND 0x08 // sound Main Cmd
+
+//sangsu fix : sound sub command for IPC
+#define OEM_SOUND_SET_VOLUME_CTRL 0x03
+#define OEM_SOUND_GET_VOLUME_CTRL 0x04
+#define OEM_SOUND_SET_AUDIO_PATH_CTRL 0x05
+#define OEM_SOUND_GET_AUDIO_PATH_CTRL 0x06
+
+//sangsu fix : audio path for IPC
+#define OEM_SOUND_AUDIO_PATH_HANDSET 0x01
+#define OEM_SOUND_AUDIO_PATH_HEADSET 0x02
+#define OEM_SOUND_AUDIO_PATH_HANDFREE 0x03
+#define OEM_SOUND_AUDIO_PATH_BLUETOOTH 0x04
+#define OEM_SOUND_AUDIO_PATH_STREOBT 0x05
+#define OEM_SOUND_AUDIO_PATH_SPEAKER 0x06
+#define OEM_SOUND_AUDIO_PATH_HEADSET35 0x07
+#define OEM_SOUND_AUDIO_PATH_BT_NSEC_OFF 0x08
+
+// sangsu fix : volume level for IPC
+#define OEM_SOUND_VOLUME_LEVEL_MUTE 0x00
+#define OEM_SOUND_VOLUME_LEVEL1 0x01
+#define OEM_SOUND_VOLUME_LEVEL2 0x02
+#define OEM_SOUND_VOLUME_LEVEL3 0x03
+#define OEM_SOUND_VOLUME_LEVEL4 0x04
+#define OEM_SOUND_VOLUME_LEVEL5 0x05
+#define OEM_SOUND_VOLUME_LEVEL6 0x06
+#define OEM_SOUND_VOLUME_LEVEL7 0x07
+#define OEM_SOUND_VOLUME_LEVEL8 0x08
+
+// For synchronizing I2S clocking
+#if defined SYNCHRONIZE_CP
+#define OEM_SOUND_SET_CLOCK_CTRL 0x0A
+#define OEM_SOUND_CLOCK_START 0x01
+#define OEM_SOUND_CLOCK_STOP 0x00
+#endif
+
+// For VT
+#if defined VIDEO_TELEPHONY
+#define OEM_SOUND_VIDEO_CALL_STOP 0x00
+#define OEM_SOUND_VIDEO_CALL_START 0x01
+#define OEM_SOUND_SET_VIDEO_CALL_CTRL 0x07
+#endif
+
+// sangsu fix : volume type for IPC
+#define OEM_SOUND_TYPE_VOICE 0x01 // Receiver(0x00) + Voice(0x01)
+#define OEM_SOUND_TYPE_KEYTONE 0x02 // Receiver(0x00) + Key tone (0x02)
+#define OEM_SOUND_TYPE_BELL 0x03 // Receiver(0x00) + Bell (0x03)
+#define OEM_SOUND_TYPE_MESSAGE 0x04 // Receiver(0x00) + Message(0x04)
+#define OEM_SOUND_TYPE_ALARM 0x05 // Receiver(0x00) + Alarm (0x05)
+#define OEM_SOUND_TYPE_SPEAKER 0x11 // SpeakerPhone (0x10) + Voice(0x01)
+#define OEM_SOUND_TYPE_HFKVOICE 0x21 // HFK (0x20) + Voice(0x01)
+#define OEM_SOUND_TYPE_HFKKEY 0x22 // HFK (0x20) + Key tone (0x02)
+#define OEM_SOUND_TYPE_HFKBELL 0x23 // HFK (0x20) + Bell (0x03)
+#define OEM_SOUND_TYPE_HFKMSG 0x24 // HFK (0x20) + Message(0x04)
+#define OEM_SOUND_TYPE_HFKALARM 0x25 // HFK (0x20) + Alarm (0x05)
+#define OEM_SOUND_TYPE_HFKPDA 0x26 // HFK (0x20) + PDA miscellaneous sound (0x06)
+#define OEM_SOUND_TYPE_HEADSET 0x31 // Headset (0x30) + Voice(0x01)
+#define OEM_SOUND_TYPE_BTVOICE 0x41 // BT(0x40) + Voice(0x01)
+#endif
+namespace android
+{
+
+ class AudioHardwareALSA;
+
+ // ----------------------------------------------------------------------------
+
+ 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 volume);
+ 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 = "default");
+ 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);
+
+ private:
+ snd_ctl_t *mHandle;
+ };
+
+ 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();
+ virtual ~ALSAStreamOps();
+
+ status_t set(int *format,
+ uint32_t *channels,
+ uint32_t *rate);
+ virtual uint32_t sampleRate() const;
+ status_t sampleRate(uint32_t rate);
+ virtual size_t bufferSize() const;
+ virtual int format() const;
+ int getAndroidFormat(snd_pcm_format_t format);
+
+ virtual int channelCount() const;
+ status_t channelCount(int channels);
+ uint32_t getAndroidChannels(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);
+
+ const char *streamName();
+ virtual status_t setDevice(int mode, uint32_t device, uint32_t audio_mode);
+
+ const char *deviceName(int mode, uint32_t device);
+
+ void setStreamDefaults(StreamDefaults *dev) {
+ mDefaults = dev;
+ }
+
+ Mutex mLock;
+
+ private:
+ snd_pcm_t *mHandle;
+ snd_pcm_hw_params_t *mHardwareParams;
+ snd_pcm_sw_params_t *mSoftwareParams;
+ int mMode;
+ uint32_t mDevice;
+
+ StreamDefaults *mDefaults;
+ };
+
+ // ----------------------------------------------------------------------------
+
+ class AudioStreamOutALSA : public AudioStreamOut, public ALSAStreamOps
+ {
+ public:
+ AudioStreamOutALSA(AudioHardwareALSA *parent);
+ virtual ~AudioStreamOutALSA();
+
+
+ status_t set(int *format,
+ uint32_t *channelCount,
+ uint32_t *sampleRate){
+ return ALSAStreamOps::set(format, channelCount, sampleRate);
+ }
+
+ virtual uint32_t sampleRate() const
+ {
+ return ALSAStreamOps::sampleRate();
+ }
+
+ virtual size_t bufferSize() const
+ {
+ return ALSAStreamOps::bufferSize();
+ }
+
+ //virtual int channelCount() const;
+ virtual uint32_t channels() const;
+
+ virtual int format() const
+ {
+ return ALSAStreamOps::format();
+ }
+
+ virtual uint32_t latency() const;
+
+ virtual ssize_t write(const void *buffer, size_t bytes);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setDevice(int mode, uint32_t newDevice, uint32_t audio_mode);
+ virtual status_t setVolume(float left, float right); //Tushar: New arch
+
+ status_t setVolume(float volume);
+
+ status_t standby();
+ bool isStandby();
+
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ virtual status_t getRenderPosition(uint32_t *dspFrames);
+
+
+ private:
+ AudioHardwareALSA *mParent;
+ bool mPowerLock;
+ };
+
+ class AudioStreamInALSA : public AudioStreamIn, public ALSAStreamOps
+ {
+ public:
+ AudioStreamInALSA(AudioHardwareALSA *parent);
+ virtual ~AudioStreamInALSA();
+
+ status_t set(int *format,
+ uint32_t *channelCount,
+ uint32_t *sampleRate){
+ return ALSAStreamOps::set(format, channelCount, sampleRate);
+ }
+
+ //virtual uint32_t sampleRate() {
+ virtual uint32_t sampleRate() const {
+ return ALSAStreamOps::sampleRate();
+ }
+
+ virtual size_t bufferSize() const
+ {
+ return ALSAStreamOps::bufferSize();
+ }
+
+ //virtual int channelCount() const
+ virtual uint32_t channels() const
+ {
+ return ALSAStreamOps::channelCount();
+ }
+
+ virtual int format() const
+ {
+ return ALSAStreamOps::format();
+ }
+
+ virtual ssize_t read(void* buffer, ssize_t bytes);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setDevice(int mode, uint32_t newDevice, uint32_t audio_mode);
+
+ virtual status_t setGain(float gain);
+
+ virtual status_t standby();
+
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ virtual unsigned int getInputFramesLost() const { return 0; }
+
+ private:
+ AudioHardwareALSA *mParent;
+ bool mPowerLock;
+ };
+
+#if defined SEC_IPC
+ //TODO..implementation has to be done
+ class AudioHardwareIPC
+ {
+ public:
+ AudioHardwareIPC();
+ virtual ~AudioHardwareIPC();
+ status_t transmitVolumeIPC(uint32_t type, float volume);
+ status_t transmitAudioPathIPC(uint32_t path);
+#if defined SYNCHRONIZE_CP
+ status_t transmitClock_IPC(uint32_t condition);
+#endif
+ private:
+ HRilClient mClient;
+ char data[100];
+ };
+#endif
+ class AudioHardwareALSA : public AudioHardwareBase
+ {
+ 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();
+
+ /**
+ * put the audio hardware into standby mode to conserve power. Returns
+ * status based on include/utils/Errors.h
+ */
+ virtual status_t standby();
+
+ /** 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);
+#if defined TURN_ON_DEVICE_ONLY_USE
+ virtual int setMicStatus(int on); // To deliver status of input stream(activated or not). If it's activated, doesn't turn off codec.
+#endif
+ /** 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);
+
+
+
+ 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(uint32_t device);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ friend class AudioStreamOutALSA;
+ friend class AudioStreamInALSA;
+
+ ALSAMixer *mMixer;
+ AudioStreamOutALSA *mOutput;
+ AudioStreamInALSA *mInput;
+#if defined SEC_IPC
+ AudioHardwareIPC *mIPC; //for IPC
+ uint32_t mRoutes[AudioSystem::NUM_MODES];
+#endif
+
+ private:
+ Mutex mLock;
+#if defined TURN_ON_DEVICE_ONLY_USE
+ bool mActivatedInputDevice;
+#endif
+ };
+
+ // ----------------------------------------------------------------------------
+
+#if defined SEC_IPC
+// sangsu fix : global functions for IPC
+static int onRawReqComplete(HRilClient client, const void *data, size_t datalen);
+static int onUnsol(HRilClient client, const void *data, size_t datalen);
+#endif
+}; // namespace android
+#endif // ANDROID_AUDIO_HARDWARE_ALSA_H
diff --git a/libaudio/AudioPolicyManager.cpp b/libaudio/AudioPolicyManager.cpp
new file mode 100644
index 0000000..a2b4651
--- /dev/null
+++ b/libaudio/AudioPolicyManager.cpp
@@ -0,0 +1,1865 @@
+/*
+ * 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 "AudioPolicyManager"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include "AudioPolicyManager.h"
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyInterface implementation
+// ----------------------------------------------------------------------------
+
+
+status_t AudioPolicyManager::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;
+ }
+ }
+ // 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);
+ }
+ }
+ }
+ // 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 AudioPolicyManager::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 AudioPolicyManager::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;
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+ // 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 (hwOutputDesc->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 (hwOutputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
+ newDevice = getDeviceForStrategy(STRATEGY_MEDIA);
+ } else if (hwOutputDesc->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];
+ hwOutputDesc->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];
+ hwOutputDesc->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 = hwOutputDesc->mRefCount[i];
+ mOutputs.valueFor(mA2dpOutput)->changeRefCount((AudioSystem::stream_type)i, refCount);
+ hwOutputDesc->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 = hwOutputDesc->mRefCount[i];
+ mOutputs.valueFor(mDuplicatedOutput)->changeRefCount((AudioSystem::stream_type)i, refCount);
+ hwOutputDesc->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 = hwOutputDesc->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);
+ }
+ }
+
+ // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
+ if (state == AudioSystem::MODE_RINGTONE &&
+ (hwOutputDesc->isUsedByStream(AudioSystem::MUSIC) ||
+ (systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
+ mLimitRingtoneVolume = true;
+ } else {
+ mLimitRingtoneVolume = false;
+ }
+}
+
+void AudioPolicyManager::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ LOGV("setRingerMode() mode %x, mask %x", mode, mask);
+
+ mRingerMode = mode;
+}
+
+void AudioPolicyManager::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;
+ case AudioSystem::FOR_DOCK:
+ if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
+ config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) {
+ LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
+ }
+ mForceUse[usage] = config;
+ break;
+ default:
+ LOGW("setForceUse() invalid usage %d", usage);
+ break;
+ }
+}
+
+AudioSystem::forced_config AudioPolicyManager::getForceUse(AudioSystem::force_use usage)
+{
+ return mForceUse[usage];
+}
+
+void AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream,int session)
+{
+ 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 AudioPolicyManager::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream,int session)
+{
+ 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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+
+ if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
+ return BAD_VALUE;
+ }
+
+ // Force max volume if stream cannot be muted
+ if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
+
+ 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 AudioPolicyManager::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 AudioPolicyManager::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);
+ snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]);
+ 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;
+}
+
+// ----------------------------------------------------------------------------
+// AudioPolicyManager
+// ----------------------------------------------------------------------------
+
+// --- class factory
+
+
+extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
+{
+ return new AudioPolicyManager(clientInterface);
+}
+
+extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
+{
+ delete interface;
+}
+
+AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
+: mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false),mTotalEffectsCpuLoad(0),mTotalEffectsMemory(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;
+}
+
+AudioPolicyManager::~AudioPolicyManager()
+{
+ 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 AudioPolicyManager::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;
+}
+
+AudioPolicyManager::routing_strategy AudioPolicyManager::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 AudioPolicyManager::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_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_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_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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::getActiveInput()
+{
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ if (mInputs.valueAt(i)->mRefCount > 0) {
+ return mInputs.keyAt(i);
+ }
+ }
+ return 0;
+}
+
+float AudioPolicyManager::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];
+
+ 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) || mLimitRingtoneVolume) {
+ 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 AudioPolicyManager::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);
+ // do not set volume if the float value did not change
+ if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) {
+ 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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::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 AudioPolicyManager::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();
+ }
+ }
+ }
+ }
+}
+
+uint32_t AudioPolicyManager::getMaxEffectsCpuLoad()
+{
+//TODO ..newly added function in Gingerbread
+ LOGD("Inside.%s. Need implementation\n",__func__);
+ return 0;
+}
+
+uint32_t AudioPolicyManager::getMaxEffectsMemory()
+{
+//TODO ..newly added function in Gingerbread
+ LOGD("Inside.%s. Need implementation\n",__func__);
+ return 0;
+}
+
+
+uint32_t AudioPolicyManager::getStrategyForStream(AudioSystem::stream_type stream)
+{
+//TODO ..newly added function in Gingerbread
+ LOGD("Inside.%s. Need implementation\n",__func__);
+ return 0;
+
+}
+
+audio_io_handle_t AudioPolicyManager::getOutputForEffect(effect_descriptor_t *desc)
+{
+
+//TODO ..newly added function in Gingerbread
+ LOGD("Inside.%s. Need implementation\n",__func__);
+ return 0;
+
+}
+
+status_t AudioPolicyManager::registerEffect(effect_descriptor_t *desc,
+ audio_io_handle_t output,
+ uint32_t strategy,
+ int session,
+ int id)
+{
+
+//TODO ..newly added function in Gingerbread
+ LOGD("Inside.%s. Need implementation\n",__func__);
+ return 0;
+}
+
+status_t AudioPolicyManager::unregisterEffect(int id)
+{
+//TODO ..newly added function in Gingerbread
+ LOGD("Inside.%s. Need implementation\n",__func__);
+ return 0;
+}
+// --- AudioOutputDescriptor class implementation
+
+AudioPolicyManager::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 AudioPolicyManager::AudioOutputDescriptor::device()
+{
+ uint32_t device = 0;
+ if (isDuplicated()) {
+ device = mOutput1->mDevice | mOutput2->mDevice;
+ } else {
+ device = mDevice;
+ }
+ return device;
+}
+
+void AudioPolicyManager::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 AudioPolicyManager::AudioOutputDescriptor::isUsedByStrategy(routing_strategy strategy)
+{
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (AudioPolicyManager::getStrategy((AudioSystem::stream_type)i) == strategy &&
+ isUsedByStream((AudioSystem::stream_type)i)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+status_t AudioPolicyManager::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
+
+AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor()
+ : mSamplingRate(0), mFormat(0), mChannels(0),
+ mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
+{
+}
+
+status_t AudioPolicyManager::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 AudioPolicyManager::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/libaudio/AudioPolicyManager.h b/libaudio/AudioPolicyManager.h
new file mode 100644
index 0000000..35d323f
--- /dev/null
+++ b/libaudio/AudioPolicyManager.h
@@ -0,0 +1,218 @@
+/*
+ * 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 AudioPolicyManager: public AudioPolicyInterface
+{
+
+public:
+ AudioPolicyManager(AudioPolicyClientInterface *clientInterface);
+ virtual ~AudioPolicyManager();
+
+ // 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,int session);
+ virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream,int session);
+ 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);
+
+ // return the strategy corresponding to a given stream type
+ virtual uint32_t getStrategyForStream(AudioSystem::stream_type stream) ;
+
+ // Audio effect management
+ virtual audio_io_handle_t getOutputForEffect(effect_descriptor_t *desc) ;
+ virtual status_t registerEffect(effect_descriptor_t *desc,
+ audio_io_handle_t output,
+ uint32_t strategy,
+ int session,
+ int id) ;
+ virtual status_t unregisterEffect(int id) ;
+
+ 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);
+
+ uint32_t getMaxEffectsCpuLoad();
+ uint32_t getMaxEffectsMemory();
+ 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
+ bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected
+ uint32_t mTotalEffectsCpuLoad;
+ uint32_t mTotalEffectsMemory;
+};
+
+};
diff --git a/libaudio/AudioPolicyManagerBase.cpp b/libaudio/AudioPolicyManagerBase.cpp
new file mode 100644
index 0000000..1d87c0d
--- /dev/null
+++ b/libaudio/AudioPolicyManagerBase.cpp
@@ -0,0 +1,2121 @@
+/*
+ * 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 "AudioPolicyManagerBase"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include <hardware_legacy/AudioPolicyManagerBase.h>
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyInterface implementation
+// ----------------------------------------------------------------------------
+
+
+status_t AudioPolicyManagerBase::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;
+ }
+ LOGV("setDeviceConnectionState() connecting device %x", device);
+
+ // register new device as available
+ mAvailableOutputDevices |= device;
+
+#ifdef WITH_A2DP
+ // handle A2DP device connection
+ if (AudioSystem::isA2dpDevice(device)) {
+ status_t status = handleA2dpConnection(device, device_address);
+ if (status != NO_ERROR) {
+ mAvailableOutputDevices &= ~device;
+ return status;
+ }
+ } else
+#endif
+ {
+ 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);
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 &&
+ mPhoneState != AudioSystem::MODE_NORMAL) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+#endif
+ }
+ }
+ break;
+ // handle output device disconnection
+ case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
+ if (!(mAvailableOutputDevices & device)) {
+ LOGW("setDeviceConnectionState() device not connected: %x", device);
+ return INVALID_OPERATION;
+ }
+
+
+ LOGV("setDeviceConnectionState() disconnecting device %x", device);
+ // remove device from available output devices
+ mAvailableOutputDevices &= ~device;
+
+#ifdef WITH_A2DP
+ // handle A2DP device disconnection
+ if (AudioSystem::isA2dpDevice(device)) {
+ status_t status = handleA2dpDisconnection(device, device_address);
+ if (status != NO_ERROR) {
+ mAvailableOutputDevices |= device;
+ return status;
+ }
+ } else
+#endif
+ {
+ if (AudioSystem::isBluetoothScoDevice(device)) {
+ mScoDeviceAddress = "";
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 &&
+ mPhoneState != AudioSystem::MODE_NORMAL) {
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+#endif
+ }
+ }
+ } break;
+
+ default:
+ LOGE("setDeviceConnectionState() invalid state: %x", state);
+ return BAD_VALUE;
+ }
+
+ // request routing change if necessary
+ uint32_t newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+ checkOutputForAllStrategies(newDevice);
+ // A2DP outputs must be closed after checkOutputForAllStrategies() is executed
+ if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
+ closeA2dpOutputs();
+ }
+#endif
+ updateDeviceForStrategy();
+ setOutputDevice(mHardwareOutput, newDevice);
+
+ 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 AudioPolicyManagerBase::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 AudioPolicyManagerBase::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;
+ mPhoneState = state;
+ bool force = false;
+
+ // are we entering or starting a call
+ if ((oldState != AudioSystem::MODE_IN_CALL) && (state == AudioSystem::MODE_IN_CALL)) {
+ LOGV(" Entering call in setPhoneState()");
+ // force routing command to audio hardware when starting a call
+ // even if no device change is needed
+ force = true;
+ } else if ((oldState == AudioSystem::MODE_IN_CALL) && (state != AudioSystem::MODE_IN_CALL)) {
+ LOGV(" Exiting call in setPhoneState()");
+ // force routing command to audio hardware when exiting a call
+ // even if no device change is needed
+ force = true;
+ }
+
+ // check for device and output changes triggered by new phone state
+ newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+ checkOutputForAllStrategies(newDevice);
+ // suspend A2DP output if a SCO device is present.
+ if (mA2dpOutput != 0 && mScoDeviceAddress != "") {
+ if (oldState == AudioSystem::MODE_NORMAL) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ } else if (state == AudioSystem::MODE_NORMAL) {
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+ }
+#endif
+ updateDeviceForStrategy();
+
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+
+ // force routing command to audio hardware when ending call
+ // even if no device change is needed
+ if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) {
+ newDevice = hwOutputDesc->device();
+ }
+
+ // when changing from ring tone to in call mode, mute the ringing tone
+ // immediately and delay the route change to avoid sending the ring tone
+ // tail into the earpiece or headset.
+ int delayMs = 0;
+ if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) {
+ // delay the device change command by twice the output latency to have some margin
+ // and be sure that audio buffers not yet affected by the mute are out when
+ // we actually apply the route change
+ delayMs = hwOutputDesc->mLatency*2;
+ setStreamMute(AudioSystem::RING, true, mHardwareOutput);
+ }
+
+ // change routing is necessary
+ setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
+
+ // 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);
+ // unmute the ringing tone after a sufficient delay if it was muted before
+ // setting output device above
+ if (oldState == AudioSystem::MODE_RINGTONE) {
+ setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
+ }
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ handleIncallSonification(stream, true, true);
+ }
+ }
+
+ // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
+ if (state == AudioSystem::MODE_RINGTONE &&
+ (hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
+ (systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
+ mLimitRingtoneVolume = true;
+ } else {
+ mLimitRingtoneVolume = false;
+ }
+}
+
+void AudioPolicyManagerBase::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ LOGV("setRingerMode() mode %x, mask %x", mode, mask);
+
+ mRingerMode = mode;
+}
+
+void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+{
+ LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
+
+ bool forceVolumeReeval = false;
+ 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;
+ 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;
+ case AudioSystem::FOR_DOCK:
+ if (config != AudioSystem::FORCE_NONE && config != AudioSystem::FORCE_BT_CAR_DOCK &&
+ config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) {
+ LOGW("setForceUse() invalid config %d for FOR_DOCK", config);
+ }
+ forceVolumeReeval = true;
+ mForceUse[usage] = config;
+ break;
+ default:
+ LOGW("setForceUse() invalid usage %d", usage);
+ break;
+ }
+
+ // check for device and output changes triggered by new phone state
+ uint32_t newDevice = getNewDevice(mHardwareOutput, false);
+#ifdef WITH_A2DP
+ checkOutputForAllStrategies(newDevice);
+#endif
+ updateDeviceForStrategy();
+ setOutputDevice(mHardwareOutput, newDevice);
+ if (forceVolumeReeval) {
+ applyStreamVolumes(mHardwareOutput, newDevice);
+ }
+}
+
+AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage)
+{
+ return mForceUse[usage];
+}
+
+void AudioPolicyManagerBase::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 AudioPolicyManagerBase::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);
+
+#ifdef AUDIO_POLICY_TEST
+ if (mCurOutput != 0) {
+ LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
+ mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
+
+ if (mTestOutputs[mCurOutput] == 0) {
+ LOGV("getOutput() opening test output");
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = mTestDevice;
+ outputDesc->mSamplingRate = mTestSamplingRate;
+ outputDesc->mFormat = mTestFormat;
+ outputDesc->mChannels = mTestChannels;
+ outputDesc->mLatency = mTestLatencyMs;
+ outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
+ outputDesc->mRefCount[stream] = 0;
+ mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+ if (mTestOutputs[mCurOutput]) {
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"),mCurOutput);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
+ addOutput(mTestOutputs[mCurOutput], outputDesc);
+ }
+ }
+ return mTestOutputs[mCurOutput];
+ }
+#endif //AUDIO_POLICY_TEST
+
+ // open a direct output if required by specified parameters
+ if (needsDirectOuput(stream, samplingRate, format, channels, flags, device)) {
+
+ 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] = 0;
+ output = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+
+ // only accept an output with the requeted parameters
+ if (output == 0 ||
+ (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);
+ if (output != 0) {
+ mpClientInterface->closeOutput(output);
+ }
+ delete outputDesc;
+ return 0;
+ }
+ addOutput(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 a2dpDevice = device & AudioSystem::DEVICE_OUT_ALL_A2DP;
+ if (AudioSystem::popCount((AudioSystem::audio_devices)device) == 2) {
+#ifdef WITH_A2DP
+ if (a2dpUsedForSonification() && a2dpDevice != 0) {
+ // 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 (a2dpDevice != 0) {
+ // 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 AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session)
+{
+ LOGV("startOutput() output %d, stream %d, session %d", output, stream, session);
+ 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);
+
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 && !a2dpUsedForSonification() && strategy == STRATEGY_SONIFICATION) {
+ setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
+ }
+#endif
+
+ // 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);
+
+ setOutputDevice(output, getNewDevice(output));
+
+ // 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 AudioPolicyManagerBase::stopOutput(audio_io_handle_t output,
+ AudioSystem::stream_type stream,
+ int session)
+{
+ LOGV("stopOutput() output %d, stream %d, session %d", output, stream, session);
+ 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->mRefCount[stream] > 0) {
+ // decrement usage count of this stream on the output
+ outputDesc->changeRefCount(stream, -1);
+ // store time at which the last music track was stopped - see computeVolume()
+ if (stream == AudioSystem::MUSIC) {
+ mMusicStopTime = systemTime();
+ }
+
+ setOutputDevice(output, getNewDevice(output));
+
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0 && !a2dpUsedForSonification() &&
+ strategy == STRATEGY_SONIFICATION) {
+ setStrategyMute(STRATEGY_MEDIA,
+ false,
+ mA2dpOutput,
+ mOutputs.valueFor(mHardwareOutput)->mLatency*2);
+ }
+#endif
+ if (output != mHardwareOutput) {
+ setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true);
+ }
+ return NO_ERROR;
+ } else {
+ LOGW("stopOutput() refcount is already 0 for output %d", output);
+ return INVALID_OPERATION;
+ }
+}
+
+void AudioPolicyManagerBase::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;
+ }
+
+#ifdef AUDIO_POLICY_TEST
+ int testIndex = testOutputIndex(output);
+ if (testIndex != 0) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ if (outputDesc->refCount() == 0) {
+ mpClientInterface->closeOutput(output);
+ delete mOutputs.valueAt(index);
+ mOutputs.removeItem(output);
+ mTestOutputs[testIndex] = 0;
+ }
+ return;
+ }
+#endif //AUDIO_POLICY_TEST
+
+ if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
+ mpClientInterface->closeOutput(output);
+ delete mOutputs.valueAt(index);
+ mOutputs.removeItem(output);
+ }
+}
+
+audio_io_handle_t AudioPolicyManagerBase::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 (input == 0 ||
+ (samplingRate != inputDesc->mSamplingRate) ||
+ (format != inputDesc->mFormat) ||
+ (channels != inputDesc->mChannels)) {
+ LOGV("getInput() failed opening input: samplingRate %d, format %d, channels %d",
+ samplingRate, format, channels);
+ if (input != 0) {
+ mpClientInterface->closeInput(input);
+ }
+ delete inputDesc;
+ return 0;
+ }
+ mInputs.add(input, inputDesc);
+ return input;
+}
+
+status_t AudioPolicyManagerBase::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);
+
+#ifdef AUDIO_POLICY_TEST
+ if (mTestInput == 0)
+#endif //AUDIO_POLICY_TEST
+ {
+ // 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);
+
+ // use Voice Recognition mode or not for this input based on input source
+ int vr_enabled = inputDesc->mInputSource == AUDIO_SOURCE_VOICE_RECOGNITION ? 1 : 0;
+ param.addInt(String8("vr_mode"), vr_enabled);
+ LOGV("AudioPolicyManager::startInput(%d), setting vr_mode to %d", inputDesc->mInputSource, vr_enabled);
+
+ mpClientInterface->setParameters(input, param.toString());
+
+ inputDesc->mRefCount = 1;
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::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 AudioPolicyManagerBase::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 AudioPolicyManagerBase::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 AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+
+ if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
+ return BAD_VALUE;
+ }
+
+ // Force max volume if stream cannot be muted
+ if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
+
+ 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 AudioPolicyManagerBase::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;
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getOutputForEffect(effect_descriptor_t *desc)
+{
+ LOGV("getOutputForEffect()");
+ // apply simple rule where global effects are attached to the same output as MUSIC streams
+ return getOutput(AudioSystem::MUSIC);
+}
+
+status_t AudioPolicyManagerBase::registerEffect(effect_descriptor_t *desc,
+ audio_io_handle_t output,
+ uint32_t strategy,
+ int session,
+ int id)
+{
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("registerEffect() unknown output %d", output);
+ return INVALID_OPERATION;
+ }
+
+ if (mTotalEffectsCpuLoad + desc->cpuLoad > getMaxEffectsCpuLoad()) {
+ LOGW("registerEffect() CPU Load limit exceeded for Fx %s, CPU %f MIPS",
+ desc->name, (float)desc->cpuLoad/10);
+ return INVALID_OPERATION;
+ }
+ if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) {
+ LOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB",
+ desc->name, desc->memoryUsage);
+ return INVALID_OPERATION;
+ }
+ mTotalEffectsCpuLoad += desc->cpuLoad;
+ mTotalEffectsMemory += desc->memoryUsage;
+ LOGV("registerEffect() effect %s, output %d, strategy %d session %d id %d",
+ desc->name, output, strategy, session, id);
+
+ LOGV("registerEffect() CPU %d, memory %d", desc->cpuLoad, desc->memoryUsage);
+ LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+
+ EffectDescriptor *pDesc = new EffectDescriptor();
+ memcpy (&pDesc->mDesc, desc, sizeof(effect_descriptor_t));
+ pDesc->mOutput = output;
+ pDesc->mStrategy = (routing_strategy)strategy;
+ pDesc->mSession = session;
+ mEffects.add(id, pDesc);
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::unregisterEffect(int id)
+{
+ ssize_t index = mEffects.indexOfKey(id);
+ if (index < 0) {
+ LOGW("unregisterEffect() unknown effect ID %d", id);
+ return INVALID_OPERATION;
+ }
+
+ EffectDescriptor *pDesc = mEffects.valueAt(index);
+
+ if (mTotalEffectsCpuLoad < pDesc->mDesc.cpuLoad) {
+ LOGW("unregisterEffect() CPU load %d too high for total %d",
+ pDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad);
+ pDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad;
+ }
+ mTotalEffectsCpuLoad -= pDesc->mDesc.cpuLoad;
+ if (mTotalEffectsMemory < pDesc->mDesc.memoryUsage) {
+ LOGW("unregisterEffect() memory %d too big for total %d",
+ pDesc->mDesc.memoryUsage, mTotalEffectsMemory);
+ pDesc->mDesc.memoryUsage = mTotalEffectsMemory;
+ }
+ mTotalEffectsMemory -= pDesc->mDesc.memoryUsage;
+ LOGV("unregisterEffect() effect %s, ID %d, CPU %d, memory %d",
+ pDesc->mDesc.name, id, pDesc->mDesc.cpuLoad, pDesc->mDesc.memoryUsage);
+ LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory);
+
+ mEffects.removeItem(id);
+ delete pDesc;
+
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::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);
+#ifdef WITH_A2DP
+ 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, " A2DP device address: %s\n", mA2dpDeviceAddress.string());
+ result.append(buffer);
+#endif
+ snprintf(buffer, SIZE, " SCO device address: %s\n", mScoDeviceAddress.string());
+ 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, " 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);
+ snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AudioSystem::FOR_DOCK]);
+ 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 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));
+ }
+
+ snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n",
+ (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory);
+ write(fd, buffer, strlen(buffer));
+
+ snprintf(buffer, SIZE, "Registered effects:\n");
+ write(fd, buffer, strlen(buffer));
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i));
+ write(fd, buffer, strlen(buffer));
+ mEffects.valueAt(i)->dump(fd);
+ }
+
+
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// AudioPolicyManagerBase
+// ----------------------------------------------------------------------------
+
+AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface *clientInterface)
+ :
+#ifdef AUDIO_POLICY_TEST
+ Thread(false),
+#endif //AUDIO_POLICY_TEST
+ mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0),
+ mLimitRingtoneVolume(false), mTotalEffectsCpuLoad(0), mTotalEffectsMemory(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;
+
+#ifdef WITH_A2DP
+ mA2dpOutput = 0;
+ mDuplicatedOutput = 0;
+ mA2dpDeviceAddress = String8("");
+#endif
+ 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 {
+ addOutput(mHardwareOutput, outputDesc);
+ setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
+ //TODO: configure audio effect output stage here
+ }
+
+ updateDeviceForStrategy();
+#ifdef AUDIO_POLICY_TEST
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"), 0);
+ mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+
+ mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER;
+ mTestSamplingRate = 44100;
+ mTestFormat = AudioSystem::PCM_16_BIT;
+ mTestChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ mTestLatencyMs = 0;
+ mCurOutput = 0;
+ mDirectOutput = false;
+ for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+ mTestOutputs[i] = 0;
+ }
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, "AudioPolicyManagerTest");
+ run(buffer, ANDROID_PRIORITY_AUDIO);
+#endif //AUDIO_POLICY_TEST
+}
+
+AudioPolicyManagerBase::~AudioPolicyManagerBase()
+{
+#ifdef AUDIO_POLICY_TEST
+ exit();
+#endif //AUDIO_POLICY_TEST
+ 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();
+}
+
+#ifdef AUDIO_POLICY_TEST
+bool AudioPolicyManagerBase::threadLoop()
+{
+ LOGV("entering threadLoop()");
+ while (!exitPending())
+ {
+ String8 command;
+ int valueInt;
+ String8 value;
+
+ Mutex::Autolock _l(mLock);
+ mWaitWorkCV.waitRelative(mLock, milliseconds(50));
+
+ command = mpClientInterface->getParameters(0, String8("test_cmd_policy"));
+ AudioParameter param = AudioParameter(command);
+
+ if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR &&
+ valueInt != 0) {
+ LOGV("Test command %s received", command.string());
+ String8 target;
+ if (param.get(String8("target"), target) != NO_ERROR) {
+ target = "Manager";
+ }
+ if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_output"));
+ mCurOutput = valueInt;
+ }
+ if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_direct"));
+ if (value == "false") {
+ mDirectOutput = false;
+ } else if (value == "true") {
+ mDirectOutput = true;
+ }
+ }
+ if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_input"));
+ mTestInput = valueInt;
+ }
+
+ if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_format"));
+ int format = AudioSystem::INVALID_FORMAT;
+ if (value == "PCM 16 bits") {
+ format = AudioSystem::PCM_16_BIT;
+ } else if (value == "PCM 8 bits") {
+ format = AudioSystem::PCM_8_BIT;
+ } else if (value == "Compressed MP3") {
+ format = AudioSystem::MP3;
+ }
+ if (format != AudioSystem::INVALID_FORMAT) {
+ if (target == "Manager") {
+ mTestFormat = format;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("format"), format);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+ if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_channels"));
+ int channels = 0;
+
+ if (value == "Channels Stereo") {
+ channels = AudioSystem::CHANNEL_OUT_STEREO;
+ } else if (value == "Channels Mono") {
+ channels = AudioSystem::CHANNEL_OUT_MONO;
+ }
+ if (channels != 0) {
+ if (target == "Manager") {
+ mTestChannels = channels;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("channels"), channels);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+ if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_sampleRate"));
+ if (valueInt >= 0 && valueInt <= 96000) {
+ int samplingRate = valueInt;
+ if (target == "Manager") {
+ mTestSamplingRate = samplingRate;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("sampling_rate"), samplingRate);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+
+ if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_reopen"));
+
+ mpClientInterface->closeOutput(mHardwareOutput);
+ delete mOutputs.valueFor(mHardwareOutput);
+ mOutputs.removeItem(mHardwareOutput);
+
+ 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 reopen hardware output stream, samplingRate: %d, format %d, channels %d",
+ outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+ } else {
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"), 0);
+ mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+ addOutput(mHardwareOutput, outputDesc);
+ }
+ }
+
+
+ mpClientInterface->setParameters(0, String8("test_cmd_policy="));
+ }
+ }
+ return false;
+}
+
+void AudioPolicyManagerBase::exit()
+{
+ {
+ AutoMutex _l(mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ requestExitAndWait();
+}
+
+int AudioPolicyManagerBase::testOutputIndex(audio_io_handle_t output)
+{
+ for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+ if (output == mTestOutputs[i]) return i;
+ }
+ return 0;
+}
+#endif //AUDIO_POLICY_TEST
+
+// ---
+
+void AudioPolicyManagerBase::addOutput(audio_io_handle_t id, AudioOutputDescriptor *outputDesc)
+{
+ outputDesc->mId = id;
+ mOutputs.add(id, outputDesc);
+}
+
+
+#ifdef WITH_A2DP
+status_t AudioPolicyManagerBase::handleA2dpConnection(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ // 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
+ addOutput(mA2dpOutput, outputDesc);
+
+ //TODO: configure audio effect output stage here
+
+ // set initial stream volume for A2DP device
+ applyStreamVolumes(mA2dpOutput, device);
+ if (a2dpUsedForSonification()) {
+ mDuplicatedOutput = mpClientInterface->openDuplicateOutput(mA2dpOutput, mHardwareOutput);
+ }
+ if (mDuplicatedOutput != 0 ||
+ !a2dpUsedForSonification()) {
+ // 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);
+
+ if (a2dpUsedForSonification()) {
+ // 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;
+ addOutput(mDuplicatedOutput, dupOutputDesc);
+ applyStreamVolumes(mDuplicatedOutput, device);
+ }
+ } else {
+ LOGW("getOutput() could not open duplicated output for %d and %d",
+ mHardwareOutput, mA2dpOutput);
+ mpClientInterface->closeOutput(mA2dpOutput);
+ mOutputs.removeItem(mA2dpOutput);
+ mA2dpOutput = 0;
+ delete outputDesc;
+ return NO_INIT;
+ }
+ } else {
+ LOGW("setDeviceConnectionState() could not open A2DP output for device %x", device);
+ delete outputDesc;
+ return NO_INIT;
+ }
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+
+ if (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);
+ }
+ }
+
+ if (!a2dpUsedForSonification()) {
+ // mute music on A2DP output if a notification or ringtone is playing
+ uint32_t refCount = hwOutputDesc->strategyRefCount(STRATEGY_SONIFICATION);
+ for (uint32_t i = 0; i < refCount; i++) {
+ setStrategyMute(STRATEGY_MEDIA, true, mA2dpOutput);
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerBase::handleA2dpDisconnection(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ if (mA2dpOutput == 0) {
+ LOGW("setDeviceConnectionState() disconnecting A2DP and no A2DP output!");
+ return INVALID_OPERATION;
+ }
+
+ if (mA2dpDeviceAddress != device_address) {
+ LOGW("setDeviceConnectionState() disconnecting unknow A2DP sink address %s", device_address);
+ return INVALID_OPERATION;
+ }
+
+ // mute media strategy to avoid outputting 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, MUTE_TIME_MS);
+
+ if (!a2dpUsedForSonification()) {
+ // unmute music on A2DP output if a notification or ringtone is playing
+ uint32_t refCount = mOutputs.valueFor(mHardwareOutput)->strategyRefCount(STRATEGY_SONIFICATION);
+ for (uint32_t i = 0; i < refCount; i++) {
+ setStrategyMute(STRATEGY_MEDIA, false, mA2dpOutput);
+ }
+ }
+ mA2dpDeviceAddress = "";
+ return NO_ERROR;
+}
+
+void AudioPolicyManagerBase::closeA2dpOutputs()
+{
+ LOGV("setDeviceConnectionState() closing A2DP and duplicated output!");
+
+ if (mDuplicatedOutput != 0) {
+ AudioOutputDescriptor *dupOutputDesc = mOutputs.valueFor(mDuplicatedOutput);
+ AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
+ // As all active tracks on duplicated output will be deleted,
+ // and as they were also referenced on hardware output, the reference
+ // count for their stream type must be adjusted accordingly on
+ // hardware output.
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ int refCount = dupOutputDesc->mRefCount[i];
+ hwOutputDesc->changeRefCount((AudioSystem::stream_type)i,-refCount);
+ }
+
+ mpClientInterface->closeOutput(mDuplicatedOutput);
+ delete mOutputs.valueFor(mDuplicatedOutput);
+ mOutputs.removeItem(mDuplicatedOutput);
+ mDuplicatedOutput = 0;
+ }
+ if (mA2dpOutput != 0) {
+ AudioParameter param;
+ param.add(String8("closing"), String8("true"));
+ mpClientInterface->setParameters(mA2dpOutput, param.toString());
+
+ mpClientInterface->closeOutput(mA2dpOutput);
+ delete mOutputs.valueFor(mA2dpOutput);
+ mOutputs.removeItem(mA2dpOutput);
+ mA2dpOutput = 0;
+ }
+}
+
+void AudioPolicyManagerBase::checkOutputForStrategy(routing_strategy strategy, uint32_t &newDevice)
+{
+ uint32_t prevDevice = getDeviceForStrategy(strategy);
+ uint32_t curDevice = getDeviceForStrategy(strategy, false);
+ bool a2dpWasUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(prevDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
+ bool a2dpIsUsed = AudioSystem::isA2dpDevice((AudioSystem::audio_devices)(curDevice & ~AudioSystem::DEVICE_OUT_SPEAKER));
+ audio_io_handle_t srcOutput = 0;
+ audio_io_handle_t dstOutput = 0;
+
+ if (a2dpWasUsed && !a2dpIsUsed) {
+ bool dupUsed = a2dpUsedForSonification() && a2dpWasUsed && (AudioSystem::popCount(prevDevice) == 2);
+ dstOutput = mHardwareOutput;
+ if (dupUsed) {
+ LOGV("checkOutputForStrategy() moving strategy %d from duplicated", strategy);
+ srcOutput = mDuplicatedOutput;
+ } else {
+ LOGV("checkOutputForStrategy() moving strategy %d from a2dp", strategy);
+ srcOutput = mA2dpOutput;
+ }
+
+ // do not change newDevice if it was already set before this call by a previous call to
+ // getNewDevice() or checkOutputForStrategy() for a strategy with higher priority
+ if (newDevice == 0 && mOutputs.valueFor(mHardwareOutput)->isUsedByStrategy(strategy)) {
+ newDevice = getDeviceForStrategy(strategy, false);
+ }
+ }
+ if (a2dpIsUsed && !a2dpWasUsed) {
+ bool dupUsed = a2dpUsedForSonification() && a2dpIsUsed && (AudioSystem::popCount(curDevice) == 2);
+ srcOutput = mHardwareOutput;
+ if (dupUsed) {
+ LOGV("checkOutputForStrategy() moving strategy %d to duplicated", strategy);
+ dstOutput = mDuplicatedOutput;
+ } else {
+ LOGV("checkOutputForStrategy() moving strategy %d to a2dp", strategy);
+ dstOutput = mA2dpOutput;
+ }
+ }
+
+ if (srcOutput != 0 && dstOutput != 0) {
+ // Move effects associated to this strategy from previous output to new output
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ EffectDescriptor *desc = mEffects.valueAt(i);
+ if (desc->mSession != AudioSystem::SESSION_OUTPUT_STAGE &&
+ desc->mStrategy == strategy &&
+ desc->mOutput == srcOutput) {
+ LOGV("checkOutputForStrategy() moving effect %d to output %d", mEffects.keyAt(i), dstOutput);
+ mpClientInterface->moveEffects(desc->mSession, srcOutput, dstOutput);
+ desc->mOutput = dstOutput;
+ }
+ }
+ // Move tracks associated to this strategy from previous output to new output
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+ mpClientInterface->setStreamOutput((AudioSystem::stream_type)i, dstOutput);
+ }
+ }
+ }
+}
+
+void AudioPolicyManagerBase::checkOutputForAllStrategies(uint32_t &newDevice)
+{
+ // Check strategies in order of priority so that once newDevice is set
+ // for a given strategy it is not modified by subsequent calls to
+ // checkOutputForStrategy()
+ checkOutputForStrategy(STRATEGY_PHONE, newDevice);
+ checkOutputForStrategy(STRATEGY_SONIFICATION, newDevice);
+ checkOutputForStrategy(STRATEGY_MEDIA, newDevice);
+ checkOutputForStrategy(STRATEGY_DTMF, newDevice);
+}
+
+#endif
+
+uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
+{
+ uint32_t device = 0;
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+ // 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 (mPhoneState == AudioSystem::MODE_IN_CALL ||
+ outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
+ device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
+ device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
+ device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
+ } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
+ device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
+ }
+
+ LOGV("getNewDevice() selected device %x", device);
+ return device;
+}
+
+uint32_t AudioPolicyManagerBase::getStrategyForStream(AudioSystem::stream_type stream) {
+ return (uint32_t)getStrategy(stream);
+}
+
+AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::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 AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
+{
+ uint32_t device = 0;
+
+ if (fromCache) {
+ LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
+ return mDeviceForStrategy[strategy];
+ }
+
+ 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, false);
+ 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_WIRED_HEADPHONE;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+ if (device) break;
+#ifdef WITH_A2DP
+ // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
+ if (mPhoneState != AudioSystem::MODE_IN_CALL) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
+ if (device) break;
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+ if (device) break;
+ }
+#endif
+ 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;
+ }
+#ifdef WITH_A2DP
+ // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
+ // A2DP speaker when forcing to speaker output
+ if (mPhoneState != AudioSystem::MODE_IN_CALL) {
+ device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+ if (device) break;
+ }
+#endif
+ 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, false);
+ 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_WIRED_HEADPHONE;
+ }
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
+ }
+#ifdef WITH_A2DP
+ if (mA2dpOutput != 0) {
+ if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
+ break;
+ }
+ 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;
+ }
+ }
+#endif
+ if (device2 == 0) {
+ device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
+ }
+
+ // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
+ device |= device2;
+ if (device == 0) {
+ LOGE("getDeviceForStrategy() speaker device not found");
+ }
+ } break;
+
+ default:
+ LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
+ break;
+ }
+
+ LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
+ return device;
+}
+
+void AudioPolicyManagerBase::updateDeviceForStrategy()
+{
+ for (int i = 0; i < NUM_STRATEGIES; i++) {
+ mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
+ }
+}
+
+void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
+{
+ LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+
+ if (outputDesc->isDuplicated()) {
+ setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
+ setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
+ return;
+ }
+#ifdef WITH_A2DP
+ // filter devices according to output selected
+ if (output == mA2dpOutput) {
+ device &= AudioSystem::DEVICE_OUT_ALL_A2DP;
+ } else {
+ device &= ~AudioSystem::DEVICE_OUT_ALL_A2DP;
+ }
+#endif
+
+ uint32_t prevDevice = (uint32_t)outputDesc->device();
+ // Do not change the routing if:
+ // - the requestede device is 0
+ // - the requested device is the same as current device and force is not specified.
+ // Doing this check here allows the caller to call setOutputDevice() without conditions
+ if ((device == 0 || device == prevDevice) && !force) {
+ LOGV("setOutputDevice() setting same device %x or null device for output %d", device, output);
+ return;
+ }
+
+ outputDesc->mDevice = device;
+ // mute media streams if both speaker and headset are selected
+ if (output == mHardwareOutput && AudioSystem::popCount(device) == 2) {
+ setStrategyMute(STRATEGY_MEDIA, true, output);
+ // wait for the PCM output buffers to empty before proceeding with the rest of the command
+ usleep(outputDesc->mLatency*2*1000);
+ }
+#ifdef WITH_A2DP
+ // suspend A2DP output if SCO device is selected
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)device)) {
+ if (mA2dpOutput != 0) {
+ mpClientInterface->suspendOutput(mA2dpOutput);
+ }
+ }
+#endif
+ // 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);
+
+#ifdef WITH_A2DP
+ // if disconnecting SCO device, restore A2DP output
+ if (AudioSystem::isBluetoothScoDevice((AudioSystem::audio_devices)prevDevice)) {
+ if (mA2dpOutput != 0) {
+ LOGV("restore A2DP output");
+ mpClientInterface->restoreOutput(mA2dpOutput);
+ }
+ }
+#endif
+ // if changing from a combined headset + speaker route, unmute media streams
+ if (output == mHardwareOutput && AudioSystem::popCount(prevDevice) == 2) {
+ setStrategyMute(STRATEGY_MEDIA, false, output, delayMs);
+ }
+}
+
+uint32_t AudioPolicyManagerBase::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:
+ if (hasBackMicrophone()) {
+ device = AudioSystem::DEVICE_IN_BACK_MIC;
+ } else {
+ 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;
+ }
+ LOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
+ return device;
+}
+
+audio_io_handle_t AudioPolicyManagerBase::getActiveInput()
+{
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ if (mInputs.valueAt(i)->mRefCount > 0) {
+ return mInputs.keyAt(i);
+ }
+ }
+ return 0;
+}
+
+float AudioPolicyManagerBase::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];
+
+ if (device == 0) {
+ device = outputDesc->device();
+ }
+
+ int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
+ volume = AudioSystem::linearToLog(volInt);
+
+ // if a headset 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) &&
+ streamDesc.mCanBeMuted) {
+ 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->mRefCount[AudioSystem::MUSIC] || mLimitRingtoneVolume) {
+ 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 AudioPolicyManagerBase::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 (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
+ LOGV("checkAndSetVolume() stream %d muted count %d", stream, mOutputs.valueFor(output)->mMuteCount[stream]);
+ 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);
+ // do not set volume if the float value did not change
+ if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) {
+ 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 AudioPolicyManagerBase::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 AudioPolicyManagerBase::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 AudioPolicyManagerBase::setStreamMute(int stream, bool on, audio_io_handle_t output, int delayMs)
+{
+ StreamDescriptor &streamDesc = mStreams[stream];
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+ LOGV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d", stream, on, output, outputDesc->mMuteCount[stream]);
+
+ if (on) {
+ if (outputDesc->mMuteCount[stream] == 0) {
+ if (streamDesc.mCanBeMuted) {
+ checkAndSetVolume(stream, 0, output, outputDesc->device(), delayMs);
+ }
+ }
+ // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
+ outputDesc->mMuteCount[stream]++;
+ } else {
+ if (outputDesc->mMuteCount[stream] == 0) {
+ LOGW("setStreamMute() unmuting non muted stream!");
+ return;
+ }
+ if (--outputDesc->mMuteCount[stream] == 0) {
+ checkAndSetVolume(stream, streamDesc.mIndexCur, output, outputDesc->device(), delayMs);
+ }
+ }
+}
+
+void AudioPolicyManagerBase::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->mRefCount[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->device() & 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();
+ }
+ }
+ }
+ }
+}
+
+bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags,
+ uint32_t device)
+{
+ return ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+ (format !=0 && !AudioSystem::isLinearPCM(format)));
+}
+
+uint32_t AudioPolicyManagerBase::getMaxEffectsCpuLoad()
+{
+ return MAX_EFFECTS_CPU_LOAD;
+}
+
+uint32_t AudioPolicyManagerBase::getMaxEffectsMemory()
+{
+ return MAX_EFFECTS_MEMORY;
+}
+
+// --- AudioOutputDescriptor class implementation
+
+AudioPolicyManagerBase::AudioOutputDescriptor::AudioOutputDescriptor()
+ : mId(0), 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;
+ mMuteCount[i] = 0;
+ }
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::device()
+{
+ uint32_t device = 0;
+ if (isDuplicated()) {
+ device = mOutput1->mDevice | mOutput2->mDevice;
+ } else {
+ device = mDevice;
+ }
+ return device;
+}
+
+void AudioPolicyManagerBase::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]);
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount()
+{
+ uint32_t refcount = 0;
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ refcount += mRefCount[i];
+ }
+ return refcount;
+}
+
+uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::strategyRefCount(routing_strategy strategy)
+{
+ uint32_t refCount = 0;
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ if (getStrategy((AudioSystem::stream_type)i) == strategy) {
+ refCount += mRefCount[i];
+ }
+ }
+ return refCount;
+}
+
+
+status_t AudioPolicyManagerBase::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", device());
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Stream volume refCount muteCount\n");
+ result.append(buffer);
+ for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", i, mCurVolume[i], mRefCount[i], mMuteCount[i]);
+ result.append(buffer);
+ }
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+// --- AudioInputDescriptor class implementation
+
+AudioPolicyManagerBase::AudioInputDescriptor::AudioInputDescriptor()
+ : mSamplingRate(0), mFormat(0), mChannels(0),
+ mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
+{
+}
+
+status_t AudioPolicyManagerBase::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 AudioPolicyManagerBase::StreamDescriptor::dump(char* buffer, size_t size)
+{
+ snprintf(buffer, size, " %02d %02d %02d %d\n",
+ mIndexMin,
+ mIndexMax,
+ mIndexCur,
+ mCanBeMuted);
+}
+
+// --- EffectDescriptor class implementation
+
+status_t AudioPolicyManagerBase::EffectDescriptor::dump(int fd)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, " Output: %d\n", mOutput);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Session: %d\n", mSession);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " Name: %s\n", mDesc.name);
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+
+ return NO_ERROR;
+}
+
+
+
+}; // namespace android
diff --git a/libaudio/NOTICE b/libaudio/NOTICE
new file mode 100644
index 0000000..ada44e1
--- /dev/null
+++ b/libaudio/NOTICE
@@ -0,0 +1,191 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+ Copyright 2008 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+