diff options
author | Jeong-Seok Yang <jseok.yang@samsung.com> | 2010-08-25 18:32:24 -0700 |
---|---|---|
committer | Ed Heyl <edheyl@google.com> | 2010-08-25 18:52:19 -0700 |
commit | ec456383c58adf2d4c4818438a703e5a2ca949b5 (patch) | |
tree | 6d7b1ddc2610c37e911917dad5be7ebdebc6c6ad /libaudio | |
parent | 1206fad9881e5bca592fcd0fbdafc9d7f195d539 (diff) | |
download | device_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.mk | 82 | ||||
-rw-r--r-- | libaudio/AudioHardwareALSA.cpp | 2361 | ||||
-rw-r--r-- | libaudio/AudioHardwareALSA.h | 411 | ||||
-rw-r--r-- | libaudio/AudioPolicyManager.cpp | 1865 | ||||
-rw-r--r-- | libaudio/AudioPolicyManager.h | 218 | ||||
-rw-r--r-- | libaudio/AudioPolicyManagerBase.cpp | 2121 | ||||
-rw-r--r-- | libaudio/NOTICE | 191 |
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 + |