blob: 1ea826fb2e9887acc7576214a61e8377e32e0ee1 [file] [log] [blame]
/*
* Copyright (c) 2013-2021, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright (C) 2013 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.
*
* This file was modified by DTS, Inc. The portions of the
* code modified by DTS, Inc are copyrighted and
* licensed separately, as follows:
*
* (C) 2014 DTS, Inc.
*
* 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.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define LOG_TAG "AHAL: AudioDevice"
#define ATRACE_TAG (ATRACE_TAG_AUDIO|ATRACE_TAG_HAL)
#define AUDIO_CAPTURE_PERIOD_DURATION_MSEC 20
#define MIN_CHANNEL_COUNT 1
#define MAX_CHANNEL_COUNT 8
#include "AudioCommon.h"
#include "AudioDevice.h"
#include <dlfcn.h>
#include <inttypes.h>
#include <cutils/str_parms.h>
#include <vector>
#include <map>
#include<algorithm>
#include "PalApi.h"
#include "PalDefs.h"
#include <audio_extn/AudioExtn.h>
#include "audio_extn.h"
#include "battery_listener.h"
#define MIC_CHARACTERISTICS_XML_FILE "/vendor/etc/microphone_characteristics.xml"
static pal_device_id_t in_snd_device = PAL_DEVICE_NONE;
microphone_characteristics_t AudioDevice::microphones;
snd_device_to_mic_map_t AudioDevice::microphone_maps[PAL_MAX_INPUT_DEVICES];
bool AudioDevice::mic_characteristics_available = false;
card_status_t AudioDevice::sndCardState = CARD_STATUS_ONLINE;
btsco_lc3_cfg_t AudioDevice::btsco_lc3_cfg = {};
struct audio_string_to_enum {
const char* name;
unsigned int value;
};
static const struct audio_string_to_enum mic_locations[AUDIO_MICROPHONE_LOCATION_CNT] = {
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_UNKNOWN),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_MAINBODY),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_MAINBODY_MOVABLE),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_PERIPHERAL),
};
static const struct audio_string_to_enum mic_directionalities[AUDIO_MICROPHONE_DIRECTIONALITY_CNT] = {
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_OMNI),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_BI_DIRECTIONAL),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_CARDIOID),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_HYPER_CARDIOID),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_SUPER_CARDIOID),
};
static const struct audio_string_to_enum mic_channel_mapping[AUDIO_MICROPHONE_CHANNEL_MAPPING_CNT] = {
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_CHANNEL_MAPPING_DIRECT),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_CHANNEL_MAPPING_PROCESSED),
};
static const struct audio_string_to_enum device_in_types[] = {
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_AMBIENT),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_COMMUNICATION),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_HDMI),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_VOICE_CALL),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BACK_MIC),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_DEVICE),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_FM_TUNER),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_TV_TUNER),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_LINE),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_SPDIF),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLE_HEADSET),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_LOOPBACK),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_IP),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BUS),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_PROXY),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_HEADSET),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_BLE),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DEFAULT),
AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_ECHO_REFERENCE),
};
enum {
AUDIO_MICROPHONE_CHARACTERISTIC_NONE = 0u, // 0x0
AUDIO_MICROPHONE_CHARACTERISTIC_SENSITIVITY = 1u, // 0x1
AUDIO_MICROPHONE_CHARACTERISTIC_MAX_SPL = 2u, // 0x2
AUDIO_MICROPHONE_CHARACTERISTIC_MIN_SPL = 4u, // 0x4
AUDIO_MICROPHONE_CHARACTERISTIC_ORIENTATION = 8u, // 0x8
AUDIO_MICROPHONE_CHARACTERISTIC_GEOMETRIC_LOCATION = 16u, // 0x10
AUDIO_MICROPHONE_CHARACTERISTIC_ALL = 31u, /* ((((SENSITIVITY | MAX_SPL) | MIN_SPL)
| ORIENTATION) | GEOMETRIC_LOCATION) */
};
static bool hdr_set_parameters(std::shared_ptr<AudioDevice> adev,
struct str_parms *parms) {
if (adev == nullptr || parms == nullptr) {
AHAL_ERR("%s Invalid arguments", __func__);
return false;
}
int ret = 0, val = 0;
char value[32];
bool changes = false;
/* HDR Audio Parameters */
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HDR, value,
sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0)
adev->hdr_record_enabled = true;
else
adev->hdr_record_enabled = false;
changes = true;
AHAL_INFO("HDR Enabled: %d", adev->hdr_record_enabled);
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_WNR, value,
sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0)
adev->wnr_enabled = true;
else
adev->wnr_enabled = false;
AHAL_INFO("WNR Enabled: %d", adev->wnr_enabled);
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_ANS, value,
sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0)
adev->ans_enabled = true;
else
adev->ans_enabled = false;
AHAL_INFO("ANS Enabled: %d", adev->ans_enabled);
}
/* Checking for Device rotation */
ret = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_ROTATION, &val);
if (ret >= 0) {
adev->inverted = (val == ROTATION_270 || val == ROTATION_180)? true : false;
adev->orientation_landscape = (val == ROTATION_90 || val == ROTATION_270)? true : false;
changes = true;
AHAL_INFO("orientation_landscape is %d and inverted is %d", adev->orientation_landscape,
adev->inverted);
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_ORIENTATION, value,
sizeof(value));
if (ret >= 0) {
if (strncmp(value, "landscape", 9) == 0)
adev->orientation_landscape = true;
else if (strncmp(value, "portrait", 8) == 0)
adev->orientation_landscape = false;
changes = true;
AHAL_INFO("Orientation %s",
adev->orientation_landscape ? "landscape" : "portrait");
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_INVERTED, value,
sizeof(value));
if (ret >= 0) {
if (strncmp(value, "true", 4) == 0)
adev->inverted = true;
else
adev->inverted = false;
changes = true;
AHAL_INFO("Orientation inverted: %d", adev->inverted);
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_FACING, value,
sizeof(value));
if (ret >= 0) {
/*0-none, 1-back, 2-front/selfie*/
if (strncmp(value, "front", 5) == 0)
adev->facing = 2;
else if (strncmp(value, "back", 4) == 0)
adev->facing = 1;
else if (strncmp(value, "none", 4) == 0)
adev->facing = 0;
changes = true;
AHAL_INFO("Device facing %s", value);
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HDR_CHANNELS, value,
sizeof(value));
if (ret >= 0) {
val = atoi(value);
if (val != 4) {
AHAL_DBG("Invalid HDR channels: %d", val);
} else {
adev->hdr_channel_count = val;
}
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HDR_SAMPLERATE, value,
sizeof(value));
if (ret >= 0) {
val = atoi(value);
if (val != 48000) {
AHAL_DBG("Invalid HDR sample rate: %d", val);
} else {
adev->hdr_sample_rate = val;
}
}
return changes;
}
static void hdr_get_parameters(std::shared_ptr<AudioDevice> adev,
struct str_parms *query, struct str_parms *reply) {
if (adev == nullptr || query == nullptr || reply == nullptr) {
AHAL_ERR("%s Invalid arguments", __func__);
return;
}
int32_t ret;
char value[256]={0};
size_t size = 0;
/* HDR Audio Parameters */
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_HDR, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_HDR,
adev->hdr_record_enabled ? "true" : "false");
AHAL_VERBOSE("%s=%s", AUDIO_PARAMETER_KEY_HDR,
adev->hdr_record_enabled ? "true" : "false");
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_WNR, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_WNR, adev->wnr_enabled
? "true" : "false");
AHAL_VERBOSE("%s=%s", AUDIO_PARAMETER_KEY_WNR, adev->wnr_enabled
? "true" : "false");
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_ANS, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_ANS, adev->ans_enabled
? "true" : "false");
AHAL_VERBOSE("%s=%s", AUDIO_PARAMETER_KEY_ANS, adev->ans_enabled
? "true" : "false");
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_ORIENTATION, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_str(reply,AUDIO_PARAMETER_KEY_ORIENTATION,
adev->orientation_landscape ? "landscape" : "portrait");
AHAL_VERBOSE("%s=%s", AUDIO_PARAMETER_KEY_ORIENTATION,
adev->orientation_landscape ? "landscape" : "portrait");
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_INVERTED, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_INVERTED, adev->inverted
? "true" : "false");
AHAL_VERBOSE("%s=%s", AUDIO_PARAMETER_KEY_INVERTED, adev->inverted
? "true" : "false");
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_FACING, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_str(reply, AUDIO_PARAMETER_KEY_FACING,
(adev->facing == 0) ? "none" : ((adev->facing == 1) ? "back"
: "front"));
AHAL_VERBOSE("%s=%s", AUDIO_PARAMETER_KEY_FACING, (adev->facing == 0)
? "none" : ((adev->facing == 1) ? "back" : "front"));
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_HDR_CHANNELS, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_int(reply, AUDIO_PARAMETER_KEY_HDR_CHANNELS,
adev->hdr_channel_count);
AHAL_VERBOSE("%s=%d",AUDIO_PARAMETER_KEY_HDR_CHANNELS,
adev->hdr_channel_count);
}
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_HDR_SAMPLERATE, value,
sizeof(value));
if (ret >= 0) {
str_parms_add_int(reply, AUDIO_PARAMETER_KEY_HDR_SAMPLERATE,
adev->hdr_sample_rate);
AHAL_INFO("%s=%d", AUDIO_PARAMETER_KEY_HDR_SAMPLERATE, adev->hdr_sample_rate);
}
}
AudioDevice::~AudioDevice() {
audio_extn_gef_deinit(adev_);
audio_extn_sound_trigger_deinit(adev_);
AudioExtn::battery_properties_listener_deinit();
pal_deinit();
}
std::shared_ptr<AudioDevice> AudioDevice::GetInstance() {
if (!adev_) {
adev_ = std::shared_ptr<AudioDevice> (new AudioDevice());
device_ = std::shared_ptr<audio_hw_device_t> (new audio_hw_device_t());
}
return adev_;
}
std::shared_ptr<AudioDevice> AudioDevice::GetInstance(audio_hw_device_t* device) {
if (device == (audio_hw_device_t*)device_.get())
return AudioDevice::adev_;
else
return NULL;
}
std::shared_ptr<StreamOutPrimary> AudioDevice::CreateStreamOut(
audio_io_handle_t handle,
const std::set<audio_devices_t>& devices,
audio_output_flags_t flags,
struct audio_config *config,
audio_stream_out **stream_out,
const char *address) {
std::shared_ptr<StreamOutPrimary> astream = nullptr;
try {
astream = std::shared_ptr<StreamOutPrimary> (new StreamOutPrimary(handle,
devices, flags, config, address,
fnp_offload_effect_start_output_,
fnp_offload_effect_stop_output_,
fnp_visualizer_start_output_,
fnp_visualizer_stop_output_));
} catch (const std::exception& e) {
AHAL_ERR("Failed to create StreamOutPrimary");
return nullptr;
}
astream->GetStreamHandle(stream_out);
out_list_mutex.lock();
stream_out_list_.push_back(astream);
AHAL_DBG("output stream %d %p",(int)stream_out_list_.size(), stream_out);
if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) {
if (voice_)
voice_->stream_out_primary_ = astream;
}
out_list_mutex.unlock();
return astream;
}
void AudioDevice::CloseStreamOut(std::shared_ptr<StreamOutPrimary> stream) {
out_list_mutex.lock();
auto iter =
std::find(stream_out_list_.begin(), stream_out_list_.end(), stream);
if (iter == stream_out_list_.end()) {
AHAL_ERR("invalid output stream");
} else {
stream_out_list_.erase(iter);
}
out_list_mutex.unlock();
}
int AudioDevice::CreateAudioPatch(audio_patch_handle_t *handle,
const std::vector<struct audio_port_config>& sources,
const std::vector<struct audio_port_config>& sinks) {
int ret = 0;
bool new_patch = false;
AudioPatch *patch = NULL;
std::shared_ptr<StreamPrimary> stream = nullptr;
AudioPatch::PatchType patch_type = AudioPatch::PATCH_NONE;
audio_io_handle_t io_handle = AUDIO_IO_HANDLE_NONE;
audio_source_t input_source = AUDIO_SOURCE_DEFAULT;
std::set<audio_devices_t> device_types;
AHAL_DBG("enter: num sources %zu, num_sinks %zu", sources.size(), sinks.size());
if (!handle || sources.empty() || sources.size() > AUDIO_PATCH_PORTS_MAX ||
sinks.empty() || sinks.size() > AUDIO_PATCH_PORTS_MAX) {
AHAL_ERR("Invalid patch arguments");
ret = -EINVAL;
goto exit;
}
if (sources.size() > 1) {
AHAL_ERR("error multiple sources are not supported");
ret = -EINVAL;
goto exit;
}
AHAL_DBG("source role %d, source type %d", sources[0].role, sources[0].type);
// Populate source/sink information and fetch stream info
switch (sources[0].type) {
case AUDIO_PORT_TYPE_DEVICE: // Patch for audio capture or loopback
device_types.insert(sources[0].ext.device.type);
if (sinks[0].type == AUDIO_PORT_TYPE_MIX) {
io_handle = sinks[0].ext.mix.handle;
input_source = sinks[0].ext.mix.usecase.source;
patch_type = AudioPatch::PATCH_CAPTURE;
AHAL_DBG("Capture patch from device %x to mix %d",
sources[0].ext.device.type, sinks[0].ext.mix.handle);
} else {
/*Device to device patch is not implemented.
This space will need changes if audio HAL
handles device to device patches in the future.*/
patch_type = AudioPatch::PATCH_DEVICE_LOOPBACK;
AHAL_ERR("error device to device patches not supported");
ret = -ENOSYS;
goto exit;
}
break;
case AUDIO_PORT_TYPE_MIX: // Patch for audio playback
io_handle = sources[0].ext.mix.handle;
for (const auto &sink : sinks)
device_types.insert(sink.ext.device.type);
patch_type = AudioPatch::PATCH_PLAYBACK;
AHAL_DBG("Playback patch from mix handle %d to device %x",
io_handle, AudioExtn::get_device_types(device_types));
break;
case AUDIO_PORT_TYPE_SESSION:
case AUDIO_PORT_TYPE_NONE:
AHAL_ERR("Unsupported source type %d", sources[0].type);
ret = -EINVAL;
goto exit;
}
if (patch_type == AudioPatch::PATCH_PLAYBACK)
stream = OutGetStream(io_handle);
else
stream = InGetStream(io_handle);
if(!stream) {
AHAL_ERR("Failed to fetch stream with io handle %d", io_handle);
ret = -EINVAL;
goto exit;
}
// empty patch...generate new handle
if (*handle == AUDIO_PATCH_HANDLE_NONE) {
patch = new AudioPatch(patch_type, sources, sinks);
*handle = patch->handle;
new_patch = true;
} else {
std::lock_guard<std::mutex> lock(patch_map_mutex);
auto it = patch_map_.find(*handle);
if (it == patch_map_.end()) {
AHAL_ERR("Unable to fetch patch with handle %d", *handle);
ret = -EINVAL;
goto exit;
}
patch = &(*it->second);
patch->type = patch_type;
patch->sources = sources;
patch->sinks = sinks;
}
if (voice_ && patch_type == AudioPatch::PATCH_PLAYBACK)
ret = voice_->RouteStream(device_types);
ret |= stream->RouteStream(device_types);
if (ret) {
if (new_patch)
delete patch;
AHAL_ERR("Stream routing failed for io_handle %d", io_handle);
} else if (new_patch) {
// new patch...add to patch map
std::lock_guard<std::mutex> lock(patch_map_mutex);
patch_map_[patch->handle] = patch;
AHAL_DBG("Added a new patch with handle %d", patch->handle);
}
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioDevice::ReleaseAudioPatch(audio_patch_handle_t handle) {
int ret = 0;
AudioPatch *patch = NULL;
std::shared_ptr<StreamPrimary> stream = nullptr;
audio_io_handle_t io_handle = AUDIO_IO_HANDLE_NONE;
AudioPatch::PatchType patch_type = AudioPatch::PATCH_NONE;
AHAL_DBG("Release patch with handle %d", handle);
if (handle == AUDIO_PATCH_HANDLE_NONE) {
AHAL_ERR("Invalid patch handle %d", handle);
return -EINVAL;
}
// grab the io_handle from the patch
patch_map_mutex.lock();
auto patch_it = patch_map_.find(handle);
if (patch_it == patch_map_.end() || !patch_it->second) {
AHAL_ERR("error patch info not found with handle %d", handle);
patch_map_mutex.unlock();
return -EINVAL;
}
patch = &(*patch_it->second);
patch_type = patch->type;
switch (patch->sources[0].type) {
case AUDIO_PORT_TYPE_MIX:
io_handle = patch->sources[0].ext.mix.handle;
break;
case AUDIO_PORT_TYPE_DEVICE:
if (patch->type == AudioPatch::PATCH_CAPTURE)
io_handle = patch->sinks[0].ext.mix.handle;
break;
case AUDIO_PORT_TYPE_SESSION:
case AUDIO_PORT_TYPE_NONE:
AHAL_DBG("Invalid port type: %d", patch->sources[0].type);
patch_map_mutex.unlock();
return -EINVAL;
}
patch_map_mutex.unlock();
if (patch_type == AudioPatch::PATCH_PLAYBACK)
stream = OutGetStream(io_handle);
else
stream = InGetStream(io_handle);
if (!stream) {
AHAL_ERR("Failed to fetch stream with io handle %d", io_handle);
return -EINVAL;
}
ret = stream->RouteStream({AUDIO_DEVICE_NONE});
if (ret)
AHAL_ERR("Stream routing failed for io_handle %d", io_handle);
std::lock_guard lock(patch_map_mutex);
patch_map_.erase(handle);
delete patch;
AHAL_DBG("Successfully released patch %d", handle);
return ret;
}
std::shared_ptr<StreamInPrimary> AudioDevice::CreateStreamIn(
audio_io_handle_t handle,
const std::set<audio_devices_t>& devices,
audio_input_flags_t flags,
struct audio_config *config,
const char *address,
audio_stream_in **stream_in,
audio_source_t source) {
std::shared_ptr<StreamInPrimary> astream (new StreamInPrimary(handle,
devices, flags, config,
address, source));
astream->GetStreamHandle(stream_in);
in_list_mutex.lock();
stream_in_list_.push_back(astream);
in_list_mutex.unlock();
AHAL_DBG("input stream %d %p",(int)stream_in_list_.size(), stream_in);
if (voice_) {
voice_->stream_in_primary_ = astream;
}
return astream;
}
void AudioDevice::CloseStreamIn(std::shared_ptr<StreamInPrimary> stream) {
in_list_mutex.lock();
auto iter =
std::find(stream_in_list_.begin(), stream_in_list_.end(), stream);
if (iter == stream_in_list_.end()) {
AHAL_ERR("invalid input stream");
} else {
stream_in_list_.erase(iter);
if (voice_) {
if (stream_in_list_.size() == 0) {
voice_->stream_in_primary_ = nullptr;
} else {
voice_->stream_in_primary_ = stream_in_list_[0];
}
}
}
in_list_mutex.unlock();
}
static int adev_close(hw_device_t *device __unused) {
return 0;
}
static int adev_init_check(const struct audio_hw_device *dev __unused) {
return 0;
}
void adev_on_battery_status_changed(bool charging)
{
std::shared_ptr<AudioDevice>adevice = AudioDevice::GetInstance();
AHAL_DBG("battery status changed to %scharging",
charging ? "" : "not ");
adevice->SetChargingMode(charging);
}
static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) {
std::shared_ptr<AudioDevice>adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return -EINVAL;
}
return adevice->SetVoiceVolume(volume);
}
static int adev_pal_global_callback(uint32_t event_id, uint32_t *event_data,
uint64_t cookie) {
AHAL_DBG("event_id (%d), event_data (%d), cookie %" PRIu64,
event_id, *event_data, cookie);
switch (event_id) {
case PAL_SND_CARD_STATE :
AudioDevice::sndCardState = (card_status_t)*event_data;
AHAL_DBG("sound card status changed %d sndCardState %d",
*event_data, AudioDevice::sndCardState);
break;
default :
AHAL_ERR("Invalid event id:%d", event_id);
return -EINVAL;
}
return 0;
}
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) {
int32_t ret = 0;
std::shared_ptr<StreamOutPrimary> astream;
AHAL_DBG("enter: format(%#x) sample_rate(%d) channel_mask(%#x) devices(%#x)\
flags(%#x) address(%s)", config->format, config->sample_rate,
config->channel_mask, devices, flags, address);
std::shared_ptr<AudioDevice>adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
goto exit;
}
/* This check is added for oflload streams, so that
* flinger will fallback to DB stream during SSR.
*/
if (AudioDevice::sndCardState == CARD_STATUS_OFFLINE &&
(flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ||
flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
AHAL_ERR("error: sound card offline");
ret = -ENODEV;
goto exit;
}
/* On error AAudioService will retry with supported format passed
*/
if ((flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) &&
config->format == AUDIO_FORMAT_PCM_FLOAT) {
AHAL_ERR("unsupported format: %#x", config->format);
ret = -EINVAL;
goto exit;
}
astream = adevice->OutGetStream(handle);
if (astream == nullptr) {
astream = adevice->CreateStreamOut(handle, {devices}, flags, config, stream_out, address);
if (astream == nullptr) {
ret = -ENOMEM;
goto exit;
}
}
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream) {
std::shared_ptr<StreamOutPrimary> astream_out;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return;
}
astream_out = adevice->OutGetStream((audio_stream_t*)stream);
if (!astream_out) {
AHAL_ERR("invalid astream_in object");
return;
}
AHAL_DBG("Enter:stream_handle(%p)", astream_out.get());
adevice->CloseStreamOut(astream_out);
AHAL_DBG("Exit");
}
void adev_close_input_stream(struct audio_hw_device *dev,
struct audio_stream_in *stream)
{
std::shared_ptr<StreamInPrimary> astream_in;
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return;
}
astream_in = adevice->InGetStream((audio_stream_t*)stream);
if (!astream_in) {
AHAL_ERR("invalid astream_in object");
return;
}
AHAL_DBG("Enter:stream_handle(%p)", astream_in.get());
adevice->CloseStreamIn(astream_in);
AHAL_DBG("Exit");
}
/*
* Add 24bit recording support for MIC, CAMCORDER, UNPROCESSED input sources.
*/
static int audio_source_supports_24bit_record(audio_source_t source) {
switch(source) {
case AUDIO_SOURCE_MIC:
case AUDIO_SOURCE_CAMCORDER:
case AUDIO_SOURCE_UNPROCESSED:
case AUDIO_SOURCE_VOICE_RECOGNITION:
return true;
default:
return false;
}
}
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) {
int32_t ret = 0;
std::shared_ptr<StreamInPrimary> astream = nullptr;
audio_format_t inputFormat = config->format;
AHAL_DBG("enter: sample_rate(%d) channel_mask(%#x) devices(%#x)\
io_handle(%d) source(%d) format %x", config->sample_rate,
config->channel_mask, devices, handle, source, config->format);
std::shared_ptr<AudioDevice>adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
goto exit;
}
/* In case of any source requesting 32 bit or float return error
* indicating format supported is up to 24 bit only.
* On error AudioFlinger will retry with supported format passed.
*/
if (config->format == AUDIO_FORMAT_PCM_FLOAT ||
config->format == AUDIO_FORMAT_PCM_32_BIT) {
config->format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
ret = -EINVAL;
}
switch (config->format) {
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
case AUDIO_FORMAT_PCM_8_24_BIT:
if (audio_source_supports_24bit_record(source)) {
AHAL_DBG("set format %d for input source %d", config->format, source);
} else {
config->format = AUDIO_FORMAT_PCM_16_BIT;
ret = -EINVAL;
}
break;
default:
break;
}
if (ret != 0) {
AHAL_ERR("unsupported input format %d, fallback format %d input source %d", inputFormat,
config->format, source);
if (config->sample_rate > 48000)
config->sample_rate = 48000;
goto exit;
}
astream = adevice->InGetStream(handle);
if (astream == nullptr)
astream = adevice->CreateStreamIn(handle, {devices}, flags, config,
address, stream_in, source);
exit:
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
{
std::shared_ptr<AudioDevice>adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return -EINVAL;
}
return adevice->SetMode(mode);
}
static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return -EINVAL;
}
return adevice->SetMicMute(state);
}
static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) {
std::shared_ptr<AudioDevice> adevice =
AudioDevice::GetInstance((audio_hw_device_t *)dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return -EINVAL;
}
return adevice->GetMicMute(state);
}
static int adev_set_master_volume(struct audio_hw_device *dev __unused,
float volume __unused) {
return -ENOSYS;
}
static int adev_get_master_volume(struct audio_hw_device *dev __unused,
float *volume __unused) {
return -ENOSYS;
}
static int adev_set_master_mute(struct audio_hw_device *dev __unused,
bool muted __unused) {
return -ENOSYS;
}
static int adev_get_master_mute(struct audio_hw_device *dev __unused,
bool *muted __unused) {
return -ENOSYS;
}
static int adev_set_parameters(struct audio_hw_device *dev,
const char *kvpairs) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return -EINVAL;
}
return adevice->SetParameters(kvpairs);
}
static char* adev_get_parameters(const struct audio_hw_device *dev,
const char *keys) {
std::shared_ptr<AudioDevice> adevice =
AudioDevice::GetInstance((audio_hw_device_t*)dev);
if (!adevice) {
AHAL_ERR("invalid adevice object");
return NULL;
}
return adevice->GetParameters(keys);
}
static int check_input_parameters(uint32_t sample_rate,
audio_format_t format,
int channel_count)
{
int ret = 0;
static std::vector<int> channel_counts_supported = {1,2,3,4,6,8,10,12,14};
static std::vector<uint32_t> sample_rate_supported = {8000,11025,12000,16000,
22050,24000,32000,44100,48000,
88200,96000,176400,192000 };
if (((format != AUDIO_FORMAT_PCM_16_BIT) && (format != AUDIO_FORMAT_PCM_8_24_BIT) &&
(format != AUDIO_FORMAT_PCM_24_BIT_PACKED) && (format != AUDIO_FORMAT_PCM_32_BIT) &&
(format != AUDIO_FORMAT_PCM_FLOAT))) {
AHAL_ERR("format not supported!!! format:%d", format);
return -EINVAL;
}
if ((channel_count < MIN_CHANNEL_COUNT) || (channel_count > MAX_CHANNEL_COUNT)) {
ALOGE("%s: unsupported channel count (%d) passed Min / Max (%d / %d)", __func__,
channel_count, MIN_CHANNEL_COUNT, MAX_CHANNEL_COUNT);
return -EINVAL;
}
if ( std::find(channel_counts_supported.begin(),
channel_counts_supported.end(),
channel_count) == channel_counts_supported.end() ) {
AHAL_ERR("channel count not supported!!! chanel count:%d", channel_count);
return -EINVAL;
}
if ( std::find(sample_rate_supported.begin(), sample_rate_supported.end(),
sample_rate) == sample_rate_supported.end() ) {
AHAL_ERR("sample rate not supported!!! sample_rate:%d", sample_rate);
return -EINVAL;
}
return ret;
}
static size_t adev_get_input_buffer_size(
const struct audio_hw_device *dev __unused,
const struct audio_config *config ) {
size_t size = 0;
uint32_t bytes_per_period_sample = 0;
/* input for compress formats */
if (config && !audio_is_linear_pcm(config->format)) {
if (config->format == AUDIO_FORMAT_AAC_LC) {
return COMPRESS_CAPTURE_AAC_MAX_OUTPUT_BUFFER_SIZE;
}
return 0;
}
if (config != NULL) {
int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
/* Don't know if USB HIFI in this context so use true to be conservative */
if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0) {
AHAL_ERR("input parameters not supported!!!");
return 0;
}
size = (config->sample_rate * AUDIO_CAPTURE_PERIOD_DURATION_MSEC) / 1000;
bytes_per_period_sample = audio_bytes_per_sample(config->format) * channel_count;
size *= bytes_per_period_sample;
}
/* make sure the size is multiple of 32 bytes and additionally multiple of
* the frame_size (required for 24bit samples and non-power-of-2 channel counts)
* At 48 kHz mono 16-bit PCM:
* 5.000 ms = 240 frames = 15*16*1*2 = 480, a whole multiple of 32 (15)
* 3.333 ms = 160 frames = 10*16*1*2 = 320, a whole multiple of 32 (10)
* Also, make sure the size is multiple of bytes per period sample
*/
size = nearest_multiple(size, lcm(32, bytes_per_period_sample));
return size;
}
int adev_release_audio_patch(struct audio_hw_device *dev,
audio_patch_handle_t handle) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("GetInstance() failed");
return -EINVAL;
}
return adevice->ReleaseAudioPatch(handle);
}
int adev_create_audio_patch(struct audio_hw_device *dev,
unsigned int num_sources,
const struct audio_port_config *sources,
unsigned int num_sinks,
const struct audio_port_config *sinks,
audio_patch_handle_t *handle) {
if (!handle) {
AHAL_ERR("Invalid handle");
return -EINVAL;
}
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(dev);
if (!adevice) {
AHAL_ERR("GetInstance() failed");
return -EINVAL;
}
std::vector<struct audio_port_config> source_vec(sources, sources + num_sources);
std::vector<struct audio_port_config> sink_vec(sinks, sinks + num_sinks);
return adevice->CreateAudioPatch(handle, source_vec, sink_vec);
}
int get_audio_port_v7(struct audio_hw_device *dev, struct audio_port_v7 *config) {
std::ignore = dev;
std::ignore = config;
return 0;
}
int adev_set_device_connected_state_v7(struct audio_hw_device *dev,
struct audio_port_v7 *port,
bool connected) {
std::ignore = dev;
std::ignore = port;
std::ignore = connected;
return -ENOSYS;
}
int adev_set_audio_port_config(struct audio_hw_device *dev,
const struct audio_port_config *config)
{
std::ignore = dev;
std::ignore = config;
return 0;
}
static int adev_dump(const audio_hw_device_t *device, int fd)
{
dprintf(fd, " \n");
dprintf(fd, "PrimaryHal adev: \n");
int major = (device->common.version >> 8) & 0xff;
int minor = device->common.version & 0xff;
dprintf(fd, "Device API Version: %d.%d \n", major, minor);
#ifdef PAL_HIDL_ENABLED
dprintf(fd, "PAL HIDL enabled");
#else
dprintf(fd, "PAL HIDL disabled");
#endif
return 0;
}
static int adev_get_microphones(const struct audio_hw_device *dev __unused,
struct audio_microphone_characteristic_t *mic_array,
size_t *mic_count) {
return AudioDevice::get_microphones(mic_array, mic_count);
}
int AudioDevice::Init(hw_device_t **device, const hw_module_t *module) {
int ret = 0;
bool is_charging = false;
/*
* register HIDL services for PAL & AGM
* pal_init() depends on AGM, so need to initialize
* hidl interface before calling to pal_init()
*/
ret = AudioExtn::audio_extn_hidl_init();
if (ret) {
AHAL_ERR("audio_extn_hidl_init failed ret=(%d)", ret);
return ret;
}
ret = pal_init();
if (ret) {
AHAL_ERR("pal_init failed ret=(%d)", ret);
return -EINVAL;
}
ret = pal_register_global_callback(&adev_pal_global_callback, (uint64_t)this);
if (ret) {
AHAL_ERR("pal register callback failed ret=(%d)", ret);
}
adev_->device_.get()->common.tag = HARDWARE_DEVICE_TAG;
adev_->device_.get()->common.version = AUDIO_DEVICE_API_VERSION_3_2;
adev_->device_.get()->common.close = adev_close;
adev_->device_.get()->init_check = adev_init_check;
adev_->device_.get()->set_voice_volume = adev_set_voice_volume;
adev_->device_.get()->set_master_volume = adev_set_master_volume;
adev_->device_.get()->get_master_volume = adev_get_master_volume;
adev_->device_.get()->set_master_mute = adev_set_master_mute;
adev_->device_.get()->get_master_mute = adev_get_master_mute;
adev_->device_.get()->set_mode = adev_set_mode;
adev_->device_.get()->set_mic_mute = adev_set_mic_mute;
adev_->device_.get()->get_mic_mute = adev_get_mic_mute;
adev_->device_.get()->set_parameters = adev_set_parameters;
adev_->device_.get()->get_parameters = adev_get_parameters;
adev_->device_.get()->get_input_buffer_size = adev_get_input_buffer_size;
adev_->device_.get()->open_output_stream = adev_open_output_stream;
adev_->device_.get()->close_output_stream = adev_close_output_stream;
adev_->device_.get()->open_input_stream = adev_open_input_stream;
adev_->device_.get()->close_input_stream = adev_close_input_stream;
adev_->device_.get()->create_audio_patch = adev_create_audio_patch;
adev_->device_.get()->release_audio_patch = adev_release_audio_patch;
adev_->device_.get()->get_audio_port_v7 = get_audio_port_v7;
adev_->device_.get()->set_audio_port_config = adev_set_audio_port_config;
adev_->device_.get()->dump = adev_dump;
adev_->device_.get()->get_microphones = adev_get_microphones;
#ifdef USEHIDL7_1
adev_->device_.get()->set_device_connected_state_v7 = adev_set_device_connected_state_v7;
#endif
adev_->device_.get()->common.module = (struct hw_module_t *)module;
*device = &(adev_->device_.get()->common);
// visualizer lib
if (access(VISUALIZER_LIBRARY_PATH, R_OK) == 0) {
visualizer_lib_ = dlopen(VISUALIZER_LIBRARY_PATH, RTLD_NOW);
if (visualizer_lib_ == NULL) {
AHAL_ERR("DLOPEN failed for %s", VISUALIZER_LIBRARY_PATH);
} else {
AHAL_VERBOSE("DLOPEN successful for %s", VISUALIZER_LIBRARY_PATH);
fnp_visualizer_start_output_ =
(int (*)(audio_io_handle_t, pal_stream_handle_t*))dlsym(visualizer_lib_,
"visualizer_hal_start_output");
fnp_visualizer_stop_output_ =
(int (*)(audio_io_handle_t, pal_stream_handle_t*))dlsym(visualizer_lib_,
"visualizer_hal_stop_output");
}
}
// offload effect lib
if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) {
offload_effects_lib_ = dlopen(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH,
RTLD_NOW);
if (offload_effects_lib_ == NULL) {
AHAL_ERR("DLOPEN failed for %s",
OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH);
} else {
AHAL_VERBOSE("DLOPEN successful for %s",
OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH);
fnp_offload_effect_start_output_ =
(int (*)(audio_io_handle_t, pal_stream_handle_t*))dlsym(
offload_effects_lib_,
"offload_effects_bundle_hal_start_output");
fnp_offload_effect_stop_output_ =
(int (*)(audio_io_handle_t, pal_stream_handle_t*))dlsym(
offload_effects_lib_,
"offload_effects_bundle_hal_stop_output");
}
}
audio_extn_sound_trigger_init(adev_);
AudioExtn::hfp_feature_init(property_get_bool("vendor.audio.feature.hfp.enable", false));
AudioExtn::a2dp_source_feature_init(property_get_bool("vendor.audio.feature.a2dp_offload.enable", false));
AudioExtn::audio_extn_fm_init();
AudioExtn::audio_extn_kpi_optimize_feature_init(
property_get_bool("vendor.audio.feature.kpi_optimize.enable", false));
AudioExtn::battery_listener_feature_init(
property_get_bool("vendor.audio.feature.battery_listener.enable", false));
AudioExtn::battery_properties_listener_init(adev_on_battery_status_changed);
is_charging = AudioExtn::battery_properties_is_charging();
SetChargingMode(is_charging);
AudioExtn::audio_extn_perf_lock_init();
adev_->perf_lock_opts[0] = 0x40400000;
adev_->perf_lock_opts[1] = 0x1;
adev_->perf_lock_opts[2] = 0x40C00000;
adev_->perf_lock_opts[3] = 0x1;
adev_->perf_lock_opts_size = 4;
voice_ = VoiceInit();
mute_ = false;
current_rotation = PAL_SPEAKER_ROTATION_LR;
FillAndroidDeviceMap();
audio_extn_gef_init(adev_);
adev_init_ref_count += 1;
memset(&microphones, 0, sizeof(microphone_characteristics_t));
memset(&microphone_maps, 0, sizeof(PAL_MAX_INPUT_DEVICES*sizeof(snd_device_to_mic_map_t)));
if (!parse_xml())
mic_characteristics_available = true;
return ret;
}
std::shared_ptr<AudioVoice> AudioDevice::VoiceInit() {
std::shared_ptr<AudioVoice> voice (new AudioVoice());
return voice;
}
int AudioDevice::SetGEFParam(void *data, int length) {
return pal_set_param(PAL_PARAM_ID_UIEFFECT, data, length);
}
int AudioDevice::GetGEFParam(void *data, int *length) {
return pal_get_param(PAL_PARAM_ID_UIEFFECT, nullptr, (size_t *)length, data);
}
std::shared_ptr<StreamOutPrimary> AudioDevice::OutGetStream(audio_io_handle_t handle) {
std::shared_ptr<StreamOutPrimary> astream_out = NULL;
out_list_mutex.lock();
for (int i = 0; i < stream_out_list_.size(); i++) {
if (stream_out_list_[i]->handle_ == handle) {
AHAL_INFO("Found existing stream associated with iohandle %d",
handle);
astream_out = stream_out_list_[i];
break;
}
}
out_list_mutex.unlock();
return astream_out;
}
std::vector<std::shared_ptr<StreamOutPrimary>> AudioDevice::OutGetBLEStreamOutputs() {
std::shared_ptr<StreamOutPrimary> astream_out;
std::vector<std::shared_ptr<StreamOutPrimary>> astream_out_list;
audio_stream_out* stream_out = NULL;
/* In case of dev switch to BLE device, stream is associated with old
* device but not the BLE until dev switch process completed. Thus get the all
* active output streams.
*/
out_list_mutex.lock();
for (int i = 0; i < stream_out_list_.size(); i++) {
stream_out_list_[i]->GetStreamHandle(&stream_out);
if (stream_out_list_[i]->stream_.get() == (audio_stream_out*) stream_out) {
astream_out_list.push_back(stream_out_list_[i]);
}
}
out_list_mutex.unlock();
return astream_out_list;
}
std::shared_ptr<StreamOutPrimary> AudioDevice::OutGetStream(audio_stream_t* stream_out) {
std::shared_ptr<StreamOutPrimary> astream_out = NULL;
AHAL_VERBOSE("stream_out(%p)", stream_out);
out_list_mutex.lock();
for (int i = 0; i < stream_out_list_.size(); i++) {
if (stream_out_list_[i]->stream_.get() ==
(audio_stream_out*) stream_out) {
AHAL_VERBOSE("Found stream associated with stream_out");
astream_out = stream_out_list_[i];
AHAL_VERBOSE("astream_out(%p)", astream_out->stream_.get());
break;
}
}
out_list_mutex.unlock();
return astream_out;
}
std::shared_ptr<StreamInPrimary> AudioDevice::InGetStream (audio_io_handle_t handle) {
std::shared_ptr<StreamInPrimary> astream_in = NULL;
in_list_mutex.lock();
for (int i = 0; i < stream_in_list_.size(); i++) {
if (stream_in_list_[i]->handle_ == handle) {
AHAL_INFO("Found existing stream associated with iohandle %d",
handle);
astream_in = stream_in_list_[i];
break;
}
}
in_list_mutex.unlock();
return astream_in;
}
std::shared_ptr<StreamInPrimary> AudioDevice::InGetStream (audio_stream_t* stream_in) {
std::shared_ptr<StreamInPrimary> astream_in = NULL;
AHAL_VERBOSE("stream_in(%p)", stream_in);
in_list_mutex.lock();
for (int i = 0; i < stream_in_list_.size(); i++) {
if (stream_in_list_[i]->stream_.get() == (audio_stream_in*) stream_in) {
AHAL_VERBOSE("Found existing stream associated with astream_in");
astream_in = stream_in_list_[i];
AHAL_VERBOSE("astream_in(%p)", astream_in->stream_.get());
break;
}
}
in_list_mutex.unlock();
return astream_in;
}
std::vector<std::shared_ptr<StreamInPrimary>> AudioDevice::InGetBLEStreamInputs() {
std::shared_ptr<StreamInPrimary> astream_in;
std::vector<std::shared_ptr<StreamInPrimary>> astream_in_list;
audio_stream_in* stream_in = NULL;
/* In case of dev switch to BLE device, stream is associated with old
* device but not the BLE until dev switch process completed. Thus get the all
* active input streams.
*/
in_list_mutex.lock();
for (int i = 0; i < stream_in_list_.size(); i++) {
stream_in_list_[i]->GetStreamHandle(&stream_in);
if (stream_in_list_[i]->stream_.get() == (audio_stream_in*)stream_in) {
astream_in_list.push_back(stream_in_list_[i]);
}
}
in_list_mutex.unlock();
return astream_in_list;
}
int AudioDevice::SetMicMute(bool state) {
int ret = 0;
std::shared_ptr<StreamInPrimary> astream_in;
mute_ = state;
AHAL_DBG("%s: enter: %d", __func__, state);
if (AudioExtn::audio_extn_hfp_is_active(adev_))
ret = AudioExtn::audio_extn_hfp_set_mic_mute(state);
if (voice_)
ret = voice_->SetMicMute(state);
for (int i = 0; i < stream_in_list_.size(); i++) {
astream_in = stream_in_list_[i];
if (astream_in) {
AHAL_VERBOSE("Found existing stream associated with astream_in");
ret = astream_in->SetMicMute(state);
}
}
AHAL_DBG("exit: ret %d", ret);
return ret;
}
int AudioDevice::GetMicMute(bool *state) {
*state = mute_;
return 0;
}
int AudioDevice::SetMode(const audio_mode_t mode) {
int ret = 0;
AHAL_DBG("enter: mode: %d", mode);
ret = voice_->SetMode(mode);
AHAL_DBG("Exit ret: %d", ret);
return ret;
}
int AudioDevice::add_input_headset_if_usb_out_headset(int *device_count,
pal_device_id_t** pal_device_ids, bool conn_state)
{
bool is_usb_headset = false;
int count = *device_count;
pal_device_id_t* temp = NULL;
for (int i = 0; i < count; i++) {
if (*pal_device_ids[i] == PAL_DEVICE_OUT_USB_HEADSET) {
is_usb_headset = true;
usb_out_headset = true;
break;
}
}
if (is_usb_headset) {
temp = (pal_device_id_t *) realloc(*pal_device_ids,
(count + 1) * sizeof(pal_device_id_t));
if (!temp)
return -ENOMEM;
*pal_device_ids = temp;
temp[count] = PAL_DEVICE_IN_USB_HEADSET;
*device_count = count + 1;
if (conn_state)
usb_input_dev_enabled = true;
else {
usb_input_dev_enabled = false;
usb_out_headset = false;
}
}
return 0;
}
int AudioDevice::SetParameters(const char *kvpairs) {
int ret = 0, val = 0;
struct str_parms *parms = NULL;
char value[256];
int pal_device_count = 0;
pal_device_id_t* pal_device_ids = NULL;
char *test_r = NULL;
char *cfg_str = NULL;
bool changes_done = false;
audio_stream_in* stream_in = NULL;
audio_stream_out* stream_out = NULL;
std::shared_ptr<StreamInPrimary> astream_in = NULL;
std::shared_ptr<StreamOutPrimary> astream_out = NULL;
uint8_t channels = 0;
std::set<audio_devices_t> new_devices;
AHAL_DBG("enter: %s", kvpairs);
ret = voice_->VoiceSetParameters(kvpairs);
if (ret)
AHAL_ERR("Error in VoiceSetParameters %d", ret);
parms = str_parms_create_str(kvpairs);
if (!parms) {
AHAL_ERR("Error in str_parms_create_str");
ret = 0;
return ret;
}
AudioExtn::audio_extn_set_parameters(adev_, parms);
if ( (property_get_bool("vendor.audio.hdr.record.enable", false)) ||
(property_get_bool("vendor.audio.hdr.spf.record.enable", false))) {
changes_done = hdr_set_parameters(adev_, parms);
if (changes_done) {
for (int i = 0; i < stream_in_list_.size(); i++) {
stream_in_list_[i]->GetStreamHandle(&stream_in);
astream_in = adev_->InGetStream((audio_stream_t*)stream_in);
if ( (astream_in->source_ == AUDIO_SOURCE_UNPROCESSED) &&
(astream_in->config_.sample_rate == 48000) ) {
AHAL_DBG("Forcing PAL device switch for HDR");
channels =
audio_channel_count_from_in_mask(astream_in->config_.channel_mask);
if (channels == 4) {
if (adev_->hdr_record_enabled) {
new_devices = astream_in->mAndroidInDevices;
astream_in->RouteStream(new_devices, true);
}
}
break;
} else if (property_get_bool("vendor.audio.hdr.spf.record.enable", false)) {
new_devices = astream_in->mAndroidInDevices;
astream_in->RouteStream(new_devices, true);
}
}
}
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HAC, value, sizeof(value));
if (ret >= 0) {
adev_->hac_voip = false;
if (strcmp(value, AUDIO_PARAMETER_VALUE_HAC_ON) == 0) {
adev_->hac_voip = true;
for (int i = 0; i < stream_out_list_.size(); i++) {
stream_out_list_[i]->GetStreamHandle(&stream_out);
astream_out = adev_->OutGetStream((audio_stream_t*)stream_out);
if (astream_out->GetUseCase() == USECASE_AUDIO_PLAYBACK_VOIP) {
new_devices = astream_out->mAndroidOutDevices;
astream_out->RouteStream(new_devices, true);
break;
}
}
}
}
ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
if (ret >= 0) {
pal_param_screen_state_t param_screen_st;
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
param_screen_st.screen_state = true;
AHAL_DBG(" - screen = on");
ret = pal_set_param( PAL_PARAM_ID_SCREEN_STATE, (void*)&param_screen_st, sizeof(pal_param_screen_state_t));
} else {
AHAL_DBG(" - screen = off");
param_screen_st.screen_state = false;
ret = pal_set_param( PAL_PARAM_ID_SCREEN_STATE, (void*)&param_screen_st, sizeof(pal_param_screen_state_t));
}
}
ret = str_parms_get_str(parms, "UHQA", value, sizeof(value));
if (ret >= 0) {
pal_param_uhqa_t param_uhqa_flag;
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
param_uhqa_flag.uhqa_state = true;
AHAL_DBG(" - UHQA = on");
ret = pal_set_param(PAL_PARAM_ID_UHQA_FLAG, (void*)&param_uhqa_flag,
sizeof(pal_param_uhqa_t));
} else {
param_uhqa_flag.uhqa_state = false;
AHAL_DBG(" - UHQA = false");
ret = pal_set_param(PAL_PARAM_ID_UHQA_FLAG, (void*)&param_uhqa_flag,
sizeof(pal_param_uhqa_t));
}
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT,
value, sizeof(value));
if (ret >= 0) {
pal_param_device_connection_t param_device_connection;
val = atoi(value);
audio_devices_t device = (audio_devices_t)val;
if (audio_is_usb_out_device(device) || audio_is_usb_in_device(device)) {
ret = str_parms_get_str(parms, "card", value, sizeof(value));
if (ret >= 0) {
param_device_connection.device_config.usb_addr.card_id = atoi(value);
if ((usb_card_id_ == param_device_connection.device_config.usb_addr.card_id) &&
(audio_is_usb_in_device(device)) && (usb_input_dev_enabled == true)) {
AHAL_INFO("plugin card :%d device num=%d already added", usb_card_id_,
param_device_connection.device_config.usb_addr.device_num);
ret = 0;
goto exit;
}
usb_card_id_ = param_device_connection.device_config.usb_addr.card_id;
AHAL_INFO("plugin card=%d",
param_device_connection.device_config.usb_addr.card_id);
}
ret = str_parms_get_str(parms, "device", value, sizeof(value));
if (ret >= 0) {
param_device_connection.device_config.usb_addr.device_num = atoi(value);
usb_dev_num_ = param_device_connection.device_config.usb_addr.device_num;
AHAL_INFO("plugin device num=%d",
param_device_connection.device_config.usb_addr.device_num);
}
} else if (val == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
int controller = -1, stream = -1;
AudioExtn::get_controller_stream_from_params(parms, &controller, &stream);
param_device_connection.device_config.dp_config.controller = controller;
dp_controller = controller;
param_device_connection.device_config.dp_config.stream = stream;
dp_stream = stream;
AHAL_INFO("plugin device cont %d stream %d", controller, stream);
}
if (device) {
pal_device_ids = (pal_device_id_t *) calloc(1, sizeof(pal_device_id_t));
pal_device_count = GetPalDeviceIds({device}, pal_device_ids);
ret = add_input_headset_if_usb_out_headset(&pal_device_count, &pal_device_ids, true);
if (ret) {
if (pal_device_ids)
free(pal_device_ids);
AHAL_ERR("adding input headset failed, error:%d", ret);
goto exit;
}
for (int i = 0; i < pal_device_count; i++) {
param_device_connection.connection_state = true;
param_device_connection.id = pal_device_ids[i];
ret = pal_set_param(PAL_PARAM_ID_DEVICE_CONNECTION,
(void*)&param_device_connection,
sizeof(pal_param_device_connection_t));
if (ret!=0) {
AHAL_ERR("pal set param failed for device connection, pal_device_ids:%d",
pal_device_ids[i]);
}
}
AHAL_INFO("pal set param success for device connection");
/* check if capture profile is supported or not */
if (audio_is_usb_out_device(device) || audio_is_usb_in_device(device)) {
pal_param_device_capability_t *device_cap_query = (pal_param_device_capability_t *)
malloc(sizeof(pal_param_device_capability_t));
if (device_cap_query) {
dynamic_media_config_t dynamic_media_config;
size_t payload_size = 0;
device_cap_query->id = PAL_DEVICE_IN_USB_HEADSET;
device_cap_query->addr.card_id = usb_card_id_;
device_cap_query->addr.device_num = usb_dev_num_;
device_cap_query->config = &dynamic_media_config;
device_cap_query->is_playback = false;
ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,
(void **)&device_cap_query,
&payload_size, nullptr);
if ((dynamic_media_config.sample_rate[0] == 0 && dynamic_media_config.format[0] == 0 &&
dynamic_media_config.mask[0] == 0) || (dynamic_media_config.jack_status == false))
usb_input_dev_enabled = false;
else
usb_input_dev_enabled = true;
free(device_cap_query);
} else {
AHAL_ERR("Failed to allocate mem for device_cap_query");
}
}
if (pal_device_ids) {
free(pal_device_ids);
pal_device_ids = NULL;
}
}
}
/* Checking for Device rotation */
ret = str_parms_get_int(parms, "rotation", &val);
if (ret >= 0) {
int isRotationReq = 0;
pal_param_device_rotation_t param_device_rotation;
switch (val) {
case 270:
{
if (PAL_SPEAKER_ROTATION_LR == current_rotation) {
/* Device rotated from normal position to inverted landscape. */
current_rotation = PAL_SPEAKER_ROTATION_RL;
isRotationReq = 1;
param_device_rotation.rotation_type = PAL_SPEAKER_ROTATION_RL;
}
}
break;
case 0:
case 180:
case 90:
{
if (PAL_SPEAKER_ROTATION_RL == current_rotation) {
/* Phone was in inverted landspace and now is changed to portrait
* or inverted portrait. Notify PAL to swap the speaker.
*/
current_rotation = PAL_SPEAKER_ROTATION_LR;
isRotationReq = 1;
param_device_rotation.rotation_type = PAL_SPEAKER_ROTATION_LR;
}
}
break;
default:
AHAL_ERR("error unexpected rotation of %d", val);
isRotationReq = -EINVAL;
}
if (1 == isRotationReq) {
/* Swap the speakers */
AHAL_DBG("Swapping the speakers ");
ret = pal_set_param(PAL_PARAM_ID_DEVICE_ROTATION,
(void*)&param_device_rotation,
sizeof(pal_param_device_rotation_t));
AHAL_DBG("Speakers swapped ");
}
}
/* Speaker Protection: Factory Test Mode */
ret = str_parms_get_str(parms, "fbsp_cfg_wait_time", value, sizeof(value));
if (ret >= 0) {
str_parms_del(parms, "fbsp_cfg_wait_time");
cfg_str = strtok_r(value, ";", &test_r);
if (cfg_str != NULL) {
pal_spkr_prot_payload spPayload;
spPayload.operationMode = PAL_SP_MODE_FACTORY_TEST;
spPayload.spkrHeatupTime = atoi(cfg_str);
ret = str_parms_get_str(parms, "fbsp_cfg_ftm_time", value, sizeof(value));
if (ret >= 0) {
str_parms_del(parms, "fbsp_cfg_ftm_time");
cfg_str = strtok_r(value, ";", &test_r);
if (cfg_str != NULL) {
spPayload.operationModeRunTime = atoi(cfg_str);
ret = pal_set_param(PAL_PARAM_ID_SP_MODE, (void*)&spPayload,
sizeof(pal_spkr_prot_payload));
} else {
AHAL_ERR("Unable to parse the FTM time");
}
} else {
AHAL_ERR("Parameter missing for the FTM time");
}
} else {
AHAL_ERR("Unable to parse the FTM wait time");
}
}
/* Speaker Protection: V-validation mode */
ret = str_parms_get_str(parms, "fbsp_v_vali_wait_time", value, sizeof(value));
if (ret >= 0) {
str_parms_del(parms, "fbsp_v_vali_wait_time");
cfg_str = strtok_r(value, ";", &test_r);
if (cfg_str != NULL) {
pal_spkr_prot_payload spPayload;
spPayload.operationMode = PAL_SP_MODE_V_VALIDATION;
spPayload.spkrHeatupTime = atoi(cfg_str);
ret = str_parms_get_str(parms, "fbsp_v_vali_vali_time", value, sizeof(value));
if (ret >= 0) {
str_parms_del(parms, "fbsp_v_vali_vali_time");
cfg_str = strtok_r(value, ";", &test_r);
if (cfg_str != NULL) {
spPayload.operationModeRunTime = atoi(cfg_str);
ret = pal_set_param(PAL_PARAM_ID_SP_MODE, (void*)&spPayload,
sizeof(pal_spkr_prot_payload));
} else {
AHAL_ERR("Unable to parse the V_Validation time");
}
} else {
AHAL_ERR("Parameter missing for the V-Validation time");
}
} else {
AHAL_ERR("Unable to parse the V-Validation wait time");
}
}
/* Speaker Protection: Dynamic calibration mode */
ret = str_parms_get_str(parms, "trigger_spkr_cal", value, sizeof(value));
if (ret >= 0) {
if ((strcmp(value, "true") == 0) || (strcmp(value, "yes") == 0)) {
pal_spkr_prot_payload spPayload;
spPayload.operationMode = PAL_SP_MODE_DYNAMIC_CAL;
ret = pal_set_param(PAL_PARAM_ID_SP_MODE, (void*)&spPayload,
sizeof(pal_spkr_prot_payload));
}
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT,
value, sizeof(value));
if (ret >= 0) {
pal_param_device_connection_t param_device_connection;
val = atoi(value);
audio_devices_t device = (audio_devices_t)val;
if (audio_is_usb_out_device(device) || audio_is_usb_in_device(device)) {
ret = str_parms_get_str(parms, "card", value, sizeof(value));
if (ret >= 0)
param_device_connection.device_config.usb_addr.card_id = atoi(value);
ret = str_parms_get_str(parms, "device", value, sizeof(value));
if (ret >= 0)
param_device_connection.device_config.usb_addr.device_num = atoi(value);
if ((usb_card_id_ == param_device_connection.device_config.usb_addr.card_id) &&
(audio_is_usb_in_device(device)) && (usb_input_dev_enabled == true)) {
usb_input_dev_enabled = false;
usb_out_headset = false;
AHAL_DBG("usb_input_dev_enabled flag is cleared.");
}
} else if (val == AUDIO_DEVICE_OUT_AUX_DIGITAL) {
int controller = -1, stream = -1;
AudioExtn::get_controller_stream_from_params(parms, &controller, &stream);
param_device_connection.device_config.dp_config.controller = controller;
param_device_connection.device_config.dp_config.stream = stream;
dp_stream = stream;
AHAL_INFO("plugin device cont %d stream %d", controller, stream);
}
if (device) {
pal_device_ids = (pal_device_id_t *) calloc(1, sizeof(pal_device_id_t));
pal_device_count = GetPalDeviceIds({device}, pal_device_ids);
ret = add_input_headset_if_usb_out_headset(&pal_device_count, &pal_device_ids, false);
if (ret) {
if (pal_device_ids)
free(pal_device_ids);
AHAL_ERR("adding input headset failed, error:%d", ret);
goto exit;
}
for (int i = 0; i < pal_device_count; i++) {
param_device_connection.connection_state = false;
param_device_connection.id = pal_device_ids[i];
ret = pal_set_param(PAL_PARAM_ID_DEVICE_CONNECTION,
(void*)&param_device_connection,
sizeof(pal_param_device_connection_t));
if (ret!=0) {
AHAL_ERR("pal set param failed for device disconnect");
}
AHAL_INFO("pal set param sucess for device disconnect");
}
}
}
if (pal_device_ids) {
free(pal_device_ids);
pal_device_ids = NULL;
}
/* A2DP parameters */
ret = str_parms_get_str(parms, AUDIO_PARAMETER_RECONFIG_A2DP, value, sizeof(value));
if (ret >= 0) {
pal_param_bta2dp_t param_bt_a2dp;
param_bt_a2dp.reconfig = true;
AHAL_INFO("BT A2DP Reconfig command received");
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_RECONFIG, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
}
ret = str_parms_get_str(parms, "A2dpSuspended" , value, sizeof(value));
if (ret >= 0) {
audio_mode_t mode = AUDIO_MODE_NORMAL;
pal_param_bta2dp_t param_bt_a2dp;
if (strncmp(value, "true", 4) == 0)
param_bt_a2dp.a2dp_suspended = true;
else
param_bt_a2dp.a2dp_suspended = false;
param_bt_a2dp.dev_id = PAL_DEVICE_OUT_BLUETOOTH_A2DP;
if (voice_)
voice_->get_voice_call_state(&mode);
param_bt_a2dp.is_in_call = (mode != AUDIO_MODE_NORMAL);
AHAL_INFO("BT A2DP Suspended = %s, command received", value);
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_SUSPENDED, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
}
ret = str_parms_get_str(parms, "TwsChannelConfig", value, sizeof(value));
if (ret >= 0) {
pal_param_bta2dp_t param_bt_a2dp;
AHAL_INFO("Setting tws channel mode to %s", value);
if (!(strncmp(value, "mono", strlen(value))))
param_bt_a2dp.is_tws_mono_mode_on = true;
else if (!(strncmp(value,"dual-mono",strlen(value))))
param_bt_a2dp.is_tws_mono_mode_on = false;
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_TWS_CONFIG, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
}
ret = str_parms_get_str(parms, "LEAMono", value, sizeof(value));
if (ret >= 0) {
pal_param_bta2dp_t param_bt_a2dp;
AHAL_INFO("Setting LC3 channel mode to %s", value);
if (!(strncmp(value, "true", strlen(value))))
param_bt_a2dp.is_lc3_mono_mode_on = true;
else
param_bt_a2dp.is_lc3_mono_mode_on = false;
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_LC3_CONFIG, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
}
/* SCO parameters */
ret = str_parms_get_str(parms, "BT_SCO", value, sizeof(value));
if (ret >= 0) {
pal_param_btsco_t param_bt_sco;
memset(&param_bt_sco, 0, sizeof(pal_param_btsco_t));
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
param_bt_sco.bt_sco_on = true;
} else {
param_bt_sco.bt_sco_on = false;
}
AHAL_INFO("BTSCO on = %d", param_bt_sco.bt_sco_on);
ret = pal_set_param(PAL_PARAM_ID_BT_SCO, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value));
if (ret >= 0) {
pal_param_btsco_t param_bt_sco = {};
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
param_bt_sco.bt_wb_speech_enabled = true;
else
param_bt_sco.bt_wb_speech_enabled = false;
AHAL_INFO("BTSCO WB mode = %d", param_bt_sco.bt_wb_speech_enabled);
ret = pal_set_param(PAL_PARAM_ID_BT_SCO_WB, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
}
ret = str_parms_get_str(parms, "bt_swb", value, sizeof(value));
if (ret >= 0) {
pal_param_btsco_t param_bt_sco = {};
val = atoi(value);
param_bt_sco.bt_swb_speech_mode = val;
AHAL_INFO("BTSCO SWB mode = 0x%x", val);
ret = pal_set_param(PAL_PARAM_ID_BT_SCO_SWB, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
}
ret = str_parms_get_str(parms, "bt_ble", value, sizeof(value));
if (ret >= 0) {
pal_param_btsco_t param_bt_sco = {};
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
bt_lc3_speech_enabled = true;
// turn off wideband, super-wideband
param_bt_sco.bt_wb_speech_enabled = false;
ret = pal_set_param(PAL_PARAM_ID_BT_SCO_WB, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
param_bt_sco.bt_swb_speech_mode = 0xFFFF;
ret = pal_set_param(PAL_PARAM_ID_BT_SCO_SWB, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
} else {
bt_lc3_speech_enabled = false;
param_bt_sco.bt_lc3_speech_enabled = false;
ret = pal_set_param(PAL_PARAM_ID_BT_SCO_LC3, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
// clear btsco_lc3_cfg to avoid stale and partial cfg being used in next round
memset(&btsco_lc3_cfg, 0, sizeof(btsco_lc3_cfg_t));
}
AHAL_INFO("BTSCO LC3 mode = %d", bt_lc3_speech_enabled);
}
ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value));
if (ret >= 0) {
pal_param_btsco_t param_bt_sco = {};
if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
AHAL_INFO("BTSCO NREC mode = ON");
param_bt_sco.bt_sco_nrec = true;
} else {
AHAL_INFO("BTSCO NREC mode = OFF");
param_bt_sco.bt_sco_nrec = false;
}
ret = pal_set_param(PAL_PARAM_ID_BT_SCO_NREC, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
}
for (auto& key : lc3_reserved_params) {
ret = str_parms_get_str(parms, key, value, sizeof(value));
if (ret < 0)
continue;
if (!strcmp(key, "Codec") && (!strcmp(value, "LC3"))) {
btsco_lc3_cfg.fields_map |= LC3_CODEC_BIT;
} else if (!strcmp(key, "StreamMap")) {
strlcpy(btsco_lc3_cfg.streamMap, value, PAL_LC3_MAX_STRING_LEN);
btsco_lc3_cfg.fields_map |= LC3_STREAM_MAP_BIT;
} else if (!strcmp(key, "FrameDuration")) {
btsco_lc3_cfg.frame_duration = atoi(value);
btsco_lc3_cfg.fields_map |= LC3_FRAME_DURATION_BIT;
} else if (!strcmp(key, "Blocks_forSDU")) {
btsco_lc3_cfg.num_blocks = atoi(value);
btsco_lc3_cfg.fields_map |= LC3_BLOCKS_FORSDU_BIT;
} else if (!strcmp(key, "rxconfig_index")) {
btsco_lc3_cfg.rxconfig_index = atoi(value);
btsco_lc3_cfg.fields_map |= LC3_RXCFG_IDX_BIT;
} else if (!strcmp(key, "txconfig_index")) {
btsco_lc3_cfg.txconfig_index = atoi(value);
btsco_lc3_cfg.fields_map |= LC3_TXCFG_IDX_BIT;
} else if (!strcmp(key, "version")) {
btsco_lc3_cfg.api_version = atoi(value);
btsco_lc3_cfg.fields_map |= LC3_VERSION_BIT;
} else if (!strcmp(key, "vendor")) {
strlcpy(btsco_lc3_cfg.vendor, value, PAL_LC3_MAX_STRING_LEN);
btsco_lc3_cfg.fields_map |= LC3_VENDOR_BIT;
}
}
if (((btsco_lc3_cfg.fields_map & LC3_BIT_MASK) == LC3_BIT_VALID) &&
(bt_lc3_speech_enabled == true)) {
pal_param_btsco_t param_bt_sco = {};
param_bt_sco.bt_lc3_speech_enabled = bt_lc3_speech_enabled;
param_bt_sco.lc3_cfg.frame_duration = btsco_lc3_cfg.frame_duration;
param_bt_sco.lc3_cfg.num_blocks = btsco_lc3_cfg.num_blocks;
param_bt_sco.lc3_cfg.rxconfig_index = btsco_lc3_cfg.rxconfig_index;
param_bt_sco.lc3_cfg.txconfig_index = btsco_lc3_cfg.txconfig_index;
param_bt_sco.lc3_cfg.api_version = btsco_lc3_cfg.api_version;
strlcpy(param_bt_sco.lc3_cfg.streamMap, btsco_lc3_cfg.streamMap, PAL_LC3_MAX_STRING_LEN);
strlcpy(param_bt_sco.lc3_cfg.vendor, btsco_lc3_cfg.vendor, PAL_LC3_MAX_STRING_LEN);
AHAL_INFO("BTSCO LC3 mode = on, sending..");
ret = pal_set_param(PAL_PARAM_ID_BT_SCO_LC3, (void *)&param_bt_sco,
sizeof(pal_param_btsco_t));
memset(&btsco_lc3_cfg, 0, sizeof(btsco_lc3_cfg_t));
}
ret = str_parms_get_str(parms, "wfd_channel_cap", value, sizeof(value));
if (ret >= 0) {
pal_param_proxy_channel_config_t param_out_proxy;
val = atoi(value);
param_out_proxy.num_proxy_channels = val;
AHAL_INFO("Proxy channels: %d", val);
ret = pal_set_param(PAL_PARAM_ID_PROXY_CHANNEL_CONFIG, (void *)&param_out_proxy,
sizeof(pal_param_proxy_channel_config_t));
}
ret = str_parms_get_str(parms, "haptics_volume", value, sizeof(value));
if (ret >= 0) {
struct pal_volume_data* volume = NULL;
volume = (struct pal_volume_data *)malloc(sizeof(struct pal_volume_data)
+sizeof(struct pal_channel_vol_kv));
if (volume) {
volume->no_of_volpair = 1;
//For haptics, there is only one channel (FL).
volume->volume_pair[0].channel_mask = 0x01;
volume->volume_pair[0].vol = atof(value);
AHAL_INFO("Setting Haptics Volume as %f", volume->volume_pair[0].vol);
ret = pal_set_param(PAL_PARAM_ID_HAPTICS_VOLUME, (void *)volume,
sizeof(pal_volume_data));
free(volume);
}
}
ret = str_parms_get_str(parms, "haptics_intensity", value, sizeof(value));
if (ret >=0) {
pal_param_haptics_intensity_t hIntensity;
val = atoi(value);
hIntensity.intensity = val;
AHAL_INFO("Setting Haptics Volume as %d", hIntensity.intensity);
ret = pal_set_param(PAL_PARAM_ID_HAPTICS_INTENSITY, (void *)&hIntensity,
sizeof(pal_param_haptics_intensity_t));
}
ret = str_parms_get_str(parms, "A2dpCaptureSuspend", value, sizeof(value));
if (ret >= 0) {
audio_mode_t mode = AUDIO_MODE_NORMAL;
pal_param_bta2dp_t param_bt_a2dp;
if (strncmp(value, "true", 4) == 0)
param_bt_a2dp.a2dp_capture_suspended = true;
else
param_bt_a2dp.a2dp_capture_suspended = false;
param_bt_a2dp.dev_id = PAL_DEVICE_IN_BLUETOOTH_A2DP;
if (voice_)
voice_->get_voice_call_state(&mode);
param_bt_a2dp.is_in_call = (mode != AUDIO_MODE_NORMAL);
AHAL_INFO("BT A2DP Capture Suspended = %s, command received", value);
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_CAPTURE_SUSPENDED, (void*)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
}
exit:
if (parms)
str_parms_destroy(parms);
AHAL_DBG("exit: %s", kvpairs);
return 0;
}
int AudioDevice::SetVoiceVolume(float volume) {
return voice_->SetVoiceVolume(volume);
}
char* AudioDevice::GetParameters(const char *keys) {
int32_t ret;
int32_t val = 0;
char *str;
char value[256]={0};
size_t size = 0;
struct str_parms *reply = str_parms_create();
struct str_parms *query = str_parms_create_str(keys);
if (!query || !reply) {
if (reply) {
str_parms_destroy(reply);
}
if (query) {
str_parms_destroy(query);
}
AHAL_ERR("failed to create query or reply");
return NULL;
}
AHAL_VERBOSE("enter");
ret = str_parms_get_str(query, AUDIO_PARAMETER_A2DP_RECONFIG_SUPPORTED,
value, sizeof(value));
if (ret >= 0) {
pal_param_bta2dp_t *param_bt_a2dp_ptr, param_bt_a2dp;
param_bt_a2dp_ptr = &param_bt_a2dp;
param_bt_a2dp_ptr->dev_id = PAL_DEVICE_OUT_BLUETOOTH_A2DP;
ret = pal_get_param(PAL_PARAM_ID_BT_A2DP_RECONFIG_SUPPORTED,
(void **)&param_bt_a2dp_ptr, &size, nullptr);
if (!ret) {
if (size < sizeof(pal_param_bta2dp_t)) {
AHAL_ERR("size returned is smaller for BT_A2DP_RECONFIG_SUPPORTED");
goto exit;
}
val = param_bt_a2dp_ptr->reconfig_supported;
str_parms_add_int(reply, AUDIO_PARAMETER_A2DP_RECONFIG_SUPPORTED, val);
AHAL_VERBOSE("isReconfigA2dpSupported = %d", val);
}
}
ret = str_parms_get_str(query, "A2dpSuspended", value, sizeof(value));
if (ret >= 0) {
pal_param_bta2dp_t *param_bt_a2dp_ptr, param_bt_a2dp;
param_bt_a2dp_ptr = &param_bt_a2dp;
param_bt_a2dp_ptr->dev_id = PAL_DEVICE_OUT_BLUETOOTH_A2DP;
ret = pal_get_param(PAL_PARAM_ID_BT_A2DP_SUSPENDED,
(void **)&param_bt_a2dp_ptr, &size, nullptr);
if (!ret) {
if (size < sizeof(pal_param_bta2dp_t)) {
AHAL_ERR("size returned is smaller for BT_A2DP_SUSPENDED");
goto exit;
}
val = param_bt_a2dp_ptr->a2dp_suspended;
str_parms_add_int(reply, "A2dpSuspended", val);
AHAL_VERBOSE("A2dpSuspended = %d", val);
}
}
ret = str_parms_get_str(query, "get_ftm_param", value, sizeof(value));
if (ret >= 0) {
char ftm_value[255];
ret = pal_get_param(PAL_PARAM_ID_SP_MODE, (void **)&ftm_value, &size, nullptr);
if (!ret) {
if (size > 0) {
str_parms_add_str(reply, "get_ftm_param", ftm_value);
}
else
AHAL_ERR("Error happened for getting FTM param");
}
}
ret = str_parms_get_str(query, "get_spkr_cal", value, sizeof(value));
if (ret >= 0) {
char cal_value[255];
ret = pal_get_param(PAL_PARAM_ID_SP_GET_CAL, (void **)&cal_value, &size, nullptr);
if (!ret) {
if (size > 0) {
str_parms_add_str(reply, "get_spkr_cal", cal_value);
}
else
AHAL_ERR("Error happened for getting Cal param");
}
}
AudioExtn::audio_extn_get_parameters(adev_, query, reply);
if (voice_)
voice_->VoiceGetParameters(query, reply);
if (property_get_bool("vendor.audio.hdr.record.enable", false))
hdr_get_parameters(adev_, query, reply);
exit:
str = str_parms_to_str(reply);
str_parms_destroy(query);
str_parms_destroy(reply);
if (str)
AHAL_VERBOSE("exit: returns - %s", str);
return str;
}
void AudioDevice::FillAndroidDeviceMap() {
android_device_map_.clear();
/* go through all devices and pushback */
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_NONE, PAL_DEVICE_NONE));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_EARPIECE, PAL_DEVICE_OUT_HANDSET));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_SPEAKER, PAL_DEVICE_OUT_SPEAKER));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_WIRED_HEADSET, PAL_DEVICE_OUT_WIRED_HEADSET));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_WIRED_HEADPHONE, PAL_DEVICE_OUT_WIRED_HEADPHONE));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLUETOOTH_SCO, PAL_DEVICE_OUT_BLUETOOTH_SCO));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, PAL_DEVICE_OUT_BLUETOOTH_SCO));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, PAL_DEVICE_OUT_BLUETOOTH_SCO));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, PAL_DEVICE_OUT_BLUETOOTH_A2DP));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLE_HEADSET, PAL_DEVICE_OUT_BLUETOOTH_BLE));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLE_SPEAKER, PAL_DEVICE_OUT_BLUETOOTH_BLE));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, PAL_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, PAL_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_AUX_DIGITAL, PAL_DEVICE_OUT_AUX_DIGITAL));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_HDMI, PAL_DEVICE_OUT_HDMI));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, PAL_DEVICE_OUT_ANLG_DOCK_HEADSET));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, PAL_DEVICE_OUT_DGTL_DOCK_HEADSET));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_USB_ACCESSORY, PAL_DEVICE_OUT_USB_ACCESSORY));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_USB_DEVICE, PAL_DEVICE_OUT_USB_DEVICE));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, PAL_DEVICE_OUT_REMOTE_SUBMIX));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_TELEPHONY_TX, PAL_DEVICE_NONE));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_LINE, PAL_DEVICE_OUT_WIRED_HEADPHONE));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_HDMI_ARC, PAL_DEVICE_OUT_HDMI_ARC));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_SPDIF, PAL_DEVICE_OUT_SPDIF));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_FM, PAL_DEVICE_OUT_FM));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_AUX_LINE, PAL_DEVICE_OUT_AUX_LINE));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_SPEAKER_SAFE, PAL_DEVICE_OUT_SPEAKER));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_IP, PAL_DEVICE_OUT_IP));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BUS, PAL_DEVICE_OUT_BUS));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_PROXY, PAL_DEVICE_OUT_PROXY));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_USB_HEADSET, PAL_DEVICE_OUT_USB_HEADSET));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_DEFAULT, PAL_DEVICE_OUT_SPEAKER));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_HEARING_AID, PAL_DEVICE_OUT_HEARING_AID));
#ifdef USEHIDL7_1
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_OUT_BLE_BROADCAST, PAL_DEVICE_OUT_BLUETOOTH_BLE_BROADCAST));
#endif
/* go through all in devices and pushback */
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_BUILTIN_MIC, PAL_DEVICE_IN_HANDSET_MIC));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_BACK_MIC, PAL_DEVICE_IN_SPEAKER_MIC));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_COMMUNICATION, PAL_DEVICE_IN_COMMUNICATION));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_AMBIENT, PAL_DEVICE_IN_AMBIENT);
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, PAL_DEVICE_IN_BLUETOOTH_SCO_HEADSET));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_WIRED_HEADSET, PAL_DEVICE_IN_WIRED_HEADSET));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_AUX_DIGITAL, PAL_DEVICE_IN_AUX_DIGITAL));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_HDMI, PAL_DEVICE_IN_HDMI));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_VOICE_CALL, PAL_DEVICE_IN_HANDSET_MIC));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_TELEPHONY_RX, PAL_DEVICE_IN_TELEPHONY_RX));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_REMOTE_SUBMIX, PAL_DEVICE_IN_REMOTE_SUBMIX);
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, PAL_DEVICE_IN_ANLG_DOCK_HEADSET);
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, PAL_DEVICE_IN_DGTL_DOCK_HEADSET);
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_USB_ACCESSORY, PAL_DEVICE_IN_USB_ACCESSORY));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_USB_DEVICE, PAL_DEVICE_IN_USB_HEADSET));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_FM_TUNER, PAL_DEVICE_IN_FM_TUNER));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_TV_TUNER, PAL_DEVICE_IN_TV_TUNER);
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_LINE, PAL_DEVICE_IN_LINE));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_SPDIF, PAL_DEVICE_IN_SPDIF));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_BLUETOOTH_A2DP, PAL_DEVICE_IN_BLUETOOTH_A2DP));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_BLE_HEADSET, PAL_DEVICE_IN_BLUETOOTH_BLE));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_LOOPBACK, PAL_DEVICE_IN_LOOPBACK);
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_IP, PAL_DEVICE_IN_IP);
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_BUS, PAL_DEVICE_IN_BUS);
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_PROXY, PAL_DEVICE_IN_PROXY));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_USB_HEADSET, PAL_DEVICE_IN_USB_HEADSET));
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_HDMI_ARC, PAL_DEVICE_IN_HDMI_ARC);
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_BLUETOOTH_BLE, PAL_DEVICE_IN_BLUETOOTH_BLE);
//android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_DEFAULT, PAL_DEVICE_IN_DEFAULT));
android_device_map_.insert(std::make_pair(AUDIO_DEVICE_IN_ECHO_REFERENCE, PAL_DEVICE_IN_ECHO_REF));
}
int AudioDevice::GetPalDeviceIds(const std::set<audio_devices_t>& hal_device_ids,
pal_device_id_t* pal_device_id) {
int device_count = 0;
if (!pal_device_id) {
AHAL_ERR("invalid pal device id");
goto error;
}
// pal device ids is supposed to have to space for the new ids
AHAL_DBG("haldeviceIds: %zu", hal_device_ids.size());
for(auto hal_device_id : hal_device_ids) {
auto it = android_device_map_.find(hal_device_id);
if (it != android_device_map_.end() &&
audio_is_input_device(it->first) == audio_is_input_device(hal_device_id)) {
AHAL_DBG("Found haldeviceId: %x and PAL Device ID %d",
it->first, it->second);
if (it->second == PAL_DEVICE_OUT_AUX_DIGITAL ||
it->second == PAL_DEVICE_OUT_HDMI) {
AHAL_DBG("dp_controller: %d dp_stream: %d",
dp_controller, dp_stream);
if (dp_controller * MAX_STREAMS_PER_CONTROLLER + dp_stream) {
pal_device_id[device_count] = PAL_DEVICE_OUT_AUX_DIGITAL_1;
} else {
pal_device_id[device_count] = it->second;
}
} else {
pal_device_id[device_count] = it->second;
}
}
++device_count;
}
error:
AHAL_DBG("devices allocated %zu, pal device ids before returning %d",
hal_device_ids.size(), device_count);
return device_count;
}
void AudioDevice::SetChargingMode(bool is_charging) {
int32_t result = 0;
pal_param_charging_state_t charge_state;
AHAL_DBG("enter, is_charging %d", is_charging);
is_charging_ = is_charging;
charge_state.charging_state = is_charging;
result = pal_set_param(PAL_PARAM_ID_CHARGING_STATE, (void*)&charge_state,
sizeof(pal_param_charging_state_t));
if (result)
AHAL_DBG("error while handling charging event result(%d)\n",
result);
AHAL_DBG("exit");
}
hw_device_t* AudioDevice::GetAudioDeviceCommon()
{
return &(adev_->device_.get()->common);
}
static int adev_open(const hw_module_t *module, const char *name __unused,
hw_device_t **device) {
int32_t ret = 0;
AHAL_DBG("Enter");
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
if (!adevice) {
AHAL_ERR("error, GetInstance failed");
}
adevice->adev_init_mutex.lock();
if (adevice->adev_init_ref_count != 0) {
*device = adevice->GetAudioDeviceCommon();
adevice->adev_init_ref_count++;
adevice->adev_init_mutex.unlock();
AHAL_DBG("returning existing instance of adev, exiting");
goto exit;
}
ret = adevice->Init(device, module);
if (ret || (*device == NULL)) {
AHAL_ERR("error, audio device init failed, ret(%d),*device(%p)",
ret, *device);
}
adevice->adev_init_mutex.unlock();
exit:
AHAL_DBG("Exit, status %d", ret);
return ret;
}
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_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "QTI Audio HAL",
.author = "The Linux Foundation",
.methods = &hal_module_methods,
},
};
audio_patch_handle_t AudioPatch::generate_patch_handle_l() {
static audio_patch_handle_t handles = AUDIO_PATCH_HANDLE_NONE;
if (++handles < 0)
handles = AUDIO_PATCH_HANDLE_NONE + 1;
return handles;
}
AudioPatch::AudioPatch(PatchType patch_type,
const std::vector<struct audio_port_config>& sources,
const std::vector<struct audio_port_config>& sinks):
type(patch_type), sources(sources), sinks(sinks) {
static std::mutex patch_lock;
std::lock_guard<std::mutex> lock(patch_lock);
handle = AudioPatch::generate_patch_handle_l();
}
bool AudioDevice::find_enum_by_string(const struct audio_string_to_enum * table, const char * name,
int32_t len, unsigned int *value)
{
if (table == NULL) {
ALOGE("%s: table is NULL", __func__);
return false;
}
if (name == NULL) {
ALOGE("null key");
return false;
}
for (int i = 0; i < len; i++) {
if (!strcmp(table[i].name, name)) {
*value = table[i].value;
return true;
}
}
return false;
}
bool AudioDevice::set_microphone_characteristic(struct audio_microphone_characteristic_t *mic)
{
if (microphones.declared_mic_count >= AUDIO_MICROPHONE_MAX_COUNT) {
AHAL_ERR("mic number is more than maximum number");
return false;
}
for (size_t ch = 0; ch < AUDIO_CHANNEL_COUNT_MAX; ch++) {
mic->channel_mapping[ch] = AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED;
}
microphones.microphone[microphones.declared_mic_count++] = *mic;
return true;
}
int32_t AudioDevice::get_microphones(struct audio_microphone_characteristic_t *mic_array, size_t *mic_count)
{
if (!mic_characteristics_available)
return -EIO;
if (mic_count == NULL) {
AHAL_ERR("Invalid mic_count!!!");
return -EINVAL;
}
if (mic_array == NULL) {
AHAL_ERR("Invalid mic_array!!!");
return -EINVAL;
}
if (*mic_count == 0) {
AHAL_INFO("mic_count is ZERO");
return 0;
}
size_t max_mic_count = *mic_count;
size_t actual_mic_count = 0;
for (size_t i = 0; i < max_mic_count && i < microphones.declared_mic_count; i++) {
mic_array[i] = microphones.microphone[i];
actual_mic_count++;
}
*mic_count = actual_mic_count;
return 0;
}
void AudioDevice::process_microphone_characteristics(const XML_Char **attr)
{
struct audio_microphone_characteristic_t microphone = {};
uint32_t curIdx = 0;
uint32_t valid_mask;
if (strcmp(attr[curIdx++], "valid_mask")) {
AHAL_ERR("valid_mask not found");
goto done;
}
valid_mask = atoi(attr[curIdx++]);
if (strcmp(attr[curIdx++], "device_id")) {
AHAL_ERR("device_id not found");
goto done;
}
if (strlen(attr[curIdx]) > AUDIO_MICROPHONE_ID_MAX_LEN) {
AHAL_ERR("device_id is too long : %s ", attr[curIdx]);
goto done;
}
strlcpy(microphone.device_id, attr[curIdx++], sizeof(microphone.device_id));
if (strcmp(attr[curIdx++], "type")) {
AHAL_ERR("device type not found");
goto done;
}
if (!find_enum_by_string(device_in_types, (char*)attr[curIdx++],
ARRAY_SIZE(device_in_types), (unsigned int *)(&microphone.device))) {
AHAL_ERR("device type: %s not found", attr[--curIdx]);
goto done;
}
if (strcmp(attr[curIdx++], "address")) {
AHAL_ERR("address not found");
goto done;
}
if (strlen(attr[curIdx]) > AUDIO_DEVICE_MAX_ADDRESS_LEN) {
AHAL_ERR("address %s is too long", attr[curIdx]);
goto done;
}
strlcpy(microphone.address, attr[curIdx++], sizeof(microphone.address));
if (strlen(microphone.address) == 0) {
// If the address is empty, populate the address according to device type.
if (microphone.device == AUDIO_DEVICE_IN_BUILTIN_MIC) {
strlcpy(microphone.address, AUDIO_BOTTOM_MICROPHONE_ADDRESS, sizeof(microphone.address));
} else if (microphone.device == AUDIO_DEVICE_IN_BACK_MIC) {
strlcpy(microphone.address, AUDIO_BACK_MICROPHONE_ADDRESS, sizeof(microphone.address));
}
}
if (strcmp(attr[curIdx++], "location")) {
AHAL_ERR("location not found");
goto done;
}
if (!find_enum_by_string(mic_locations, (char*)attr[curIdx++],
AUDIO_MICROPHONE_LOCATION_CNT, (unsigned int *)(&microphone.location))) {
AHAL_ERR("location: %s not found", attr[--curIdx]);
goto done;
}
if (strcmp(attr[curIdx++], "group")) {
AHAL_ERR("group not found");
goto done;
}
microphone.group = atoi(attr[curIdx++]);
if (strcmp(attr[curIdx++], "index_in_the_group")) {
AHAL_ERR("index_in_the_group not found");
goto done;
}
microphone.index_in_the_group = atoi(attr[curIdx++]);
if (strcmp(attr[curIdx++], "directionality")) {
AHAL_ERR("directionality not found");
goto done;
}
if (!find_enum_by_string(mic_directionalities, (char*)attr[curIdx++],
AUDIO_MICROPHONE_DIRECTIONALITY_CNT, (unsigned int *)(&microphone.directionality))) {
AHAL_ERR("directionality : %s not found", attr[--curIdx]);
goto done;
}
if (strcmp(attr[curIdx++], "num_frequency_responses")) {
AHAL_ERR("num_frequency_responses not found");
goto done;
}
microphone.num_frequency_responses = atoi(attr[curIdx++]);
if (microphone.num_frequency_responses > AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
AHAL_ERR("num_frequency_responses is too large");
goto done;
}
if (microphone.num_frequency_responses > 0) {
if (strcmp(attr[curIdx++], "frequencies")) {
AHAL_ERR("frequencies not found");
goto done;
}
char *context = NULL;
char *token = strtok_r((char *)attr[curIdx++], " ", &context);
uint32_t num_frequencies = 0;
while (token) {
microphone.frequency_responses[0][num_frequencies++] = atof(token);
if (num_frequencies >= AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
break;
}
token = strtok_r(NULL, " ", &context);
}
if (strcmp(attr[curIdx++], "responses")) {
AHAL_ERR("responses not found");
goto done;
}
token = strtok_r((char *)attr[curIdx++], " ", &context);
uint32_t num_responses = 0;
while (token) {
microphone.frequency_responses[1][num_responses++] = atof(token);
if (num_responses >= AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
break;
}
token = strtok_r(NULL, " ", &context);
}
if (num_frequencies != num_responses
|| num_frequencies != microphone.num_frequency_responses) {
AHAL_ERR("num of frequency and response not match: %u, %u, %u",
num_frequencies, num_responses, microphone.num_frequency_responses);
goto done;
}
}
if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_SENSITIVITY) {
if (strcmp(attr[curIdx++], "sensitivity")) {
AHAL_ERR("sensitivity not found");
goto done;
}
microphone.sensitivity = atof(attr[curIdx++]);
} else {
microphone.sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
}
if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_MAX_SPL) {
if (strcmp(attr[curIdx++], "max_spl")) {
AHAL_ERR("max_spl not found");
goto done;
}
microphone.max_spl = atof(attr[curIdx++]);
} else {
microphone.max_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
}
if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_MIN_SPL) {
if (strcmp(attr[curIdx++], "min_spl")) {
AHAL_ERR("min_spl not found");
goto done;
}
microphone.min_spl = atof(attr[curIdx++]);
} else {
microphone.min_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
}
if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_ORIENTATION) {
if (strcmp(attr[curIdx++], "orientation")) {
AHAL_ERR("orientation not found");
goto done;
}
char *context = NULL;
char *token = strtok_r((char *)attr[curIdx++], " ", &context);
float orientation[3];
uint32_t idx = 0;
while (token) {
orientation[idx++] = atof(token);
if (idx >= 3) {
break;
}
token = strtok_r(NULL, " ", &context);
}
if (idx != 3) {
AHAL_ERR("orientation invalid");
goto done;
}
microphone.orientation.x = orientation[0];
microphone.orientation.y = orientation[1];
microphone.orientation.z = orientation[2];
} else {
microphone.orientation.x = 0.0f;
microphone.orientation.y = 0.0f;
microphone.orientation.z = 0.0f;
}
if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_GEOMETRIC_LOCATION) {
if (strcmp(attr[curIdx++], "geometric_location")) {
AHAL_ERR("geometric_location not found");
goto done;
}
char *context = NULL;
char *token = strtok_r((char *)attr[curIdx++], " ", &context);
float geometric_location[3];
uint32_t idx = 0;
while (token) {
geometric_location[idx++] = atof(token);
if (idx >= 3) {
break;
}
token = strtok_r(NULL, " ", &context);
}
if (idx != 3) {
AHAL_ERR("geometric_location invalid");
goto done;
}
microphone.geometric_location.x = geometric_location[0];
microphone.geometric_location.y = geometric_location[1];
microphone.geometric_location.z = geometric_location[2];
} else {
microphone.geometric_location.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
microphone.geometric_location.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
microphone.geometric_location.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
}
set_microphone_characteristic(&microphone);
done:
return;
}
bool AudioDevice::is_input_pal_dev_id(int deviceId)
{
if ((deviceId > PAL_DEVICE_IN_MIN) && (deviceId < PAL_DEVICE_IN_MAX))
return true;
return false;
}
void AudioDevice::process_snd_dev(const XML_Char **attr)
{
uint32_t curIdx = 0;
in_snd_device = PAL_DEVICE_NONE;
if (strcmp(attr[curIdx++], "in_snd_device")) {
AHAL_ERR("snd_device not found");
return;
}
in_snd_device = deviceIdLUT.at((char *)attr[curIdx++]);
if (!is_input_pal_dev_id(in_snd_device)) {
AHAL_ERR("Sound device not valid");
in_snd_device = PAL_DEVICE_NONE;
}
return;
}
bool AudioDevice::set_microphone_map(pal_device_id_t in_snd_device,
const mic_info_t *info)
{
uint32_t map_idx;
uint32_t count;
if (!is_input_pal_dev_id(in_snd_device)) {
AHAL_ERR("Sound device not valid");
return false;
}
map_idx = (MIC_INFO_MAP_INDEX(in_snd_device));
count = microphone_maps[map_idx].mic_count++;
if (count >= AUDIO_MICROPHONE_MAX_COUNT) {
AHAL_ERR("Microphone count is greater than max allowed value");
microphone_maps[map_idx].mic_count--;
return false;
}
microphone_maps[map_idx].microphones[count] = *info;
return true;
}
bool AudioDevice::is_built_in_input_dev(pal_device_id_t deviceId)
{
if ((PAL_DEVICE_IN_HANDSET_MIC == deviceId) || (PAL_DEVICE_IN_SPEAKER_MIC == deviceId) ||
(PAL_DEVICE_IN_HANDSET_VA_MIC == deviceId) || (PAL_DEVICE_IN_ULTRASOUND_MIC == deviceId))
return true;
return false;
}
void AudioDevice::process_mic_info(const XML_Char **attr)
{
uint32_t curIdx = 0;
mic_info_t microphone;
char *context = NULL;
uint32_t idx = 0;
const char *token;
memset(&microphone.channel_mapping, AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED,
sizeof(microphone.channel_mapping));
if (strcmp(attr[curIdx++], "mic_device_id")) {
AHAL_ERR("mic_device_id not found");
goto on_error;
}
strlcpy(microphone.device_id, (char *)attr[curIdx++],
AUDIO_MICROPHONE_ID_MAX_LEN);
if (strcmp(attr[curIdx++], "channel_mapping")) {
AHAL_ERR("channel_mapping not found");
goto on_error;
}
token = strtok_r((char *)attr[curIdx++], " ", &context);
while (token) {
if (!find_enum_by_string(mic_channel_mapping, token,
AUDIO_MICROPHONE_CHANNEL_MAPPING_CNT,
(unsigned int *)(&(microphone.channel_mapping[idx++])))) {
AHAL_ERR("channel_mapping: %s not found", attr[--curIdx]);
goto on_error;
}
token = strtok_r(NULL, " ", &context);
}
microphone.channel_count = idx;
set_microphone_map(in_snd_device, &microphone);
return;
on_error:
in_snd_device = PAL_DEVICE_NONE;
return;
}
int32_t AudioDevice::get_active_microphones(uint32_t channels, pal_device_id_t id,
struct audio_microphone_characteristic_t *mic_array,
uint32_t *mic_count)
{
uint32_t actual_mic_count = 0;
mic_info_t *m_info;
uint32_t count = 0;
uint32_t idx;
uint32_t max_mic_count = microphones.declared_mic_count;
if (!mic_characteristics_available)
return -EIO;
if (mic_count == NULL) {
AHAL_ERR("Invalid mic_count!!!");
return -EINVAL;
}
if (mic_array == NULL) {
AHAL_ERR("Invalid mic_array!!!");
return -EINVAL;
}
if (!is_input_pal_dev_id(id)) {
AHAL_ERR("Invalid input sound device!!!");
return -EINVAL;
}
if (*mic_count == 0) {
AHAL_INFO("mic_count is ZERO");
return 0;
}
if (is_input_pal_dev_id(id)) {
idx = MIC_INFO_MAP_INDEX(id);
count = microphone_maps[idx].mic_count;
m_info = microphone_maps[idx].microphones;
for (size_t i = 0; i < count; i++) {
unsigned int channels_for_active_mic = channels;
if (channels_for_active_mic > m_info[i].channel_count) {
channels_for_active_mic = m_info[i].channel_count;
}
for (size_t j = 0; j < max_mic_count; j++) {
if (strcmp(microphones.microphone[j].device_id,
m_info[i].device_id) == 0) {
mic_array[actual_mic_count] = microphones.microphone[j];
for (size_t ch = 0; ch < channels_for_active_mic; ch++) {
mic_array[actual_mic_count].channel_mapping[ch] =
m_info[i].channel_mapping[ch];
}
actual_mic_count++;
break;
}
}
}
}
*mic_count = actual_mic_count;
return 0;
}
void AudioDevice::xml_start_tag(void *userdata, const XML_Char *tag_name,
const XML_Char **attr)
{
xml_userdata_t *data = (xml_userdata_t *)userdata;
if (!strcmp(tag_name, "microphone")) {
if (TAG_MICROPHONE_CHARACTERISTIC != data->tag) {
AHAL_ERR("microphone tag only supported with MICROPHONE_CHARACTERISTIC section");
return;
}
process_microphone_characteristics(attr);
return;
} else if (!strcmp(tag_name, "snd_dev")) {
if (TAG_INPUT_SND_DEVICE_TO_MIC_MAPPING != data->tag) {
AHAL_ERR("snd_dev tag only supported with INPUT_SND_DEVICE_TO_MIC_MAPPING section");
return;
}
process_snd_dev(attr);
return;
} else if (!strcmp(tag_name, "mic_info")) {
if (TAG_INPUT_SND_DEVICE_TO_MIC_MAPPING != data->tag) {
AHAL_ERR("mic_info tag only supported with INPUT_SND_DEVICE_TO_MIC_MAPPING section");
return;
}
if (PAL_DEVICE_NONE == in_snd_device) {
AHAL_ERR("Error in previous tags, do not process mic info");
return;
}
process_mic_info(attr);
return;
}
if (!strcmp(tag_name, "microphone_characteristics")) {
data->tag = TAG_MICROPHONE_CHARACTERISTIC;
} else if (!strcmp(tag_name, "snd_devices")) {
data->tag = TAG_SND_DEVICES;
} else if (!strcmp(tag_name, "input_snd_device")) {
if (TAG_SND_DEVICES != data->tag) {
AHAL_ERR("input_snd_device tag only supported with SND_DEVICES section");
return;
}
data->tag = TAG_INPUT_SND_DEVICE;
} else if (!strcmp(tag_name, "input_snd_device_mic_mapping")) {
if (TAG_INPUT_SND_DEVICE != data->tag) {
AHAL_ERR("input_snd_device_mic_mapping tag only supported with INPUT_SND_DEVICE section");
return;
}
data->tag = TAG_INPUT_SND_DEVICE_TO_MIC_MAPPING;;
}
}
void AudioDevice::xml_end_tag(void *userdata, const XML_Char *tag_name)
{
xml_userdata_t *data = (xml_userdata_t *)userdata;
if (!strcmp(tag_name, "input_snd_device"))
data->tag = TAG_SND_DEVICES;
else if (!strcmp(tag_name, "input_snd_device_mic_mapping"))
data->tag = TAG_INPUT_SND_DEVICE;
}
void AudioDevice::xml_char_data_handler(void *userdata, const XML_Char *s, int len)
{
xml_userdata_t *data = (xml_userdata_t *)userdata;
if (len + data->offs >= sizeof(data->data_buf) ) {
data->offs += len;
/* string length overflow, return */
return;
} else {
memcpy(data->data_buf + data->offs, s, len);
data->offs += len;
}
}
int AudioDevice::parse_xml()
{
XML_Parser parser;
FILE *file = NULL;
int ret = 0;
int bytes_read;
void *buf = NULL;
xml_userdata_t xml_user_data;
memset(&xml_user_data, 0, sizeof(xml_user_data));
file = fopen(MIC_CHARACTERISTICS_XML_FILE, "r");
if(!file) {
ret = -EIO;
AHAL_ERR("Failed to open xml file name %s", MIC_CHARACTERISTICS_XML_FILE);
goto done;
}
parser = XML_ParserCreate(NULL);
if (!parser) {
ret = -EINVAL;
AHAL_ERR("Failed to create XML parser ret %d", ret);
goto closeFile;
}
XML_SetUserData(parser, &xml_user_data);
XML_SetElementHandler(parser, xml_start_tag, xml_end_tag);
XML_SetCharacterDataHandler(parser, xml_char_data_handler);
while (1) {
buf = XML_GetBuffer(parser, XML_READ_BUFFER_SIZE);
if(buf == NULL) {
ret = -EINVAL;
AHAL_ERR("XML_Getbuffer failed ret %d", ret);
goto freeParser;
}
bytes_read = fread(buf, 1, XML_READ_BUFFER_SIZE, file);
if(bytes_read < 0) {
ret = -EINVAL;
AHAL_ERR("fread failed ret %d", ret);
goto freeParser;
}
if(XML_ParseBuffer(parser, bytes_read, bytes_read == 0) == XML_STATUS_ERROR) {
ret = -EINVAL;
AHAL_ERR("XML ParseBuffer failed for %s file ret %d", MIC_CHARACTERISTICS_XML_FILE, ret);
goto freeParser;
}
if (bytes_read == 0)
break;
}
freeParser:
XML_ParserFree(parser);
closeFile:
fclose(file);
file = NULL;
done:
return ret;
}