exynos: Import alternative audio HAL
* From https://gitlab.com/Linaro/96boards/e850-96/platform/hardware/samsung_slsi/-/tree/3e8336d6f04504fb2377a52909c98cd38ac1d368/exynos/libaudio/audiohal
Change-Id: I46d50402adb1794cad204056c2cec90b92e9b60e
diff --git a/include/libaudio/audiohal/audio_definition.h b/include/libaudio/audiohal/audio_definition.h
new file mode 100644
index 0000000..69191db
--- /dev/null
+++ b/include/libaudio/audiohal/audio_definition.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_DEFINITION_H__
+#define __EXYNOS_AUDIOHAL_DEFINITION_H__
+
+/* This header file has common definitions for AudioHAL and AudioProxy */
+
+#define PREDEFINED_CAPTURE_DURATION 20 // 20ms
+#define LOW_LATENCY_CAPTURE_SAMPLE_RATE 48000
+
+#define MAX_MIXER_LENGTH 256
+#define DEFAULT_MIXER_PATH "/vendor/etc/"
+#define DEFAULT_MIXER_FILE "mixer_paths.xml"
+#define MIXER_PATH_INFO "/proc/device-tree/sound/mixer-paths"
+
+// Duration for Normal Capture
+#define PREDEFINED_MEDIA_CAPTURE_DURATION 20 // 20ms
+#define PREDEFINED_LOW_CAPTURE_DURATION 4 // 4ms
+
+// Duration for USB Playback and Capture
+#define PREDEFINED_USB_PLAYBACK_DURATION 20 // 20ms
+#define PREDEFINED_USB_CAPTURE_DURATION 10 // 10ms
+
+
+#define DEFAULT_MEDIA_BIT_WIDTH 16
+#define DEFAULT_MEDIA_SAMPLING_RATE 48000
+
+#define UHQA_MEDIA_BIT_WIDTH 24
+#define UHQA_MEDIA_SAMPLING_RATE 192000
+
+#define SUHQA_MEDIA_BIT_WIDTH 32
+#define SUHQA_MEDIA_SAMPLING_RATE 384000
+
+
+/**
+ ** Customization
+ ** If these are defined at other header file, please disable this if block
+ **/
+#define AUDIO_PARAMETER_KEY_FMRADIO_MODE "fm_mode"
+#define AUDIO_PARAMETER_KEY_FMRADIO_VOLUME "fm_radio_volume"
+
+// Factory Mode
+#define AUDIO_PARAMETER_KEY_FACTORY_RMS_TEST "factory_test_mic_check"
+
+#define AUDIO_PARAMETER_FACTORY_TEST_LOOPBACK "factory_test_loopback"
+#define AUDIO_PARAMETER_FACTORY_TEST_TYPE "factory_test_type"
+#define AUDIO_PARAMETER_FACTORY_TEST_PATH "factory_test_path"
+#define AUDIO_PARAMETER_FACTORY_TEST_ROUTE "factory_test_route"
+
+#define AUDIO_PARAMETER_SEAMLESS_VOICE "seamless_voice"
+
+#endif // __EXYNOS_AUDIOHAL_DEFINITION_H__
diff --git a/include/libaudio/audiohal/audio_devices.h b/include/libaudio/audiohal/audio_devices.h
new file mode 100644
index 0000000..b4b0bac
--- /dev/null
+++ b/include/libaudio/audiohal/audio_devices.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_DEVICE_H__
+#define __EXYNOS_AUDIOHAL_DEVICE_H__
+
+/**
+ ** Audio Input/Output Device based on Target Device
+ **/
+typedef enum {
+ DEVICE_MIN = 0,
+
+ // Playback Devices
+ DEVICE_EARPIECE = 0, // handset or receiver
+ DEVICE_SPEAKER,
+ DEVICE_HEADSET, // headphone + mic
+ DEVICE_HEADPHONE, // headphone or earphone
+ DEVICE_SPEAKER_AND_HEADSET,
+ DEVICE_SPEAKER_AND_HEADPHONE,
+ DEVICE_BT_HEADSET,
+ DEVICE_FM_EXTERNAL,
+ DEVICE_SPEAKER_AND_BT_HEADSET,
+ DEVICE_USB_HEADSET,
+ DEVICE_AUX_DIGITAL,
+ DEVICE_LINE_OUT,
+ DEVICE_SPEAKER_AND_LINEOUT,
+
+ // Special Playback Devices
+ DEVICE_CALL_FWD,
+
+ // Capture Devices
+ DEVICE_MAIN_MIC,
+ DEVICE_HEADSET_MIC,
+ DEVICE_HEADSET_MAIN_MIC,
+ DEVICE_BT_HEADSET_MIC,
+ DEVICE_BT_NREC_HEADSET_MIC,
+ DEVICE_USB_HEADSET_MIC,
+
+ DEVICE_HANDSET_MIC,
+ DEVICE_SPEAKER_MIC,
+ DEVICE_HEADPHONE_MIC,
+
+ DEVICE_SUB_MIC,
+ DEVICE_STEREO_MIC,
+ DEVICE_FULL_MIC,
+ DEVICE_HCO_MIC,
+ DEVICE_VCO_MIC,
+
+ DEVICE_FM_TUNER,
+ DEVICE_LINE_OUT_MIC,
+
+ DEVICE_NONE,
+ DEVICE_MAX,
+ DEVICE_CNT = DEVICE_MAX
+} device_type;
+
+
+/**
+ ** Audio Input/Output Sampling Rate Modifier
+ **/
+typedef enum {
+ MODIFIER_MIN = 0,
+
+ /* RX modifier */
+ MODIFIER_BT_SCO_RX_NB = 0,
+ MODIFIER_BT_SCO_RX_WB,
+
+ /* TX modifier */
+ MODIFIER_BT_SCO_TX_NB,
+ MODIFIER_BT_SCO_TX_WB,
+
+ MODIFIER_NONE,
+ MODIFIER_MAX,
+ MODIFIER_CNT = MODIFIER_MAX
+} modifier_type;
+
+
+#endif // __EXYNOS_AUDIOHAL_DEVICE_H__
diff --git a/include/libaudio/audiohal/audio_log.h b/include/libaudio/audiohal/audio_log.h
new file mode 100644
index 0000000..c950ba2
--- /dev/null
+++ b/include/libaudio/audiohal/audio_log.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_LOG_H__
+#define __EXYNOS_AUDIOHAL_LOG_H__
+
+
+char * audiomode_table[AUDIO_MODE_CNT] = {
+ [AUDIO_MODE_NORMAL] = "normal mode",
+ [AUDIO_MODE_RINGTONE] = "ringtone mode",
+ [AUDIO_MODE_IN_CALL] = "in_call mode",
+ [AUDIO_MODE_IN_COMMUNICATION] = "in_comm mode",
+};
+
+char * callmode_table[CALL_MODE_CNT] = {
+ [CALL_OFF] = "Call Off",
+ [VOICE_CALL] = "Voice Call Mode",
+ [VOLTE_CALL] = "VoLTE Call Mode",
+ [VOWIFI_CALL] = "VoWiFi Call Mode",
+};
+
+#endif // __EXYNOS_AUDIOHAL_LOG_H__
\ No newline at end of file
diff --git a/include/libaudio/audiohal/audio_mixers.h b/include/libaudio/audiohal/audio_mixers.h
new file mode 100644
index 0000000..4480135
--- /dev/null
+++ b/include/libaudio/audiohal/audio_mixers.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef AUDIO_MIXERS_H
+#define AUDIO_MIXERS_H
+
+/* Mixer Values Definition */
+enum {
+ MIXER_VALUE_OFF = 0,
+ MIXER_VALUE_ON,
+};
+
+#define MIXER_CTL_VAL_INVALID -1
+
+/* Specific Mixer Name */
+// To use Virtual PCM Device for APCall
+#define ABOX_APCALLBUFFTYPE_CONTROL_NAME "ABOX PCM ext APCALL BUFFTYPE"
+#define ABOX_APCALL_SPEECH_PARAM_CONTROL_NAME "ABOX Speech Param"
+#define ABOX_APCALL_MUTE_CONTROL_NAME "ABOX ERAP info Mute Primary"
+#define ABOX_APCALL_MUTE_COUNT 20
+#define ABOX_BT_MUTE_COUNT 30
+
+#endif /* AUDIO_MIXERS */
diff --git a/include/libaudio/audiohal/audio_offload.h b/include/libaudio/audiohal/audio_offload.h
new file mode 100644
index 0000000..6b28cca
--- /dev/null
+++ b/include/libaudio/audiohal/audio_offload.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_OFFLOAD_H__
+#define __EXYNOS_AUDIOHAL_OFFLOAD_H__
+
+#define OFFLOAD_EFFECT_LIBRARY_PATH ""
+
+/**
+ ** Compress Offload Message List
+ **/
+typedef enum {
+ OFFLOAD_MSG_INVALID = 0,
+
+ OFFLOAD_MSG_WAIT_WRITE,
+ OFFLOAD_MSG_WAIT_DRAIN,
+ OFFLOAD_MSG_WAIT_PARTIAL_DRAIN,
+ OFFLOAD_MSG_EXIT,
+
+ OFFLOAD_MSG_MAX,
+} offload_msg_type;
+
+#endif // __EXYNOS_AUDIOHAL_OFFLOAD_H__
diff --git a/include/libaudio/audiohal/audio_proxy_interface.h b/include/libaudio/audiohal/audio_proxy_interface.h
new file mode 100644
index 0000000..437bd48
--- /dev/null
+++ b/include/libaudio/audiohal/audio_proxy_interface.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef AUDIO_PROXY_INTERFACE_H
+#define AUDIO_PROXY_INTERFACE_H
+
+
+/* Volume Type */
+enum {
+ VOLUME_TYPE_OFFLOAD = 0,
+};
+
+/* Compress Function Type */
+enum {
+ COMPRESS_TYPE_WAIT = 0,
+ COMPRESS_TYPE_NEXTTRACK,
+ COMPRESS_TYPE_PARTIALDRAIN,
+ COMPRESS_TYPE_DRAIN,
+};
+
+/* Special Audio Device Type */
+enum {
+ BUILTIN_EARPIECE = 0,
+ BUILTIN_SPEAKER,
+ BUILTIN_MIC,
+ PROXIMITY_SENSOR,
+};
+
+/* Audio Device Configuration Type */
+enum {
+ DEVICE_CONFIG_NONE = 0,
+ DEVICE_CONFIG_INTERNAL,
+ DEVICE_CONFIG_EXTERNAL,
+};
+
+enum {
+ DEVICE_BLUETOOTH = 0,
+ DEVICE_FMRADIO,
+};
+
+/* A-Box Configuration Type */
+enum {
+ NEED_VOICEPCM_REOPEN = 0,
+ SUPPORT_USB_BY_PRIMARY,
+ SUPPORT_A2DP_BY_PRIMARY,
+
+};
+
+
+// Audio Capability Check Utility Functions
+int get_supported_device_number(void *proxy, int device_type);
+int get_supported_config(void *proxy, int device_type);
+bool is_needed_config(void *proxy, int config_type);
+
+// Audio Usage Check Utility Functions
+bool is_active_usage_APCall(void *proxy);
+bool is_usage_CPCall(audio_usage ausage);
+bool is_active_usage_CPCall(void *proxy);
+bool is_usage_APCall(audio_usage ausage);
+
+// Audio Stream Proxy Get/Set Fungtions
+uint32_t proxy_get_actual_channel_count(void *proxy_stream); // Return Actual Channel Count
+uint32_t proxy_get_actual_sampling_rate(void *proxy_stream); // Return Actual Samplung Rate
+uint32_t proxy_get_actual_period_size(void *proxy_stream);
+uint32_t proxy_get_actual_period_count(void *proxy_stream);
+int32_t proxy_get_actual_format(void *proxy_stream); // Return Actual Android Audio Format, not PCM Format
+
+// Audio Stream Proxy Offload Functions
+void proxy_offload_set_nonblock(void *proxy_stream);
+int proxy_offload_compress_func(void *proxy_stream, int func_type);
+int proxy_offload_pause(void *proxy_stream);
+int proxy_offload_resume(void *proxy_stream);
+
+// Audio Stream Proxy Playback Stream Functions
+void *proxy_create_playback_stream(void *proxy, int type, void *config, char *address);
+void proxy_destroy_playback_stream(void *proxy_stream);
+int proxy_close_playback_stream(void *proxy_stream);
+int proxy_open_playback_stream(void *proxy_stream, int32_t min_size_frames, void *mmap_info);
+int proxy_start_playback_stream(void *proxy_stream);
+int proxy_write_playback_buffer(void *proxy_stream, void *buffer, int bytes);
+int proxy_stop_playback_stream(void *proxy_stream);
+int proxy_reconfig_playback_stream(void *proxy_stream, int type, void *config);
+int proxy_get_render_position(void *proxy_stream, uint32_t *frames);
+int proxy_get_presen_position(void *proxy_stream, uint64_t *frames, struct timespec *timestamp);
+int proxy_getparam_playback_stream(void *proxy_stream, void *query_params, void *reply_params);
+int proxy_setparam_playback_stream(void *proxy_stream, void *parameters);
+uint32_t proxy_get_playback_latency(void *proxy_stream);
+void proxy_dump_playback_stream(void *proxy_stream, int fd);
+
+// Audio Stream Proxy Capture Stream Functions
+void *proxy_create_capture_stream(void *proxy, int type, int usage, void *config, char *address);
+void proxy_destroy_capture_stream(void *proxy_stream);
+int proxy_close_capture_stream(void *proxy_stream);
+int proxy_open_capture_stream(void *proxy_stream, int32_t min_size_frames, void *mmap_info);
+int proxy_start_capture_stream(void *proxy_stream);
+int proxy_read_capture_buffer(void *proxy_stream, void *buffer, int bytes);
+int proxy_stop_capture_stream(void *proxy_stream);
+int proxy_reconfig_capture_stream(void *proxy_stream, int type, void *config);
+int proxy_reconfig_capture_usage(void *proxy_stream, int type, int usage);
+int proxy_get_capture_pos(void *proxy_stream, int64_t *frames, int64_t *time);
+int proxy_get_active_microphones(void *proxy_stream, void *array, int *count);
+int proxy_getparam_capture_stream(void *proxy_stream, void *query_params, void *reply_params);
+int proxy_setparam_capture_stream(void *proxy_stream, void *parameters);
+void proxy_dump_capture_stream(void *proxy_stream, int fd);
+void proxy_update_capture_usage(void *proxy_stream, int usage);
+
+int proxy_get_mmap_position(void *proxy_stream, void *pos);
+
+// Audio Device Proxy Path Route Functions
+bool proxy_init_route(void *proxy, char *path);
+void proxy_deinit_route(void *proxy);
+bool proxy_update_route(void *proxy, int ausage, int device);
+bool proxy_set_route(void *proxy, int ausage, int device, int modifier, bool set);
+
+// Audio Device Proxy Functions
+void proxy_stop_voice_call(void *proxy);
+void proxy_start_voice_call(void *proxy);
+void proxy_stop_fm_radio(void *proxy);
+void proxy_start_fm_radio(void *proxy);
+
+int proxy_get_mixer_value_int(void *proxy, const char *name);
+int proxy_get_mixer_value_array(void *proxy, const char *name, void *value, int count);
+void proxy_set_mixer_value_int(void *proxy, const char *name, int value);
+void proxy_set_mixer_value_string(void *proxy, const char *name, const char *value);
+void proxy_set_mixer_value_array(void *proxy, const char *name, const void *value, int count);
+
+void proxy_set_audiomode(void *proxy, int audiomode);
+void proxy_set_volume(void *proxy, int volume_type, float left, float right);
+void proxy_set_communication_volume(void *proxy, int volume);
+void proxy_set_upscale(void *proxy, int sampling_rate, int pcm_format);
+#ifdef SUPPORT_STHAL_INTERFACE
+int proxy_check_sthalstate(void *proxy);
+#endif
+void proxy_call_status(void *proxy, int status);
+int proxy_set_parameters(void *proxy, void *parameters);
+int proxy_get_microphones(void *proxy, void *array, int *count);
+
+void proxy_init_offload_effect_lib(void *proxy);
+void proxy_update_offload_effect(void *proxy_stream, int type);
+
+// Audio Device Proxy Dump Function
+int proxy_fw_dump(int fd);
+
+// Audio Device Proxy Creation/Destruction
+bool proxy_is_initialized(void);
+void *proxy_init(void);
+void proxy_deinit(void *proxy);
+
+#endif /* AUDIO_PROXY_INTERFACE_H */
diff --git a/include/libaudio/audiohal/audio_streams.h b/include/libaudio/audiohal/audio_streams.h
new file mode 100644
index 0000000..ffc9d9a
--- /dev/null
+++ b/include/libaudio/audiohal/audio_streams.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_STREAM_H__
+#define __EXYNOS_AUDIOHAL_STREAM_H__
+
+/*
+ * Audio Streams based on Audio Profile in Audio Policy Configuration
+ */
+typedef enum {
+ ASTREAM_MIN = 0,
+
+ ASTREAM_PLAYBACK_NO_ATTRIBUTE = 0, // For No Attributes Output Profile
+ ASTREAM_PLAYBACK_PRIMARY, // For Primary Output Profile
+ ASTREAM_PLAYBACK_FAST, // For Fast Output Profile
+ ASTREAM_PLAYBACK_DEEP_BUFFER, // For Deep Buffer Output Profile
+ ASTREAM_PLAYBACK_LOW_LATENCY, // For Low Latency Output Profile
+ ASTREAM_PLAYBACK_COMPR_OFFLOAD, // For Compress Offload Profile
+ ASTREAM_PLAYBACK_MMAP, // For MMAP NoIRQ Output Profile
+ ASTREAM_PLAYBACK_USB_DEVICE, // For USB Output Profile
+ ASTREAM_PLAYBACK_AUX_DIGITAL, // For HDMI/DP Profile
+ ASTREAM_PLAYBACK_INCALL_MUSIC, // For music uplink during Call
+
+ ASTREAM_CAPTURE_NO_ATTRIBUTE, // For No Attributes Input Profile
+ ASTREAM_CAPTURE_PRIMARY, // For Primary Input Profile
+ ASTREAM_CAPTURE_CALL, // For Call Recording
+ ASTREAM_CAPTURE_LOW_LATENCY, // For Low Latency Input Profile
+ ASTREAM_CAPTURE_MMAP, // For MMAP NoIRQ Input Profile
+ ASTREAM_CAPTURE_USB_DEVICE, // For USB Input Profile
+ ASTREAM_CAPTURE_FM_TUNER, // For FM Radio Playback
+ ASTREAM_CAPTURE_FM_RECORDING, // For FM Radio Recording
+#ifdef SUPPORT_STHAL_INTERFACE
+ ASTREAM_CAPTURE_HOTWORD, // For VTS seamless Input Profile
+#endif
+
+ ASTREAM_NONE,
+ ASTREAM_MAX,
+ ASTREAM_CNT = ASTREAM_MAX
+} audio_stream_type;
+
+#endif // __EXYNOS_AUDIOHAL_STREAM_H__
diff --git a/include/libaudio/audiohal/audio_tables.h b/include/libaudio/audiohal/audio_tables.h
new file mode 100644
index 0000000..c9d9dab
--- /dev/null
+++ b/include/libaudio/audiohal/audio_tables.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_TABLE_H__
+#define __EXYNOS_AUDIOHAL_TABLE_H__
+
+/*
+ * Audio Streams Table for readable log messages
+ */
+char * stream_table[ASTREAM_CNT] = {
+ [ASTREAM_PLAYBACK_NO_ATTRIBUTE] = "no_attribute_out",
+ [ASTREAM_PLAYBACK_PRIMARY] = "primary_out",
+ [ASTREAM_PLAYBACK_FAST] = "fast_out",
+ [ASTREAM_PLAYBACK_DEEP_BUFFER] = "deep_out",
+ [ASTREAM_PLAYBACK_LOW_LATENCY] = "low_out",
+ [ASTREAM_PLAYBACK_COMPR_OFFLOAD] = "offload_out",
+ [ASTREAM_PLAYBACK_MMAP] = "mmap_out",
+ [ASTREAM_PLAYBACK_USB_DEVICE] = "usb_out",
+ [ASTREAM_PLAYBACK_AUX_DIGITAL] = "aux_out",
+ [ASTREAM_PLAYBACK_INCALL_MUSIC] = "incall_music",
+
+ [ASTREAM_CAPTURE_NO_ATTRIBUTE] = "no_attribute_in",
+ [ASTREAM_CAPTURE_PRIMARY] = "primary_in",
+ [ASTREAM_CAPTURE_CALL] = "callrec_in",
+ [ASTREAM_CAPTURE_LOW_LATENCY] = "low_in",
+ [ASTREAM_CAPTURE_MMAP] = "mmap_in",
+ [ASTREAM_CAPTURE_USB_DEVICE] = "usb_in",
+ [ASTREAM_CAPTURE_FM_TUNER] = "fm_tuner",
+ [ASTREAM_CAPTURE_FM_RECORDING] = "fmrec_in",
+#ifdef SUPPORT_STHAL_INTERFACE
+ [ASTREAM_CAPTURE_HOTWORD] = "hotword_in",
+#endif
+
+ [ASTREAM_NONE] = "none"
+};
+
+/**
+ ** Audio Usage Table for readable log messages
+ **/
+char * usage_table[AUSAGE_CNT] = {
+ [AUSAGE_MEDIA] = "media",
+ [AUSAGE_RECORDING] = "recording",
+ [AUSAGE_CAMCORDER] = "camcoder",
+
+ [AUSAGE_VOICE_CALL_NB] = "voice_call_nb",
+ [AUSAGE_VOICE_CALL_NB_HAC] = "voice_call_nb_hac",
+ [AUSAGE_VOICE_CALL_WB] = "voice_call_wb",
+ [AUSAGE_VOICE_CALL_WB_HAC] = "voice_call_wb_hac",
+ [AUSAGE_VOLTE_CALL_NB] = "volte_call_nb",
+ [AUSAGE_VOLTE_CALL_WB] = "volte_call_wb",
+ [AUSAGE_VOLTE_CALL_SWB] = "volte_vt_call_swb",
+ [AUSAGE_VOLTE_VT_CALL_NB] = "volte_vt_call_nb",
+ [AUSAGE_VOLTE_VT_CALL_WB] = "volte_vt_call_wb",
+ [AUSAGE_VOLTE_VT_CALL_SWB] = "volte_call_swb",
+ [AUSAGE_TTY] = "tty_mode",
+ [AUSAGE_INCALL_MUSIC] = "incall_music",
+
+ [AUSAGE_WIFI_CALL_NB] = "vowifi_call_nb",
+ [AUSAGE_WIFI_CALL_WB] = "vowifi_call_wb",
+ [AUSAGE_WIFI_CALL_SWB] = "vowifi_call_swb",
+ [AUSAGE_VIDEO_CALL] = "video_call",
+ [AUSAGE_VOIP_CALL] = "voip_call",
+ [AUSAGE_COMMUNICATION] = "voip_call",
+ [AUSAGE_AP_TTY] = "ap_tty_mode",
+
+ [AUSAGE_INCALL_UPLINK] = "callrecord_uplink",
+ [AUSAGE_INCALL_DOWNLINK] = "callrecord_downlink",
+ [AUSAGE_INCALL_UPLINK_DOWNLINK] = "callrecord",
+
+ [AUSAGE_RECOGNITION] = "recognition",
+
+ [AUSAGE_FM_RADIO_TUNER] = "fm_radio",
+ [AUSAGE_FM_RADIO_CAPTURE] = "fm_radio",
+#ifdef SUPPORT_STHAL_INTERFACE
+ [AUSAGE_HOTWORD_SEAMLESS] = "hotword_seamless",
+ [AUSAGE_HOTWORD_RECORD] = "hotword_record",
+#endif
+
+ [AUSAGE_LOOPBACK] = "factory_loopback",
+ [AUSAGE_LOOPBACK_NODELAY] = "factory_loopback_nodelay",
+ [AUSAGE_LOOPBACK_REALTIME] = "factory_loopback_realtime",
+ [AUSAGE_LOOPBACK_CODEC] = "factory_loopback_codec",
+ [AUSAGE_RMS] = "factory_rms",
+
+ [AUSAGE_NONE] = "none",
+};
+
+/**
+ ** Usage Path(AP/CP to Codec) Configuration based on Audio Usage
+ **/
+char * usage_path_table[AUSAGE_CNT] = {
+ [AUSAGE_MEDIA] = "media",
+ [AUSAGE_RECORDING] = "recording",
+ [AUSAGE_CAMCORDER] = "camcorder",
+
+ [AUSAGE_VOICE_CALL_NB] = "incall_nb",
+ [AUSAGE_VOICE_CALL_NB_HAC] = "incall_nb_hac",
+ [AUSAGE_VOICE_CALL_WB] = "incall_wb",
+ [AUSAGE_VOICE_CALL_WB_HAC] = "incall_wb_hac",
+ [AUSAGE_VOLTE_CALL_NB] = "incall_nb",
+ [AUSAGE_VOLTE_CALL_WB] = "incall_nb",
+ [AUSAGE_VOLTE_CALL_SWB] = "incall_nb",
+ [AUSAGE_VOLTE_VT_CALL_NB] = "incall_nb",
+ [AUSAGE_VOLTE_VT_CALL_WB] = "incall_nb",
+ [AUSAGE_VOLTE_VT_CALL_SWB] = "incall_nb",
+ [AUSAGE_TTY] = "tty_mode",
+ [AUSAGE_INCALL_MUSIC] = "incall_music",
+
+ [AUSAGE_WIFI_CALL_NB] = "wificall_nb",
+ [AUSAGE_WIFI_CALL_WB] = "wificall_wb",
+ [AUSAGE_WIFI_CALL_SWB] = "wificall_evs",
+ [AUSAGE_VIDEO_CALL] = "video_call",
+ [AUSAGE_VOIP_CALL] = "voip",
+ [AUSAGE_COMMUNICATION] = "communication",
+ [AUSAGE_AP_TTY] = "ap_tty_mode",
+
+ [AUSAGE_INCALL_UPLINK] = "callrecord_uplink",
+ [AUSAGE_INCALL_DOWNLINK] = "callrecord_downlink",
+ [AUSAGE_INCALL_UPLINK_DOWNLINK] = "callrecord",
+
+ [AUSAGE_RECOGNITION] = "recognition",
+ [AUSAGE_FM_RADIO_TUNER] = "fm_radio",
+ [AUSAGE_FM_RADIO_CAPTURE] = "fm_radio",
+#ifdef SUPPORT_STHAL_INTERFACE
+ [AUSAGE_HOTWORD_SEAMLESS] = "hotword_seamless", //dummy definition not used
+ [AUSAGE_HOTWORD_RECORD] = "hotword_record", //dummy definition not used
+#endif
+
+ [AUSAGE_LOOPBACK] = "loopback_packet",
+ [AUSAGE_LOOPBACK_NODELAY] = "loopback",
+ [AUSAGE_LOOPBACK_REALTIME] = "realtimeloopback",
+ [AUSAGE_LOOPBACK_CODEC] = "loopback_codec",
+ [AUSAGE_RMS] = "echo_test",
+
+ [AUSAGE_NONE] = "none",
+};
+
+/**
+ ** Device Path(Codec to Device) Configuration based on Audio Input/Output Device
+ **/
+char * device_table[DEVICE_CNT] = {
+ // Playback Devices
+ [DEVICE_EARPIECE] = "handset",
+ [DEVICE_SPEAKER] = "speaker",
+ [DEVICE_HEADSET] = "headset",
+ [DEVICE_HEADPHONE] = "headphone",
+ [DEVICE_SPEAKER_AND_HEADSET] = "speaker-headset",
+ [DEVICE_SPEAKER_AND_HEADPHONE] = "speaker-headphone",
+ [DEVICE_BT_HEADSET] = "bt-sco-headset",
+ [DEVICE_FM_EXTERNAL] = "external",
+ [DEVICE_SPEAKER_AND_BT_HEADSET] = "speaker-bt-sco-headset",
+ [DEVICE_USB_HEADSET] = "usb-headset",
+ [DEVICE_AUX_DIGITAL] = "aux-digital",
+ [DEVICE_LINE_OUT] = "lineout",
+ [DEVICE_SPEAKER_AND_LINEOUT] = "speaker-lineout",
+
+ // Special Playback Devices
+ [DEVICE_CALL_FWD] = "",
+
+ // Capture Devices
+ [DEVICE_MAIN_MIC] = "mic",
+ [DEVICE_HEADSET_MIC] = "headset-mic",
+ [DEVICE_HEADSET_MAIN_MIC] = "headset-main-mic",
+ [DEVICE_BT_HEADSET_MIC] = "bt-sco-headset-in",
+ [DEVICE_BT_NREC_HEADSET_MIC] = "bt-sco-nrec-headset-in",
+ [DEVICE_USB_HEADSET_MIC] = "usb-headset-mic",
+
+ [DEVICE_HANDSET_MIC] = "handset-mic",
+ [DEVICE_SPEAKER_MIC] = "speaker-mic",
+ [DEVICE_HEADPHONE_MIC] = "headphone-mic",
+
+ [DEVICE_SUB_MIC] = "2nd-mic",
+ [DEVICE_STEREO_MIC] = "dualmic",
+ [DEVICE_FULL_MIC] = "full-mic",
+ [DEVICE_HCO_MIC] = "hco-mic",
+ [DEVICE_VCO_MIC] = "vco-mic",
+
+ [DEVICE_FM_TUNER] = "fm-tuner",
+
+ [DEVICE_LINE_OUT_MIC] = "lineout-mic",
+
+ [DEVICE_NONE] = "none",
+};
+
+/**
+ ** Sampling Rate Modifier Configuration based on Audio Input/Output Device
+ **/
+char * modifier_table[MODIFIER_MAX] = {
+ /* RX modifier */
+ [MODIFIER_BT_SCO_RX_NB] = "set-bt-sco-rx-rate-nb",
+ [MODIFIER_BT_SCO_RX_WB] = "set-bt-sco-rx-rate-wb",
+
+ /* TX modifier */
+ [MODIFIER_BT_SCO_TX_NB] = "set-bt-sco-tx-rate-nb",
+ [MODIFIER_BT_SCO_TX_WB] = "set-bt-sco-tx-rate-wb",
+
+ [MODIFIER_NONE] = "none",
+};
+
+/**
+ ** Offload Message Table for readable log messages
+ **/
+char * offload_msg_table[OFFLOAD_MSG_MAX] = {
+ [OFFLOAD_MSG_INVALID] = "Offload Message_Invalid",
+ [OFFLOAD_MSG_WAIT_WRITE] = "Offload Message_Wait to write",
+ [OFFLOAD_MSG_WAIT_DRAIN] = "Offload Message_Wait to drain",
+ [OFFLOAD_MSG_WAIT_PARTIAL_DRAIN] = "Offload Message_Wait to drain partially",
+ [OFFLOAD_MSG_EXIT] = "Offload Message_Wait to exit",
+};
+
+
+#endif // __EXYNOS_AUDIOHAL_TABLE_H__
diff --git a/include/libaudio/audiohal/audio_usages.h b/include/libaudio/audiohal/audio_usages.h
new file mode 100644
index 0000000..5b21af4
--- /dev/null
+++ b/include/libaudio/audiohal/audio_usages.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_USAGE_H__
+#define __EXYNOS_AUDIOHAL_USAGE_H__
+
+/**
+ ** Audio Usages Definition
+ **/
+typedef enum {
+ AUSAGE_PLAYBACK,
+ AUSAGE_CAPTURE,
+} audio_usage_type;
+
+typedef enum {
+ AUSAGE_MIN = 0,
+
+ // Media Playback/Recording Usages
+ // These audio usages are defined from stream own usage
+ AUSAGE_MEDIA = 0,
+ AUSAGE_RECORDING,
+ AUSAGE_CAMCORDER,
+
+ // Call Usages
+ // These audio usages are defined from Audio Mode and Voice Status
+ AUSAGE_CPCALL_MIN,
+ AUSAGE_VOICE_CALL_NB = AUSAGE_CPCALL_MIN,
+ AUSAGE_VOICE_CALL_NB_HAC,
+ AUSAGE_VOICE_CALL_WB,
+ AUSAGE_VOICE_CALL_WB_HAC,
+ AUSAGE_VOLTE_CALL_NB,
+ AUSAGE_VOLTE_CALL_WB,
+ AUSAGE_VOLTE_CALL_SWB,
+ AUSAGE_VOLTE_VT_CALL_NB,
+ AUSAGE_VOLTE_VT_CALL_WB,
+ AUSAGE_VOLTE_VT_CALL_SWB,
+ AUSAGE_TTY,
+ AUSAGE_INCALL_MUSIC, //Music playback during CP call, sent to other device as CP Tx
+ AUSAGE_CPCALL_MAX = AUSAGE_INCALL_MUSIC,
+
+ AUSAGE_APCALL_MIN,
+ AUSAGE_WIFI_CALL_NB = AUSAGE_APCALL_MIN,
+ AUSAGE_WIFI_CALL_WB,
+ AUSAGE_WIFI_CALL_SWB,
+ AUSAGE_VIDEO_CALL,
+ AUSAGE_VOIP_CALL,
+ AUSAGE_COMMUNICATION,
+ AUSAGE_AP_TTY,
+ AUSAGE_APCALL_MAX = AUSAGE_AP_TTY,
+
+ // Call Recording Usages
+ AUSAGE_INCALL_UPLINK,
+ AUSAGE_INCALL_DOWNLINK,
+ AUSAGE_INCALL_UPLINK_DOWNLINK,
+
+ // Voice Recognition Usages
+ AUSAGE_RECOGNITION,
+
+ // Other Audio Usages
+ AUSAGE_FM_RADIO_TUNER, // for FM radio playback
+ AUSAGE_FM_RADIO_CAPTURE, // for FM radio capture
+
+ // Voice WakeUp Usages
+#ifdef SUPPORT_STHAL_INTERFACE
+ AUSAGE_HOTWORD_SEAMLESS,
+ AUSAGE_HOTWORD_RECORD,
+#endif
+
+ // Factory Mode Test Usages
+ AUSAGE_LOOPBACK_MIN,
+ AUSAGE_LOOPBACK = AUSAGE_LOOPBACK_MIN, //packet
+ AUSAGE_LOOPBACK_NODELAY, //packet_nodelay
+ AUSAGE_LOOPBACK_REALTIME,
+ AUSAGE_LOOPBACK_CODEC,
+ AUSAGE_LOOPBACK_MAX = AUSAGE_LOOPBACK_CODEC,
+
+ AUSAGE_RMS, //RMS Test
+
+ AUSAGE_NONE,
+ AUSAGE_MAX,
+ AUSAGE_CNT = AUSAGE_MAX
+} audio_usage;
+
+#endif // __EXYNOS_AUDIOHAL_USAGE_H__
diff --git a/include/libaudio/audiohal/voice_definition.h b/include/libaudio/audiohal/voice_definition.h
new file mode 100644
index 0000000..0489ee3
--- /dev/null
+++ b/include/libaudio/audiohal/voice_definition.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __AUDIOHAL_VOICE_DEFINITION_H__
+#define __AUDIOHAL_VOICE_DEFINITION_H__
+
+/**
+ ** Custermization
+ ** If these are defined at other header file, please disable this if block
+ **/
+
+#define CP1 0 // DualCP CP1, DualMode Sim1
+#define CP2 1 // DualCP CP2, DualMode Sim2
+
+enum bt_nrec_status {
+ BT_NREC_INITIALIZED = -1,
+ BT_NREC_ON = 0,
+ BT_NREC_OFF = 1
+};
+
+#define TTY_MODE_OFF 0x00000010
+#define TTY_MODE_FULL 0x00000020
+#define TTY_MODE_VCO 0x00000040
+#define TTY_MODE_HCO 0x00000080
+#define TTY_MODE_CLEAR 0xFFFFFF0F
+
+#define TTY_MODE_OFF_RIL 0
+#define TTY_MODE_FULL_RIL 1
+#define TTY_MODE_HCO_RIL 2
+#define TTY_MODE_VCO_RIL 3
+
+#define HAC_MODE_OFF 0x00000100
+#define HAC_MODE_ON 0x00000200
+#define HAC_MODE_CLEAR 0xFFFFF0FF
+
+#define AUDIO_PARAMETER_KEY_EXTRA_VOLUME "extraVolume"
+#define AUDIO_PARAMETER_KEY_CALL_FORWARDING "call_forwarding"
+
+#define AUDIO_PARAMETER_VOLTE_STATUS "VoLTEstate"
+
+
+#define SWB 2
+#define WB 1
+#define NB 0
+#define SWB_SAMPLING_RATE 32000
+#define WB_SAMPLING_RATE 16000
+#define NB_SAMPLING_RATE 8000
+
+#endif // __AUDIOHAL_VOICE_DEFINITION_H__
diff --git a/libaudio/audiohal/Android.mk b/libaudio/audiohal/Android.mk
new file mode 100644
index 0000000..d9273c3
--- /dev/null
+++ b/libaudio/audiohal/Android.mk
@@ -0,0 +1,47 @@
+# Copyright (C) 2014 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.
+
+#
+# Primary Audio HAL
+#
+ifeq ($(BOARD_USE_AUDIOHAL), true)
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ audio_hw.c \
+ voice_manager.c \
+ factory_manager.c
+
+LOCAL_C_INCLUDES += \
+ $(TOP)/hardware/samsung_slsi-linaro/exynos/include/libaudio/audiohal \
+ $(TOP)/hardware/samsung_slsi-linaro/exynos/libaudio/audioril-sit \
+ $(TOP)/hardware/samsung_slsi-linaro/exynos/libaudio/audioril-sit/include
+
+LOCAL_HEADER_LIBRARIES := libhardware_headers
+LOCAL_SHARED_LIBRARIES := liblog libcutils libprocessgroup libaudioproxy
+LOCAL_SHARED_LIBRARIES += libaudio-ril
+
+ifeq ($(BOARD_USE_SOUNDTRIGGER_HAL),true)
+LOCAL_CFLAGS += -DSUPPORT_STHAL_INTERFACE
+endif
+
+LOCAL_MODULE := audio.primary.$(TARGET_SOC)
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+endif
diff --git a/libaudio/audiohal/NOTICE b/libaudio/audiohal/NOTICE
new file mode 100644
index 0000000..316b4eb
--- /dev/null
+++ b/libaudio/audiohal/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2014, 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.
+
+ 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
+
diff --git a/libaudio/audiohal/audio_hw.c b/libaudio/audiohal/audio_hw.c
new file mode 100644
index 0000000..1bcdbcd
--- /dev/null
+++ b/libaudio/audiohal/audio_hw.c
@@ -0,0 +1,4626 @@
+/*
+ * Copyright (C) 2017 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 "audio_hw_primary"
+#define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <sys/prctl.h>
+#include <system/thread_defs.h>
+
+#include <log/log.h>
+#include <cutils/str_parms.h>
+#include <cutils/sched_policy.h>
+
+#include "audio_hw.h"
+#include "audio_tables.h"
+#include "audio_mixers.h"
+#include "audio_proxy_interface.h"
+
+#include "audio_log.h"
+
+
+
+/******************************************************************************/
+/** Note: the following macro is used for extremely verbose logging message. **/
+/** In order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG **/
+/** set to 0; but one side effect of this is to turn all LOGV's as well. Some**/
+/** messages are so verbose that we want to suppress them even when we have **/
+/** ALOG_ASSERT turned on. Do not uncomment the #def below unless you **/
+/** really know what you are doing and want to see all of the extremely **/
+/** verbose messages. **/
+/******************************************************************************/
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGD
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+//#define ROUTING_VERBOSE_LOGGING
+#ifdef ROUTING_VERBOSE_LOGGING
+#define ALOGRV ALOGD
+#else
+#define ALOGRV(a...) do { } while(0)
+#endif
+
+
+
+/******************************************************************************/
+/** **/
+/** Audio Device is Singleton **/
+/** **/
+/******************************************************************************/
+
+/*
+ * There are some race condition in HW Binder to open/close HAL.
+ * In order to avoid abnormal audio device open and close, adev_init_lock will
+ * protect opening before close.
+ */
+static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct audio_device *instance = NULL;
+static unsigned int adev_ref_count = 0;
+
+static struct audio_device* getAudioDeviceInstance(void)
+{
+ if (instance == NULL) {
+ instance = calloc(1, sizeof(struct audio_device));
+ ALOGI("proxy-%s: created Audio HW Device Instance!", __func__);
+ }
+ return instance;
+}
+
+static void destroyAudioDeviceInstance(void)
+{
+ if (instance) {
+ free(instance);
+ instance = NULL;
+ ALOGI("proxy-%s: destroyed Audio HW Device Instance!", __func__);
+ }
+ return;
+}
+
+
+/******************************************************************************/
+/** **/
+/** The Local Functions for Mode Selection **/
+/** **/
+/******************************************************************************/
+/* CP Centric Call Mode or not */
+static bool isCPCallMode(struct audio_device *adev)
+{
+ audio_mode_t mode = adev->amode;
+
+ if (mode == AUDIO_MODE_IN_CALL)
+ return true;
+ else
+ return false;
+}
+
+/* AP Centric Call Mode or not */
+static bool isAPCallMode(struct audio_device *adev)
+{
+ audio_mode_t mode = adev->amode;
+
+ if (mode == AUDIO_MODE_IN_COMMUNICATION)
+ return true;
+ else
+ return false;
+}
+
+static bool isCallMode(struct audio_device *adev)
+{
+ if (isCPCallMode(adev) || isAPCallMode(adev))
+ return true;
+ else
+ return false;
+}
+
+/* Factory Mode or not */
+static bool isFactoryMode(struct audio_device *adev)
+{
+ if (adev->factory && adev->factory->mode != FACTORY_MODE_NONE)
+ return true;
+ else
+ return false;
+}
+
+static bool isCallRecording(audio_source_t source)
+{
+ if (source == AUDIO_SOURCE_VOICE_UPLINK ||
+ source == AUDIO_SOURCE_VOICE_DOWNLINK ||
+ source == AUDIO_SOURCE_VOICE_CALL)
+ return true;
+ else
+ return false;
+}
+
+static bool isFMRadioOn(struct audio_device *adev)
+{
+ if (adev->fm_state == FM_ON || adev->fm_state == FM_RECORDING) return true;
+ else return false;
+}
+
+/* VoIP SE (Speech Enhancement) Triggering or not */
+static bool need_voipse_on(struct audio_device *adev)
+{
+ if (isAPCallMode(adev) && adev->voipse_on == false)
+ return true;
+ else
+ return false;
+}
+
+static bool isUSBHeadsetConnect(struct audio_device *adev)
+{
+ if(adev->actual_playback_device == AUDIO_DEVICE_OUT_USB_DEVICE || adev->actual_playback_device == AUDIO_DEVICE_OUT_USB_ACCESSORY ||
+ adev->actual_playback_device == AUDIO_DEVICE_OUT_USB_HEADSET)
+ return true;
+ else
+ return false;
+}
+
+
+/******************************************************************************/
+/** **/
+/** The Local Functions for Customer Features **/
+/** **/
+/******************************************************************************/
+static char *bool_to_str(int value)
+{
+ return value?"True":"False";
+}
+
+/******************************************************************************/
+/** **/
+/** The Local Functions for Audio Path Routing **/
+/** **/
+/******************************************************************************/
+// Factory Mode Usage
+static audio_usage adev_get_ausage_for_factory(struct audio_device *adev)
+{
+ audio_usage ausage = AUSAGE_NONE;
+
+ if (adev->factory && adev->factory->mode == FACTORY_MODE_LOOPBACK) {
+ ALOGI("device-%s: AUSAGE_MODE_LOOPBACK", __func__);
+ if(adev->factory->loopback_mode == FACTORY_LOOPBACK_PACKET ) {
+ ausage = AUSAGE_LOOPBACK;
+ } else if(adev->factory->loopback_mode == FACTORY_LOOPBACK_CODEC) {
+ ausage = AUSAGE_LOOPBACK_CODEC;
+ } else if(adev->factory->loopback_mode == FACTORY_LOOPBACK_REALTIME) {
+ ausage = AUSAGE_LOOPBACK_REALTIME;
+ } else if(adev->factory->loopback_mode == FACTORY_LOOPBACK_PACKET_NODELAY) {
+ ausage = AUSAGE_LOOPBACK_NODELAY;
+ } else {
+ ausage = AUSAGE_LOOPBACK;
+ }
+ } else if (adev->factory && adev->factory->mode == FACTORY_MODE_RMS) {
+ ALOGI("device-%s: AUSAGE_MODE_RMS", __func__);
+ ausage = AUSAGE_RMS;
+ }
+
+ return ausage;
+}
+
+// VoLTE Call Usage
+static audio_usage adev_get_ausage_for_volte(struct audio_device *adev)
+{
+ volte_status_t volte_status = VOLTE_OFF;
+ int samplingrate = VOICE_SR_NB;
+ audio_usage ausage = AUSAGE_NONE;
+
+ if (adev->voice) {
+ volte_status = voice_get_volte_status(adev->voice);
+ if (volte_status == VOLTE_VOICE) {
+ /* VoLTE Voice Call Usage */
+ samplingrate = voice_get_samplingrate(adev->voice);
+ switch (samplingrate) {
+ case VOICE_SR_SWB:
+ ausage = AUSAGE_VOLTE_CALL_SWB;
+ break;
+
+ case VOICE_SR_WB:
+ ausage = AUSAGE_VOLTE_CALL_WB;
+ break;
+
+ case VOICE_SR_NB:
+ default:
+ ausage = AUSAGE_VOLTE_CALL_NB;
+ break;
+ }
+ } else if (volte_status == VOLTE_VIDEO) {
+ /* VoLTE Video Call Usage */
+ samplingrate = voice_get_samplingrate(adev->voice);
+ switch (samplingrate) {
+ case VOICE_SR_SWB:
+ ausage = AUSAGE_VOLTE_VT_CALL_SWB;
+ break;
+
+ case VOICE_SR_WB:
+ ausage = AUSAGE_VOLTE_VT_CALL_WB;
+ break;
+
+ case VOICE_SR_NB:
+ default:
+ ausage = AUSAGE_VOLTE_VT_CALL_NB;
+ break;
+ }
+ }
+ }
+
+ return ausage;
+}
+
+// Voice Call or VoLTE Call Usage
+static audio_usage adev_get_ausage_for_call(struct audio_device *adev)
+{
+ audio_usage ausage = AUSAGE_NONE;
+
+ if (adev->voice) {
+ ALOGI("%s: incallmusic_on (%d)", __func__, adev->incallmusic_on);
+ /* Check Special Call Usage */
+ if (adev->voice->tty_mode != TTY_MODE_OFF)
+ return AUSAGE_TTY;
+ else if (adev->incallmusic_on)
+ return AUSAGE_INCALL_MUSIC;
+
+ /* Check Normal Call Usage(Voice Call or VoLTE Call) */
+ if (voice_get_volte_status(adev->voice) != VOLTE_OFF)
+ ausage = adev_get_ausage_for_volte(adev);
+ else {
+ int samplingrate = voice_get_samplingrate(adev->voice);
+ switch (samplingrate) {
+ case VOICE_SR_WB:
+ if (adev->voice->hac_mode == HAC_MODE_ON)
+ ausage = AUSAGE_VOICE_CALL_WB_HAC;
+ else
+ ausage = AUSAGE_VOICE_CALL_WB;
+ break;
+
+ case VOICE_SR_NB:
+ default:
+ if (adev->voice->hac_mode == HAC_MODE_ON)
+ ausage = AUSAGE_VOICE_CALL_NB_HAC;
+ else
+ ausage = AUSAGE_VOICE_CALL_NB;
+ break;
+ }
+ }
+ }
+
+ return ausage;
+}
+
+// VoIP Call Usage such as VoWiFi Call
+static audio_usage adev_get_ausage_for_voip(struct audio_device *adev)
+{
+ audio_usage ausage = AUSAGE_COMMUNICATION;
+
+ if (adev->voice && adev->voice->csvtcall) {
+ ausage = AUSAGE_VIDEO_CALL;
+ } else if (adev->voice && adev->voice->voip_wificalling) {
+ if (adev->voice->tty_mode != TTY_MODE_OFF) {
+ ausage = AUSAGE_AP_TTY;
+ } else {
+ if (adev->primary_output->common.requested_devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+ if (adev->voice->bluetooth_samplerate == WB_SAMPLING_RATE)
+ ausage = AUSAGE_WIFI_CALL_WB;
+ else
+ ausage = AUSAGE_WIFI_CALL_NB;
+ } else {
+ int vowifi_band = voice_get_vowifi_band(adev->voice);
+ switch (vowifi_band) {
+ case VOICE_SR_SWB:
+ ausage = AUSAGE_WIFI_CALL_SWB;
+ break;
+
+ case VOICE_SR_WB:
+ ausage = AUSAGE_WIFI_CALL_WB;
+ break;
+
+ case VOICE_SR_NB:
+ default:
+ ausage = AUSAGE_WIFI_CALL_NB;
+ break;
+ }
+ }
+ }
+ } else {
+ if (adev->active_input) {
+ if (adev->active_input->requested_source == AUDIO_SOURCE_MIC)
+ ausage = AUSAGE_VOIP_CALL;
+ }
+ }
+
+ return ausage;
+}
+
+static audio_usage adev_get_playback_ausage(struct audio_device *adev)
+{
+ audio_usage ausage = AUSAGE_NONE;
+
+ if (isFactoryMode(adev)) {
+ ausage = adev_get_ausage_for_factory(adev);
+ } else if (isCPCallMode(adev)) {
+ ausage = adev_get_ausage_for_call(adev);
+ } else if (isAPCallMode(adev)) {
+ ausage = adev_get_ausage_for_voip(adev);
+ } else if (isFMRadioOn(adev) && (popcount(adev->primary_output->common.requested_devices) == 1)) {
+ ausage = AUSAGE_FM_RADIO_TUNER;
+ } else {
+ ausage = AUSAGE_NONE;
+ }
+
+ return ausage;
+}
+
+static audio_usage adev_get_capture_ausage(struct audio_device *adev, struct stream_in *in)
+{
+ audio_usage ausage = AUSAGE_RECORDING;
+
+ if (isFactoryMode(adev)) {
+ ausage = adev_get_ausage_for_factory(adev);
+ } else if (isCPCallMode(adev)) {
+ if (in->requested_source == AUDIO_SOURCE_VOICE_UPLINK)
+ ausage = AUSAGE_INCALL_UPLINK;
+ else if (in->requested_source == AUDIO_SOURCE_VOICE_DOWNLINK)
+ ausage = AUSAGE_INCALL_DOWNLINK;
+ else if (in->requested_source == AUDIO_SOURCE_VOICE_CALL)
+ ausage = AUSAGE_INCALL_UPLINK_DOWNLINK;
+
+ } else if (isAPCallMode(adev)) {
+ ausage = adev_get_ausage_for_voip(adev);
+ } else {
+ if (in->requested_source == AUDIO_SOURCE_CAMCORDER) {
+ ausage = AUSAGE_CAMCORDER;
+ } else if (in->requested_source == AUDIO_SOURCE_VOICE_RECOGNITION) {
+ ausage = AUSAGE_RECOGNITION;
+ }
+ }
+
+ return ausage;
+}
+
+static audio_usage adev_get_ausage_from_stream(void *stream, audio_usage_type usage_type)
+{
+ struct audio_device *adev = NULL;
+ audio_usage selected_usage = AUSAGE_NONE;
+
+ if (usage_type == AUSAGE_PLAYBACK) {
+ struct stream_out *stream_out = (struct stream_out *)stream;
+ adev = stream_out->adev;
+
+ selected_usage = adev_get_playback_ausage(adev);
+ if (selected_usage == AUSAGE_NONE)
+ selected_usage = stream_out->common.stream_usage;
+ } else {
+ struct stream_in *stream_in = (struct stream_in *)stream;
+ adev = stream_in->adev;
+
+ selected_usage = adev_get_capture_ausage(adev, stream);
+ if (selected_usage == AUSAGE_NONE)
+ selected_usage = stream_in->common.stream_usage;
+ }
+
+ return selected_usage;
+}
+
+device_type get_indevice_id_from_outdevice(struct audio_device *adev, device_type devices)
+{
+ if (isCallMode(adev)) {
+ device_type in = DEVICE_NONE;
+
+ switch (devices) {
+ case DEVICE_EARPIECE:
+ in = DEVICE_HANDSET_MIC;
+ break;
+ case DEVICE_SPEAKER:
+ in = DEVICE_SPEAKER_MIC;
+ break;
+ case DEVICE_HEADSET:
+ in = DEVICE_HEADSET_MIC;
+ break;
+ case DEVICE_HEADPHONE:
+ in = DEVICE_HEADPHONE_MIC;
+ break;
+ case DEVICE_BT_HEADSET:
+ if (isAPCallMode(adev)) {
+ if (adev->voice->bluetooth_nrec) {
+ in = DEVICE_BT_HEADSET_MIC; // nrec is supported by BT side
+ } else {
+ in = DEVICE_BT_NREC_HEADSET_MIC;
+ }
+ } else {
+ in = DEVICE_BT_HEADSET_MIC;
+ }
+ break;
+ case DEVICE_USB_HEADSET:
+ if (adev->actual_capture_device != AUDIO_DEVICE_IN_USB_DEVICE)
+ in = DEVICE_HANDSET_MIC;
+ else
+ in = DEVICE_USB_HEADSET_MIC;
+ break;
+ case DEVICE_LINE_OUT:
+ in = DEVICE_LINE_OUT_MIC;
+ break;
+ case DEVICE_NONE:
+ in = DEVICE_NONE;
+ break;
+ default:
+ if(adev->support_reciever) {
+ in = DEVICE_HANDSET_MIC;
+ } else {
+ in = DEVICE_SPEAKER_MIC;
+ }
+ break;
+ }
+
+ if ((adev->voice && (adev->voice->tty_mode != TTY_MODE_OFF))
+ && (devices & (DEVICE_HEADPHONE|DEVICE_HEADSET|DEVICE_SPEAKER|DEVICE_LINE_OUT))) {
+ switch (adev->voice->tty_mode) {
+ case TTY_MODE_FULL:
+ in = DEVICE_FULL_MIC;
+ break;
+ case TTY_MODE_HCO:
+ if (devices & (DEVICE_HEADPHONE|DEVICE_HEADSET|DEVICE_LINE_OUT)) {
+ in = DEVICE_HCO_MIC;
+ }
+ break;
+ case TTY_MODE_VCO:
+ in = DEVICE_VCO_MIC;
+ break;
+ default:
+ ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice->tty_mode);
+ }
+ }
+
+ return in;
+ }
+
+ switch (devices) {
+ case DEVICE_SPEAKER:
+ case DEVICE_EARPIECE:
+ case DEVICE_HEADPHONE:
+ case DEVICE_SPEAKER_AND_HEADPHONE:
+ case DEVICE_AUX_DIGITAL:
+ case DEVICE_LINE_OUT:
+ case DEVICE_SPEAKER_AND_LINEOUT:
+ return DEVICE_MAIN_MIC;
+
+ case DEVICE_HEADSET:
+ case DEVICE_SPEAKER_AND_HEADSET:
+ return DEVICE_HEADSET_MIC;
+
+ case DEVICE_BT_HEADSET:
+ case DEVICE_SPEAKER_AND_BT_HEADSET:
+ return DEVICE_BT_HEADSET_MIC;
+
+ case DEVICE_USB_HEADSET:
+ return DEVICE_USB_HEADSET_MIC;
+
+ default:
+ return DEVICE_NONE;
+ }
+
+ return DEVICE_NONE;
+}
+
+device_type get_device_id(struct audio_device *adev, audio_devices_t devices)
+{
+ device_type ret = DEVICE_NONE;
+
+ if (isCallMode(adev)) {
+ if (devices > AUDIO_DEVICE_BIT_IN) {
+ /* Input Devices */
+ return get_indevice_id_from_outdevice(adev,
+ get_device_id(adev, adev->primary_output->common.requested_devices));
+ } else {
+ switch (devices) {
+ case AUDIO_DEVICE_OUT_EARPIECE:
+ ret = DEVICE_EARPIECE;
+ break;
+ case AUDIO_DEVICE_OUT_SPEAKER:
+ ret = DEVICE_SPEAKER;
+ break;
+ case AUDIO_DEVICE_OUT_WIRED_HEADSET:
+ ret = DEVICE_HEADSET;
+ break;
+ case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
+ ret = DEVICE_HEADPHONE;
+ break;
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET|AUDIO_DEVICE_OUT_SPEAKER:
+ ret = DEVICE_BT_HEADSET;
+ break;
+ case AUDIO_DEVICE_OUT_USB_DEVICE:
+ ret = DEVICE_USB_HEADSET;
+ break;
+ case AUDIO_DEVICE_OUT_LINE:
+ ret = DEVICE_LINE_OUT;
+ break;
+ case AUDIO_DEVICE_OUT_TELEPHONY_TX:
+ /* Assuming primary-out routing might have already requested, as call started */
+ ret = get_device_id(adev, adev->primary_output->common.requested_devices);
+ break;
+ case AUDIO_DEVICE_NONE:
+ ret = DEVICE_NONE;
+ break;
+ default:
+ if(adev->support_reciever) {
+ ret = DEVICE_EARPIECE;
+ } else {
+ ret = DEVICE_SPEAKER;
+ }
+ break;
+ }
+
+ if ((adev->voice->tty_mode != TTY_MODE_OFF)
+ && (devices & (AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_LINE|
+ AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_SPEAKER))) {
+ switch (adev->voice->tty_mode) {
+ case TTY_MODE_FULL:
+ ret = DEVICE_HEADSET;
+ break;
+ case TTY_MODE_HCO:
+ if (devices & (AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_WIRED_HEADSET|
+ AUDIO_DEVICE_OUT_LINE)) {
+ ret = DEVICE_EARPIECE;
+ }
+ break;
+ case TTY_MODE_VCO:
+ ret = DEVICE_HEADSET;
+ break;
+ default:
+ ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice->tty_mode);
+ }
+ }
+
+ if (isCPCallMode(adev)) {
+ if (adev->voice->call_forwarding)
+ ret = DEVICE_CALL_FWD;
+ }
+ }
+
+ if (ret != DEVICE_NONE)
+ return ret;
+ }
+
+ if (devices > AUDIO_DEVICE_BIT_IN) {
+ /* Input Devices */
+ if (popcount(devices) >= 2) {
+ switch (devices) {
+ case AUDIO_DEVICE_IN_BUILTIN_MIC: return DEVICE_MAIN_MIC;
+ case AUDIO_DEVICE_IN_WIRED_HEADSET: return DEVICE_HEADSET_MIC;
+ case AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET: return DEVICE_BT_HEADSET_MIC;
+ case AUDIO_DEVICE_IN_BACK_MIC: return DEVICE_SUB_MIC;
+ case AUDIO_DEVICE_IN_USB_ACCESSORY:
+ case AUDIO_DEVICE_IN_USB_DEVICE:
+ case AUDIO_DEVICE_IN_USB_HEADSET: return DEVICE_USB_HEADSET_MIC;
+ case AUDIO_DEVICE_IN_FM_TUNER: return DEVICE_FM_TUNER;
+ default: return DEVICE_MAIN_MIC;
+ }
+ }
+ } else {
+ /* Output Devices */
+ if (adev->fm_via_a2dp && isFMRadioOn(adev)) {
+ return DEVICE_FM_EXTERNAL;
+ }
+
+ if (popcount(devices) == 1) {
+ /* Single Device */
+ switch (devices) {
+ case AUDIO_DEVICE_OUT_EARPIECE: return DEVICE_EARPIECE;
+ case AUDIO_DEVICE_OUT_SPEAKER: return DEVICE_SPEAKER;
+ case AUDIO_DEVICE_OUT_WIRED_HEADSET: return DEVICE_HEADSET;
+ case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: return DEVICE_HEADPHONE;
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: return DEVICE_BT_HEADSET;
+ case AUDIO_DEVICE_OUT_AUX_DIGITAL: return DEVICE_AUX_DIGITAL;
+ case AUDIO_DEVICE_OUT_USB_ACCESSORY:
+ case AUDIO_DEVICE_OUT_USB_DEVICE:
+ case AUDIO_DEVICE_OUT_USB_HEADSET: return DEVICE_USB_HEADSET;
+ case AUDIO_DEVICE_OUT_LINE: return DEVICE_LINE_OUT;
+
+ case AUDIO_DEVICE_NONE:
+ default: return DEVICE_NONE;
+ }
+ } else if (popcount(devices) == 2) {
+ /* Dual Device */
+ if (devices == (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET))
+ return DEVICE_SPEAKER_AND_HEADSET;
+ if (devices == (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE))
+ return DEVICE_SPEAKER_AND_HEADPHONE;
+ if ((devices & AUDIO_DEVICE_OUT_ALL_SCO) && (devices & AUDIO_DEVICE_OUT_SPEAKER))
+ return DEVICE_SPEAKER_AND_BT_HEADSET;
+ if ((devices & AUDIO_DEVICE_OUT_USB_DEVICE) && (devices & AUDIO_DEVICE_OUT_SPEAKER))
+ return DEVICE_SPEAKER;
+ if (devices == (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_LINE))
+ return DEVICE_SPEAKER_AND_LINEOUT;
+ }
+ }
+
+ return ret;
+}
+
+static int get_apcall_speech_param(struct stream_out *out)
+{
+ struct audio_device *adev = out->adev;
+ device_type device = get_device_id(adev, out->common.requested_devices);
+ int ret = 8;
+ //Voip WB
+ if(device == DEVICE_EARPIECE)return 8;
+ if(device == DEVICE_SPEAKER)return 9;
+ if(device == DEVICE_HEADSET || device == DEVICE_SPEAKER_AND_HEADSET)return 11;
+ if(device == DEVICE_HEADPHONE || device == DEVICE_SPEAKER_AND_HEADPHONE)return 10;
+ if(device == DEVICE_LINE_OUT || device == DEVICE_SPEAKER_AND_LINEOUT)return 12;
+ if(device == DEVICE_BT_HEADSET || device == DEVICE_SPEAKER_AND_BT_HEADSET) {
+ if (adev->voice) {
+ if (adev->voice->bluetooth_nrec) {
+ ALOGI("device-%s NREC OFF :%d", __func__, adev->voice->bluetooth_nrec);
+ return 6;
+ } else {
+ ALOGI("device-%s NREC OFF :%d", __func__, adev->voice->bluetooth_nrec);
+ return 7;
+ }
+ } else {
+ return 7;
+ }
+ }
+ return ret;
+}
+
+static device_type adev_get_device(void *stream, audio_usage_type usage_type)
+{
+ struct audio_device *adev = NULL;
+ device_type selected_device = DEVICE_NONE;
+
+ if (usage_type == AUSAGE_PLAYBACK) {
+ struct stream_out *out = (struct stream_out *)stream;
+ adev = out->adev;
+
+ if (adev->factory && adev->factory->mode != FACTORY_MODE_NONE)
+ selected_device = get_device_id(adev, adev->factory->out_device);
+ else
+ selected_device = get_device_id(adev, out->common.requested_devices);
+ } else {
+ struct stream_in *in = (struct stream_in *)stream;
+ adev = in->adev;
+
+ if (adev->factory && adev->factory->mode != FACTORY_MODE_NONE)
+ selected_device = get_device_id(adev, adev->factory->in_device);
+ else
+ selected_device = get_device_id(adev, in->common.requested_devices);
+ }
+
+ return selected_device;
+}
+
+static device_type adev_get_capture_device(void *stream, audio_usage_type usage_type)
+{
+ struct audio_device *adev = NULL;
+ device_type selected_device = DEVICE_NONE;
+
+ if (usage_type == AUSAGE_PLAYBACK) {
+ struct stream_out *out = (struct stream_out *)stream;
+ adev = out->adev;
+
+ if(adev->factory && adev->factory->mode != FACTORY_MODE_NONE)
+ selected_device = get_device_id(adev, adev->factory->in_device);
+ else
+ selected_device = get_indevice_id_from_outdevice(adev, get_device_id(adev, out->common.requested_devices));
+ } else {
+ struct stream_in *in = (struct stream_in *)stream;
+ adev = in->adev;
+
+ if(adev->factory && adev->factory->mode != FACTORY_MODE_NONE)
+ selected_device = get_device_id(adev, adev->factory->in_device);
+ else
+ selected_device = get_device_id(adev, in->common.requested_devices);
+ }
+
+ return selected_device;
+}
+
+// Modifier Selection
+static modifier_type adev_get_modifier(struct audio_device *adev, device_type device)
+{
+ modifier_type type = MODIFIER_NONE;
+
+ switch (device) {
+ case DEVICE_BT_HEADSET:
+ case DEVICE_SPEAKER_AND_BT_HEADSET:
+ if (adev->voice && adev->voice->bluetooth_samplerate == WB_SAMPLING_RATE)
+ type = MODIFIER_BT_SCO_RX_WB;
+ else
+ type = MODIFIER_BT_SCO_RX_NB;
+ break;
+
+ case DEVICE_BT_HEADSET_MIC:
+ case DEVICE_BT_NREC_HEADSET_MIC:
+ if (adev->voice && adev->voice->bluetooth_samplerate == WB_SAMPLING_RATE)
+ type = MODIFIER_BT_SCO_TX_WB;
+ else
+ type = MODIFIER_BT_SCO_TX_NB;
+ break;
+
+ default:
+ ALOGVV("device-%s: (%d) device, skip to set mixer", __func__, device);
+ break;
+ }
+
+ ALOGVV("device-%s return type:%d", __func__, type);
+ return type;
+}
+
+// Active Stream Count
+static int get_active_playback_count(struct audio_device *adev, struct stream_out *out)
+{
+ int active_count = 0;
+ struct listnode *node;
+ struct playback_stream *out_node;
+ struct stream_out *temp_out;
+
+ list_for_each(node, &adev->playback_list)
+ {
+ out_node = node_to_item(node, struct playback_stream, node);
+ if (out_node) {
+ temp_out = out_node->out;
+ if ((temp_out != out) &&
+ (temp_out->common.stream_type != ASTREAM_PLAYBACK_AUX_DIGITAL &&
+ temp_out->common.stream_type != ASTREAM_PLAYBACK_USB_DEVICE) &&
+ (temp_out->common.stream_status > STATUS_STANDBY))
+ active_count++;
+ }
+ }
+
+ return active_count;
+}
+
+static int get_active_capture_count(struct audio_device *adev)
+{
+ int active_count = 0;
+ struct listnode *node;
+ struct capture_stream *in_node;
+ struct stream_in *in;
+
+ list_for_each(node, &adev->capture_list)
+ {
+ in_node = node_to_item(node, struct capture_stream, node);
+ if (in_node) {
+ in = in_node->in;
+ if ((in->common.stream_type != ASTREAM_CAPTURE_CALL) &&
+ (in->common.stream_status > STATUS_STANDBY))
+ active_count++;
+ }
+ }
+
+ return active_count;
+}
+
+void update_call_stream(struct stream_out *out, audio_devices_t current_devices, audio_devices_t new_devices)
+{
+ struct audio_device *adev = out->adev;
+ struct listnode *node, *auxi;
+ struct playback_stream *out_node;
+ struct capture_stream *in_node;
+ device_type cur_device = get_device_id(adev, current_devices);
+ device_type new_device = get_device_id(adev, new_devices);
+ device_type cur_in_device = DEVICE_NONE;
+ device_type new_in_device = DEVICE_NONE;
+ bool path_changed = false;
+ bool call_state_changed = false;
+ bool call_state_BT = false;
+
+ ALOGI("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_t *lock[ASTREAM_MAX] = {NULL, };
+
+ if (isCallMode(adev)) {
+ cur_in_device = adev->active_capture_device;
+ new_in_device = get_indevice_id_from_outdevice(adev, new_device);
+ }
+
+ if ((cur_device != new_device) ||
+ ((cur_in_device != new_in_device)
+ && ((cur_in_device == DEVICE_USB_HEADSET_MIC) || (new_in_device == DEVICE_USB_HEADSET_MIC)))) {
+ path_changed = true;
+ }
+
+ if ((isAPCallMode(adev) && !is_active_usage_APCall(adev->proxy)) ||
+ (isCPCallMode(adev) && is_active_usage_APCall(adev->proxy)) ||
+ (!isCallMode(adev) && (cur_device == DEVICE_EARPIECE)) ||
+ (!isCallMode(adev) && (cur_device == DEVICE_SPEAKER))) {
+ call_state_changed = true;
+ }
+
+ if((adev->previous_amode == AUDIO_MODE_RINGTONE) && !is_active_usage_CPCall(adev->proxy) && isCPCallMode(adev) && new_device == DEVICE_BT_HEADSET) {
+ call_state_changed = true;
+ call_state_BT = true;
+ }
+
+ list_for_each_safe(node, auxi, &adev->playback_list) {
+ out_node = node_to_item(node, struct playback_stream, node);
+ if (out_node && out_node->out &&
+ (call_state_BT || ((cur_device == DEVICE_EARPIECE) || (new_device == DEVICE_EARPIECE)) ||
+ ((cur_device == DEVICE_SPEAKER) || (new_device == DEVICE_SPEAKER)))) {
+ lock[out_node->out->common.stream_type] = &out_node->out->common.lock;
+ if (lock[out_node->out->common.stream_type]) {
+ pthread_mutex_lock(lock[out_node->out->common.stream_type]);
+ /* RDMA PCM re-open for rcv <-> other path during call or when ending call*/
+ if ((path_changed || call_state_changed) &&
+ out_node->out->common.proxy_stream && out_node->out->common.stream_status != STATUS_STANDBY) {
+ proxy_stop_playback_stream((void *)(out_node->out->common.proxy_stream));
+ proxy_close_playback_stream((void *)(out_node->out->common.proxy_stream));
+ out_node->out->common.stream_status = STATUS_STANDBY;
+ } else {
+ pthread_mutex_unlock(lock[out_node->out->common.stream_type]);
+ lock[out_node->out->common.stream_type] = NULL;
+ }
+ }
+ }
+ }
+
+ list_for_each_safe(node, auxi, &adev->capture_list) {
+ in_node = node_to_item(node, struct capture_stream, node);
+ if (in_node && in_node->in) {
+ lock[in_node->in->common.stream_type] = &in_node->in->common.lock;
+ if (lock[in_node->in->common.stream_type] &&
+ ((in_node->in->common.stream_type == ASTREAM_CAPTURE_PRIMARY)
+ || (in_node->in->common.stream_type == ASTREAM_CAPTURE_CALL))) {
+ pthread_mutex_lock(lock[in_node->in->common.stream_type]);
+ /* WDMA PCM re-open for path change during call or when entering or ending call*/
+ if (((path_changed && isCallMode(adev)) ||
+ (call_state_changed && isAPCallMode(adev)) ||
+ (isCPCallMode(adev) && (in_node->in->common.stream_type == ASTREAM_CAPTURE_PRIMARY) && isCallRecording(in_node->in->requested_source)) ||
+ (!isCPCallMode(adev) && (in_node->in->common.stream_type == ASTREAM_CAPTURE_CALL || in_node->in->common.stream_type == ASTREAM_CAPTURE_PRIMARY))) &&
+ in_node->in->common.proxy_stream && in_node->in->common.stream_status == STATUS_PLAYING) {
+ proxy_stop_capture_stream((void *)(in_node->in->common.proxy_stream));
+ proxy_close_capture_stream((void *)(in_node->in->common.proxy_stream));
+ in_node->in->pcm_reconfig = true;
+ } else {
+ pthread_mutex_unlock(lock[in_node->in->common.stream_type]);
+ lock[in_node->in->common.stream_type] = NULL;
+ }
+ }
+ }
+ }
+
+ pthread_mutex_lock(&adev->lock);
+ if ((path_changed || adev->active_playback_ausage == AUSAGE_INCALL_MUSIC) &&
+ adev->voice && voice_is_call_active(adev->voice)) {
+ if (!adev->voice->mute_voice)
+ voice_set_rx_mute(adev->voice, true);
+
+ voice_set_call_active(adev->voice, false);
+ proxy_stop_voice_call(adev->proxy);
+ }
+
+ /* Do actual routing */
+ if (isCallMode(adev))
+ adev_set_route((void *)out, AUSAGE_PLAYBACK, ROUTE, CALL_DRIVE);
+ else if (out->common.stream_status == STATUS_STANDBY)
+ adev_set_route((void *)out, AUSAGE_PLAYBACK, UNROUTE, CALL_DRIVE); // to disable call rx/tx path
+ else
+ ALOGI("%s-%s: skip route", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_unlock(&adev->lock);
+
+ for(int idx = 0; idx < ASTREAM_MAX; idx++) {
+ if(lock[idx] != NULL)
+ pthread_mutex_unlock(lock[idx]);
+ }
+}
+
+void update_capture_stream(struct stream_in *in, audio_devices_t current_devices, audio_devices_t new_devices)
+{
+ struct audio_device *adev = in->adev;
+ device_type cur_in_device = get_device_id(adev, current_devices);
+ device_type new_in_device = get_device_id(adev, new_devices);
+
+ if(!isAPCallMode(adev) && !isCPCallMode(adev)) {
+ if((cur_in_device == DEVICE_BT_HEADSET_MIC && new_in_device != DEVICE_BT_HEADSET_MIC)
+ || (cur_in_device != DEVICE_BT_HEADSET_MIC && new_in_device == DEVICE_BT_HEADSET_MIC)) {
+ if (in->common.stream_status > STATUS_STANDBY) {
+ in->pcm_reconfig = true;
+ ALOGI("%s pcm_reconfig=true", __func__);
+ }
+ }
+ }
+}
+
+/* This function checks that output stream is Primary Output Stream or not */
+static bool is_primary_output(struct audio_device *adev, struct stream_out *out)
+{
+ return out == adev->primary_output;
+}
+
+/* This function checks that output stream can control Voice Call */
+static bool output_drives_call(struct audio_device *adev, struct stream_out *out)
+{
+ /* Only Primary Output Stream can control Voice Call */
+ return (out == adev->primary_output ||
+ out->common.stream_usage == AUSAGE_INCALL_MUSIC);
+}
+
+static bool adev_init_route(struct audio_device *adev)
+{
+ FILE *fp = NULL;
+ char mixer_info[MAX_MIXER_LENGTH] = MIXER_PATH_INFO;
+ char mixer_file[MAX_MIXER_LENGTH];
+ char mixer_path[MAX_MIXER_LENGTH];
+ bool use_default = true;
+ bool ret;
+
+ fp = fopen(mixer_info, "r");
+ if (fp) {
+ int readBytes = fread(mixer_file, sizeof(char), sizeof(mixer_file), fp);
+ if (readBytes > 0) {
+ mixer_file[readBytes] = 0; // add termination character
+
+ memset(mixer_path, 0, MAX_MIXER_LENGTH);
+ strcpy(mixer_path, DEFAULT_MIXER_PATH);
+ strcat(mixer_path, mixer_file);
+
+ ALOGI("proxy-%s: there is mixer_info, will use specific mixer file(%s)",
+ __func__, mixer_path);
+ ret = proxy_init_route(adev->proxy, mixer_path);
+ if (ret)
+ use_default = false;
+ }
+ fclose(fp);
+ }
+
+ if (use_default) {
+ memset(mixer_path, 0, MAX_MIXER_LENGTH);
+ strcpy(mixer_path, DEFAULT_MIXER_PATH);
+ strcat(mixer_path, DEFAULT_MIXER_FILE);
+
+ ALOGI("proxy-%s: no mixer_info or there is error, will use default mixer file(%s)",
+ __func__, mixer_path);
+ ret = proxy_init_route(adev->proxy, mixer_path);
+ }
+
+ return ret;
+}
+
+static void adev_deinit_route(struct audio_device *adev)
+{
+ proxy_deinit_route(adev->proxy);
+ return ;
+}
+
+bool adev_set_route(void *stream, audio_usage_type usage_type, bool set, force_route force)
+{
+ struct audio_device *adev = NULL;
+ struct stream_out *out = NULL;
+ struct stream_in *in = NULL;
+
+ bool drive_capture_route = false;
+ bool capture_set = set;
+
+ bool ret = true;
+
+ ALOGD("adev_set_route");
+ // Check Audio Path Routing Skip
+ {
+ if (usage_type == AUSAGE_PLAYBACK) {
+ out = (struct stream_out *)stream;
+ adev = out->adev;
+ }
+ else if (usage_type == AUSAGE_CAPTURE) {
+ in = (struct stream_in *)stream;
+ adev = in->adev;
+ }
+
+ //
+ if ((usage_type == AUSAGE_CAPTURE) && isAPCallMode(adev)) {
+ if (set) {
+ if (in->common.stream_status > STATUS_STANDBY) {
+ ALOGI("%s: need to routing tx/rx both for ap call case", __func__);
+ adev_set_route((void *)adev->primary_output, AUSAGE_PLAYBACK, ROUTE, CALL_DRIVE);
+ return ret;
+ }
+ } else {
+ ALOGI("%s: disable only tx for ap call case", __func__);
+ }
+ }
+ }
+
+ // Playback(Output) Path Control
+ if (usage_type == AUSAGE_PLAYBACK) {
+ audio_usage new_playback_ausage = AUSAGE_NONE;
+ device_type new_playback_device = DEVICE_NONE;
+ modifier_type new_playback_modifier = MODIFIER_NONE;
+
+ audio_usage old_playback_ausage = AUSAGE_NONE;
+ device_type old_playback_device = DEVICE_NONE;
+ modifier_type old_playback_modifier = MODIFIER_NONE;
+
+ out = (struct stream_out *)stream;
+ adev = out->adev;
+
+ if (output_drives_call(adev, out) && force == CALL_DRIVE) {
+ drive_capture_route = true;
+
+ if ((isAPCallMode(adev) || (!isCPCallMode(adev) && is_active_usage_CPCall(adev->proxy)))
+ && !(adev->active_input && (adev->active_input->common.stream_status > STATUS_STANDBY))) {
+ capture_set = UNROUTE; // need to enable rx and disable rx/tx both
+ }
+ ALOGI("%s: %s rx and %s tx both for CALL_DRIVE state",
+ __func__, set ? "enable" : "disable", capture_set ? "enable" : "disable");
+ }
+
+ if (set) {
+ // Selection Audio Usage & Audio Device & Modifier
+ new_playback_ausage = adev_get_ausage_from_stream(stream, usage_type);
+ new_playback_device = adev_get_device(stream, usage_type);
+ new_playback_modifier = adev_get_modifier(adev, new_playback_device);
+
+ /*
+ ** In cases of DP Audio or USB Audio Playback, special DMA is used
+ ** without any relation to audio path routing. So, keep previous path.
+ */
+ if (new_playback_ausage == AUSAGE_MEDIA &&
+ (new_playback_device == DEVICE_AUX_DIGITAL || new_playback_device == DEVICE_USB_HEADSET)) {
+ ALOGI("%s-%s: keep current route for device(%s) and usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[adev->active_playback_device],
+ usage_table[adev->active_playback_ausage]);
+ return ret;
+ }
+
+ if (adev->is_playback_path_routed) {
+ /* Route Change Case */
+ if ((adev->active_playback_ausage == new_playback_ausage) &&
+ (adev->active_playback_device == new_playback_device)) {
+ // Requested same usage and same device, skip!!!
+ ALOGI("%s-%s-1: skip re-route as same device(%s) and same usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[new_playback_device], usage_table[new_playback_ausage]);
+ } else {
+ // Requested different usage or device, re-route!!!
+ proxy_set_route(adev->proxy, (int)new_playback_ausage,
+ (int)new_playback_device, (int)new_playback_modifier, ROUTE);
+ ALOGI("%s-%s-1: re-routes to device(%s) for usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[new_playback_device], usage_table[new_playback_ausage]);
+ }
+ } else {
+ /* New Route Case */
+ proxy_set_route(adev->proxy, (int)new_playback_ausage,
+ (int)new_playback_device, (int)new_playback_modifier, ROUTE);
+ ALOGI("%s-%s-1: routes to device(%s) for usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[new_playback_device], usage_table[new_playback_ausage]);
+
+ adev->is_playback_path_routed = true;
+ }
+ } else {
+ // Get current active Audio Usage & Audio Device & Modifier
+ old_playback_ausage = adev->active_playback_ausage;
+ old_playback_device = adev->active_playback_device;
+ old_playback_modifier = adev->active_playback_modifier;
+
+ if (adev->is_playback_path_routed) {
+ /* Route Reset Case */
+ if ((force != FORCE_ROUTE) &&
+ ((get_active_playback_count(adev, out) > 0) || isCPCallMode(adev)
+ || (adev->factory && adev->factory->mode == FACTORY_MODE_LOOPBACK)
+ || (isFMRadioOn(adev)))) {
+ // There are some active playback stream or CP Call Mode or Loopback Mode or FM Radio is ON
+ ALOGI("%s-%s-1: current device(%s) is still in use by other streams or CP Call Mode or Loopback Mode or FM Radio is ON",
+ stream_table[out->common.stream_type], __func__,
+ device_table[old_playback_device]);
+
+ new_playback_ausage = old_playback_ausage;
+ new_playback_device = old_playback_device;
+ new_playback_modifier = old_playback_modifier;
+ } else {
+ // There are no active playback stream
+ proxy_set_route(adev->proxy, (int)old_playback_ausage,
+ (int)old_playback_device, (int)old_playback_modifier, UNROUTE);
+
+ ALOGI("%s-%s-1: unroutes to device(%s) for usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[old_playback_device], usage_table[old_playback_ausage]);
+
+ adev->is_playback_path_routed = false;
+ }
+ } else {
+ /* Abnormal Case */
+ ALOGE("%s-%s-1: already unrouted", stream_table[out->common.stream_type], __func__);
+ }
+ }
+
+ adev->active_playback_ausage = new_playback_ausage;
+ adev->active_playback_device = new_playback_device;
+ adev->active_playback_modifier = new_playback_modifier;
+ }
+
+ // Capture(Input) Path Control
+ if (usage_type == AUSAGE_CAPTURE ||
+ (usage_type == AUSAGE_PLAYBACK && drive_capture_route)) {
+ audio_usage new_capture_ausage = AUSAGE_NONE;
+ device_type new_capture_device = DEVICE_NONE;
+ modifier_type new_capture_modifier = MODIFIER_NONE;
+
+ audio_usage old_capture_ausage = AUSAGE_NONE;
+ device_type old_capture_device = DEVICE_NONE;
+ modifier_type old_capture_modifier = MODIFIER_NONE;
+
+ // In case of Call, Primary Playback Stream will drive Capture(Input) Path Control
+ if (drive_capture_route) {
+ if (capture_set) {
+ // Selection Audio Usage & Audio Device & Modifier
+ new_capture_ausage = adev_get_ausage_from_stream(stream, usage_type);
+ new_capture_device = adev_get_capture_device(stream, usage_type);
+ new_capture_modifier = adev_get_modifier(adev, new_capture_device);
+
+ if (adev->is_capture_path_routed) {
+ /* Route Change Case */
+ if ((adev->active_capture_ausage == new_capture_ausage) &&
+ (adev->active_capture_device == new_capture_device)) {
+ // Requested same usage and same device, skip!!!
+ ALOGI("%s-%s-2: skip re-route as same device(%s) and same usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[new_capture_device], usage_table[new_capture_ausage]);
+ } else {
+ // Requested different usage or device, re-route!!!
+ proxy_set_route(adev->proxy, (int)new_capture_ausage,
+ (int)new_capture_device, (int)new_capture_modifier, ROUTE);
+ ALOGI("%s-%s-2: re-routes to device(%s) for usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[new_capture_device], usage_table[new_capture_ausage]);
+ }
+ } else {
+ /* New Route Case */
+ proxy_set_route(adev->proxy, (int)new_capture_ausage,
+ (int)new_capture_device, (int)new_capture_modifier, ROUTE);
+ ALOGI("%s-%s-2: routes to device(%s) for usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[new_capture_device], usage_table[new_capture_ausage]);
+
+ adev->is_capture_path_routed = true;
+ }
+ } else {
+ // Get current active Audio Usage & Audio Device & Modifier
+ old_capture_ausage = adev->active_capture_ausage;
+ old_capture_device = adev->active_capture_device;
+ old_capture_modifier = adev->active_capture_modifier;
+
+ if (adev->is_capture_path_routed) {
+ /* Route Reset Case */
+ if (get_active_capture_count(adev) > 0) {
+ // There are some active capture stream
+ ALOGI("%s-%s-2: current device(%s) is still in use by other streams",
+ stream_table[out->common.stream_type], __func__,
+ device_table[old_capture_device]);
+
+ new_capture_ausage = old_capture_ausage;
+ new_capture_device = old_capture_device;
+ new_capture_modifier = old_capture_modifier;
+ } else {
+ // There are no active capture stream
+ proxy_set_route(adev->proxy, (int)old_capture_ausage,
+ (int)old_capture_device, (int)old_capture_modifier, UNROUTE);
+
+ ALOGI("%s-%s-2: unroutes to device(%s) for usage(%s)",
+ stream_table[out->common.stream_type], __func__,
+ device_table[old_capture_device], usage_table[old_capture_ausage]);
+
+ adev->is_capture_path_routed = false;
+ }
+ } else {
+ /* Abnormal Case */
+ ALOGE("%s-%s-2: already unrouted", stream_table[out->common.stream_type], __func__);
+ }
+ }
+ }
+ // General Capture Stream
+ else {
+ in = (struct stream_in *)stream;
+ adev = in->adev;
+
+ if (set) {
+ // Selection Audio Usage & Audio Device & Modifier
+ new_capture_ausage = adev_get_ausage_from_stream(stream, usage_type);
+ new_capture_device = adev_get_device(stream, usage_type);
+ new_capture_modifier = adev_get_modifier(adev, new_capture_device);
+
+ if (adev->is_capture_path_routed) {
+ /* Route Change Case */
+ if ((adev->fm_need_route == false)&&(((adev->active_capture_ausage == new_capture_ausage) &&
+ (adev->active_capture_device == new_capture_device))||
+ ((isFMRadioOn(adev)) && (new_capture_ausage != AUSAGE_CAMCORDER)))) {
+ // Requested same usage and same device, skip!!!
+ ALOGI("%s-%s-3: skip re-route as same device(%s) and same usage(%s)",
+ stream_table[in->common.stream_type], __func__,
+ device_table[new_capture_device], usage_table[new_capture_ausage]);
+ } else {
+ // Requested different usage or device, re-route!!!
+ proxy_set_route(adev->proxy, (int)new_capture_ausage,
+ (int)new_capture_device, (int)new_capture_modifier, ROUTE);
+ ALOGI("%s-%s-3: re-routes to device(%s) for usage(%s)",
+ stream_table[in->common.stream_type], __func__,
+ device_table[new_capture_device], usage_table[new_capture_ausage]);
+ }
+ } else {
+ /* New Route Case */
+ proxy_set_route(adev->proxy, (int)new_capture_ausage,
+ (int)new_capture_device, (int)new_capture_modifier, ROUTE);
+ ALOGI("%s-%s-3: routes to device(%s) for usage(%s)",
+ stream_table[in->common.stream_type], __func__,
+ device_table[new_capture_device], usage_table[new_capture_ausage]);
+
+ adev->is_capture_path_routed = true;
+ }
+ } else {
+ // Get current active Audio Usage & Audio Device & Modifier
+ old_capture_ausage = adev->active_capture_ausage;
+ old_capture_device = adev->active_capture_device;
+ old_capture_modifier = adev->active_capture_modifier;
+
+ if (adev->is_capture_path_routed) {
+ /* Route Reset Case */
+ if ((force != FORCE_ROUTE) &&
+ ((get_active_capture_count(adev) > 0) || isCPCallMode(adev))) {
+ // There are some active capture stream
+ ALOGI("%s-%s-3: current device(%s) is still in use by other streams or CallMode",
+ stream_table[in->common.stream_type], __func__,
+ device_table[old_capture_device]);
+
+ new_capture_ausage = old_capture_ausage;
+ new_capture_device = old_capture_device;
+ new_capture_modifier = old_capture_modifier;
+ } else {
+ // There are no active capture stream
+ proxy_set_route(adev->proxy, (int)old_capture_ausage,
+ (int)old_capture_device, (int)old_capture_modifier, UNROUTE);
+
+ ALOGI("%s-%s-3: unroutes to device(%s) for usage(%s)",
+ stream_table[in->common.stream_type], __func__,
+ device_table[old_capture_device], usage_table[old_capture_ausage]);
+
+ adev->is_capture_path_routed = false;
+ }
+ } else {
+ /* Abnormal Case */
+ ALOGE("%s-%s-3: already unrouted", stream_table[in->common.stream_type], __func__);
+ }
+ }
+ }
+
+ adev->active_capture_ausage = new_capture_ausage;
+ adev->active_capture_device = new_capture_device;
+ adev->active_capture_modifier = new_capture_modifier;
+ }
+
+ return ret;
+}
+
+// set call fwd path
+void set_call_forwarding(struct audio_device *adev, bool mode)
+{
+ ALOGI("%s: enter : %s", __func__, mode ? "on" : "off");
+ if (adev->voice && voice_is_call_mode(adev->voice)) {
+ pthread_mutex_unlock(&adev->lock);
+ if (adev->primary_output && adev->primary_output->common.requested_devices != 0)
+ update_call_stream(adev->primary_output, adev->primary_output->common.requested_devices, adev->primary_output->common.requested_devices);
+ pthread_mutex_lock(&adev->lock);
+
+ /* Start Call */
+ if (adev->proxy)
+ proxy_start_voice_call(adev->proxy);
+ voice_set_call_active(adev->voice, true);
+ } else {
+ if (adev->primary_output)
+ adev_set_route((void *)adev->primary_output, AUSAGE_PLAYBACK, ROUTE, CALL_DRIVE);
+ }
+}
+
+
+/****************************************************************************/
+/** **/
+/** Compress Offload Specific Functions Implementation **/
+/** **/
+/****************************************************************************/
+static int send_offload_msg(struct stream_out *out, offload_msg_type msg)
+{
+ struct offload_msg *msg_node = NULL;
+ int ret = 0;
+
+ msg_node = (struct offload_msg *)calloc(1, sizeof(struct offload_msg));
+ if (msg_node) {
+ msg_node->msg = msg;
+
+ list_add_tail(&out->offload.msg_list, &msg_node->node);
+ pthread_cond_signal(&out->offload.msg_cond);
+
+ ALOGVV("offload_out-%s: Sent Message = %s", __func__, offload_msg_table[msg]);
+ } else {
+ ALOGE("offload_out-%s: Failed to allocate memory for Offload MSG", __func__);
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+
+static offload_msg_type recv_offload_msg(struct stream_out *out)
+{
+ struct listnode *offload_msg_list = &(out->offload.msg_list);
+
+ struct listnode *head = list_head(offload_msg_list);
+ struct offload_msg *msg_node = node_to_item(head, struct offload_msg, node);
+ offload_msg_type msg = msg_node->msg;
+
+ list_remove(head);
+ free(msg_node);
+
+ ALOGVV("offload_out-%s: Received Message = %s", __func__, offload_msg_table[msg]);
+ return msg;
+}
+
+static void *offload_cbthread_loop(void *context)
+{
+ struct stream_out *out = (struct stream_out *) context;
+ bool get_exit = false;
+ int ret = 0;
+
+ setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO);
+ set_sched_policy(0, SP_FOREGROUND);
+ prctl(PR_SET_NAME, (unsigned long)"Offload Callback", 0, 0, 0);
+
+ ALOGI("%s-%s: Started running Offload Callback Thread", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ do {
+ offload_msg_type msg = OFFLOAD_MSG_INVALID;
+ stream_callback_event_t event;
+ bool need_callback = true;
+
+ if (list_empty(&out->offload.msg_list)) {
+ ALOGVV("%s-%s: transit to sleep", stream_table[out->common.stream_type], __func__);
+ pthread_cond_wait(&out->offload.msg_cond, &out->common.lock);
+ ALOGVV("%s-%s: transit to wake-up", stream_table[out->common.stream_type], __func__);
+ }
+
+ if (!list_empty(&out->offload.msg_list))
+ msg = recv_offload_msg(out);
+
+ if (msg == OFFLOAD_MSG_EXIT) {
+ get_exit = true;
+ continue;
+ }
+
+ out->offload.callback_thread_blocked = true;
+ pthread_mutex_unlock(&out->common.lock);
+
+ switch (msg) {
+ case OFFLOAD_MSG_WAIT_WRITE:
+ // call compress_wait
+ ret = proxy_offload_compress_func(out->common.proxy_stream, COMPRESS_TYPE_WAIT);
+ // In case of Wait(Write Block), Error Callback is not needed.
+ event = STREAM_CBK_EVENT_WRITE_READY;
+ break;
+
+ case OFFLOAD_MSG_WAIT_PARTIAL_DRAIN:
+ // call compress_next_track
+ ret = proxy_offload_compress_func(out->common.proxy_stream, COMPRESS_TYPE_NEXTTRACK);
+
+ // call compress_partial_drain
+ ret = proxy_offload_compress_func(out->common.proxy_stream, COMPRESS_TYPE_PARTIALDRAIN);
+ if (ret) {
+ event = STREAM_CBK_EVENT_ERROR;
+ ALOGE("%s-%s: will Callback Error", stream_table[out->common.stream_type], __func__);
+ } else
+ event = STREAM_CBK_EVENT_DRAIN_READY;
+
+ /* gapless playback requires compress_start for kernel 4.4 while moving to Next track
+ therefore once partial drain is completed state changed to IDLE and when next
+ compress_write is called state is changed back to PLAYING */
+ pthread_mutex_lock(&out->common.lock);
+ proxy_stop_playback_stream(out->common.proxy_stream);
+ out->common.stream_status = STATUS_IDLE;
+ pthread_mutex_unlock(&out->common.lock);
+ ALOGI("%s-%s: Transit to Idle", stream_table[out->common.stream_type], __func__);
+ break;
+
+ case OFFLOAD_MSG_WAIT_DRAIN:
+ // call compress_drain
+ ret = proxy_offload_compress_func(out->common.proxy_stream, COMPRESS_TYPE_DRAIN);
+ if (ret) {
+ event = STREAM_CBK_EVENT_ERROR;
+ ALOGE("%s-%s: will Callback Error", stream_table[out->common.stream_type], __func__);
+ } else
+ event = STREAM_CBK_EVENT_DRAIN_READY;
+ break;
+
+ default:
+ ALOGE("Invalid message = %u", msg);
+ need_callback = false;
+ break;
+ }
+
+ pthread_mutex_lock(&out->common.lock);
+ out->offload.callback_thread_blocked = false;
+ pthread_cond_signal(&out->offload.sync_cond);
+
+ if (need_callback) {
+ out->offload.callback(event, NULL, out->offload.cookie);
+ if (event == STREAM_CBK_EVENT_DRAIN_READY)
+ ALOGD("%s-%s: Callback to Platform with %d", stream_table[out->common.stream_type],
+ __func__, event);
+ }
+ } while(!get_exit);
+
+ /* Clean the message list */
+ pthread_cond_signal(&out->offload.sync_cond);
+ while(!list_empty(&out->offload.msg_list))
+ recv_offload_msg(out);
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGI("%s-%s: Stopped running Offload Callback Thread", stream_table[out->common.stream_type], __func__);
+ return NULL;
+}
+
+static int create_offload_callback_thread(struct stream_out *out)
+{
+ pthread_cond_init(&out->offload.msg_cond, (const pthread_condattr_t *) NULL);
+ pthread_cond_init(&out->offload.sync_cond, (const pthread_condattr_t *) NULL);
+
+ pthread_create(&out->offload.callback_thread, (const pthread_attr_t *) NULL, offload_cbthread_loop, out);
+ out->offload.callback_thread_blocked = false;
+
+ return 0;
+}
+
+static int destroy_offload_callback_thread(struct stream_out *out)
+{
+ int ret = 0;
+
+ pthread_mutex_lock(&out->common.lock);
+ ret = send_offload_msg(out, OFFLOAD_MSG_EXIT);
+ pthread_mutex_unlock(&out->common.lock);
+
+ pthread_join(out->offload.callback_thread, (void **) NULL);
+ ALOGI("%s-%s: Joined Offload Callback Thread!", stream_table[out->common.stream_type], __func__);
+
+ pthread_cond_destroy(&out->offload.sync_cond);
+ pthread_cond_destroy(&out->offload.msg_cond);
+
+ return 0;
+}
+
+/****************************************************************************/
+/** **/
+/** The Stream_Out Function Implementation **/
+/** **/
+/****************************************************************************/
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ ALOGVV("%s-%s: exit with sample rate = %u", stream_table[out->common.stream_type], __func__,
+ out->common.requested_sample_rate);
+ return out->common.requested_sample_rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream __unused, uint32_t rate __unused)
+{
+ return -ENOSYS;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ size_t buffer_size = 0;
+
+ ALOGVV("%s-%s: enter", stream_table[out->common.stream_type], __func__);
+
+ // will return buffer size based on requested PCM configuration
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD)
+ buffer_size = proxy_get_actual_period_size(out->common.proxy_stream);
+ else
+ buffer_size = proxy_get_actual_period_size(out->common.proxy_stream) *
+ (unsigned int)audio_stream_out_frame_size((const struct audio_stream_out *)stream);
+
+ ALOGVV("%s-%s: exit with %d bytes", stream_table[out->common.stream_type], __func__, (int)buffer_size);
+ return buffer_size;
+}
+
+static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ ALOGVV("%s-%s: exit with channel mask = 0x%x", stream_table[out->common.stream_type], __func__,
+ out->common.requested_channel_mask);
+ return out->common.requested_channel_mask;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ ALOGVV("%s-%s: exit with audio format = 0x%x", stream_table[out->common.stream_type], __func__,
+ out->common.requested_format);
+ return out->common.requested_format;
+}
+
+static int out_set_format(struct audio_stream *stream __unused, audio_format_t format __unused)
+{
+ return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->adev;
+
+ ALOGVV("%s-%s: enter", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_status > STATUS_STANDBY) {
+ /* Stops stream & transit to Idle. */
+ if (out->common.stream_status > STATUS_IDLE) {
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD &&
+ out->offload.callback_thread_blocked)
+ pthread_cond_wait(&out->offload.sync_cond, &out->common.lock);
+
+ proxy_stop_playback_stream((void *)(out->common.proxy_stream));
+ out->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[out->common.stream_type], __func__);
+ }
+
+ /* Closes device & transit to Standby. */
+ proxy_close_playback_stream((void *)(out->common.proxy_stream));
+ out->common.stream_status = STATUS_STANDBY;
+ ALOGI("%s-%s: transited to Standby", stream_table[out->common.stream_type], __func__);
+
+ // Check VoIP SE Untrigger
+ if (out->common.stream_type == ASTREAM_PLAYBACK_PRIMARY && adev->voipse_on) {
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALLBUFFTYPE_CONTROL_NAME, MIXER_VALUE_OFF);
+ pthread_mutex_lock(&adev->lock);
+ adev->voipse_on = false;
+ pthread_mutex_unlock(&adev->lock);
+ ALOGI("%s-%s: VoIP SE Un-Triggered!", stream_table[out->common.stream_type], __func__);
+ }
+
+ // Have to unroute Audio Path after close PCM Device
+ pthread_mutex_lock(&adev->lock);
+ if (adev->is_playback_path_routed) {
+ if (out->common.stream_type == ASTREAM_PLAYBACK_INCALL_MUSIC &&
+ adev->incallmusic_on && isCPCallMode(adev)) {
+ ALOGI("%s-%s: try to re-route to call path for standby", stream_table[out->common.stream_type], __func__);
+ // Incall-music should be disabled before re-routing call path, to get proper call path
+ adev->incallmusic_on = false;
+ if (adev->primary_output){
+ pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&out->common.lock);
+ update_call_stream(adev->primary_output, adev->primary_output->common.requested_devices, adev->primary_output->common.requested_devices);
+ proxy_start_voice_call(adev->proxy);
+ pthread_mutex_lock(&out->common.lock);
+ pthread_mutex_lock(&adev->lock);
+ }
+ else
+ ALOGW("%s-%s: Call routing skipped", stream_table[out->common.stream_type], __func__);
+ } else {
+ ALOGI("%s-%s: try to unroute for standby", stream_table[out->common.stream_type], __func__);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_INCALL_MUSIC)
+ adev->incallmusic_on = false;
+ adev_set_route((void *)out, AUSAGE_PLAYBACK, UNROUTE, NON_FORCE_ROUTE);
+ }
+ }
+ out->force = NON_FORCE_ROUTE;
+ out->rollback_devices = AUDIO_DEVICE_NONE;
+
+ pthread_mutex_unlock(&adev->lock);
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGVV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ ALOGV("%s-%s: enter with fd(%d)", stream_table[out->common.stream_type], __func__, fd);
+
+ const size_t len = 256;
+ char buffer[len];
+ snprintf(buffer, len, "\nAudio Stream Out(%x)::dump\n", out->requested_flags);
+ write(fd,buffer,strlen(buffer));
+ bool justLocked = pthread_mutex_trylock(&out->common.lock) == 0;
+ snprintf(buffer, len, "\tMutex: %s\n", justLocked ? "locked" : "unlocked");
+ write(fd,buffer,strlen(buffer));
+ if(justLocked)
+ pthread_mutex_unlock(&out->common.lock);
+
+ snprintf(buffer, len, "\toutput devices %d\n",out->common.requested_devices);
+ write(fd,buffer,strlen(buffer));
+
+ snprintf(buffer, len, "\toutput flgas: %x\n",out->requested_flags);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\toutput sample rate: %u\n",out->common.requested_sample_rate);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\toutput channel mask: %d\n",out->common.requested_channel_mask);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\toutput format: %d\n",out->common.requested_format);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\toutput audio usage: %d\n",out->common.stream_usage);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\toutput standby state: %d\n",out->common.stream_status);
+ write(fd,buffer,strlen(buffer));
+
+ proxy_dump_playback_stream(out->common.proxy_stream, fd);
+
+ ALOGV("%s-%s: exit with fd(%d)", stream_table[out->common.stream_type], __func__, fd);
+
+ return 0;
+}
+
+static audio_devices_t out_get_device(const struct audio_stream *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ ALOGVV("%s-%s: exit with device = %u", stream_table[out->common.stream_type], __func__,
+ out->common.requested_devices);
+ return out->common.requested_devices;
+}
+
+static int out_set_device(struct audio_stream *stream __unused, audio_devices_t device __unused)
+{
+ return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->adev;
+
+ struct str_parms *parms;
+ char value[32];
+ int ret = 0;
+
+ ALOGD("%s-%s: enter with param = %s", stream_table[out->common.stream_type], __func__, kvpairs);
+
+ parms = str_parms_create_str(kvpairs);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ proxy_setparam_playback_stream(out->common.proxy_stream, (void *)parms);
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+ if (ret >= 0) {
+ audio_devices_t requested_devices = atoi(value);
+ audio_devices_t current_devices = out->common.requested_devices;
+ bool need_routing = false;
+
+ if(out->common.stream_type == ASTREAM_PLAYBACK_INCALL_MUSIC && isUSBHeadsetConnect(adev) && requested_devices == AUDIO_DEVICE_OUT_TELEPHONY_TX && isCallMode(adev)){
+ requested_devices = AUDIO_DEVICE_OUT_EARPIECE;
+ ALOGD("%s-%s: incall music (usb) -> requested_devices = AUDIO_DEVICE_OUT_EARPIECE", stream_table[out->common.stream_type], __func__);
+ }
+ /*
+ * AudioFlinger informs Audio path is this device.
+ * AudioHAL has to decide to do actual routing or not.
+ */
+ if (requested_devices != AUDIO_DEVICE_NONE) {
+ ALOGI("%s-%s: requested to change route from %s to %s",
+ stream_table[out->common.stream_type], __func__,
+ device_table[get_device_id(adev, current_devices)],
+ device_table[get_device_id(adev, requested_devices)]);
+
+ /* Assign requested device to Output Stream */
+ out->common.requested_devices = requested_devices;
+
+ if(out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD){
+ adev->update_offload_volume = true;
+ }
+
+ pthread_mutex_lock(&adev->lock);
+
+ /* Check actual routing is needed or not */
+ // Check Force Routing for Alarm sound/Shutter tone or Call Routing
+ if (is_primary_output(adev, out) && (popcount(requested_devices) == 2)) {
+ need_routing = true;
+ }
+ // Actual routing is needed at CP Voice Call routing request or device change request
+ if ((output_drives_call(adev, out) && isCallMode(adev)) ||
+ (adev->is_playback_path_routed || (out->common.stream_status > STATUS_STANDBY))) {
+ need_routing = true;
+ }
+
+ pthread_mutex_unlock(&adev->lock);
+
+ if (need_routing) {
+ /* Check routing type */
+ out->force = NON_FORCE_ROUTE;
+ if (is_primary_output(adev, out) && (popcount(requested_devices) == 2)) {
+ // Force Routing for Alarm sound/Shutter tone, It needs rollback to previous device
+ out->force = FORCE_ROUTE;
+ out->rollback_devices = adev->current_devices;
+ ALOGD("%s-%s: rollback_devices = %d", stream_table[out->common.stream_type], __func__, out->rollback_devices);
+ } else if (output_drives_call(adev, out)) {
+ // CALL_DRIVE routing is for both playback and capture device change
+ // case 1. Device change during CP call mode
+ // case 2. To reset call rx/tx devices When disconnect CP call
+ // case 3. Device change when it has active input during AP call mode
+ if (isCPCallMode(adev)
+ || (!isCPCallMode(adev) && is_active_usage_CPCall(adev->proxy))
+ || (isAPCallMode(adev) && adev->active_input && (adev->active_input->common.stream_status > STATUS_STANDBY)))
+ out->force = CALL_DRIVE;
+
+ // update incall-music if routing is request for incall-music stream
+ if (out->common.stream_type == ASTREAM_PLAYBACK_INCALL_MUSIC && isCPCallMode(adev) &&
+ requested_devices != AUDIO_DEVICE_NONE) {
+ adev->incallmusic_on = true;
+ ALOGD("%s-%s: enable Incall Music = %d", stream_table[out->common.stream_type], __func__, adev->incallmusic_on);
+ }
+ } else {
+ adev->current_devices = requested_devices;
+ ALOGD("%s-%s: adev->current_devices = %d", stream_table[out->common.stream_type], __func__, adev->current_devices);
+ }
+
+ pthread_mutex_lock(&adev->lock);
+ if (output_drives_call(adev, out) && isCallMode(adev)) {
+ pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&out->common.lock);
+ update_call_stream(out, current_devices, requested_devices);
+ pthread_mutex_lock(&out->common.lock);
+ } else {
+ /* Do actual routing */
+ adev_set_route((void *)out, AUSAGE_PLAYBACK, ROUTE, out->force);
+ pthread_mutex_unlock(&adev->lock);
+ }
+
+ /* Primary output stream can be handled routing for CP Centric call */
+ if (output_drives_call(adev, out) && isCPCallMode(adev)) {
+ if (adev->voice) {
+ /* Set Path to RIL-Client */
+ // In order to reduce set_path latency, call it before Voice PCM Create/Start
+ voice_set_path(adev->voice, out->common.requested_devices);
+ ALOGV("%s-%s: RIL Route Updated for %s",
+ stream_table[out->common.stream_type], __func__,
+ device_table[get_device_id(adev, out->common.requested_devices)]);
+
+ if (!voice_is_call_active(adev->voice)) {
+ /* Start Call */
+ proxy_start_voice_call(adev->proxy);
+ voice_set_call_active(adev->voice, true);
+ ALOGI("%s-%s: *** Started CP Voice Call ***",
+ stream_table[out->common.stream_type], __func__);
+ }
+
+ /* Set Volume to RIL-Client */
+ voice_set_volume(adev->voice, adev->voice_volume);
+ ALOGV("%s-%s: RIL Volume Updated with %f",
+ stream_table[out->common.stream_type],__func__, adev->voice_volume);
+ }
+ }
+ } else {
+ if (!is_primary_output(adev, out)) {
+ adev->current_devices = requested_devices;
+ ALOGD("%s-%s: adev->current_devices = %d", stream_table[out->common.stream_type], __func__, adev->current_devices);
+ }
+ ALOGV("%s-%s: real routing is not needed", stream_table[out->common.stream_type], __func__);
+ }
+ } else {
+ /* When audio device will be changed, AudioFlinger requests to route with AUDIO_DEVICE_NONE */
+ ALOGV("%s-%s: requested to change route to AUDIO_DEVICE_NONE",
+ stream_table[out->common.stream_type], __func__);
+ }
+ }
+
+ /*
+ * Android Audio System can change PCM Configurations(Format, Channel and Rate) for Audio Stream
+ * when this Audio Stream is not working.
+ */
+ // Change Audio Format
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_FORMAT, value, sizeof(value));
+ if (ret >= 0) {
+ if (out->common.stream_status > STATUS_READY)
+ goto not_acceptable;
+ else {
+ audio_format_t new_format = (audio_format_t)atoi(value);
+ if ((new_format != out->common.requested_format) && (new_format != AUDIO_FORMAT_DEFAULT)) {
+ struct audio_config new_config;
+
+ out->common.requested_format = new_format;
+
+ new_config.sample_rate = out->common.requested_sample_rate;
+ new_config.channel_mask = out->common.requested_channel_mask;
+ new_config.format = new_format;
+ proxy_reconfig_playback_stream(out->common.proxy_stream, out->common.stream_type,
+ &new_config);
+ ALOGD("%s-%s: changed format to %#x from %#x",
+ stream_table[out->common.stream_type], __func__,
+ new_format, out->common.requested_format);
+ } else
+ ALOGD("%s-%s: requested to change same format to %#x",
+ stream_table[out->common.stream_type], __func__, new_format);
+ }
+ }
+
+ // Change Audio Channel Mask
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_CHANNELS, value, sizeof(value));
+ if (ret >= 0) {
+ if (out->common.stream_status > STATUS_READY)
+ goto not_acceptable;
+ else {
+ audio_channel_mask_t new_chmask = (audio_channel_mask_t)atoi(value);
+ if ((new_chmask != out->common.requested_channel_mask) && (new_chmask != AUDIO_CHANNEL_NONE)) {
+ struct audio_config new_config;
+
+ out->common.requested_channel_mask = new_chmask;
+
+ new_config.sample_rate = out->common.requested_sample_rate;
+ new_config.channel_mask = new_chmask;
+ new_config.format = out->common.requested_format;
+ proxy_reconfig_playback_stream(out->common.proxy_stream, out->common.stream_type,
+ &new_config);
+ ALOGD("%s-%s: changed channel mask to %#x from %#x",
+ stream_table[out->common.stream_type], __func__,
+ new_chmask, out->common.requested_channel_mask);
+ } else
+ ALOGD("%s-%s: requested to change same channel mask to %#x",
+ stream_table[out->common.stream_type], __func__, new_chmask);
+ }
+ }
+
+ // Change Audio Sampling Rate
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE, value, sizeof(value));
+ if (ret >= 0) {
+ if (out->common.stream_status > STATUS_READY)
+ goto not_acceptable;
+ else {
+ uint32_t new_rate = (uint32_t)atoi(value);
+ if ((new_rate != out->common.requested_sample_rate) && (new_rate != 0)) {
+ struct audio_config new_config;
+
+ out->common.requested_sample_rate = new_rate;
+
+ new_config.sample_rate = new_rate;
+ new_config.channel_mask = out->common.requested_channel_mask;
+ new_config.format = out->common.requested_format;
+ proxy_reconfig_playback_stream(out->common.proxy_stream, out->common.stream_type,
+ &new_config);
+ ALOGD("%s-%s: changed sampling rate to %dHz from %dHz",
+ stream_table[out->common.stream_type], __func__,
+ new_rate, out->common.requested_sample_rate);
+ } else
+ ALOGD("%s-%s: requested to change same sampling rate to %dHz",
+ stream_table[out->common.stream_type], __func__, new_rate);
+ }
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ str_parms_destroy(parms);
+ ALOGVV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return 0;
+
+not_acceptable:
+ pthread_mutex_unlock(&out->common.lock);
+ str_parms_destroy(parms);
+ ALOGE("%s-%s: This parameter cannot accept as Stream is working",
+ stream_table[out->common.stream_type], __func__);
+ return -ENOSYS;
+}
+
+static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct str_parms *query = str_parms_create_str(keys);
+ struct str_parms *reply = str_parms_create();
+ char * result_str;
+
+ ALOGD("%s-%s: enter with param = %s", stream_table[out->common.stream_type], __func__, keys);
+
+ pthread_mutex_lock(&out->common.lock);
+
+ // Get Current Devices
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_ROUTING)) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, (int)out->common.requested_devices);
+ }
+
+ // Get Current Audio Format
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_FORMAT, (int)out->common.requested_format);
+ }
+
+ // Get Current Audio Frame Count
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FRAME_COUNT)) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+ (int)proxy_get_actual_period_size(out->common.proxy_stream));
+ }
+
+ // Some parameters can be gotten from Audio Stream Proxy
+ proxy_getparam_playback_stream(out->common.proxy_stream, query, reply);
+
+ result_str = str_parms_to_str(reply);
+ str_parms_destroy(query);
+ str_parms_destroy(reply);
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGD("%s-%s: exit with %s", stream_table[out->common.stream_type], __func__, result_str);
+ return result_str;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
+{
+ return 0;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
+{
+ return 0;
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ return proxy_get_playback_latency(out->common.proxy_stream);
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left, float right)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->adev;
+ int ret = 0;
+
+ ALOGVV("%s-%s: enter", stream_table[out->common.stream_type], __func__);
+
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ if (out->vol_left != left || out->vol_right != right|| adev->update_offload_volume) {
+ out->vol_left = left;
+ out->vol_right = right;
+
+ proxy_set_volume(adev->proxy, VOLUME_TYPE_OFFLOAD, left, right);
+
+ if (adev->update_offload_volume)
+ adev->update_offload_volume = false;
+ }
+ } else{
+ ALOGE("%s-%s: Don't support volume control for this stream",
+ stream_table[out->common.stream_type], __func__);
+ ret = -ENOSYS;
+ }
+
+ ALOGVV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->adev;
+ int ret = 0, wrote = 0;
+ int voip_speech_param = 0;
+ bool voip_need_mute = false;
+ //ALOGVV("%s-%s: enter", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_status == STATUS_STANDBY) {
+ out->common.stream_status = STATUS_READY;
+ ALOGI("%s-%s: transited to Ready", stream_table[out->common.stream_type], __func__);
+
+ // Have to route Audio Path before open PCM Device
+ pthread_mutex_lock(&adev->lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_INCALL_MUSIC && isCPCallMode(adev)) {
+ ALOGI("%s-%s: try to route incall-music call path", stream_table[out->common.stream_type], __func__);
+ /* Incall-music should be enabled before routing call path, to get proper call path,
+ incase if standby is called and re-started */
+ adev->incallmusic_on = true;
+ adev_set_route((void *)out, AUSAGE_PLAYBACK, ROUTE, CALL_DRIVE);
+ } else if (!adev->is_playback_path_routed) {
+ ALOGI("%s-%s: try to route for playback", stream_table[out->common.stream_type], __func__);
+ adev_set_route((void *)out, AUSAGE_PLAYBACK, ROUTE, NON_FORCE_ROUTE);
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ // Check VoIP SE Trigger
+ if (need_voipse_on(adev) && out->common.stream_type == ASTREAM_PLAYBACK_PRIMARY &&
+ is_active_usage_APCall(adev->proxy)) {
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALLBUFFTYPE_CONTROL_NAME, MIXER_VALUE_ON);
+ pthread_mutex_lock(&adev->lock);
+ adev->voipse_on = true;
+ pthread_mutex_unlock(&adev->lock);
+ ALOGI("%s-%s: VoIP SE Triggered-1!", stream_table[out->common.stream_type], __func__);
+ //Speech param should call after AP CALL BUFFTYE - SE Solution works properly.
+ voip_speech_param = get_apcall_speech_param(out);
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALL_SPEECH_PARAM_CONTROL_NAME, voip_speech_param);
+ ALOGI("%s-%s: VoIP SE speech param : %d !", stream_table[out->common.stream_type], __func__,voip_speech_param);
+ voip_need_mute = true;
+ }
+
+ ret = proxy_open_playback_stream((void *)(out->common.proxy_stream), 0, NULL);
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to open Proxy Playback Stream!",
+ stream_table[out->common.stream_type], __func__);
+ pthread_mutex_unlock(&out->common.lock);
+ return ret;
+ } else {
+ out->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[out->common.stream_type], __func__);
+ }
+ }
+
+ if (out->common.stream_status > STATUS_READY) {
+ if (buffer && bytes > 0) {
+ /* Pre-Processing */
+ // Check VoIP SE Trigger
+ if (need_voipse_on(adev) && out->common.stream_type == ASTREAM_PLAYBACK_PRIMARY &&
+ is_active_usage_APCall(adev->proxy)) {
+ // In case of check VoIP after PCM Open, this PCM device needs to re-open
+ proxy_close_playback_stream((void *)(out->common.proxy_stream));
+
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALLBUFFTYPE_CONTROL_NAME, MIXER_VALUE_ON);
+ pthread_mutex_lock(&adev->lock);
+ adev->voipse_on = true;
+ pthread_mutex_unlock(&adev->lock);
+ ALOGI("%s-%s: VoIP SE Triggered-2!", stream_table[out->common.stream_type], __func__);
+
+ voip_speech_param = get_apcall_speech_param(out);
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALL_SPEECH_PARAM_CONTROL_NAME, voip_speech_param);
+ ALOGI("%s-%s: VoIP SE speech param : %d ", stream_table[out->common.stream_type], __func__,voip_speech_param);
+
+ proxy_open_playback_stream((void *)(out->common.proxy_stream), 0, NULL);
+ voip_need_mute = true;
+ } else if (out->common.stream_type == ASTREAM_PLAYBACK_PRIMARY && adev->voipse_on && !isAPCallMode(adev)) {
+ // In case of check VoIP after PCM Open, this PCM device needs to re-open
+ proxy_close_playback_stream((void *)(out->common.proxy_stream));
+
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALLBUFFTYPE_CONTROL_NAME, MIXER_VALUE_OFF);
+ pthread_mutex_lock(&adev->lock);
+ adev->voipse_on = false;
+ pthread_mutex_unlock(&adev->lock);
+ ALOGI("%s-%s: VoIP SE Un-Triggered!", stream_table[out->common.stream_type], __func__);
+
+ proxy_open_playback_stream((void *)(out->common.proxy_stream), 0, NULL);
+ voip_need_mute = true;
+ }
+
+ if(out->common.stream_type == ASTREAM_PLAYBACK_INCALL_MUSIC &&
+ (isUSBHeadsetConnect(adev) || !isCallMode(adev)) && out->common.stream_status == STATUS_PLAYING) {
+ pthread_mutex_unlock(&out->common.lock);
+ return 0;
+ } else {
+ wrote = proxy_write_playback_buffer((void *)(out->common.proxy_stream), (void *)buffer, (int)bytes);
+ if (wrote >= 0) {
+ if (out->common.stream_status == STATUS_IDLE) {
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ // Update Offload Effects, if needed
+ }
+
+ ret = proxy_start_playback_stream((void *)(out->common.proxy_stream));
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to start Proxy Playback Stream!",
+ stream_table[out->common.stream_type], __func__);
+ pthread_mutex_unlock(&out->common.lock);
+ return ret;
+ } else {
+ out->common.stream_status = STATUS_PLAYING;
+ ALOGI("%s-%s: transited to Playing",
+ stream_table[out->common.stream_type], __func__);
+ }
+ }
+ if(voip_need_mute){
+ ALOGI("%s-%s: Mute for voip se transition",
+ stream_table[out->common.stream_type], __func__);
+ usleep(2000);
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALL_MUTE_CONTROL_NAME, ABOX_APCALL_MUTE_COUNT);
+ }
+
+ if ((out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) && (wrote < (ssize_t)bytes)) {
+ /* Compress Device has no available buffer, we have to wait */
+ ALOGVV("%s-%s: There are no available buffer in Compress Device, Need to wait",
+ stream_table[out->common.stream_type], __func__);
+ ret = send_offload_msg(out, OFFLOAD_MSG_WAIT_WRITE);
+ }
+ }
+ }
+ }
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ //ALOGVV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return wrote;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream, uint32_t *dsp_frames)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ pthread_mutex_lock(&out->common.lock);
+ int ret = proxy_get_render_position(out->common.proxy_stream, dsp_frames);
+ pthread_mutex_unlock(&out->common.lock);
+
+ return ret;
+}
+
+static int out_get_next_write_timestamp(const struct audio_stream_out *stream __unused,
+ int64_t *timestamp __unused)
+{
+ return -ENOSYS;
+}
+
+static int out_get_presentation_position(const struct audio_stream_out *stream,
+ uint64_t *frames, struct timespec *timestamp)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ pthread_mutex_lock(&out->common.lock);
+ int ret = proxy_get_presen_position(out->common.proxy_stream, frames, timestamp);
+ pthread_mutex_unlock(&out->common.lock);
+
+ return ret;
+}
+
+static int out_set_callback(struct audio_stream_out *stream, stream_callback_t callback, void *cookie)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int ret = -EINVAL;
+
+ ALOGV("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ if (callback && cookie) {
+ out->offload.callback = callback;
+ out->offload.cookie = cookie;
+ ALOGD("%s-%s: set callback function & cookie", stream_table[out->common.stream_type], __func__);
+
+ ret = 0;
+ }
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static int out_pause(struct audio_stream_out* stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ if (out->common.stream_status == STATUS_PLAYING) {
+ // Stop Visualizer
+
+ ret = proxy_offload_pause(out->common.proxy_stream);
+ if (ret == 0) {
+ out->common.stream_status = STATUS_PAUSED;
+ ALOGI("%s-%s: transit to Paused", stream_table[out->common.stream_type], __func__);
+ } else {
+ ALOGE("%s-%s: failed to pause", stream_table[out->common.stream_type], __func__);
+ }
+ } else {
+ ALOGV("%s-%s: abnormal status(%u) for pausing", stream_table[out->common.stream_type],
+ __func__, out->common.stream_status);
+ }
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static int out_resume(struct audio_stream_out* stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ if (out->common.stream_status== STATUS_PAUSED) {
+ ret = proxy_offload_resume(out->common.proxy_stream);
+ if (ret == 0) {
+ out->common.stream_status = STATUS_PLAYING;
+ ALOGI("%s-%s: transit to Playing", stream_table[out->common.stream_type], __func__);
+
+ // Start Visualizer
+ } else {
+ ALOGE("%s-%s: failed to resume", stream_table[out->common.stream_type], __func__);
+ }
+ } else {
+ ALOGV("%s-%s: abnormal State(%u) for resuming", stream_table[out->common.stream_type],
+ __func__, out->common.stream_status);
+ }
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static int out_drain(struct audio_stream_out* stream, audio_drain_type_t type )
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered with type = %d", stream_table[out->common.stream_type], __func__, type);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ if (out->common.stream_status > STATUS_IDLE) {
+ if (type == AUDIO_DRAIN_EARLY_NOTIFY)
+ ret = send_offload_msg(out, OFFLOAD_MSG_WAIT_PARTIAL_DRAIN);
+ else
+ ret = send_offload_msg(out, OFFLOAD_MSG_WAIT_DRAIN);
+ } else {
+ out->offload.callback(STREAM_CBK_EVENT_DRAIN_READY, NULL, out->offload.cookie);
+ ALOGD("%s-%s: State is IDLE. Return callback with drain_ready",
+ stream_table[out->common.stream_type], __func__);
+ }
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static int out_flush(struct audio_stream_out* stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ if (out->common.stream_status > STATUS_IDLE) {
+ ret = proxy_stop_playback_stream((void *)(out->common.proxy_stream));
+ out->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transit to Idle due to flush", stream_table[out->common.stream_type], __func__);
+ } else {
+ ret = 0;
+ ALOGV("%s-%s: this stream is already stopped", stream_table[out->common.stream_type], __func__);
+ }
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+// For MMAP NOIRQ Stream
+static int out_stop(const struct audio_stream_out* stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_MMAP) {
+ if (out->common.stream_status == STATUS_PLAYING) {
+ /* Stops stream & transit to Idle. */
+ proxy_stop_playback_stream((void *)(out->common.proxy_stream));
+ out->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[out->common.stream_type], __func__);
+
+ } else
+ ALOGV("%s-%s: invalid operation - stream status (%d)",
+ stream_table[out->common.stream_type], __func__, out->common.stream_status);
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static int out_start(const struct audio_stream_out* stream)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_MMAP) {
+ if (out->common.stream_status == STATUS_IDLE) {
+ /* Starts stream & transit to Playing. */
+ ret = proxy_start_playback_stream((void *)(out->common.proxy_stream));
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to start Proxy Playback Stream!",
+ stream_table[out->common.stream_type], __func__);
+ } else {
+ out->common.stream_status = STATUS_PLAYING;
+ ALOGI("%s-%s: transited to Playing",
+ stream_table[out->common.stream_type], __func__);
+ }
+ } else
+ ALOGV("%s-%s: invalid operation - stream status (%d)",
+ stream_table[out->common.stream_type], __func__, out->common.stream_status);
+ }
+ pthread_mutex_unlock(&out->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static int out_create_mmap_buffer(const struct audio_stream_out *stream,
+ int32_t min_size_frames,
+ struct audio_mmap_buffer_info *info)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ struct audio_device *adev = out->adev;
+ int ret = 0;
+
+ ALOGD("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ pthread_mutex_lock(&out->common.lock);
+
+ if (info == NULL || min_size_frames == 0) {
+ ALOGE("%s-%s: info = %p, min_size_frames = %d", stream_table[out->common.stream_type],
+ __func__, info, min_size_frames);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (out->common.stream_type != ASTREAM_PLAYBACK_MMAP || out->common.stream_status != STATUS_STANDBY) {
+ ALOGE("%s-%s: invalid operation - stream status (%d)", stream_table[out->common.stream_type],
+ __func__, out->common.stream_status);
+ ret = -ENOSYS;
+ goto exit;
+ } else {
+ out->common.stream_status = STATUS_READY;
+ ALOGI("%s-%s: transited to Ready", stream_table[out->common.stream_type], __func__);
+
+ // Have to route Audio Path before open PCM Device
+ pthread_mutex_lock(&adev->lock);
+ if (!adev->is_playback_path_routed) {
+ ALOGI("%s-%s: try to route for playback", stream_table[out->common.stream_type], __func__);
+ adev_set_route((void *)out, AUSAGE_PLAYBACK, ROUTE, NON_FORCE_ROUTE);
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ /* Opens stream & transit to Idle. */
+ ret = proxy_open_playback_stream((void *)(out->common.proxy_stream), min_size_frames, (void *)info);
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to open Proxy Playback Stream!",
+ stream_table[out->common.stream_type], __func__);
+
+ out->common.stream_status = STATUS_STANDBY;
+ ALOGI("%s-%s: transited to StandBy", stream_table[out->common.stream_type], __func__);
+ } else {
+ out->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[out->common.stream_type], __func__);
+ }
+ }
+
+exit:
+ pthread_mutex_unlock(&out->common.lock);
+ ALOGD("%s-%s: exited", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static int out_get_mmap_position(const struct audio_stream_out *stream,
+ struct audio_mmap_position *position)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+ int ret = 0;
+
+ ALOGV("%s-%s: entered", stream_table[out->common.stream_type], __func__);
+
+ if (position == NULL) return -EINVAL;
+ if (out->common.stream_type != ASTREAM_PLAYBACK_MMAP) return -ENOSYS;
+
+ ret = proxy_get_mmap_position((void *)(out->common.proxy_stream), (void *)position);
+
+ ALOGV("%s-%s: exited", stream_table[out->common.stream_type], __func__);
+ return ret;
+}
+
+static void out_update_source_metadata(struct audio_stream_out *stream,
+ const struct source_metadata* source_metadata __unused)
+{
+ struct stream_out *out = (struct stream_out *)stream;
+
+ ALOGD("%s-%s: called, but not implemented yet", stream_table[out->common.stream_type], __func__);
+ if (source_metadata->track_count > 0) {
+ ALOGD("%s-%s: This stream has %zu tracks", stream_table[out->common.stream_type], __func__,
+ source_metadata->track_count);
+
+ for (int i = 0; i < (int)source_metadata->track_count; i++) {
+ ALOGD("%d Track has Usage(%d), Content Type(%d), Gain(%f)", i+1,
+ (int)source_metadata->tracks[i].usage,
+ (int)source_metadata->tracks[i].content_type,
+ source_metadata->tracks[i].gain);
+ }
+ }
+
+ return ;
+
+}
+
+
+/****************************************************************************/
+/** **/
+/** The Stream_In Function Implementation **/
+/** **/
+/****************************************************************************/
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ ALOGVV("%s-%s: exit with sample rate = %u", stream_table[in->common.stream_type], __func__,
+ in->common.requested_sample_rate);
+ return in->common.requested_sample_rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream __unused, uint32_t rate __unused)
+{
+ return -ENOSYS;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ size_t buffer_size = 0;
+
+ ALOGVV("%s-%s: enter", stream_table[in->common.stream_type], __func__);
+
+ // will return buffer size based on requested PCM configuration
+ if (in->common.requested_sample_rate != proxy_get_actual_sampling_rate(in->common.proxy_stream)) {
+ if (in->common.stream_type == ASTREAM_CAPTURE_PRIMARY)
+ buffer_size = (in->common.requested_sample_rate * PREDEFINED_MEDIA_CAPTURE_DURATION) / 1000;
+ else if (in->common.stream_type == ASTREAM_CAPTURE_LOW_LATENCY)
+ buffer_size = (in->common.requested_sample_rate * PREDEFINED_LOW_CAPTURE_DURATION) / 1000;
+ else
+ buffer_size = proxy_get_actual_period_size(in->common.proxy_stream);
+ } else
+ buffer_size = proxy_get_actual_period_size(in->common.proxy_stream);
+
+ buffer_size *= (unsigned int)audio_stream_in_frame_size((const struct audio_stream_in *)stream);
+ ALOGVV("%s-%s: exit with %d bytes", stream_table[in->common.stream_type], __func__, (int)buffer_size);
+ return buffer_size;
+}
+
+static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ ALOGVV("%s-%s: exit with channel mask = 0x%x", stream_table[in->common.stream_type], __func__,
+ in->common.requested_channel_mask);
+ return in->common.requested_channel_mask;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ ALOGVV("%s-%s: exit with audio format = 0x%x", stream_table[in->common.stream_type], __func__,
+ in->common.requested_format);
+ return in->common.requested_format;
+}
+
+static int in_set_format(struct audio_stream *stream __unused, audio_format_t format __unused)
+{
+ return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->adev;
+
+ ALOGVV("%s-%s: enter", stream_table[in->common.stream_type], __func__);
+
+ pthread_mutex_lock(&in->common.lock);
+ if (in->common.stream_status > STATUS_STANDBY) {
+ /* Stops stream & transit to Idle. */
+ if (in->common.stream_status > STATUS_IDLE) {
+ proxy_stop_capture_stream((void *)(in->common.proxy_stream));
+ in->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[in->common.stream_type], __func__);
+ }
+
+ /* Closes device & transit to Standby. */
+ proxy_close_capture_stream((void *)(in->common.proxy_stream));
+ in->common.stream_status = STATUS_STANDBY;
+ ALOGI("%s-%s: transited to Standby", stream_table[in->common.stream_type], __func__);
+
+ // Have to unroute Audio Path after close PCM Device
+ pthread_mutex_lock(&adev->lock);
+#ifdef SUPPORT_STHAL_INTERFACE
+ if (in->common.stream_type != ASTREAM_CAPTURE_HOTWORD &&
+ adev->is_capture_path_routed)
+#else
+ if (adev->is_capture_path_routed)
+#endif
+ {
+ ALOGI("%s-%s: try to unroute for standby", stream_table[in->common.stream_type], __func__);
+ adev_set_route((void *)in, AUSAGE_CAPTURE, UNROUTE, NON_FORCE_ROUTE);
+ }
+ if (adev->active_input)
+ adev->active_input = NULL;
+
+ pthread_mutex_unlock(&adev->lock);
+ }
+ pthread_mutex_unlock(&in->common.lock);
+
+ ALOGVV("%s-%s: exit", stream_table[in->common.stream_type], __func__);
+ return 0;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ ALOGVV("%s-%s: enter with fd(%d)", stream_table[in->common.stream_type], __func__, fd);
+
+ const size_t len = 256;
+ char buffer[len];
+ bool justLocked = false;
+
+ snprintf(buffer, len, "Audio Stream Input::dump\n");
+ write(fd,buffer,strlen(buffer));
+ justLocked = pthread_mutex_trylock(&in->common.lock) == 0;
+ if(justLocked)
+ pthread_mutex_unlock(&in->common.lock);
+ snprintf(buffer, len, "\tinput Mutex: %s\n", justLocked ? "locked" : "unlocked");
+ write(fd,buffer,strlen(buffer));
+
+ snprintf(buffer, len, "\tinput devices: %d\n", in->common.requested_devices);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tinput source: %d\n", in->requested_source);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tinput flags: %x\n",in->requested_flags);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tinput sample rate: %u\n",in->common.requested_sample_rate);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tinput channel mask: %u\n",in->common.requested_channel_mask);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tinput format: %d\n",in->common.requested_format);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tinput audio usage: %d\n",in->common.stream_usage);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tinut standby state: %d\n",in->common.stream_status);
+ write(fd,buffer,strlen(buffer));
+ //snprintf(buffer, len, "\tinput mixer_path_setup: %s\n",bool_to_str(in->mixer_path_setup));
+ //write(fd,buffer,strlen(buffer));
+
+ proxy_dump_capture_stream(in->common.proxy_stream, fd);
+
+ ALOGVV("%s-%s: exit with fd(%d)", stream_table[in->common.stream_type], __func__, fd);
+ return 0;
+}
+
+static audio_devices_t in_get_device(const struct audio_stream *stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ ALOGVV("%s-%s: exit with device = %u", stream_table[in->common.stream_type], __func__,
+ in->common.requested_devices);
+ return in->common.requested_devices;
+}
+
+static int in_set_device(struct audio_stream *stream __unused, audio_devices_t device __unused)
+{
+ return -ENOSYS;
+}
+
+void stop_active_input(struct stream_in *in)
+{
+ struct audio_device *adev = in->adev;
+
+ if (in->common.stream_status > STATUS_STANDBY) {
+ in->common.stream_status = STATUS_STANDBY;
+ pthread_mutex_lock(&adev->lock);
+ proxy_stop_capture_stream((void *)(in->common.proxy_stream));
+ proxy_close_capture_stream((void *)(in->common.proxy_stream));
+ pthread_mutex_unlock(&adev->lock);
+ }
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->adev;
+
+ struct str_parms *parms;
+ char value[32];
+ int ret = 0;
+
+ ALOGD("%s-%s: enter with param = %s", stream_table[in->common.stream_type], __func__, kvpairs);
+
+#ifdef SUPPORT_STHAL_INTERFACE
+ if (in->common.stream_type == ASTREAM_CAPTURE_HOTWORD) {
+ ALOGD("%s-%s: exit", stream_table[in->common.stream_type], __func__);
+ return 0;
+ }
+#endif
+
+ if (in->common.stream_type == ASTREAM_CAPTURE_USB_DEVICE) {
+ pthread_mutex_lock(&in->common.lock);
+ proxy_setparam_capture_stream(in->common.proxy_stream, (void *)kvpairs);
+ pthread_mutex_unlock(&in->common.lock);
+ }
+
+ parms = str_parms_create_str(kvpairs);
+
+ pthread_mutex_lock(&in->common.lock);
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+ if (ret >= 0) {
+ audio_devices_t requested_devices = atoi(value);
+ audio_devices_t current_devices = in->common.requested_devices;
+ bool need_routing = false;
+
+ /*
+ * AudioFlinger informs Audio path is this device.
+ * AudioHAL has to decide to do actual routing or not.
+ */
+ if (requested_devices != AUDIO_DEVICE_NONE) {
+ ALOGI("%s-%s: requested to change route from %s to %s",
+ stream_table[in->common.stream_type], __func__,
+ device_table[get_device_id(adev, current_devices)],
+ device_table[get_device_id(adev, requested_devices)]);
+
+ /* Assign requested device to Input Stream */
+ in->common.requested_devices = requested_devices;
+
+ update_capture_stream(in, current_devices, requested_devices);
+
+ pthread_mutex_lock(&adev->lock);
+ /* Check actual routing is needed or not */
+ // Actual routing is needed at device change request
+ if (adev->is_capture_path_routed || (in->common.stream_status > STATUS_STANDBY)) {
+ if (!isCPCallMode(adev))
+ need_routing = true;
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ if (need_routing) {
+ pthread_mutex_lock(&adev->lock);
+ /* Do actual routing */
+ adev_set_route((void *)in, AUSAGE_CAPTURE, ROUTE, NON_FORCE_ROUTE);
+ pthread_mutex_unlock(&adev->lock);
+ }
+ } else {
+ /* When audio device will be changed, AudioFlinger requests to route with AUDIO_DEVICE_NONE */
+ ALOGV("%s-%s: requested to change route to AUDIO_DEVICE_NONE",
+ stream_table[in->common.stream_type], __func__);
+ //pthread_mutex_lock(&adev->lock);
+ //adev_set_route((void *)in, AUSAGE_CAPTURE, UNROUTE, NON_FORCE_ROUTE);
+ //pthread_mutex_unlock(&adev->lock);
+ }
+ }
+
+ /*
+ * Android Audio System can change PCM Configurations(Format, Channel and Rate) for Audio Stream
+ * when this Audio Stream is not working.
+ */
+ // Change Audio Format
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_FORMAT, value, sizeof(value));
+ if (ret >= 0) {
+ if (in->common.stream_status > STATUS_READY)
+ goto not_acceptable;
+ else {
+ audio_format_t new_format = (audio_format_t)atoi(value);
+ if ((new_format != in->common.requested_format) && (new_format != AUDIO_FORMAT_DEFAULT)) {
+ struct audio_config new_config;
+
+ in->common.requested_format = new_format;
+
+ new_config.sample_rate = in->common.requested_sample_rate;
+ new_config.channel_mask = in->common.requested_channel_mask;
+ new_config.format = new_format;
+ proxy_reconfig_capture_stream(in->common.proxy_stream, in->common.stream_type,
+ &new_config);
+ ALOGD("%s-%s: changed format to %#x from %#x",
+ stream_table[in->common.stream_type], __func__,
+ new_format, in->common.requested_format);
+ } else
+ ALOGD("%s-%s: requested to change same format to %#x",
+ stream_table[in->common.stream_type], __func__, new_format);
+ }
+ }
+
+ // Change Audio Channel Mask
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_CHANNELS, value, sizeof(value));
+ if (ret >= 0) {
+ if (in->common.stream_status > STATUS_READY)
+ goto not_acceptable;
+ else {
+ audio_channel_mask_t new_chmask = (audio_channel_mask_t)atoi(value);
+ if ((new_chmask != in->common.requested_channel_mask) && (new_chmask != AUDIO_CHANNEL_NONE)) {
+ struct audio_config new_config;
+
+ in->common.requested_channel_mask = new_chmask;
+
+ new_config.sample_rate = in->common.requested_sample_rate;
+ new_config.channel_mask = new_chmask;
+ new_config.format = in->common.requested_format;
+ proxy_reconfig_capture_stream(in->common.proxy_stream, in->common.stream_type,
+ &new_config);
+ ALOGD("%s-%s: changed channel mask to %#x from %#x",
+ stream_table[in->common.stream_type], __func__,
+ new_chmask, in->common.requested_channel_mask);
+ } else
+ ALOGD("%s-%s: requested to change same channel mask to %#x",
+ stream_table[in->common.stream_type], __func__, new_chmask);
+ }
+ }
+
+ // Change Audio Sampling Rate
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE, value, sizeof(value));
+ if (ret >= 0) {
+ if (in->common.stream_status > STATUS_READY)
+ goto not_acceptable;
+ else {
+ uint32_t new_rate = (uint32_t)atoi(value);
+ if ((new_rate != in->common.requested_sample_rate) && (new_rate != 0)) {
+ struct audio_config new_config;
+
+ in->common.requested_sample_rate = new_rate;
+
+ new_config.sample_rate = new_rate;
+ new_config.channel_mask = in->common.requested_channel_mask;
+ new_config.format = in->common.requested_format;
+ proxy_reconfig_capture_stream(in->common.proxy_stream, in->common.stream_type,
+ &new_config);
+ ALOGD("%s-%s: changed sampling rate to %dHz from %dHz",
+ stream_table[in->common.stream_type], __func__,
+ new_rate, in->common.requested_sample_rate);
+ } else
+ ALOGD("%s-%s: requested to change same sampling rate to %dHz",
+ stream_table[in->common.stream_type], __func__, new_rate);
+ }
+ }
+
+ // Change Audio Input Source
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, value, sizeof(value));
+ if (ret >= 0) {
+ if (in->common.stream_status > STATUS_READY)
+ goto not_acceptable;
+ else {
+ unsigned int new_source = (unsigned int)atoi(value);
+ if ((new_source != in->requested_source) && (new_source != AUDIO_SOURCE_DEFAULT)) {
+ in->requested_source = new_source;
+
+ if (in->requested_source == AUDIO_SOURCE_VOICE_CALL) {
+ in->common.stream_usage = adev_get_capture_ausage(adev, in);
+ proxy_update_capture_usage((void *)(in->common.proxy_stream),(int)in->common.stream_usage);
+ }
+ ALOGD("%s-%s: changed source to %d from %d ", stream_table[in->common.stream_type],
+ __func__, new_source, in->requested_source);
+ } else
+ ALOGD("%s-%s: requested to change same source to %d",
+ stream_table[in->common.stream_type], __func__, new_source);
+ }
+ }
+ pthread_mutex_unlock(&in->common.lock);
+
+ str_parms_destroy(parms);
+ ALOGVV("%s-%s: exit", stream_table[in->common.stream_type], __func__);
+ return 0;
+
+not_acceptable:
+ pthread_mutex_unlock(&in->common.lock);
+ str_parms_destroy(parms);
+ ALOGE("%s-%s: This parameter cannot accept as Stream is working",
+ stream_table[in->common.stream_type], __func__);
+ return -ENOSYS;
+}
+
+static char * in_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct str_parms *query = str_parms_create_str(keys);
+ struct str_parms *reply = str_parms_create();
+ char * result_str;
+
+ ALOGD("%s-%s: enter with param = %s", stream_table[in->common.stream_type], __func__, keys);
+
+ pthread_mutex_lock(&in->common.lock);
+
+ // Get Current Devices
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_ROUTING)) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, (int)in->common.requested_devices);
+ }
+
+ // Get Current Audio Format
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_FORMAT, (int)in->common.requested_format);
+ }
+
+ // Get Current Audio Frame Count
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FRAME_COUNT)) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+ (int)proxy_get_actual_period_size(in->common.proxy_stream));
+ }
+
+ // Get Current Audio Input Source
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_INPUT_SOURCE)) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_INPUT_SOURCE, (int)in->requested_source);
+ }
+
+ // Some parameters can be gotten from Audio Stream Proxy
+ proxy_getparam_capture_stream(in->common.proxy_stream, query, reply);
+
+ result_str = str_parms_to_str(reply);
+ str_parms_destroy(query);
+ str_parms_destroy(reply);
+ pthread_mutex_unlock(&in->common.lock);
+
+ ALOGD("%s-%s: exit with %s", stream_table[in->common.stream_type], __func__, result_str);
+ return result_str;
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
+{
+ return 0;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream __unused, effect_handle_t effect __unused)
+{
+ return 0;
+}
+
+static int in_set_gain(struct audio_stream_in *stream __unused, float gain __unused)
+{
+ return 0;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void* buffer, size_t bytes)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->adev;
+ int ret = 0;
+ bool need_reconfig = false;
+
+ //ALOGVV("%s-%s: enter", stream_table[in->common.stream_type], __func__);
+
+ pthread_mutex_lock(&in->common.lock);
+
+ if (in->pcm_reconfig) {
+ ALOGD(" %s: pcm reconfig", __func__);
+ stop_active_input(in);
+ in->pcm_reconfig = false;
+ need_reconfig = true;
+ }
+
+ if (in->common.stream_status == STATUS_STANDBY) {
+ in->common.stream_status = STATUS_READY;
+ ALOGI("%s-%s: transited to Ready", stream_table[in->common.stream_type], __func__);
+
+ if ((isCPCallMode(adev) && is_active_usage_CPCall(adev->proxy) && (in->common.stream_type != ASTREAM_CAPTURE_CALL))
+ || (!isCPCallMode(adev) && !is_active_usage_CPCall(adev->proxy) && (in->common.stream_type == ASTREAM_CAPTURE_CALL || (need_reconfig && in->common.stream_type == ASTREAM_CAPTURE_PRIMARY)))) {
+ audio_stream_type new_stream_type = in->common.stream_type;
+ if (isCPCallMode(adev) && isCallRecording(in->requested_source)) {
+ new_stream_type = ASTREAM_CAPTURE_CALL;
+ ALOGD(" %s: pcm reconfig as ASTREAM_CAPTURE_CALL", __func__);
+ } else {
+ new_stream_type = ASTREAM_CAPTURE_PRIMARY;
+ ALOGD(" %s: pcm reconfig as ASTREAM_CAPTURE_PRIMARY", __func__);
+ }
+ in->common.stream_usage = adev_get_capture_ausage(adev, in);
+ ALOGI("%s-%s: updated capture usage(%s)", stream_table[in->common.stream_type], __func__, usage_table[in->common.stream_usage]);
+
+ in->common.stream_type = new_stream_type;
+ in->common.stream_usage = adev_get_capture_ausage(adev, in);
+ ALOGI("%s-%s: updated capture usage(%s)", stream_table[in->common.stream_type], __func__, usage_table[in->common.stream_usage]);
+
+ proxy_reconfig_capture_usage((void *)(in->common.proxy_stream),
+ (int)in->common.stream_type,
+ (int)in->common.stream_usage);
+
+ }
+
+ // Have to route Audio Path before open PCM Device
+ pthread_mutex_lock(&adev->lock);
+ adev->active_input = in;
+
+#ifdef SUPPORT_STHAL_INTERFACE
+ if (in->common.stream_type != ASTREAM_CAPTURE_HOTWORD &&
+ !adev->is_capture_path_routed)
+#else
+ if (!adev->is_capture_path_routed)
+#endif
+ {
+ if (is_factory_bt_realtime_loopback_mode(adev->factory)) {
+ ALOGI("%s skip routing for BT realtime loopback", __func__);
+ } else if (in->common.stream_type == ASTREAM_CAPTURE_CALL) {
+ ALOGI("%s skip routing for call recording", __func__);
+ } else {
+ ALOGI("%s-%s: try to route for capture", stream_table[in->common.stream_type], __func__);
+ adev_set_route((void *)in, AUSAGE_CAPTURE, ROUTE, NON_FORCE_ROUTE);
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ ret = proxy_open_capture_stream((void *)(in->common.proxy_stream), 0, NULL);
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to open Proxy Capture Stream!",
+ stream_table[in->common.stream_type], __func__);
+ pthread_mutex_unlock(&in->common.lock);
+ return (ssize_t)ret;
+ } else {
+ in->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[in->common.stream_type], __func__);
+ }
+ }
+
+ if (in->common.stream_status == STATUS_IDLE) {
+ ret = proxy_start_capture_stream((void *)(in->common.proxy_stream));
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to start Proxy Capture Stream!",
+ stream_table[in->common.stream_type], __func__);
+ pthread_mutex_unlock(&in->common.lock);
+ return (ssize_t)ret;
+ } else {
+ in->common.stream_status = STATUS_PLAYING;
+ ALOGI("%s-%s: transited to Capturing",
+ stream_table[in->common.stream_type], __func__);
+ }
+ }
+
+ if ((in->common.stream_status == STATUS_PLAYING) && (buffer && bytes > 0)) {
+ ret = proxy_read_capture_buffer((void *)(in->common.proxy_stream),
+ (void *)buffer, (int)bytes);
+
+ /* Post-Processing */
+ // Instead of writing zeroes here, we could trust the hardware to always provide zeroes when muted.
+ if(adev->mic_mute && (isAPCallMode(adev)||!isCallRecording(in->requested_source))) {
+ if (ret >= 0)
+ memset(buffer, 0, bytes);
+ }
+ }
+ pthread_mutex_unlock(&in->common.lock);
+
+ //ALOGVV("%s-%s: exit", stream_table[in->common.stream_type], __func__);
+ return (ssize_t)ret;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream __unused)
+{
+ return 0;
+}
+
+static int in_get_capture_position(const struct audio_stream_in *stream,
+ int64_t *frames, int64_t *time)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ pthread_mutex_lock(&in->common.lock);
+ int ret = proxy_get_capture_pos(in->common.proxy_stream, frames, time);
+ pthread_mutex_unlock(&in->common.lock);
+
+ return ret;
+}
+
+// For MMAP NOIRQ Stream
+static int in_stop(const struct audio_stream_in* stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered", stream_table[in->common.stream_type], __func__);
+
+ pthread_mutex_lock(&in->common.lock);
+ if (in->common.stream_type == ASTREAM_CAPTURE_MMAP) {
+ if (in->common.stream_status == STATUS_PLAYING) {
+ /* Stops stream & transit to Idle. */
+ proxy_stop_capture_stream((void *)(in->common.proxy_stream));
+ in->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[in->common.stream_type], __func__);
+
+ } else
+ ALOGV("%s-%s: invalid operation - stream status (%d)",
+ stream_table[in->common.stream_type], __func__, in->common.stream_status);
+ }
+ pthread_mutex_unlock(&in->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[in->common.stream_type], __func__);
+ return ret;
+}
+
+static int in_start(const struct audio_stream_in* stream)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ int ret = -ENOSYS;
+
+ ALOGV("%s-%s: entered", stream_table[in->common.stream_type], __func__);
+
+ pthread_mutex_lock(&in->common.lock);
+ if (in->common.stream_type == ASTREAM_CAPTURE_MMAP) {
+ if (in->common.stream_status == STATUS_IDLE) {
+ /* Starts stream & transit to Playing. */
+ ret = proxy_start_capture_stream((void *)(in->common.proxy_stream));
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to start Proxy Capture Stream!",
+ stream_table[in->common.stream_type], __func__);
+ } else {
+ in->common.stream_status = STATUS_PLAYING;
+ ALOGI("%s-%s: transited to Playing",
+ stream_table[in->common.stream_type], __func__);
+ }
+ } else
+ ALOGV("%s-%s: invalid operation - stream status (%d)",
+ stream_table[in->common.stream_type], __func__, in->common.stream_status);
+ }
+ pthread_mutex_unlock(&in->common.lock);
+
+ ALOGV("%s-%s: exit", stream_table[in->common.stream_type], __func__);
+ return ret;
+}
+
+static int in_create_mmap_buffer(const struct audio_stream_in *stream,
+ int32_t min_size_frames,
+ struct audio_mmap_buffer_info *info)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ struct audio_device *adev = in->adev;
+ int ret = 0;
+
+ ALOGD("%s-%s: entered", stream_table[in->common.stream_type], __func__);
+
+ pthread_mutex_lock(&in->common.lock);
+
+ if (info == NULL || min_size_frames == 0) {
+ ALOGE("%s-%s: info = %p, min_size_frames = %d", stream_table[in->common.stream_type],
+ __func__, info, min_size_frames);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (in->common.stream_type != ASTREAM_CAPTURE_MMAP || in->common.stream_status != STATUS_STANDBY) {
+ ALOGE("%s-%s: invalid operation - stream status (%d)", stream_table[in->common.stream_type],
+ __func__, in->common.stream_status);
+ ret = -ENOSYS;
+ goto exit;
+ } else {
+ in->common.stream_status = STATUS_READY;
+ ALOGI("%s-%s: transited to Ready", stream_table[in->common.stream_type], __func__);
+
+ // Have to route Audio Path before open PCM Device
+ pthread_mutex_lock(&adev->lock);
+ if (!adev->is_capture_path_routed) {
+ ALOGI("%s-%s: try to route for capture", stream_table[in->common.stream_type], __func__);
+ adev_set_route((void *)in, AUSAGE_CAPTURE, ROUTE, NON_FORCE_ROUTE);
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ /* Opens stream & transit to Idle. */
+ ret = proxy_open_capture_stream((void *)(in->common.proxy_stream), min_size_frames, (void *)info);
+ if (ret != 0) {
+ ALOGE("%s-%s: failed to open Proxy Capture Stream!",
+ stream_table[in->common.stream_type], __func__);
+
+ in->common.stream_status = STATUS_STANDBY;
+ ALOGI("%s-%s: transited to StandBy", stream_table[in->common.stream_type], __func__);
+ } else {
+ in->common.stream_status = STATUS_IDLE;
+ ALOGI("%s-%s: transited to Idle", stream_table[in->common.stream_type], __func__);
+ }
+ }
+
+exit:
+ pthread_mutex_unlock(&in->common.lock);
+ ALOGD("%s-%s: exited", stream_table[in->common.stream_type], __func__);
+ return ret;
+}
+
+static int in_get_mmap_position(const struct audio_stream_in *stream,
+ struct audio_mmap_position *position)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ int ret = 0;
+
+ ALOGV("%s-%s: entered", stream_table[in->common.stream_type], __func__);
+
+ if (position == NULL) return -EINVAL;
+ if (in->common.stream_type != ASTREAM_CAPTURE_MMAP) return -ENOSYS;
+
+ ret = proxy_get_mmap_position((void *)(in->common.proxy_stream), (void *)position);
+
+ ALOGV("%s-%s: exited", stream_table[in->common.stream_type], __func__);
+
+ return 0;
+}
+
+static int in_get_active_microphones(const struct audio_stream_in *stream,
+ struct audio_microphone_characteristic_t *mic_array,
+ size_t *mic_count)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+ int ret = 0;
+
+ ALOGVV("%s-%s: entered", stream_table[in->common.stream_type], __func__);
+
+ if (mic_array == NULL || mic_count == NULL) return -EINVAL;
+
+ ret = proxy_get_active_microphones(in->common.proxy_stream, (void *)mic_array, (int *)mic_count);
+
+ ALOGVV("%s-%s: exited", stream_table[in->common.stream_type], __func__);
+ return ret;
+}
+
+static void in_update_sink_metadata(struct audio_stream_in *stream,
+ const struct sink_metadata* sink_metadata)
+{
+ struct stream_in *in = (struct stream_in *)stream;
+
+ ALOGD("%s-%s: called, but not implemented yet", stream_table[in->common.stream_type], __func__);
+ if (sink_metadata->track_count > 0) {
+ ALOGD("%s-%s: This stream has %zu tracks", stream_table[in->common.stream_type], __func__,
+ sink_metadata->track_count);
+
+ for (int i = 0; i < (int)sink_metadata->track_count; i++) {
+ ALOGD("%d Track has Source(%d), Gain(%f)", i+1, (int)sink_metadata->tracks[i].source,
+ sink_metadata->tracks[i].gain);
+ }
+ }
+
+ return ;
+
+}
+
+/*****************************************************************************/
+/** **/
+/** The Audio Device Function Implementation **/
+/** **/
+/*****************************************************************************/
+static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev __unused)
+{
+ ALOGVV("device-%s: enter", __func__);
+
+ ALOGVV("device-%s: exit", __func__);
+ return 0;
+}
+
+static int adev_init_check(const struct audio_hw_device *dev)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ int ret = -ENODEV;
+
+ ALOGVV("device-%s: enter", __func__);
+
+ if (adev) {
+ if (adev->proxy) {
+ if (adev->is_route_created)
+ ret = 0;
+ else
+ ALOGE("device-%s: Audio Route is not created yet", __func__);
+ } else
+ ALOGE("device-%s: Audio Proxy is not created yet", __func__);
+ } else
+ ALOGE("device-%s: Primary Audio HW Device is not created yet", __func__);
+
+ ALOGVV("device-%s: exit", __func__);
+ return ret;
+}
+
+static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_out *primary_output = adev->primary_output;
+
+ if(volume < 0.0f || volume > 1.0f) {
+ ALOGD("device-%s: invalid volume (%f)", __func__, volume);
+ return -EINVAL;
+ }
+
+ pthread_mutex_lock(&adev->lock);
+ if (adev->voice && voice_is_call_mode(adev->voice))
+ voice_set_volume(adev->voice, volume);
+ else if (adev->voice_volume == 0.0 &&
+ (get_active_playback_count(adev, primary_output) > 0) &&
+ (primary_output->force == FORCE_ROUTE && primary_output->rollback_devices != AUDIO_DEVICE_NONE)) {
+ /*
+ * When Force-Route stream such as Alarm sound/Shutter tone starts, voice_volume sets 0.
+ * And when this stream stopped, voice_volume is also rolled back.
+ * This is right route rollback time without abnormal routing during 3 seconds standby.
+ */
+ ALOGI("device-%s: try to roll back", __func__);
+ primary_output->common.requested_devices = primary_output->rollback_devices;
+ adev_set_route((void *)primary_output, AUSAGE_PLAYBACK, ROUTE, NON_FORCE_ROUTE);
+ }
+ else if(isAPCallMode(adev))
+ {
+ int apvolume = 0;
+ if(adev->voice)
+ apvolume = voice_get_volume_index(adev->voice,volume);
+ ALOGI("device-%s: AP Call set volume to (%d)", __func__,apvolume);
+ if (adev->proxy)
+ proxy_set_communication_volume(adev->proxy,apvolume);
+ }
+ adev->voice_volume = volume;
+ ALOGD("device-%s: set volume to (%f)", __func__, volume);
+ pthread_mutex_unlock(&adev->lock);
+
+ return 0;
+}
+
+static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+
+ ALOGD("device-%s: enter", __func__);
+
+ pthread_mutex_lock(&adev->lock);
+
+ /* Add code to keep mode 2 during realcall state, prevent call mute issue */
+ if (adev->voice) {
+ if ((mode == AUDIO_MODE_IN_COMMUNICATION) && isCPCallMode(adev) && adev->voice->realcall) {
+ mode = adev->amode; // AUDIO_MODE_IN_CALL
+ adev->voice->keep_call_mode = true;
+ ALOGI("%s: Keep the mode %d while CP Voice call is active", __func__, mode);
+ } else if ((mode == AUDIO_MODE_IN_CALL) && adev->voice->keep_call_mode) {
+ /* Reset pre mode, don't need to backup voip mode after call end (2->3->2)*/
+ adev->voice->keep_call_mode = false;
+ }
+ }
+
+ if (adev->amode != mode) {
+ /* Changing Android Audio Mode */
+ adev->previous_amode = adev->amode;;
+ adev->amode = mode;
+ ALOGD("device-%s: changed audio mode from (%s) to (%s)", __func__,
+ audiomode_table[(int)adev->previous_amode], audiomode_table[(int)adev->amode]);
+
+ proxy_set_audiomode(adev->proxy, (int)mode);
+
+ if (adev->voice) {
+ if ((mode == AUDIO_MODE_NORMAL || mode == AUDIO_MODE_IN_COMMUNICATION) &&
+ voice_is_call_active(adev->voice)) {
+ /* Change from Voice Call Mode to Normal Mode */
+ /* Reset keep mode state, don't need to backup voip mode after call end */
+ adev->voice->keep_call_mode = false;
+
+ /* Stop Voice Call */
+ voice_set_call_active(adev->voice, false);
+ proxy_stop_voice_call(adev->proxy);
+ ALOGD("device-%s: *** Stopped CP Voice Call ***", __func__);
+
+ pthread_mutex_unlock(&adev->lock);
+ update_call_stream(adev->primary_output, adev->primary_output->common.requested_devices, adev->primary_output->common.requested_devices);
+ pthread_mutex_lock(&adev->lock);
+
+ /* Changing Call Status */
+ voice_set_call_mode(adev->voice, false);
+
+ proxy_call_status(adev->proxy, false);
+ } else if (mode == AUDIO_MODE_IN_CALL) {
+ /* Change from Normal/Ringtone Mode to Voice Call Mode */
+ /* We cannot start Voice Call right now because we don't know which device will be used.
+ So, we need to delay Voice Call start when get the routing information for Voice Call */
+ if(adev->previous_amode == AUDIO_MODE_RINGTONE && get_device_id(adev, adev->primary_output->common.requested_devices) == DEVICE_BT_HEADSET) {
+ proxy_set_mixer_value_int(adev->proxy, ABOX_APCALL_MUTE_CONTROL_NAME, ABOX_BT_MUTE_COUNT);
+ }
+
+ /* Changing Call Status */
+ voice_set_call_mode(adev->voice, true);
+
+ proxy_call_status(adev->proxy, true);
+ }
+ } else
+ ALOGE("device-%s: There is no Voice manager", __func__);
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ ALOGD("device-%s: exit", __func__);
+ return 0;
+}
+
+static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+
+ pthread_mutex_lock(&adev->lock);
+ if (adev->voice && (adev->mic_mute != state))
+ voice_set_mic_mute(adev->voice, state);
+ adev->mic_mute = state;
+ ALOGD("device-%s: set MIC Mute to (%d)", __func__, (int)state);
+ pthread_mutex_unlock(&adev->lock);
+
+ return 0;
+}
+
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+
+ pthread_mutex_lock(&adev->lock);
+ *state = adev->mic_mute;
+ pthread_mutex_unlock(&adev->lock);
+
+ return 0;
+}
+
+static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_out *primary_out = adev->primary_output;
+
+ struct str_parms *parms;
+ char value[256];
+ int val;
+ int ret = 0; // for parameter handling
+ int status = 0; // for return value
+
+ ALOGD("device-%s: enter with key(%s)", __func__, kvpairs);
+
+ pthread_mutex_lock(&adev->lock);
+
+ parms = str_parms_create_str(kvpairs);
+
+ status = proxy_set_parameters(adev->proxy, parms);
+ if (status != 0) goto done;
+
+ ret = str_parms_get_int(parms, AUDIO_PARAMETER_DEVICE_CONNECT, &val);
+ if (ret >= 0) {
+ audio_devices_t device = (audio_devices_t)val;
+
+ if (device > AUDIO_DEVICE_BIT_IN) {
+ adev->previous_capture_device = adev->actual_capture_device;
+ adev->actual_capture_device = device;
+
+ if (device & AUDIO_DEVICE_IN_USB_DEVICE) {
+ voice_set_usb_mic(adev->voice, true);
+ }
+ } else {
+ adev->previous_playback_device = adev->actual_playback_device;
+ adev->actual_playback_device = device;
+ }
+
+ ALOGI("device-%s: connected device(%s)", __func__, device_table[get_device_id(adev, device)]);
+ str_parms_del(parms, AUDIO_PARAMETER_DEVICE_CONNECT);
+ }
+
+ ret = str_parms_get_int(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, &val);
+ if (ret >= 0) {
+ audio_devices_t device = (audio_devices_t)val;
+ ALOGI("device-%s: disconnected device(%s)", __func__, device_table[get_device_id(adev, device)]);
+
+ if (device > AUDIO_DEVICE_BIT_IN) {
+ adev->actual_capture_device = adev->previous_capture_device;
+ adev->previous_capture_device = device;
+
+ if (device & AUDIO_DEVICE_IN_USB_DEVICE) {
+ voice_set_usb_mic(adev->voice, false);
+ }
+ } else {
+ adev->actual_playback_device = adev->previous_playback_device;
+ adev->previous_playback_device = device;
+ }
+ str_parms_del(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT);
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SCREEN_STATE, value, sizeof(value));
+ if (ret >= 0) {
+ ALOGI("device-%s: Screen State = %s)", __func__, value);
+ if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
+ adev->screen_on = true;
+ else
+ adev->screen_on = false;
+
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_SCREEN_STATE);
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FMRADIO_MODE, value, sizeof(value));
+ if (ret >= 0) {
+ ALOGV("device-%s: FM_Mode = %s", __func__, value);
+ if(strncmp(value, "on", 2) == 0) {
+ ALOGI("device-%s: FM Radio Start", __func__);
+ } else {
+ if (adev->fm_state == FM_ON || adev->fm_state == FM_RECORDING) adev->fm_state = FM_OFF;
+ }
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_FMRADIO_MODE);
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FMRADIO_VOLUME, value, sizeof(value));
+ if (ret >= 0) {
+ ALOGV("device-%s: FM_Radio_Volume = %s", __func__, value);
+ if(strncmp(value, "on", 2) == 0) {
+ adev_set_route((void *)primary_out, AUSAGE_PLAYBACK, ROUTE, NON_FORCE_ROUTE);
+ proxy_start_fm_radio(adev->proxy);
+ } else {
+ proxy_stop_fm_radio(adev->proxy);
+
+ if (is_active_usage_APCall(adev->proxy) || (adev->voice && voice_is_call_active(adev->voice)))
+ ALOGV("device-%s: FM_Radio_Volume = %s, skip unroute path during call", __func__, value);
+ else {
+ if(adev->primary_output->common.stream_status == STATUS_STANDBY)
+ adev_set_route((void *)primary_out, AUSAGE_PLAYBACK, UNROUTE, NON_FORCE_ROUTE);
+ }
+ }
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_FMRADIO_VOLUME);
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_SEAMLESS_VOICE, value, sizeof(value));
+ if (ret >= 0) {
+ bool seamless_mode = (strcmp(value, "on")) ? false : true;
+
+ if(seamless_mode && !adev->seamless_enabled) {
+ adev->seamless_enabled = true;
+ } else if(!seamless_mode && adev->seamless_enabled) {
+ adev->seamless_enabled = false;
+ }
+ ALOGV("seamless_enabled = %d", adev->seamless_enabled);
+ str_parms_del(parms, AUDIO_PARAMETER_SEAMLESS_VOICE);
+ }
+
+ // For Voice Manager
+ if (adev->voice)
+ voice_set_parameters(adev, parms);
+
+ // For Factory Manager
+ if (adev->factory && adev->voice)
+ factory_set_parameters(adev, parms);
+
+done:
+ str_parms_destroy(parms);
+ pthread_mutex_unlock(&adev->lock);
+
+ ALOGD("device-%s: exit", __func__);
+ return status;
+}
+
+static char * adev_get_parameters(const struct audio_hw_device *dev, const char *keys)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct str_parms *reply = str_parms_create();
+ struct str_parms *query = str_parms_create_str(keys);
+ char *str;
+
+ ALOGVV("device-%s: enter with key(%s)", __func__, keys);
+
+ pthread_mutex_lock(&adev->lock);
+
+ int ret = 0;
+ char value[32];
+
+ //requested by phone app to check call_forwarding status.
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_CALL_FORWARDING, value, sizeof(value));
+ if (ret >= 0) {
+ strlcpy(value, ((adev->voice && adev->voice->call_forwarding) ? "true" : "false"), sizeof(value));
+ ALOGI("device-%s: call_forwarding is %s", __func__, value);
+ str_parms_add_str(reply, AUDIO_PARAMETER_KEY_CALL_FORWARDING, value);
+ }
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_EXTRA_VOLUME, value, sizeof(value));
+ if (ret >= 0) {
+ strlcpy(value, ((adev->voice && adev->voice->extra_volume) ? "true" : "false"), sizeof(value));
+ ALOGI("device-%s: extra_volume is %s", __func__, value);
+ str_parms_add_str(reply, AUDIO_PARAMETER_KEY_EXTRA_VOLUME, value);
+ }
+
+ str = str_parms_to_str(reply);
+ str_parms_destroy(query);
+ str_parms_destroy(reply);
+
+ pthread_mutex_unlock(&adev->lock);
+
+ ALOGVV("device-%s: exit with %s", __func__, str);
+ return str;
+}
+
+static size_t adev_get_input_buffer_size(
+ const struct audio_hw_device *dev __unused,
+ const struct audio_config *config)
+{
+ size_t size = 0;
+ unsigned int period_size = 0;
+
+ ALOGVV("device-%s: enter with SR(%d Hz), Channel(%d)", __func__,
+ config->sample_rate, audio_channel_count_from_in_mask(config->channel_mask));
+
+ /*
+ * To calcurate actual buffer size, it needs period size.
+ * However, it cannot fix period size at this time.
+ * So, pre-defined period size will be using.
+ */
+
+ // return buffer size based on requested PCM configuration
+ period_size = (config->sample_rate * PREDEFINED_CAPTURE_DURATION) / 1000;
+ size = period_size * audio_bytes_per_sample(config->format) *
+ audio_channel_count_from_in_mask(config->channel_mask);
+ ALOGVV("device-%s: exit with %d bytes for %d ms", __func__, (int)size, PREDEFINED_CAPTURE_DURATION);
+ return size;
+}
+
+static int adev_open_output_stream(
+ struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_out *out = NULL;
+ int ret;
+
+ ALOGD("device-%s: enter: io_handle (%d), sample_rate(%u) channel_mask(%#x) format(%#x) framecount(%u) devices(%#x) flags(%#x)",
+ __func__, handle, config->sample_rate, config->channel_mask, config->format, config->frame_count, devices, flags);
+
+ *stream_out = NULL;
+
+ /* Allocates the memory for structure audio_stream_out. */
+ out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
+ if (!out) {
+ ALOGE("device-%s: failed to allocate memory for stream_out", __func__);
+ return -ENOMEM;
+ }
+ out->adev = adev;
+
+ /* Saves the requested parameters from Android Platform. */
+ out->common.handle = handle;
+ out->common.requested_devices = devices;
+ out->common.requested_sample_rate = config->sample_rate;
+ out->common.requested_channel_mask = config->channel_mask;
+ out->common.requested_format = config->format;
+ out->common.requested_frame_count = config->frame_count;
+ out->requested_flags = flags;
+
+ /*
+ * Sets Stream Type & Audio Usage Type from Audio Flags and Devices.
+ * These information can be used to decide Mixer Path.
+ */
+ out->common.stream_type = ASTREAM_NONE;
+ out->common.stream_usage = AUSAGE_NONE;
+
+ if (flags == AUDIO_OUTPUT_FLAG_NONE) {
+ /* Case: No Attributes Playback Stream */
+ if (devices == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+ /* Sub-Case: Playback with Aux Digital */
+ ALOGI("device-%s: requested to open AUX-DIGITAL playback stream", __func__);
+ out->common.stream_type = ASTREAM_PLAYBACK_AUX_DIGITAL;
+ out->common.stream_usage = AUSAGE_MEDIA;
+ } else if ((devices == AUDIO_DEVICE_OUT_USB_ACCESSORY) || (devices == AUDIO_DEVICE_OUT_USB_DEVICE) ||
+ (devices == AUDIO_DEVICE_OUT_USB_HEADSET)) {
+ ALOGI("device-%s: requested to open USB Device playback stream with address (%s)",
+ __func__, address);
+ out->common.stream_type = ASTREAM_PLAYBACK_USB_DEVICE;
+ out->common.stream_usage = AUSAGE_MEDIA;
+ } else {
+ ALOGI("device-%s: requested to open No Attributes playback stream", __func__);
+ out->common.stream_type = ASTREAM_PLAYBACK_NO_ATTRIBUTE;
+ out->common.stream_usage = AUSAGE_MEDIA;
+ }
+ } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) {
+ /* Case: Direct Playback Stream */
+ if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) &&
+ ((flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING) != 0)) {
+ /* Sub-Case: Direct Playback Stream with Non-Blocking Compress Offload */
+ ALOGI("device-%s: requested to open Compress Offload playback stream", __func__);
+ out->common.stream_type = ASTREAM_PLAYBACK_COMPR_OFFLOAD;
+ out->common.stream_usage = AUSAGE_MEDIA;
+
+ /* Maps the function pointers in structure audio_stream_out as actual function. */
+ out->stream.set_callback = out_set_callback;
+ out->stream.pause = out_pause;
+ out->stream.resume = out_resume;
+ out->stream.drain = out_drain;
+ out->stream.flush = out_flush;
+
+ pthread_mutex_lock(&adev->lock);
+ if (adev->compress_output == NULL)
+ adev->compress_output = out;
+ pthread_mutex_unlock(&adev->lock);
+ } else if ((flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) != 0) {
+ /* Case: MMAP No IRQ Playback Stream */
+ ALOGI("device-%s: requested to open MMAP No IRQ playback stream", __func__);
+
+ out->common.stream_type = ASTREAM_PLAYBACK_MMAP;
+ out->common.stream_usage = AUSAGE_MEDIA;
+
+ /* Maps the function pointers in structure audio_stream_out as actual function. */
+ out->stream.start = out_start;
+ out->stream.stop = out_stop;
+ out->stream.create_mmap_buffer = out_create_mmap_buffer;
+ out->stream.get_mmap_position = out_get_mmap_position;
+ }
+ } else if ((flags & AUDIO_OUTPUT_FLAG_PRIMARY) != 0) {
+ /*
+ * Initializes Voice Manager.
+ * Voice Manager is handling Voice to support Call scenarios.
+ */
+ if (!adev->voice) {
+ adev->voice = voice_init();
+ if(!adev->voice)
+ ALOGE("device-%s: failed to init Voice Manager!", __func__);
+ else
+ ALOGD("device-%s: initialized Voice Manager!", __func__);
+ }
+
+ /* Case: Primary Playback Stream */
+ ALOGI("device-%s: requested to open Primary playback stream", __func__);
+
+ pthread_mutex_lock(&adev->lock);
+ if (adev->primary_output == NULL)
+ adev->primary_output = out;
+ else {
+ ALOGE("device-%s: Primary playback stream is already opened", __func__);
+ ret = -EEXIST;
+ pthread_mutex_unlock(&adev->lock);
+ goto err_open;
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ out->common.stream_type = ASTREAM_PLAYBACK_PRIMARY;
+ out->common.stream_usage = AUSAGE_MEDIA;
+ } else if ((flags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
+ /* Case: Fast Playback Stream */
+ if ((flags & AUDIO_OUTPUT_FLAG_RAW) != 0) {
+ /* Sub-Case: Low Latency Playback Stream for ProAudio */
+ ALOGI("device-%s: requested to open Low Latency playback stream", __func__);
+
+ out->common.stream_type = ASTREAM_PLAYBACK_LOW_LATENCY;
+ } else {
+ /* Sub-Case: Normal Fast Playback Stream */
+ ALOGI("device-%s: requested to open Fast playback stream", __func__);
+
+ out->common.stream_type = ASTREAM_PLAYBACK_FAST;
+ }
+ out->common.stream_usage = AUSAGE_MEDIA;
+ } else if ((flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) {
+ /* Case: Deep Buffer Playback Stream */
+ ALOGI("device-%s: requested to open Deep Buffer playback stream", __func__);
+
+ out->common.stream_type = ASTREAM_PLAYBACK_DEEP_BUFFER;
+ out->common.stream_usage = AUSAGE_MEDIA;
+ } else if ((flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) != 0) {
+ /* Case: Incall Music Playback Stream */
+ ALOGI("device-%s: requested to open Incall Music playback stream", __func__);
+
+ out->common.stream_type = ASTREAM_PLAYBACK_INCALL_MUSIC;
+ out->common.stream_usage = AUSAGE_INCALL_MUSIC;
+ } else {
+ /* Error Case: Not Supported usage */
+ ALOGI("device-%s: requested to open un-supported output", __func__);
+
+ ret = -EINVAL;
+ goto err_open;
+ }
+
+ /* Maps the function pointers in structure audio_stream_out as actual function. */
+ out->stream.common.get_sample_rate = out_get_sample_rate;
+ out->stream.common.set_sample_rate = out_set_sample_rate;
+ out->stream.common.get_buffer_size = out_get_buffer_size;
+ out->stream.common.get_channels = out_get_channels;
+ out->stream.common.get_format = out_get_format;
+ out->stream.common.set_format = out_set_format;
+ out->stream.common.standby = out_standby;
+ out->stream.common.dump = out_dump;
+ out->stream.common.get_device = out_get_device;
+ out->stream.common.set_device = out_set_device;
+ out->stream.common.set_parameters = out_set_parameters;
+ out->stream.common.get_parameters = out_get_parameters;
+ out->stream.common.add_audio_effect = out_add_audio_effect;
+ out->stream.common.remove_audio_effect = out_remove_audio_effect;
+
+ out->stream.get_latency = out_get_latency;
+ out->stream.set_volume = out_set_volume;
+ out->stream.write = out_write;
+ out->stream.get_render_position = out_get_render_position;
+ out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
+ out->stream.get_presentation_position = out_get_presentation_position;
+
+ // For AudioHAL V4
+ out->stream.update_source_metadata = out_update_source_metadata;
+
+ /* Sets platform-specific information. */
+ pthread_mutex_init(&out->common.lock, (const pthread_mutexattr_t *) NULL);
+ pthread_mutex_lock(&out->common.lock);
+
+ // Creates Proxy Stream
+ out->common.proxy_stream = proxy_create_playback_stream(adev->proxy,
+ (int)out->common.stream_type,
+ (void *)config, (char *)address);
+ if (!out->common.proxy_stream) {
+ ALOGE("%s-%s: failed to create Audio Proxy Stream", stream_table[out->common.stream_type], __func__);
+
+ ret = -EINVAL;
+ pthread_mutex_unlock(&out->common.lock);
+ goto err_open;
+ }
+
+ // Special Process for Compress Offload
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ /* Creates Callback Thread for supporting Non-Blocking Mode */
+ if (flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING) {
+ ALOGV("%s-%s: Need to work as Nonblock Mode!", stream_table[out->common.stream_type], __func__);
+ proxy_offload_set_nonblock(out->common.proxy_stream);
+ out->offload.nonblock_flag = 1;
+
+ create_offload_callback_thread(out);
+ list_init(&out->offload.msg_list);
+ }
+
+ /* Connects Offload Effect Libraries */
+ out->common.offload_audio_format = AUDIO_FORMAT_PCM_16_BIT;
+ }
+
+ // Special Process for USB Device or DP Audio
+ // In general, this stream will be opened at Null Configuration.
+ // So it needs to update configuration as actual value
+ if (out->common.stream_type == ASTREAM_PLAYBACK_USB_DEVICE ||
+ out->common.stream_type == ASTREAM_PLAYBACK_AUX_DIGITAL) {
+ if ((config->sample_rate == 0) || (out->common.stream_type == ASTREAM_PLAYBACK_USB_DEVICE)) {
+ // In case of Sampling Rate for USB, it needs to update anytime
+ // because Sampling Rate can be changed by alsa_util library.
+ config->sample_rate = proxy_get_actual_sampling_rate(out->common.proxy_stream);
+ out->common.requested_sample_rate = config->sample_rate;
+ }
+
+ if (config->channel_mask == AUDIO_CHANNEL_NONE) {
+ config->channel_mask = audio_channel_out_mask_from_count(
+ proxy_get_actual_channel_count(out->common.proxy_stream));
+ out->common.requested_channel_mask = config->channel_mask;
+ }
+
+ if (config->format == AUDIO_FORMAT_DEFAULT) {
+ config->format = (audio_format_t)proxy_get_actual_format(out->common.proxy_stream);
+ out->common.requested_format = config->format;
+ }
+ }
+
+ out->common.stream_status = STATUS_STANDBY; // Not open PCM Device, yet
+ ALOGI("%s-%s: transited to Standby", stream_table[out->common.stream_type], __func__);
+
+ // Adds this stream into playback list
+ pthread_mutex_lock(&adev->lock);
+ struct playback_stream *out_node = (struct playback_stream *)calloc(1, sizeof(struct playback_stream));
+ out_node->out = out;
+ list_add_tail(&adev->playback_list, &out_node->node);
+ pthread_mutex_unlock(&adev->lock);
+
+ out->force = NON_FORCE_ROUTE;
+ out->rollback_devices = AUDIO_DEVICE_NONE;
+ pthread_mutex_unlock(&out->common.lock);
+
+ /* Sets the structure audio_stream_out for return. */
+ *stream_out = &out->stream;
+
+ ALOGI("device-%s: opened %s stream", __func__, stream_table[out->common.stream_type]);
+ return 0;
+
+err_open:
+ if (out->common.proxy_stream) {
+ proxy_destroy_playback_stream(out->common.proxy_stream);
+ out->common.proxy_stream = NULL;
+ }
+
+ pthread_mutex_lock(&adev->lock);
+ if (out->common.stream_type == ASTREAM_PLAYBACK_PRIMARY)
+ adev->primary_output = NULL;
+ pthread_mutex_unlock(&adev->lock);
+
+ free(out);
+ *stream_out = NULL;
+
+ ALOGI("device-%s: failed to open this stream as error(%d)", __func__, ret);
+ return ret;
+}
+
+static void adev_close_output_stream(
+ struct audio_hw_device *dev,
+ struct audio_stream_out *stream)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_out *out = (struct stream_out *)stream;
+
+ struct listnode *node, *auxi;
+ struct playback_stream *out_node;
+
+ ALOGVV("device-%s: enter", __func__);
+
+ if (out) {
+ ALOGI("%s-%s: try to close plyback stream", stream_table[out->common.stream_type], __func__);
+ out_standby(&stream->common);
+
+ pthread_mutex_lock(&out->common.lock);
+ pthread_mutex_lock(&adev->lock);
+
+ // Removes this stream from playback list
+ list_for_each_safe(node, auxi, &adev->playback_list)
+ {
+ out_node = node_to_item(node, struct playback_stream, node);
+ if (out_node->out == out) {
+ list_remove(node);
+ free(out_node);
+ }
+ }
+
+ if (adev->primary_output == out) {
+ ALOGI("%s-%s: requested to close Primary playback stream",
+ stream_table[out->common.stream_type], __func__);
+ adev->primary_output = NULL;
+ }
+
+ if (adev->compress_output == out) {
+ ALOGI("%s-%s: requested to close Compress Offload playback stream",
+ stream_table[out->common.stream_type], __func__);
+ adev->compress_output = NULL;
+ }
+
+ pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_unlock(&out->common.lock);
+
+ if (out->common.stream_type == ASTREAM_PLAYBACK_COMPR_OFFLOAD) {
+ if (out->offload.nonblock_flag)
+ destroy_offload_callback_thread(out);
+ }
+
+ if (out->common.stream_type == ASTREAM_PLAYBACK_INCALL_MUSIC) {
+ adev->incallmusic_on = false;
+ }
+
+ pthread_mutex_lock(&out->common.lock);
+ proxy_destroy_playback_stream(out->common.proxy_stream);
+ out->common.proxy_stream = NULL;
+ pthread_mutex_unlock(&out->common.lock);
+
+ pthread_mutex_destroy(&out->common.lock);
+
+ ALOGI("%s-%s: closed playback stream", stream_table[out->common.stream_type], __func__);
+ free(out);
+ }
+
+ ALOGVV("device-%s: exit", __func__);
+ return;
+}
+
+static int adev_open_input_stream(
+ struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in,
+ audio_input_flags_t flags,
+ const char *address,
+ audio_source_t source)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_in *in = NULL;
+ int ret;
+
+ ALOGD("device-%s: enter: io_handle (%d), sample_rate(%d) channel_mask(%#x) format(%#x) framecount(%u) devices(%#x) flags(%#x) sources(%d)",
+ __func__, handle, config->sample_rate, config->channel_mask, config->format, config->frame_count, devices, flags, source);
+
+ *stream_in = NULL;
+
+ /* Allocates the memory for structure audio_stream_in. */
+ in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
+ if (!in) {
+ ALOGE("device-%s: failed to allocate memory for stream_in", __func__);
+ return -ENOMEM;
+ }
+ in->adev = adev;
+
+ /* Saves the requested parameters from Android Platform. */
+ in->common.handle = handle;
+ in->common.requested_devices = devices;
+ in->common.requested_sample_rate = config->sample_rate;
+ in->common.requested_channel_mask = config->channel_mask;
+ in->common.requested_format = config->format;
+ in->common.requested_frame_count = config->frame_count;
+ in->requested_flags = flags;
+ in->requested_source = source;
+
+ /*
+ * Sets Stream Type & Audio Usage Type from Audio Flags, Sources and Devices.
+ * These information can be used to decide Mixer Path.
+ */
+ in->common.stream_type = ASTREAM_NONE;
+ in->common.stream_usage = AUSAGE_NONE;
+
+ if ((flags & AUDIO_INPUT_FLAG_FAST) != 0) {
+ if (isCallMode(adev) && config->sample_rate != LOW_LATENCY_CAPTURE_SAMPLE_RATE) {
+ flags &= ~AUDIO_INPUT_FLAG_FAST;
+ ALOGD("device-%s: Denied to open Low Latency input. flags changed(%#x)", __func__, flags);
+ }
+ }
+
+#ifdef SUPPORT_STHAL_INTERFACE
+ if (source == AUDIO_SOURCE_HOTWORD ||
+ (adev->seamless_enabled == true && source == AUDIO_SOURCE_VOICE_RECOGNITION) ||
+ (((flags & AUDIO_INPUT_FLAG_HW_HOTWORD) != 0) && source == AUDIO_SOURCE_VOICE_RECOGNITION)) {
+ /* Case: Use ST HAL interface for Capture */
+ in->common.stream_type = ASTREAM_CAPTURE_HOTWORD;
+ if (source == AUDIO_SOURCE_HOTWORD ||
+ (adev->seamless_enabled == true && source == AUDIO_SOURCE_VOICE_RECOGNITION)) {
+ in->common.stream_usage = AUSAGE_HOTWORD_SEAMLESS;
+ ALOGD("device-%s: Requested to open VTS Seamless input", __func__);
+ } else {
+ in->common.stream_usage = AUSAGE_HOTWORD_RECORD;
+ ALOGD("device-%s: Requested to open VTS Record input", __func__);
+ }
+ } else if (flags == AUDIO_INPUT_FLAG_NONE)
+#else
+ if (flags == AUDIO_INPUT_FLAG_NONE)
+#endif
+ {
+ if (isCPCallMode(adev) &&
+ is_usage_CPCall(adev->active_capture_ausage) &&
+ isCallRecording(in->requested_source)) {
+ /* Case: CP Voice Call Recording Stream */
+ ALOGI("device-%s: requested to open CP Voice Call Recording stream", __func__);
+ in->common.stream_type = ASTREAM_CAPTURE_CALL;
+
+ switch (source) {
+ case AUDIO_SOURCE_VOICE_UPLINK:
+ ALOGI("device-%s: needs only uplink voice", __func__);
+ in->common.stream_usage = AUSAGE_INCALL_UPLINK;
+ break;
+
+ case AUDIO_SOURCE_VOICE_DOWNLINK:
+ ALOGI("device-%s: needs only downlink voice", __func__);
+ in->common.stream_usage = AUSAGE_INCALL_DOWNLINK;
+ break;
+
+ case AUDIO_SOURCE_VOICE_CALL:
+ ALOGI("device-%s: needs uplink/downlink voice both", __func__);
+ in->common.stream_usage = AUSAGE_INCALL_UPLINK_DOWNLINK;
+ break;
+
+ default:
+ ALOGI("device-%s: needs only uplink voice from normal source", __func__);
+ in->common.stream_usage = AUSAGE_INCALL_UPLINK;
+ break;
+ }
+ }
+ else if ((source == AUDIO_SOURCE_FM_TUNER ) && (devices == AUDIO_DEVICE_IN_FM_TUNER)) {
+ ALOGI("device-%s: requested to open FM Radio Tuner stream", __func__);
+ adev->fm_state = FM_ON;
+ adev->fm_need_route = true;
+ in->common.stream_type = ASTREAM_CAPTURE_FM_TUNER;
+ in->common.stream_usage = AUSAGE_FM_RADIO_TUNER;
+ // FM Tuner routing
+ adev_set_route((void *)in, AUSAGE_CAPTURE, ROUTE, NON_FORCE_ROUTE);
+ adev->fm_need_route = false;
+
+ } else {
+ /* Case: Normal Recording Stream */
+ if ((devices == AUDIO_DEVICE_IN_USB_ACCESSORY) || (devices == AUDIO_DEVICE_IN_USB_DEVICE) ||
+ (devices == AUDIO_DEVICE_IN_USB_HEADSET)) {
+ /* Case: USB Capture Stream */
+ ALOGI("device-%s: requested to open USB Device capture stream with address (%s)",
+ __func__, address);
+ in->common.stream_type = ASTREAM_CAPTURE_USB_DEVICE;
+ in->common.stream_usage = AUSAGE_RECORDING;
+ } else if ((devices == AUDIO_DEVICE_IN_DEFAULT) && (source == AUDIO_SOURCE_DEFAULT)) {
+ /* Case: No Attributes Capture Stream */
+ ALOGI("device-%s: requested to open No Attribute capture stream", __func__);
+ in->common.stream_type = ASTREAM_CAPTURE_NO_ATTRIBUTE;
+ in->common.stream_usage = AUSAGE_RECORDING;
+ } else if(devices == AUDIO_DEVICE_IN_TELEPHONY_RX && isUSBHeadsetConnect(adev)) {
+ /* Error Case: Not Supported samspling rate */
+ ALOGI("device-%s: requested to open AUDIO_DEVICE_IN_TELEPHONY_RX", __func__);
+ ret = -EINVAL;
+ goto err_open;
+ } else {
+ /* Case: Primary Capture Stream */
+ ALOGI("device-%s: requested to open Primay capture stream", __func__);
+ in->common.stream_type = ASTREAM_CAPTURE_PRIMARY;
+
+ switch (source) {
+ case AUDIO_SOURCE_MIC:
+ in->common.stream_usage = AUSAGE_RECORDING;
+ break;
+
+ case AUDIO_SOURCE_CAMCORDER:
+ in->common.stream_usage = AUSAGE_CAMCORDER;
+ break;
+
+ case AUDIO_SOURCE_VOICE_RECOGNITION:
+ in->common.stream_usage = AUSAGE_RECOGNITION;
+ break;
+
+ case AUDIO_SOURCE_DEFAULT:
+ default:
+ in->common.stream_usage = AUSAGE_RECORDING;
+ break;
+ }
+ }
+ }
+ } else if ((flags & AUDIO_INPUT_FLAG_FAST) != 0) {
+ /* Case: Low Latency Capture Stream */
+ ALOGI("device-%s: requested to open Low Latency capture stream", __func__);
+ in->common.stream_type = ASTREAM_CAPTURE_LOW_LATENCY;
+ in->common.stream_usage = AUSAGE_MEDIA;
+ } else if ((flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0) {
+ if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE) {
+ /* Case: MMAP No IRQ Capture Stream */
+ ALOGI("device-%s: requested to open MMAP No IRQ capture stream", __func__);
+
+ in->common.stream_type = ASTREAM_CAPTURE_MMAP;
+ in->common.stream_usage = AUSAGE_MEDIA;
+
+ /* Maps the function pointers in structure audio_stream_in as actual function. */
+ in->stream.start = in_start;
+ in->stream.stop = in_stop;
+ in->stream.create_mmap_buffer = in_create_mmap_buffer;
+ in->stream.get_mmap_position = in_get_mmap_position;
+ } else {
+ /* Error Case: Not Supported samspling rate */
+ ALOGI("device-%s: requested to open MMAP input with abnormal sampling rate(%d)",
+ __func__, config->sample_rate);
+
+ ret = -EINVAL;
+ goto err_open;
+ }
+ } else {
+ /* Error Case: Not Supported usage */
+ ALOGI("device-%s: requested to open un-supported output", __func__);
+
+ ret = -EINVAL;
+ goto err_open;
+ }
+
+
+ /* Maps the function pointers in structure audio_stream_in as actual function. */
+ in->stream.common.get_sample_rate = in_get_sample_rate;
+ in->stream.common.set_sample_rate = in_set_sample_rate;
+ in->stream.common.get_buffer_size = in_get_buffer_size;
+ in->stream.common.get_channels = in_get_channels;
+ in->stream.common.get_format = in_get_format;
+ in->stream.common.set_format = in_set_format;
+ in->stream.common.standby = in_standby;
+ in->stream.common.dump = in_dump;
+ in->stream.common.get_device = in_get_device;
+ in->stream.common.set_device = in_set_device;
+ in->stream.common.set_parameters = in_set_parameters;
+ in->stream.common.get_parameters = in_get_parameters;
+ in->stream.common.add_audio_effect = in_add_audio_effect;
+ in->stream.common.remove_audio_effect = in_remove_audio_effect;
+
+ in->stream.set_gain = in_set_gain;
+ in->stream.read = in_read;
+ in->stream.get_input_frames_lost = in_get_input_frames_lost;
+ in->stream.get_capture_position = in_get_capture_position;
+
+ // For AudioHAL V4
+ in->stream.get_active_microphones = in_get_active_microphones;
+ in->stream.update_sink_metadata = in_update_sink_metadata;
+
+ /* Sets Platform-specific information. */
+ pthread_mutex_init(&in->common.lock, (const pthread_mutexattr_t *) NULL);
+ pthread_mutex_lock(&in->common.lock);
+
+ // Creates Proxy Stream
+ in->common.proxy_stream = proxy_create_capture_stream(adev->proxy,
+ (int)in->common.stream_type,
+ (int)in->common.stream_usage,
+ (void *)config, (char *)address);
+ if (!in->common.proxy_stream) {
+ ALOGE("%s-%s: failed to create Audio Proxy Stream", stream_table[in->common.stream_type], __func__);
+
+ ret = -EINVAL;
+ pthread_mutex_unlock(&in->common.lock);
+ goto err_open;
+ }
+
+ // Process for USB Device
+ if (in->common.stream_type == ASTREAM_CAPTURE_USB_DEVICE) {
+ if (config->sample_rate == 0) {
+ config->sample_rate = proxy_get_actual_sampling_rate(in->common.proxy_stream);
+ in->common.requested_sample_rate = config->sample_rate;
+ }
+
+ if (config->channel_mask == AUDIO_CHANNEL_NONE) {
+ config->channel_mask = audio_channel_in_mask_from_count(
+ proxy_get_actual_channel_count(in->common.proxy_stream));
+ in->common.requested_channel_mask = config->channel_mask;
+ }
+
+ if (config->format == AUDIO_FORMAT_DEFAULT) {
+ config->format = (audio_format_t)proxy_get_actual_format(in->common.proxy_stream);
+ in->common.requested_format = config->format;
+ }
+ }
+
+ in->common.stream_status = STATUS_STANDBY; // Not open PCM Device, yet
+ ALOGI("%s-%s: transited to Standby", stream_table[in->common.stream_type], __func__);
+
+ // Adds this stream into capture list
+ pthread_mutex_lock(&adev->lock);
+ struct capture_stream *in_node = (struct capture_stream *)calloc(1, sizeof(struct capture_stream));
+ in_node->in = in;
+ list_add_tail(&adev->capture_list, &in_node->node);
+
+ // Customer Specific
+ adev->pcmread_latency = proxy_get_actual_period_size(in->common.proxy_stream) * 1000 /
+ proxy_get_actual_sampling_rate(in->common.proxy_stream);
+ in->pcm_reconfig = false;
+
+ pthread_mutex_unlock(&adev->lock);
+
+ pthread_mutex_unlock(&in->common.lock);
+
+ /* Sets the structure audio_stream_in for return. */
+ *stream_in = &in->stream;
+
+ ALOGI("device-%s: opened %s stream", __func__, stream_table[in->common.stream_type]);
+ return 0;
+
+err_open:
+ if (in)
+ free(in);
+ *stream_in = NULL;
+
+ ALOGI("device-%s: failed to open this stream as error(%d)", __func__, ret);
+ return ret;
+}
+
+static void adev_close_input_stream(
+ struct audio_hw_device *dev,
+ struct audio_stream_in *stream)
+{
+ struct audio_device *adev = (struct audio_device *)dev;
+ struct stream_in *in = (struct stream_in *)stream;
+
+ struct listnode *node, *auxi;
+ struct capture_stream *in_node;
+
+ ALOGI("device-%s: enter", __func__);
+
+ if (in) {
+ ALOGI("%s-%s: try to close capture stream", stream_table[in->common.stream_type], __func__);
+ in_standby(&stream->common);
+
+ pthread_mutex_lock(&in->common.lock);
+ pthread_mutex_lock(&adev->lock);
+
+ // disable fm radio stream
+ if (in->common.stream_usage == AUSAGE_FM_RADIO_TUNER) {
+ adev->fm_state = FM_OFF;
+ adev_set_route((void *)in, AUSAGE_CAPTURE, UNROUTE, NON_FORCE_ROUTE);
+ }
+
+ if (in->common.stream_usage == AUSAGE_FM_RADIO_CAPTURE) {
+ adev->fm_state = FM_ON;
+ adev_set_route((void *)in, AUSAGE_CAPTURE, UNROUTE, NON_FORCE_ROUTE);
+ }
+
+ // Removes this stream from capture list
+ list_for_each_safe(node, auxi, &adev->capture_list)
+ {
+ in_node = node_to_item(node, struct capture_stream, node);
+ if (in_node->in == in) {
+ list_remove(node);
+ free(in_node);
+ }
+ }
+ pthread_mutex_unlock(&adev->lock);
+
+ proxy_destroy_capture_stream(in->common.proxy_stream);
+ in->common.proxy_stream = NULL;
+
+ pthread_mutex_unlock(&in->common.lock);
+ pthread_mutex_destroy(&in->common.lock);
+
+ ALOGI("%s-%s: closed capture stream", stream_table[in->common.stream_type], __func__);
+ free(in);
+ }
+
+ ALOGVV("device-%s: exit", __func__);
+ return;
+}
+
+
+static int adev_get_microphones(const audio_hw_device_t *device,
+ struct audio_microphone_characteristic_t *mic_array,
+ size_t *mic_count)
+{
+ struct audio_device *adev = (struct audio_device *)device;
+ int ret = 0;
+
+ ALOGVV("device-%s: entered", __func__);
+
+ if (mic_array == NULL || mic_count == NULL) return -EINVAL;
+
+ ret = proxy_get_microphones(adev->proxy, (void *)mic_array, (int *)mic_count);
+
+ ALOGVV("device-%s: exited", __func__);
+ return ret;
+}
+
+static int adev_dump(const audio_hw_device_t *device, int fd)
+{
+ struct audio_device *adev = (struct audio_device *)device;
+
+ ALOGV("device-%s: enter with file descriptor(%d)", __func__, fd);
+
+ const size_t len = 256;
+ char buffer[len];
+
+ snprintf(buffer, len, "\nAudioDevice HAL::dump\n");
+ write(fd,buffer,strlen(buffer));
+
+ bool justLocked = pthread_mutex_trylock(&adev->lock) == 0;
+ snprintf(buffer, len, "\tMutex: %s\n", justLocked ? "locked" : "unlocked");
+ write(fd,buffer,strlen(buffer));
+ if(justLocked)
+ pthread_mutex_unlock(&adev->lock);
+
+ snprintf(buffer, len, "1. Common part\n");
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tAudio Mode: %d\n",adev->amode);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tAudio Previous Mode: %d\n",adev->previous_amode);
+ write(fd,buffer,strlen(buffer));
+
+ snprintf(buffer, len, "\n2. About Call part\n");
+ write(fd,buffer,strlen(buffer));
+ if(adev->voice) {
+ snprintf(buffer, len, "\tCall Active: %d\n", adev->voice->call_status);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tRealCall: %s\n",bool_to_str(adev->voice->realcall));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tVoLTE state: %s\n",bool_to_str(adev->voice->volte_status));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tPrevious VoLTE state: %s\n",bool_to_str(adev->voice->previous_volte_status));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tcurrent modem: %d\n",adev->voice->cur_modem);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tVoice Volume: %f\n",adev->voice_volume);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tVoice Volume Max Index: %d\n",adev->voice->volume_steps_max);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tMute Voice: %s\n",bool_to_str(adev->voice->mute_voice));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\twb_amr: %d\n",adev->voice->voice_samplingrate);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\textra volume: %s\n",bool_to_str(adev->voice->extra_volume));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tcall forwarding: %s\n",bool_to_str(adev->voice->call_forwarding)) ;
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tTTY mode: %d\n",adev->voice->tty_mode) ;
+ write(fd,buffer,strlen(buffer));
+ }
+ snprintf(buffer, len, "\tMic Mute: %s\n",bool_to_str(adev->mic_mute));
+ write(fd,buffer,strlen(buffer));
+ //snprintf(buffer, len, "\tspectro: %s\n",bool_to_str(adev->spectro)) ;
+ //write(fd,buffer,strlen(buffer));
+
+ snprintf(buffer, len, "\n3. About Communications part\n");
+ write(fd,buffer,strlen(buffer));
+ if(adev->voice) {
+ snprintf(buffer, len, "\tVoip wificalling: %s\n",bool_to_str(adev->voice->voip_wificalling));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tCSVT Call: %s\n",bool_to_str(adev->voice->csvtcall));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tVoIP RX active: %s\n",bool_to_str(adev->voice->voip_rx_active)) ;
+ write(fd,buffer,strlen(buffer));
+ }
+
+ snprintf(buffer, len, "\n4. About Connectivity part\n");
+ write(fd,buffer,strlen(buffer));
+ if(adev->voice) {
+ snprintf(buffer, len, "\tBluetooth_nrec: %d\n",adev->voice->bluetooth_nrec);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tBluetooth samplerate: %u\n",adev->voice->bluetooth_samplerate);
+ write(fd,buffer,strlen(buffer));
+ }
+
+ snprintf(buffer, len, "\n5. About Device Routing part\n");
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tAudio Playback Usage Mode: %d\n",adev->active_playback_ausage);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tAudio Capture Usage Mode: %d\n",adev->active_capture_ausage);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tSupport rev: %s\n",bool_to_str(adev->support_reciever));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tSupport backmic: %s\n",bool_to_str(adev->support_backmic));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tSupport thirdmic: %s\n",bool_to_str(adev->support_thirdmic));
+ write(fd,buffer,strlen(buffer));
+
+ snprintf(buffer, len, "\n6. About Offload Playback part\n");
+ write(fd,buffer,strlen(buffer));
+
+ if(adev->factory) {
+ snprintf(buffer, len, "\n7. About Factory Test part\n");
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tLoopback Out Device: %x\n",adev->factory->out_device);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tLoopback In Device: %x\n",adev->factory->in_device);
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tFactory mode: %s\n",bool_to_str(adev->factory->mode));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tis RMS Enable: %s\n",bool_to_str(adev->factory->rms_test_enable));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tfactory loopback mode: %d\n\n",adev->factory->loopback_mode);
+ write(fd,buffer,strlen(buffer));
+ }
+
+ snprintf(buffer, len, "\n9. FM Radio part\n");
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tFM Radio State: %s\n",adev->fm_state == FM_ON ? "FM_ON" : "FM_OFF");
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tFM Radio via BT: %s\n",bool_to_str(adev->fm_via_a2dp));
+ write(fd,buffer,strlen(buffer));
+ snprintf(buffer, len, "\tFM Radio Mute: %s\n",bool_to_str(adev->fm_radio_mute));
+ write(fd,buffer,strlen(buffer));
+
+ voice_ril_dump(fd);
+
+ if(adev->primary_output)
+ out_dump((struct audio_stream *)adev->primary_output,fd);
+ if(adev->active_input)
+ in_dump((struct audio_stream *)adev->active_input,fd);
+
+ proxy_fw_dump(fd);
+
+ ALOGV("device-%s: exit with file descriptor(%d)", __func__, fd);
+ return 0;
+}
+
+static int adev_close(hw_device_t *device)
+{
+ struct audio_device *adev = (struct audio_device *)device;
+
+ ALOGI("device-%s: enter", __func__);
+
+ if (adev) {
+ pthread_mutex_lock(&adev_init_lock);
+
+ if ((--adev_ref_count) == 0) {
+ /* Clean up Platform-specific information. */
+ pthread_mutex_lock(&adev->lock);
+
+ if (adev->voice) {
+ voice_deinit(adev->voice);
+ adev->voice = NULL;
+ }
+
+ adev_deinit_route(adev);
+
+ /* Clear external structures. */
+ if (adev->proxy) {
+ proxy_deinit(adev->proxy);
+ adev->proxy = NULL;
+ }
+
+ pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_destroy(&adev->lock);
+
+ destroyAudioDeviceInstance();
+ ALOGI("device-%s: closed Primary Audio HW Device(ref = %d)", __func__, adev_ref_count);
+ } else
+ ALOGI("device-%s: closed existing Primary Audio HW Device(ref = %d)", __func__, adev_ref_count);
+
+ pthread_mutex_unlock(&adev_init_lock);
+ }
+
+ return 0;
+}
+
+static int adev_open(
+ const hw_module_t* module,
+ const char* name,
+ hw_device_t** device)
+{
+ struct audio_device *adev = NULL;
+
+ ALOGI("device-%s: enter", __func__);
+
+ /* Check Interface Name. It must be AUDIO_HARDWARE_INTERFACE. */
+ if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) {
+ ALOGE("device-%s: invalid request: Interface Name = %s", __func__, name);
+ return -EINVAL;
+ }
+
+ pthread_mutex_lock(&adev_init_lock);
+ /* Create globally unique instance for Structure audio_device. */
+ adev = getAudioDeviceInstance();
+ if (!adev) {
+ ALOGE("device-%s: failed to allocate memory for audio_device", __func__);
+ pthread_mutex_unlock(&adev_init_lock);
+ return -ENOMEM;
+ }
+
+ if (adev_ref_count != 0) {
+ *device = &adev->hw_device.common;
+ adev_ref_count++;
+ ALOGI("device-%s: opened existing Primary Audio HW Device(ref = %d)", __func__, adev_ref_count);
+ pthread_mutex_unlock(&adev_init_lock);
+ return 0;
+ }
+
+ /* Initialize Audio Device Mutex Lock. */
+ pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
+
+ /*
+ * Map function pointers in Structure audio_hw_device as real function.
+ */
+ adev->hw_device.common.tag = HARDWARE_DEVICE_TAG;
+ adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+ adev->hw_device.common.module = (struct hw_module_t *) module;
+ adev->hw_device.common.close = adev_close;
+
+ /* Enumerates what devices are supported by audio_hw_device implementation. */
+ adev->hw_device.get_supported_devices = adev_get_supported_devices;
+ /* Checks to see if the audio hardware interface has been initialized. */
+ adev->hw_device.init_check = adev_init_check;
+ /* Sets the audio volume of a voice call. */
+ adev->hw_device.set_voice_volume = adev_set_voice_volume;
+ /* Sets/Gets the master volume value for the HAL. */
+ // Not Supported
+ adev->hw_device.set_master_volume = NULL;
+ adev->hw_device.get_master_volume = NULL;
+ /* Called when the audio mode changes. */
+ adev->hw_device.set_mode = adev_set_mode;
+ /* Treats mic mute. */
+ adev->hw_device.set_mic_mute = adev_set_mic_mute;
+ adev->hw_device.get_mic_mute = adev_get_mic_mute;
+ /* Sets/Gets global audio parameters. */
+ adev->hw_device.set_parameters = adev_set_parameters;
+ adev->hw_device.get_parameters = adev_get_parameters;
+ /* Returns audio input buffer size according to parameters passed. */
+ adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size;
+ /* Creates ,opens, closes and destroys the audio hardware input/output stream. */
+ adev->hw_device.open_output_stream = adev_open_output_stream;
+ adev->hw_device.close_output_stream = adev_close_output_stream;
+ adev->hw_device.open_input_stream = adev_open_input_stream;
+ adev->hw_device.close_input_stream = adev_close_input_stream;
+ /* Read available microphones characteristics for AudioHAL V4. */
+ adev->hw_device.get_microphones = adev_get_microphones;
+ /* Dumps the status of the audio hardware. */
+ adev->hw_device.dump = adev_dump;
+ /* Set/Get the master mute status for the HAL. */
+ // Not Supported
+ adev->hw_device.set_master_mute = NULL;
+ adev->hw_device.get_master_mute = NULL;
+
+ /* Creates and releases an audio patch between several source and sink ports. */
+ // Not Supported
+ adev->hw_device.create_audio_patch = NULL;
+ adev->hw_device.release_audio_patch = NULL;
+ /* Sets/Gets audio port configuration. */
+ // Not Supported
+ adev->hw_device.get_audio_port = NULL;
+ adev->hw_device.set_audio_port_config = NULL;
+
+
+ pthread_mutex_lock(&adev->lock);
+
+ /*
+ * Initializes Audio Proxy.
+ * Audio Proxy is handling ALSA & Audio Routes to support SoC dependency.
+ */
+ adev->proxy = proxy_init();
+ if (!adev->proxy) {
+ ALOGE("device-%s: failed to init Audio Proxy", __func__);
+ goto err_open;
+ }
+
+ /* Initializes Audio Route. */
+ if (adev_init_route(adev) == false) {
+ ALOGE("device-%s: failed to init Audio Route", __func__);
+ goto err_open;
+ }
+ adev->is_route_created = true;
+
+ adev->actual_playback_device = AUDIO_DEVICE_OUT_SPEAKER;
+ adev->actual_capture_device = AUDIO_DEVICE_IN_BUILTIN_MIC;
+ adev->previous_playback_device = AUDIO_DEVICE_NONE;
+ adev->previous_capture_device = AUDIO_DEVICE_NONE;
+
+ adev->is_playback_path_routed = false;
+ adev->active_playback_ausage = AUSAGE_NONE;
+ adev->active_playback_device = DEVICE_NONE;
+ adev->active_playback_modifier = MODIFIER_NONE;
+
+ adev->is_capture_path_routed = false;
+ adev->active_capture_ausage = AUSAGE_NONE;
+ adev->active_capture_device = DEVICE_NONE;
+ adev->active_capture_modifier = MODIFIER_NONE;
+
+ /* Initialize Audio Stream Lists */
+ list_init(&adev->playback_list);
+ list_init(&adev->capture_list);
+
+ /* Sets initial values. */
+ adev->primary_output = NULL;
+ adev->active_input = NULL;
+
+ adev->amode = AUDIO_MODE_NORMAL;
+ adev->previous_amode = AUDIO_MODE_NORMAL;
+ adev->call_mode = CALL_OFF;
+ adev->incallmusic_on = false;
+
+ adev->voipse_on = false;
+
+ adev->voice_volume = 0;
+ adev->mic_mute = false;
+
+ adev->fm_state = FM_OFF;
+ adev->fm_need_route = false;
+
+ /*
+ * Initializes Factory Manager.
+ * Factory Manager is handling factory mode test scenario.
+ */
+ adev->factory = factory_init();
+ if (!adev->factory)
+ ALOGE("device-%s: failed to init Factory Manager!", __func__);
+ else
+ ALOGD("device-%s: initialized Factory Manager!", __func__);
+
+ /* Customer Specific initial values */
+ adev->support_reciever = false;
+ adev->support_backmic = false;
+ adev->support_thirdmic = false;
+ adev->fm_via_a2dp= false;
+ adev->fm_radio_mute = false;
+
+ adev->pcmread_latency = 0;
+ adev->update_offload_volume = false;
+ adev->current_devices = AUDIO_DEVICE_NONE;
+
+ proxy_init_offload_effect_lib(adev->proxy);
+
+ pthread_mutex_unlock(&adev->lock);
+
+ /* Sets Structure audio_hw_device for return. */
+ *device = &adev->hw_device.common;
+ adev_ref_count++;
+ ALOGI("device-%s: opened Primary Audio HW Device(ref = %d)", __func__, adev_ref_count);
+ pthread_mutex_unlock(&adev_init_lock);
+ return 0;
+
+err_open:
+ if (adev->proxy)
+ proxy_deinit(adev->proxy);
+
+ pthread_mutex_unlock(&adev->lock);
+ pthread_mutex_destroy(&adev->lock);
+
+ free(adev);
+ *device = NULL;
+
+ ALOGI("device-%s: failed to open Primary Audio HW Device", __func__);
+ pthread_mutex_unlock(&adev_init_lock);
+ return -ENOSYS;
+}
+
+/* Entry Point for AudioHAL (Primary Audio HW Module for Android) */
+static struct hw_module_methods_t hal_module_methods = {
+ .open = adev_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = AUDIO_MODULE_API_VERSION_CURRENT,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = AUDIO_HARDWARE_MODULE_ID,
+ .name = "Exynos Primary AudioHAL",
+ .author = "Samsung",
+ .methods = &hal_module_methods,
+ },
+};
diff --git a/libaudio/audiohal/audio_hw.h b/libaudio/audiohal/audio_hw.h
new file mode 100644
index 0000000..074b351
--- /dev/null
+++ b/libaudio/audiohal/audio_hw.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_H__
+#define __EXYNOS_AUDIOHAL_H__
+
+/* Definition of AudioHAL */
+#include <system/audio.h>
+#include <hardware/hardware.h>
+#include <hardware/audio.h>
+
+#include <cutils/list.h>
+
+#include "audio_streams.h"
+#include "audio_usages.h"
+#include "audio_devices.h"
+#include "audio_offload.h"
+#include "audio_definition.h"
+
+/* Voice call - RIL interface */
+#include "voice_manager.h"
+
+/* Factory Mode Test */
+#include "factory_manager.h"
+
+
+/**
+ ** Stream Status
+ **/
+typedef enum {
+ STATUS_STANDBY = 0, // Stream is opened, but Device(PCM or Compress) is not opened yet.
+ STATUS_READY, // Stream is opened, but Device(PCM or Compress) is not opened yet. But, started something
+ STATUS_IDLE, // Stream is opened & Device(PCM or Compress) is opened.
+ STATUS_PLAYING, // Stream is opened & Device(PCM or Compress) is opened & Device is working.
+ STATUS_PAUSED, // Stream is opened & Device(Compress) is opened & Device is pausing.(only available for Compress Offload Stream)
+} stream_status;
+
+/**
+ ** Call Mode
+ **/
+typedef enum {
+ CALL_OFF = 0, // No Call Mode
+ VOICE_CALL, // CP Centric Voice Call Mode
+ VOLTE_CALL, // CP Centric VOLTE Call Mode
+ VOWIFI_CALL, // AP Centric VoWiFi Call Mode
+
+ CALL_MODE_CNT,
+ CALL_MODE_MAX = CALL_MODE_CNT - 1,
+} call_mode;
+
+/**
+ ** FM State
+ **/
+typedef enum {
+ FM_OFF = 0,
+ FM_ON,
+ FM_RECORDING,
+} fm_state;
+
+/* Macro for Routing */
+#define ROUTE true
+#define UNROUTE false
+
+typedef enum {
+ NON_FORCE_ROUTE = 0,
+ FORCE_ROUTE,
+ CALL_DRIVE
+} force_route;
+
+
+/**
+ ** Structure for Audio Output Stream
+ ** Implements audio_stream_out structure
+ **/
+struct stream_common {
+ pthread_mutex_t lock;
+
+ // Audio Proxy to provide HW Services
+ void *proxy_stream;
+
+ /* These variables are needed to save Android Request
+ becuase requested PCM Config and Real PCM Config can be different */
+ audio_io_handle_t handle;
+ audio_devices_t requested_devices;
+ uint32_t requested_sample_rate;
+ audio_channel_mask_t requested_channel_mask;
+ audio_format_t requested_format;
+ uint32_t requested_frame_count;
+ audio_format_t offload_audio_format;
+
+ /* These variables show the purpose of stream */
+ audio_stream_type stream_type;
+ audio_usage stream_usage;
+ stream_status stream_status;
+};
+
+/* Compress Offload Specific Variables */
+struct offload_msg {
+ struct listnode node;
+ offload_msg_type msg;
+};
+
+struct stream_offload {
+ int nonblock_flag;
+
+ stream_callback_t callback;
+ void *cookie;
+
+ pthread_t callback_thread;
+
+ pthread_cond_t msg_cond;
+ struct listnode msg_list;
+
+ pthread_cond_t sync_cond;
+ bool callback_thread_blocked;
+};
+
+struct stream_out {
+ struct audio_stream_out stream;
+ struct stream_common common;
+
+ audio_output_flags_t requested_flags;
+
+ struct audio_device * adev;
+
+ struct stream_offload offload;
+ float vol_left, vol_right;
+
+ /* Force Routing */
+ force_route force;
+ audio_devices_t rollback_devices;
+};
+
+struct playback_stream {
+ struct listnode node;
+ struct stream_out *out;
+};
+
+/**
+ ** Structure for Audio Input Stream
+ ** Implements audio_stream_in structure
+ **/
+struct stream_in {
+ struct audio_stream_in stream;
+ struct stream_common common;
+
+ audio_input_flags_t requested_flags;
+ audio_source_t requested_source;
+
+ bool pcm_reconfig;
+
+ struct audio_device * adev;
+};
+
+struct capture_stream {
+ struct listnode node;
+ struct stream_in *in;
+};
+
+/**
+ ** Structure for Audio Primary HW Module
+ ** Implements audio_hw_device structure
+ **/
+struct audio_device {
+ struct audio_hw_device hw_device;
+ pthread_mutex_t lock;
+
+ // Audio Proxy to provide HW Services
+ void *proxy;
+
+ // Stream Information
+ struct listnode playback_list;
+ struct listnode capture_list;
+
+ // Routing Information
+ audio_devices_t actual_playback_device;
+ audio_devices_t actual_capture_device;
+ audio_devices_t previous_playback_device;
+ audio_devices_t previous_capture_device;
+
+ bool is_route_created;
+
+ bool is_playback_path_routed;
+ audio_usage active_playback_ausage;
+ device_type active_playback_device;
+ modifier_type active_playback_modifier;
+
+ bool is_capture_path_routed;
+ audio_usage active_capture_ausage;
+ device_type active_capture_device;
+ modifier_type active_capture_modifier;
+
+ audio_devices_t current_devices;
+
+ // Voice
+ struct voice_manager *voice;
+ float voice_volume;
+ bool mic_mute;
+
+ // Factory
+ struct factory_manager *factory;
+
+ // Important Streams
+ struct stream_out *primary_output;
+ struct stream_in *active_input;
+ struct stream_out *compress_output;
+
+ // Audio/Call Modes
+ audio_mode_t amode;
+ audio_mode_t previous_amode;
+ call_mode call_mode;
+
+ //special incall-music stream
+ bool incallmusic_on;
+
+ // VoIP SE
+ bool voipse_on;
+
+ bool bluetooth_nrec;
+ bool screen_on;
+
+ fm_state fm_state;
+ bool fm_need_route;
+
+ bool seamless_enabled;
+
+ // Customer Specific varibales
+ bool support_reciever;
+ bool support_backmic;
+ bool support_thirdmic;
+ bool fm_via_a2dp;
+ bool fm_radio_mute;
+ int pcmread_latency;
+ bool update_offload_volume;
+};
+
+
+/* Functions for External Usage */
+bool adev_set_route(void *stream, audio_usage_type usage_type, bool set, force_route force);
+void set_call_forwarding(struct audio_device *adev, bool mode);
+
+//void update_uhqa_stream(struct stream_out *out);
+void update_call_stream(struct stream_out *out, audio_devices_t current_devices, audio_devices_t new_devices);
+void update_capture_stream(struct stream_in *in, audio_devices_t current_devices, audio_devices_t new_devices);
+
+#endif // __EXYNOS_AUDIOHAL_H__
diff --git a/libaudio/audiohal/factory_manager.c b/libaudio/audiohal/factory_manager.c
new file mode 100644
index 0000000..e6fbfa9
--- /dev/null
+++ b/libaudio/audiohal/factory_manager.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2014 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 "audio_hw_primary_factory"
+//#define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <log/log.h>
+#include <cutils/str_parms.h>
+
+#include "factory_manager.h"
+#include "audio_definition.h"
+#include "audio_proxy_interface.h"
+
+
+/******************************************************************************/
+/** **/
+/** The Factory Functions **/
+/** **/
+/******************************************************************************/
+bool is_factory_mode(struct factory_manager *factory)
+{
+ if(factory && factory->mode != FACTORY_MODE_NONE)
+ return true;
+ else
+ return false;
+}
+
+bool is_factory_loopback_mode(struct factory_manager *factory)
+{
+ if(factory && factory->mode == FACTORY_MODE_LOOPBACK)
+ return true;
+ else
+ return false;
+}
+
+bool is_factory_bt_realtime_loopback_mode(struct factory_manager *factory)
+{
+ if (factory && (factory->loopback_mode == FACTORY_LOOPBACK_REALTIME)
+ && (factory->out_device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) && (factory->in_device == AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)) {
+ return true;
+ }
+ return false;
+}
+
+bool is_factory_rms_mode(struct factory_manager *factory)
+{
+ if(factory && factory->mode == FACTORY_MODE_RMS)
+ return true;
+ else
+ return false;
+}
+
+
+static void factory_set_path_for_loopback(struct factory_manager *factory, const char *value)
+{
+ ALOGD("%s: enter with value(%s)", __func__, value);
+
+ ALOGI("%s : loopback = %s ",__func__, value);
+ if(strncmp(value, "ear_ear", 7) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_WIRED_HEADSET);
+ factory->in_device = (AUDIO_DEVICE_IN_WIRED_HEADSET);
+ } else if(strncmp(value, "mic1_spk", 8) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_SPEAKER);
+ factory->in_device = (AUDIO_DEVICE_IN_BUILTIN_MIC);
+ } else if(strncmp(value, "mic2_spk", 8) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_SPEAKER);
+ factory->in_device = (AUDIO_DEVICE_IN_BACK_MIC);
+ } else if(strncmp(value, "mic1_rcv", 8) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_EARPIECE);
+ factory->in_device = (AUDIO_DEVICE_IN_BUILTIN_MIC);
+ } else if(strncmp(value, "mic2_rcv", 8) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_EARPIECE);
+ factory->in_device = (AUDIO_DEVICE_IN_BACK_MIC);
+ } else if(strncmp(value, "mic1_ear", 8) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_WIRED_HEADSET);
+ factory->in_device = (AUDIO_DEVICE_IN_BUILTIN_MIC);
+ } else if(strncmp(value, "mic2_ear", 8) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_WIRED_HEADSET);
+ factory->in_device = (AUDIO_DEVICE_IN_BACK_MIC);
+ } else if(strncmp(value, "bt_bt", 5) == 0) {
+ factory->out_device = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
+ factory->in_device = (AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET);
+ } else {
+ ALOGW("factory test path doesn't exist. default set earpiece.");
+ factory->out_device = (AUDIO_DEVICE_OUT_EARPIECE);
+ factory->in_device = (AUDIO_DEVICE_IN_BUILTIN_MIC);
+ }
+
+ return;
+}
+
+static void factory_set_path_for_force_route(struct factory_manager *factory, const char *value)
+{
+
+ if (!factory->rms_test_enable)
+ factory->mode = FACTORY_MODE_FORCE_ROUTE;
+
+ if (!strcmp(value, "spk")) {
+ ALOGD("%s factory_test_route : spk", __func__);
+ factory->out_device = (AUDIO_DEVICE_OUT_SPEAKER & AUDIO_DEVICE_OUT_ALL);
+ } else if (!strcmp(value, "rcv")) {
+ ALOGD("%s factory_test_route : rcv", __func__);
+ factory->out_device = (AUDIO_DEVICE_OUT_EARPIECE & AUDIO_DEVICE_OUT_ALL);
+ } else if (!strcmp(value, "ear")) {
+ ALOGD("%s factory_test_route : ear", __func__);
+ factory->out_device = (AUDIO_DEVICE_OUT_WIRED_HEADSET & AUDIO_DEVICE_OUT_ALL);
+ } else if (!strcmp(value, "off")) {
+ ALOGD("%s factory_test_route : off", __func__);
+ factory->mode = FACTORY_MODE_NONE;
+ factory->out_device = (AUDIO_DEVICE_NONE);
+ }
+
+ return;
+}
+
+static void factory_set_path_for_rms(struct factory_manager *factory, const char *value)
+{
+ if (strcmp(value, "on") == 0) {
+ ALOGD("%s factory_test_mic_check=on", __func__);
+ factory->mode = FACTORY_MODE_RMS;
+ factory->rms_test_enable = true;
+ } else if (strcmp(value, "main") == 0) {
+ ALOGD("%s factory_test_mic_check=main", __func__);
+ factory->in_device = (AUDIO_DEVICE_IN_BUILTIN_MIC);
+ } else if (strcmp(value, "sub") == 0) {
+ ALOGD("%s factory_test_mic_check=sub", __func__);
+ factory->in_device = (AUDIO_DEVICE_IN_BACK_MIC);
+ } else if (strcmp(value, "spk_mic1") == 0) { /*at + looptest=0,3,4 */
+ ALOGD("%s factory_test_mic_check=spk_mic1", __func__);
+ factory->in_device = (AUDIO_DEVICE_IN_BUILTIN_MIC);
+ } else if (strcmp(value, "off") == 0) {
+ ALOGD("%s factory_test_mic_check=off", __func__);
+ factory->mode = FACTORY_MODE_NONE;
+ factory->in_device = (AUDIO_DEVICE_NONE);
+ factory->rms_test_enable = false;
+ }
+
+ return;
+}
+
+void factory_set_parameters(struct audio_device *adev, struct str_parms *parms)
+{
+ struct factory_manager *factory = adev->factory;
+ char *kv_pairs = str_parms_to_str(parms);
+ //ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
+
+ char value[32];
+ int err = 0;
+
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_FACTORY_TEST_TYPE, value, sizeof(value));
+ if (err >= 0 && factory) {
+ if (!strcmp(value, "codec")) {
+ factory->loopback_mode= FACTORY_LOOPBACK_CODEC;
+ } else if (!strcmp(value, "realtime")) {
+ factory->loopback_mode = FACTORY_LOOPBACK_REALTIME;
+ } else if (!strcmp(value, "pcm")) {
+ factory->loopback_mode = FACTORY_LOOPBACK_PCM;
+ } else if (!strcmp(value, "packet")) {
+ factory->loopback_mode = FACTORY_LOOPBACK_PACKET;
+ } else if (!strcmp(value, "packet_nodelay")) {
+ factory->loopback_mode = FACTORY_LOOPBACK_PACKET_NODELAY;
+ }
+ ALOGD("%s: FACTORY_TEST_TYPE=%d", __func__, factory->loopback_mode);
+ str_parms_del(parms, AUDIO_PARAMETER_FACTORY_TEST_TYPE);
+ }
+
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_FACTORY_TEST_LOOPBACK, value, sizeof(value));
+ if (err >= 0 && factory) {
+ if (!strcmp(value, AUDIO_PARAMETER_VALUE_ON)) {
+ ALOGD("%s: FACTORY_TEST_LOOPBACK=on", __func__);
+ factory->mode = FACTORY_MODE_LOOPBACK;
+#ifdef SUPPORT_STHAL_INTERFACE
+ proxy_call_status(adev->proxy, true);
+#endif
+ } else if (!strcmp(value, AUDIO_PARAMETER_VALUE_OFF)) {
+ ALOGD("%s: FACTORY_TEST_LOOPBACK=off", __func__);
+ factory->mode = FACTORY_MODE_NONE;
+ factory->out_device = (AUDIO_DEVICE_NONE);
+ factory->in_device = (AUDIO_DEVICE_NONE);
+
+ /* 1. pcm device close */
+ if (factory->loopback_mode != FACTORY_LOOPBACK_REALTIME) {
+ voice_set_loopback_device(adev->voice, FACTORY_LOOPBACK_OFF,
+ AUDIO_DEVICE_OUT_EARPIECE, AUDIO_DEVICE_IN_BUILTIN_MIC);
+ proxy_stop_voice_call(adev->proxy);
+ }
+ factory->loopback_mode = FACTORY_LOOPBACK_OFF;
+
+ /* 2. reset device */
+ if (adev->primary_output != NULL) {
+ if (adev->primary_output->common.stream_status != STATUS_STANDBY)
+ adev_set_route((void *)adev->primary_output, AUSAGE_PLAYBACK, ROUTE, CALL_DRIVE);
+ else
+ adev_set_route((void *)adev->primary_output, AUSAGE_PLAYBACK, UNROUTE, CALL_DRIVE);
+ }
+ if (factory->loopback_mode != FACTORY_LOOPBACK_REALTIME) {
+ voice_set_call_mode(adev->voice, false);
+ }
+#ifdef SUPPORT_STHAL_INTERFACE
+ proxy_call_status(adev->proxy, false);
+#endif
+ }
+ str_parms_del(parms, AUDIO_PARAMETER_FACTORY_TEST_LOOPBACK);
+ }
+
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_FACTORY_TEST_PATH, value, sizeof(value));
+ if (err > 0 && factory) {
+ factory_set_path_for_loopback(factory, value);
+
+ /* 1. set ril device */
+ if (factory->loopback_mode != FACTORY_LOOPBACK_REALTIME) {
+ voice_set_loopback_device(adev->voice, adev->factory->loopback_mode,
+ adev->factory->out_device, adev->factory->in_device);
+ }
+
+ /* 2. pcm device close for reset */
+ proxy_stop_voice_call(adev->proxy);
+
+ /* 3. set route */
+ if (adev->primary_output != NULL)
+ adev_set_route((void *)adev->primary_output, AUSAGE_PLAYBACK, ROUTE, CALL_DRIVE);
+
+ if (factory->loopback_mode != FACTORY_LOOPBACK_REALTIME) {
+ /* 4. pcm device open */
+ proxy_start_voice_call(adev->proxy);
+
+ /* 5. set volume */
+ voice_set_volume(adev->voice, adev->voice_volume);
+
+ /* 6. mic unmute */
+ voice_set_mic_mute(adev->voice, false);
+ }
+ str_parms_del(parms, AUDIO_PARAMETER_FACTORY_TEST_PATH);
+ }
+
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_FACTORY_TEST_ROUTE, value, sizeof(value));
+ if (err > 0 && factory) {
+ factory_set_path_for_force_route(factory, value);
+ if (adev->primary_output != NULL) {
+ if (adev->primary_output->common.stream_status != STATUS_STANDBY)
+ adev_set_route((void *)adev->primary_output, AUSAGE_PLAYBACK, ROUTE, FORCE_ROUTE);
+ else
+ adev_set_route((void *)adev->primary_output, AUSAGE_PLAYBACK, UNROUTE, FORCE_ROUTE);
+ }
+
+ str_parms_del(parms, AUDIO_PARAMETER_FACTORY_TEST_ROUTE);
+ }
+
+ err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FACTORY_RMS_TEST, value, sizeof(value));
+ if (err >= 0 && factory) {
+ factory_set_path_for_rms(factory, value);
+ if (adev->active_input != NULL
+ && !(adev->active_input->requested_flags & AUDIO_INPUT_FLAG_HW_HOTWORD)
+ && strcmp(value, "on") != 0)
+ adev_set_route((void *)adev->active_input, AUSAGE_CAPTURE, ROUTE, NON_FORCE_ROUTE);
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_FACTORY_RMS_TEST);
+ }
+
+ //ALOGD("%s: exit", __func__);
+
+ free(kv_pairs);
+ return ;
+}
+
+
+void factory_deinit(struct factory_manager *factory)
+{
+ if (factory)
+ free(factory);
+
+ return ;
+}
+
+struct factory_manager* factory_init()
+{
+ struct factory_manager *factory = NULL;
+
+ factory = calloc(1, sizeof(struct factory_manager));
+ if (factory) {
+ factory->mode = FACTORY_MODE_NONE;
+ factory->loopback_mode= FACTORY_LOOPBACK_OFF;
+ factory->out_device = AUDIO_DEVICE_NONE;
+ factory->in_device = AUDIO_DEVICE_NONE;
+ factory->rms_test_enable = false;
+ factory->is_dspk_changed = false;
+
+ // false : SPK2 (rcv side spk) test, using mixer string "speaker2"
+ // true : SPK CAL test, using mixer string "dual-speaker"
+ factory->is_calibration_test = false;
+ }
+
+ return factory;
+}
+
diff --git a/libaudio/audiohal/factory_manager.h b/libaudio/audiohal/factory_manager.h
new file mode 100644
index 0000000..192328e
--- /dev/null
+++ b/libaudio/audiohal/factory_manager.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_AUDIOHAL_FACTORY_H__
+#define __EXYNOS_AUDIOHAL_FACTORY_H__
+
+#include <system/audio.h>
+#include <hardware/audio.h>
+
+#include "audio_hw.h"
+
+typedef enum {
+ FACTORY_MODE_NONE,
+ FACTORY_MODE_FORCE_ROUTE,
+ FACTORY_MODE_LOOPBACK,
+ FACTORY_MODE_RMS,
+} factory_mode_status;
+
+typedef enum factory_loopback_types {
+ FACTORY_LOOPBACK_OFF = 0,
+ FACTORY_LOOPBACK_PCM = 1,
+ FACTORY_LOOPBACK_PACKET = 2,
+ FACTORY_LOOPBACK_PCM_NODELAY = 3,
+ FACTORY_LOOPBACK_PACKET_NODELAY = 4,
+ FACTORY_LOOPBACK_CODEC = 5,
+ FACTORY_LOOPBACK_REALTIME = 6
+} loopback_type;
+
+typedef enum {
+ FACTORY_SET_PARAM_OK = 0,
+ FACTORY_TEST_OFF,
+ FACTORY_LOOPBACK_TEST,
+ FACTORY_FORCEROUTE_TEST,
+ FACTORY_RMS_TEST
+} factory_set_param_return;
+
+struct factory_manager {
+ factory_mode_status mode;
+ loopback_type loopback_mode;
+ audio_devices_t out_device;
+ audio_devices_t in_device;
+ bool rms_test_enable;
+ bool is_calibration_test;
+ bool is_dspk_changed;
+};
+
+bool is_factory_mode(struct factory_manager *factory);
+bool is_factory_loopback_mode(struct factory_manager *factory);
+bool is_factory_rms_mode(struct factory_manager *factory);
+bool is_factory_bt_realtime_loopback_mode(struct factory_manager *factory);
+
+void factory_set_parameters(struct audio_device *adev, struct str_parms *parms);
+void factory_deinit(struct factory_manager *factory);
+struct factory_manager * factory_init(void);
+
+#endif // __EXYNOS_AUDIOHAL_FACTORY_H__
diff --git a/libaudio/audiohal/sit_specific.h b/libaudio/audiohal/sit_specific.h
new file mode 100644
index 0000000..ec340f6
--- /dev/null
+++ b/libaudio/audiohal/sit_specific.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __AUDIOHAL_SITSPECIFIC_H__
+#define __AUDIOHAL_SITSPECIFIC_H__
+
+
+// RIL Client for Audio
+#define RIL_CLIENT_LIBPATH "/system/lib/libsitril-audio.so"
+
+#define AUDIO_PARAMETER_VOLTE_STATUS "VoLTEstate"
+
+
+/* Syncup with RIL Audio Client */
+
+/* Voice Audio Path */
+enum ril_audio_path {
+ VOICE_AUIDO_PATH_NONE = 0,
+
+ VOICE_AUDIO_PATH_HANDSET = 1,
+ VOICE_AUIDO_PATH_HEADSET = 2,
+ VOICE_AUIDO_PATH_HANDSFREE = 3,
+ VOICE_AUIDO_PATH_BLUETOOTH = 4,
+ VOICE_AUIDO_PATH_STEREO_BLUETOOTH = 5,
+ VOICE_AUIDO_PATH_SPEAKRERPHONE = 6,
+ VOICE_AUIDO_PATH_35PI_HEADSET = 7,
+ VOICE_AUIDO_PATH_BT_NS_EC_OFF = 8,
+ VOICE_AUIDO_PATH_WB_BLUETOOTH = 9,
+ VOICE_AUIDO_PATH_WB_BT_NS_EC_OFF = 10,
+ VOICE_AUIDO_PATH_HANDSET_HAC = 11,
+
+ VOICE_AUIDO_PATH_VOLTE_HANDSET = 65,
+ VOICE_AUIDO_PATH_VOLTE_HEADSET = 66,
+ VOICE_AUIDO_PATH_VOLTE_HFK = 67,
+ VOICE_AUIDO_PATH_VOLTE_BLUETOOTH = 68,
+ VOICE_AUIDO_PATH_VOLTE_STEREO_BLUETOOTH = 69,
+ VOICE_AUIDO_PATH_VOLTE_SPEAKRERPHONE = 70,
+ VOICE_AUIDO_PATH_VOLTE_35PI_HEADSET = 71,
+ VOICE_AUIDO_PATH_VOLTE_BT_NS_EC_OFF = 72,
+ VOICE_AUIDO_PATH_VOLTE_WB_BLUETOOTH = 73,
+ VOICE_AUIDO_PATH_VOLTE_WB_BT_NS_EC_OFF = 74,
+ VOICE_AUIDO_PATH_MAX
+};
+
+/* Voice Audio Multi-MIC */
+enum ril_audio_multimic {
+ VOICE_MULTI_MIC_OFF,
+ VOICE_MULTI_MIC_ON,
+};
+
+/* Voice Audio Volume */
+enum ril_audio_volume {
+ VOICE_AUDIO_VOLUME_INVALID = -1,
+ VOICE_AUDIO_VOLUME_LEVEL0 = 0,
+ VOICE_AUDIO_VOLUME_LEVEL1,
+ VOICE_AUDIO_VOLUME_LEVEL2,
+ VOICE_AUDIO_VOLUME_LEVEL3,
+ VOICE_AUDIO_VOLUME_LEVEL4,
+ VOICE_AUDIO_VOLUME_LEVEL5,
+ VOICE_AUDIO_VOLUME_LEVEL_MAX = VOICE_AUDIO_VOLUME_LEVEL5,
+};
+
+/* Voice Audio Mute */
+enum ril_audio_mute {
+ VOICE_AUDIO_MUTE_DISABLED,
+ VOICE_AUDIO_MUTE_ENABLED,
+};
+
+/* Voice Audio Clock */
+enum ril_audio_clockmode {
+ VOICE_AUDIO_TURN_OFF_I2S,
+ VOICE_AUDIO_TURN_ON_I2S,
+};
+
+/* Voice Loopback */
+enum ril_audio_loopback {
+ VOICE_AUDIO_LOOPBACK_STOP,
+ VOICE_AUDIO_LOOPBACK_START,
+};
+
+enum ril_audio_loopback_path {
+ VOICE_AUDIO_LOOPBACK_PATH_NA = 0, //0: N/A
+
+ VOICE_AUDIO_LOOPBACK_PATH_HANDSET = 1, //1: handset
+ VOICE_AUDIO_LOOPBACK_PATH_HEADSET = 2, //2: headset
+ VOICE_AUDIO_LOOPBACK_PATH_HANDSFREE = 3, //3: handsfree
+ VOICE_AUDIO_LOOPBACK_PATH_BT = 4, //4: Bluetooth
+ VOICE_AUDIO_LOOPBACK_PATH_STEREO_BT = 5, //5: stereo Bluetooth
+ VOICE_AUDIO_LOOPBACK_PATH_SPK = 6, //6: speaker phone
+ VOICE_AUDIO_LOOPBACK_PATH_35PI_HEADSET = 7, //7: 3.5pi headset
+ VOICE_AUDIO_LOOPBACK_PATH_BT_NS_EC_OFF = 8, //8: BT NS/EC off
+ VOICE_AUDIO_LOOPBACK_PATH_WB_BT = 9, //9: WB Bluetooth
+ VOICE_AUDIO_LOOPBACK_PATH_WB_BT_NS_EC_OFF = 10, //10: WB BT NS/EC
+ VOICE_AUDIO_LOOPBACK_PATH_HANDSET_HAC = 11, //11: handset HAC
+
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_HANDSET = 65, //65: VOLTE handset
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_HEADSET = 66, //66: VOLTE headset
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_HANDSFREE = 67, //67: VOLTE hands
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_BT = 68, //68: VOLTE Bluetooth
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_STEREO_BT = 69, //69: VOLTE stere
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_SPK = 70, //70: VOLTE speaker phone
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_35PI_HEADSET = 71, //71: VOLTE 3.5pi
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_BT_NS_EC_OFF = 72, //72: VOLTE BT NS
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_WB_BT = 73, //73: VOLTE WB Blueto
+ VOICE_AUDIO_LOOPBACK_PATH_VOLTE_WB_BT_NS_EC_OFF = 74, //74: VOLTE W
+
+ VOICE_AUDIO_LOOPBACK_PATH_HEADSET_MIC1 = 129, //129: Headset ? MIC1
+ VOICE_AUDIO_LOOPBACK_PATH_HEADSET_MIC2 = 130, //130: Headset ? MIC2
+ VOICE_AUDIO_LOOPBACK_PATH_HEADSET_MIC3 = 131, //131: Headset ? MIC3
+};
+
+
+/* Event from RIL Audio Client */
+#define VOICE_AUDIO_EVENT_BASE 10000
+#define VOICE_AUDIO_EVENT_RINGBACK_STATE_CHANGED (VOICE_AUDIO_EVENT_BASE + 1)
+#define VOICE_AUDIO_EVENT_IMS_SRVCC_HANDOVER (VOICE_AUDIO_EVENT_BASE + 2)
+
+
+/* RIL Audio Client Interface Structure */
+struct rilclient_intf {
+ /* The pointer of interface library for RIL Client*/
+ void *handle;
+
+ /* Function pointers */
+ int (*ril_open_client)(void);
+ int (*ril_close_client)(void);
+ int (*ril_register_callback)(void *, int *);
+ int (*ril_set_audio_volume)(int);
+ int (*ril_set_audio_path)(int);
+ int (*ril_set_multi_mic)(int);
+ int (*ril_set_mute)(int);
+ int (*ril_set_audio_clock)(int);
+ int (*ril_set_audio_loopback)(int, int);
+ int (*ril_set_tty_mode)(int);
+};
+
+#endif // __AUDIOHAL_SITSPECIFIC_H__
diff --git a/libaudio/audiohal/voice_manager.c b/libaudio/audiohal/voice_manager.c
new file mode 100644
index 0000000..a8c3073
--- /dev/null
+++ b/libaudio/audiohal/voice_manager.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2014 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 "voice_manager"
+#define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <log/log.h>
+#include <cutils/str_parms.h>
+#include <cutils/properties.h>
+
+#include "audio_hw.h"
+#include "voice_manager.h"
+#include "sitril_interface.h"
+
+#include "audio_proxy_interface.h"
+
+#define VOLUME_STEPS_DEFAULT "5"
+#define VOLUME_STEPS_PROPERTY "ro.vendor.config.vc_call_vol_steps"
+
+
+#define DEVICE_INVALID -1
+
+
+
+/*
+ * Local Functions
+ */
+static int voice_set_sco_solution(struct voice_manager *voice, bool echo_cancel, int sample_rate)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetScoSolution(echo_cancel, sample_rate);
+ }
+ return ret;
+}
+
+static int voice_set_volte_status(struct voice_manager *voice, int status)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetVoLTEState(status);
+ }
+ return ret;
+}
+
+static int voice_set_hac_mode(struct voice_manager *voice, bool status)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetHacModeState(status);
+ }
+ return ret;
+}
+
+/*
+ * Status Check Functions
+ */
+bool voice_is_call_mode(struct voice_manager *voice)
+{
+ // True means Android Audio Mode is IN_CALL Mode
+ return (voice->call_status >= CALL_STATUS_INCALLMODE);
+}
+
+bool voice_is_call_active(struct voice_manager *voice)
+{
+ // True means Voice Call is working (Audio PCM for CP Call is Opened)
+ return (voice->call_status == CALL_STATUS_ACTIVE);
+}
+
+
+/*
+ * Set Functions
+ */
+int voice_set_call_mode(struct voice_manager *voice, bool on)
+{
+ int ret = 0;
+
+ if (voice->call_status == CALL_STATUS_INVALID && on) {
+ // RIL Audio Client is not connected yet, Re-Try!!!
+ ALOGD("vm-%s: RilClient is not opened yet! Retry!", __func__);
+ ret = SitRilOpen();
+ if (ret == 0)
+ voice->call_status = CALL_STATUS_CONNECTED;
+ else
+ voice->call_status = CALL_STATUS_INVALID;
+ }
+
+ if (voice->call_status == CALL_STATUS_CONNECTED && on)
+ voice->call_status = CALL_STATUS_INCALLMODE;
+ else if (voice->call_status == CALL_STATUS_INCALLMODE && !on)
+ voice->call_status = CALL_STATUS_CONNECTED;
+ else
+ ALOGE("vm-%s: Invalid Voice Call Status(%d) with %d", __func__, voice->call_status, on);
+
+ return ret;
+}
+
+int voice_set_call_active(struct voice_manager *voice, bool on)
+{
+ int ret = 0;
+
+ if (voice->call_status == CALL_STATUS_INCALLMODE && on) {
+ voice->call_status = CALL_STATUS_ACTIVE;
+ SitRilSetSoundClkMode(1);
+ } else if (voice->call_status == CALL_STATUS_ACTIVE && !on) {
+ voice->call_status = CALL_STATUS_INCALLMODE;
+ SitRilSetSoundClkMode(0);
+ } else
+ ALOGE("vm-%s: Invalid Voice Call Status(%d) with %d", __func__, voice->call_status, on);
+
+ return ret;
+}
+
+int voice_set_audio_mode(struct voice_manager *voice, int mode, bool status)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetAudioMode(mode, status);
+ }
+ return ret;
+}
+
+int voice_set_volume(struct voice_manager *voice, float volume)
+{
+ int ret = 0;
+
+ if (voice->call_status == CALL_STATUS_ACTIVE) {
+ SitRilSetVoiceVolume(voice->out_device, (int)(volume * voice->volume_steps_max), volume);
+ ALOGD("vm-%s: Volume = %d(%f)!", __func__, (int)(volume * voice->volume_steps_max), volume);
+ } else {
+ ALOGE("vm-%s: Voice is not Active", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int voice_set_extra_volume(struct voice_manager *voice, bool on)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetExtraVolume(on);
+ }
+ return ret;
+}
+
+int voice_set_path(struct voice_manager *voice, audio_devices_t devices)
+{
+ int ret = 0;
+ int mode = AUDIO_MODE_IN_CALL;
+
+ if (voice_is_call_mode(voice)) {
+ voice->out_device = devices;
+ SitRilSetVoicePath(mode, devices);
+ } else {
+ ALOGE("%s: Voice is not created", __func__);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int voice_set_mic_mute(struct voice_manager *voice, bool status)
+{
+ int ret = 0;
+ if (voice_is_call_mode(voice) || (voice->loopback_mode != FACTORY_LOOPBACK_OFF)) {
+ SitRilSetTxMute(status);
+ }
+ ALOGD("vm-%s: MIC Mute = %d!", __func__, status);
+ return ret;
+}
+
+int voice_set_rx_mute(struct voice_manager *voice, bool status)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetRxMute(status);
+ }
+ return ret;
+}
+
+int voice_set_usb_mic(struct voice_manager *voice, bool status)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetUSBMicState(status);
+ }
+ return ret;
+}
+
+void voice_set_call_forwarding(struct voice_manager *voice, bool callfwd)
+{
+ if (voice) {
+ SitRilSetCallFowardingMode(callfwd);
+ }
+ return;
+}
+
+void voice_set_cur_indevice_id(struct voice_manager *voice, int device)
+{
+ voice->in_device_id = device;
+ return;
+}
+
+void voice_set_parameters(struct audio_device *adev, struct str_parms *parms)
+{
+ struct voice_manager *voice = adev->voice;
+ char value[40];
+ int ret = 0;
+
+ char *kv_pairs = str_parms_to_str(parms);
+
+ //ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
+
+ // VoLTE Status Configuration
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_VOLTE_STATUS, value, sizeof(value));
+ if (ret >= 0) {
+ if (!strcmp(value, "voice")) {
+ voice->volte_status = VOLTE_VOICE;
+ ALOGD("vm-%s: VoLTE Voice Call Start!!", __func__);
+ } else if (!strcmp(value, "end")) {
+ voice->volte_status = VOLTE_OFF;
+ ALOGD("vm-%s: VoLTE Voice Call End!!", __func__);
+ } else
+ ALOGD("vm-%s: Unknown VoLTE parameters = %s!!", __func__, value);
+
+ voice_set_volte_status(voice, voice->volte_status);
+ }
+
+ // BT SCO NREC Configuration
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value));
+ if (ret >= 0) {
+ if (!strcmp(value, AUDIO_PARAMETER_VALUE_ON)) {
+ voice->bluetooth_nrec = BT_NREC_ON;
+ } else if (!strcmp(value, AUDIO_PARAMETER_VALUE_OFF)) {
+ voice->bluetooth_nrec = BT_NREC_OFF;
+ }
+
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_BT_NREC);
+ }
+
+ // BT SCO WideBand Configuration
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value));
+ if (ret >= 0) {
+ if (!strcmp(value, AUDIO_PARAMETER_VALUE_ON)) {
+ voice->bluetooth_samplerate = WB_SAMPLING_RATE;
+ } else if (!strcmp(value, AUDIO_PARAMETER_VALUE_OFF)) {
+ voice->bluetooth_samplerate = NB_SAMPLING_RATE;
+ }
+
+ uint32_t device = 0;
+ if (adev->primary_output)
+ device = adev->primary_output->common.requested_devices & AUDIO_DEVICE_OUT_ALL_SCO;
+
+ voice_set_sco_solution(voice, voice->bluetooth_nrec, voice->bluetooth_samplerate);
+
+ if(voice_is_call_active(voice) && (device != 0)) {
+ voice_set_path(voice, adev->primary_output->common.requested_devices);
+ }
+
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB);
+ }
+
+ // TTY Status Configuration
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
+ if (ret >= 0) {
+ if (!strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF)) {
+ voice->tty_mode = TTY_MODE_OFF;
+ voice_set_tty_mode(voice,TTY_MODE_OFF_RIL);
+ ALOGD("vm-%s: TTY_MODE_OFF", __func__);
+ } else if (!strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO)) {
+ voice->tty_mode = TTY_MODE_VCO;
+ voice_set_tty_mode(voice,TTY_MODE_VCO_RIL);
+ ALOGD("vm-%s: TTY_MODE_VCO", __func__);
+ } else if (!strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO)) {
+ voice->tty_mode = TTY_MODE_HCO;
+ voice_set_tty_mode(voice,TTY_MODE_HCO_RIL);
+ ALOGD("vm-%s: TTY_MODE_HCO", __func__);
+ } else if (!strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL)) {
+ voice->tty_mode = TTY_MODE_FULL;
+ voice_set_tty_mode(voice,TTY_MODE_FULL_RIL);
+ ALOGD("vm-%s: TTY_MODE_FULL", __func__);
+ } else
+ ALOGD("vm-%s: Unknown TTY_MODE parameters = %s!!", __func__, value);
+
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_TTY_MODE);
+ }
+
+ // HAC(Hearing Aid Compatibility) Status Configuration
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HAC, value, sizeof(value));
+ if (ret >= 0) {
+ if (!strcmp(value, AUDIO_PARAMETER_VALUE_HAC_ON)) {
+ voice->hac_mode = HAC_MODE_ON;
+ voice_set_hac_mode(voice,true);
+ ALOGD("vm-%s: HAC_MODE_ON", __func__);
+ } else if (!strcmp(value, AUDIO_PARAMETER_VALUE_HAC_OFF)) {
+ voice->hac_mode = HAC_MODE_OFF;
+ voice_set_hac_mode(voice,false);
+ ALOGD("vm-%s: HAC_MODE_OFF", __func__);
+ } else
+ ALOGD("vm-%s: Unknown HAC_MODE parameters = %s!!", __func__, value);
+
+ if (voice_is_call_active(voice)
+ && adev->primary_output
+ && adev->primary_output->common.requested_devices != 0) {
+ voice_set_path(voice, adev->primary_output->common.requested_devices);
+ }
+
+ str_parms_del(parms, AUDIO_PARAMETER_KEY_HAC);
+ }
+
+ //ALOGV("%s: exit with code(%d)", __func__, ret);
+
+ free(kv_pairs);
+ return ;
+}
+
+int voice_set_callback(struct voice_manager * voice __unused, void * callback_func )
+{
+ int ret = 0;
+
+ SitRilRegisterCallback(0, callback_func);
+ return ret;
+}
+
+
+/*
+ * Get Functions
+ */
+volte_status_t voice_get_volte_status(struct voice_manager *voice)
+{
+ return voice->volte_status;
+}
+
+int voice_get_samplingrate(struct voice_manager *voice)
+{
+ return voice->voice_samplingrate;
+}
+
+int voice_get_vowifi_band(struct voice_manager *voice)
+{
+ return voice->vowifi_band;
+}
+
+int voice_get_cur_indevice_id(struct voice_manager *voice)
+{
+ return voice->in_device_id;
+}
+
+
+/*
+ * Other Functions
+ */
+int voice_set_loopback_device(struct voice_manager *voice, int mode, int rx_dev, int tx_dev)
+{
+ int ret = 0;
+ if (voice) {
+ voice->loopback_mode = mode;
+ if (rx_dev == (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER)) {
+ ALOGI("%s: Set loopback rx path as RCV (Speaker2)", __func__);
+ rx_dev = AUDIO_DEVICE_OUT_EARPIECE; // set rcv as speaker2
+ }
+ SitRilSetLoopback(mode, rx_dev, tx_dev);
+ }
+ return ret;
+}
+
+void voice_ril_dump(int fd __unused)
+{
+ SitRilDump(fd);
+}
+
+int voice_get_volume_index(struct voice_manager *voice, float volume)
+{
+ return (int)(volume * voice->volume_steps_max);
+}
+
+int voice_set_tty_mode(struct voice_manager *voice, int ttymode)
+{
+ int ret = 0;
+ if (voice) {
+ SitRilSetTTYMode(ttymode);
+ }
+ return ret;
+}
+
+int voice_callback(void * handle, int event, const void *data, unsigned int datalen)
+{
+ struct voice_manager *voice = (struct voice_manager *)handle;
+ int (*funcp)(int, const void *, unsigned int) = NULL;
+
+ ALOGD("vm-%s: Called Callback Function from RIL Audio Client!", __func__);
+ if (voice) {
+#if 0
+ switch (event) {
+ case VOICE_AUDIO_EVENT_RINGBACK_STATE_CHANGED:
+ ALOGD("vm-%s: Received RINGBACK_STATE_CHANGED event!", __func__);
+ break;
+
+ case VOICE_AUDIO_EVENT_IMS_SRVCC_HANDOVER:
+ ALOGD("vm-%s: Received IMS_SRVCC_HANDOVER event!", __func__);
+ break;
+
+ default:
+ ALOGD("vm-%s: Received Unsupported event (%d)!", __func__, event);
+ return 0;
+ }
+#endif
+ funcp = voice->callback;
+ funcp(event, data, datalen);
+ }
+
+ return 0;
+}
+
+void voice_deinit(struct voice_manager *voice)
+{
+ if (voice) {
+ /* RIL */
+ SitRilClose();
+ free(voice);
+ }
+
+ return ;
+}
+
+struct voice_manager* voice_init(void)
+{
+ struct voice_manager *voice = NULL;
+ char property[PROPERTY_VALUE_MAX];
+ int ret = 0;
+
+ voice = calloc(1, sizeof(struct voice_manager));
+ if (voice) {
+
+ // At initial time, try to connect to AudioRIL
+ ret = SitRilOpen();
+ if (ret == 0)
+ voice->call_status = CALL_STATUS_CONNECTED;
+ else
+ voice->call_status = CALL_STATUS_INVALID;
+
+ // Variables
+ voice->realcall = false;
+ voice->csvtcall = false;
+ voice->keep_call_mode = false;
+
+ voice->out_device = AUDIO_DEVICE_NONE;
+ voice->in_device_id = DEVICE_INVALID;
+ voice->bluetooth_nrec = BT_NREC_INITIALIZED;
+ voice->bluetooth_samplerate = NB_SAMPLING_RATE;
+ voice->tty_mode = TTY_MODE_OFF;
+ voice->call_forwarding = false;
+ voice->mute_voice = false;
+ voice->cur_modem = CP1;
+ voice->extra_volume = false;
+
+ voice->voice_samplingrate = VOICE_SR_NB;
+ voice->loopback_mode = FACTORY_LOOPBACK_OFF;
+
+ // VoIP
+ voice->voip_wificalling = false;
+ voice->voip_rx_active = false;
+ voice->vowifi_band = WB;
+
+ // VoLTE
+ voice->volte_status = VOLTE_OFF;
+ voice->previous_volte_status = VOLTE_OFF;
+
+ property_get(VOLUME_STEPS_PROPERTY, property, VOLUME_STEPS_DEFAULT);
+ voice->volume_steps_max = atoi(property);
+ /* this catches the case where VOLUME_STEPS_PROPERTY does not contain an integer */
+ if (voice->volume_steps_max == 0)
+ voice->volume_steps_max = atoi(VOLUME_STEPS_DEFAULT);
+
+ voice->callback = NULL;
+ }
+
+ return voice;
+}
diff --git a/libaudio/audiohal/voice_manager.h b/libaudio/audiohal/voice_manager.h
new file mode 100644
index 0000000..da51419
--- /dev/null
+++ b/libaudio/audiohal/voice_manager.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __EXYNOS_VOICE_SERVICE_H__
+#define __EXYNOS_VOICE_SERVICE_H__
+
+#include <system/audio.h>
+#include <hardware/audio.h>
+
+#include "voice_definition.h"
+
+
+enum {
+ VOICE_SR_NB = 0, // Narrow Band
+ VOICE_SR_WB, // Wide Band
+ VOICE_SR_SWB // Super Wide Band
+};
+
+typedef enum {
+ VOLTE_OFF = 0,
+ VOLTE_VOICE,
+ VOLTE_VIDEO
+} volte_status_t;
+
+typedef enum {
+ CALL_STATUS_INVALID = 0, // RIL Audio Client is not connected yet
+ CALL_STATUS_CONNECTED, // RIL Audio Client is connected, but it is not IN_CALL Mode
+ CALL_STATUS_INCALLMODE, // RIL Audio Client is connected and it is IN_CALL Mode, but Voice is not working
+ CALL_STATUS_ACTIVE // RIL Audio Client is connected, it is IN_CALL Mode and Voice is working
+} call_status_t;
+
+
+struct voice_manager {
+ // Call Status
+ call_status_t call_status; // Current Call Status
+ bool realcall;
+ bool csvtcall;
+ bool keep_call_mode;
+
+ audio_devices_t out_device;
+ int in_device_id; /* for disable cur path */
+ int bluetooth_nrec;
+ int bluetooth_samplerate;
+ int tty_mode;
+ int hac_mode;
+ bool call_forwarding;
+ bool mute_voice;
+ int cur_modem;
+ bool extra_volume;
+
+ int voice_samplingrate;
+ int loopback_mode;
+
+ // VoIP
+ int vowifi_band;
+ bool voip_rx_active;
+ bool voip_wificalling;
+
+ // VoLTE
+ volte_status_t volte_status;
+ volte_status_t previous_volte_status;
+
+ int volume_steps_max; // Voice Volume maximum steps
+
+ int (*callback)(int, const void *, unsigned int); // Callback Function Pointer
+};
+
+struct audio_device;
+
+/* Local Functions */
+//int voice_check_ril_connection(struct voice_manager *voice);
+//void voice_check_multisim(struct voice_manager *voice);
+//int voice_set_current_modem(struct voice_manager *voice, int cur_modem);
+//int voice_set_wb_amr(struct voice_manager *voice, bool on);
+//int voice_set_sco_solution(struct voice_manager *voice, bool echo_cancel, int sample_rate);
+//int voice_set_dha_solution(struct voice_manager *voice, const char *data);
+//int voice_set_cover_status(struct voice_manager *voice, bool status);
+//int voice_set_volte_status(struct voice_manager *voice, int status);
+//int voice_set_hac_mode(struct voice_manager *voice, bool hac_flag);
+
+/* Status Check Functions */
+bool voice_is_call_mode (struct voice_manager *voice);
+bool voice_is_call_active (struct voice_manager *voice);
+//bool voice_is_in_voip(struct audio_device *adev); // Deprecated
+
+/* Set Functions */
+int voice_set_call_mode(struct voice_manager *voice, bool on);
+int voice_set_call_active (struct voice_manager *voice, bool on);
+int voice_set_audio_mode(struct voice_manager *voice, int mode, bool status);
+int voice_set_volume(struct voice_manager *voice, float volume);
+int voice_set_extra_volume(struct voice_manager *voice, bool on);
+int voice_set_path(struct voice_manager * voice, audio_devices_t devices);
+int voice_set_mic_mute(struct voice_manager *voice, bool status);
+int voice_set_rx_mute(struct voice_manager *voice, bool status);
+int voice_set_usb_mic(struct voice_manager *voice, bool status);
+void voice_set_call_forwarding(struct voice_manager *voice, bool callfwd);
+void voice_set_cur_indevice_id(struct voice_manager *voice, int device);
+void voice_set_parameters(struct audio_device *adev, struct str_parms *parms);
+
+/* Get Functions */
+volte_status_t voice_get_volte_status(struct voice_manager *voice);
+int voice_get_samplingrate(struct voice_manager *voice);
+int voice_get_vowifi_band(struct voice_manager *voice);
+int voice_get_cur_indevice_id(struct voice_manager *voice);
+bool voice_get_mic_mute(struct voice_manager *voice);
+int voice_get_volume_index(struct voice_manager *voice, float volume);
+int voice_set_tty_mode(struct voice_manager *voice, int ttymode);
+
+/* Other Functions */
+int voice_set_loopback_device(struct voice_manager *voice, int mode, int rx_dev, int tx_dev);
+void voice_ril_dump(int fd __unused);
+int voice_set_callback(struct voice_manager * voice, void * callback_func);
+
+/* Voice Manager related Functiuons */
+void voice_deinit(struct voice_manager *voice);
+struct voice_manager * voice_init(void);
+
+#endif // __EXYNOS_VOICE_SERVICE_H__