blob: 896666a93c28541348395c6fc3d373d8ba4052d3 [file] [log] [blame]
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define LOG_TAG "AHAL: AudioExtn"
#include <dlfcn.h>
#include <unistd.h>
#include "AudioExtn.h"
#include "AudioDevice.h"
#include "PalApi.h"
#include <cutils/properties.h>
#include "AudioCommon.h"
#define AUDIO_OUTPUT_BIT_WIDTH ((config_->offload_info.bit_width == 32) ? 24:config_->offload_info.bit_width)
#ifdef PAL_HIDL_ENABLED
#include <hidl/HidlTransportSupport.h>
#include <hidl/LegacySupport.h>
#include <pal_server_wrapper.h>
#include <vendor/qti/hardware/pal/1.0/IPAL.h>
using vendor::qti::hardware::pal::V1_0::IPAL;
using vendor::qti::hardware::pal::V1_0::implementation::PAL;
#ifdef AGM_HIDL_ENABLED
#include <agm_server_wrapper.h>
#include <vendor/qti/hardware/AGMIPC/1.0/IAGM.h>
using vendor::qti::hardware::AGMIPC::V1_0::IAGM;
using vendor::qti::hardware::AGMIPC::V1_0::implementation::AGM;
#endif
using android::hardware::defaultPassthroughServiceImplementation;
using android::sp;
using namespace android::hardware;
using android::OK;
#endif
#ifdef __LP64__
#define LIBS "/vendor/lib64/"
#else
#define LIBS "/vendor/lib/"
#endif
#define BATTERY_LISTENER_LIB_PATH LIBS"libbatterylistener.so"
#define HFP_LIB_PATH LIBS"libhfp_pal.so"
#define FM_LIB_PATH LIBS"libfmpal.so"
#define BT_IPC_SOURCE_LIB_NAME LIBS"btaudio_offload_if.so"
static batt_listener_init_t batt_listener_init;
static batt_listener_deinit_t batt_listener_deinit;
static batt_prop_is_charging_t batt_prop_is_charging;
static bool battery_listener_enabled;
static void *batt_listener_lib_handle;
static bool audio_extn_kpi_optimize_feature_enabled = false;
//TODO make this mutex part of class
std::mutex reconfig_wait_mutex_;
std::mutex AudioExtn::sLock;
std::atomic<bool> AudioExtn::sServicesRegistered = false;
int AudioExtn::audio_extn_parse_compress_metadata(struct audio_config *config_, pal_snd_dec_t *pal_snd_dec,
str_parms *parms, uint32_t *sr, uint16_t *ch, bool *isCompressMetadataAvail) {
if (config_ == NULL || pal_snd_dec == NULL || parms == NULL ||
sr == NULL || ch == NULL || isCompressMetadataAvail == NULL) {
return -EINVAL;
}
int ret = 0;
char value[32];
*sr = 0;
*ch = 0;
uint16_t flac_sample_size = ((config_->offload_info.bit_width == 32) ? 24:config_->offload_info.bit_width);
if (config_->offload_info.format == AUDIO_FORMAT_FLAC) {
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MIN_BLK_SIZE, value, sizeof(value));
if (ret >= 0) {
pal_snd_dec->flac_dec.min_blk_size = atoi(value);
*isCompressMetadataAvail = true;
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MAX_BLK_SIZE, value, sizeof(value));
if (ret >= 0) {
pal_snd_dec->flac_dec.max_blk_size = atoi(value);
*isCompressMetadataAvail = true;
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MIN_FRAME_SIZE, value, sizeof(value));
if (ret >= 0) {
pal_snd_dec->flac_dec.min_frame_size = atoi(value);
*isCompressMetadataAvail = true;
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_FLAC_MAX_FRAME_SIZE, value, sizeof(value));
if (ret >= 0) {
pal_snd_dec->flac_dec.max_frame_size = atoi(value);
*isCompressMetadataAvail = true;
}
pal_snd_dec->flac_dec.sample_size = flac_sample_size;
AHAL_DBG("FLAC metadata: sample_size %d min_blk_size %d, max_blk_size %d min_frame_size %d max_frame_size %d",
pal_snd_dec->flac_dec.sample_size,
pal_snd_dec->flac_dec.min_blk_size,
pal_snd_dec->flac_dec.max_blk_size,
pal_snd_dec->flac_dec.min_frame_size,
pal_snd_dec->flac_dec.max_frame_size);
}
else if (config_->offload_info.format == AUDIO_FORMAT_ALAC) {
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_FRAME_LENGTH, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.frame_length = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_COMPATIBLE_VERSION, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.compatible_version = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_BIT_DEPTH, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.bit_depth = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_PB, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.pb = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_MB, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.mb = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_KB, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.kb = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_NUM_CHANNELS, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.num_channels = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_MAX_RUN, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.max_run = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_MAX_FRAME_BYTES, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.max_frame_bytes = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_AVG_BIT_RATE, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.avg_bit_rate = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_SAMPLING_RATE, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.sample_rate = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_ALAC_CHANNEL_LAYOUT_TAG, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->alac_dec.channel_layout_tag = atoi(value);
}
*sr = pal_snd_dec->alac_dec.sample_rate;
*ch = pal_snd_dec->alac_dec.num_channels;
AHAL_DBG("ALAC CSD values: frameLength %d bitDepth %d numChannels %d"
" maxFrameBytes %d, avgBitRate %d, sampleRate %d",
pal_snd_dec->alac_dec.frame_length,
pal_snd_dec->alac_dec.bit_depth,
pal_snd_dec->alac_dec.num_channels,
pal_snd_dec->alac_dec.max_frame_bytes,
pal_snd_dec->alac_dec.avg_bit_rate,
pal_snd_dec->alac_dec.sample_rate);
}
else if (config_->offload_info.format == AUDIO_FORMAT_APE) {
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_COMPATIBLE_VERSION, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.compatible_version = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_COMPRESSION_LEVEL, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.compression_level = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_FORMAT_FLAGS, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.format_flags = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_BLOCKS_PER_FRAME, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.blocks_per_frame = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_FINAL_FRAME_BLOCKS, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.final_frame_blocks = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_TOTAL_FRAMES, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.total_frames = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_BITS_PER_SAMPLE, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.bits_per_sample = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_NUM_CHANNELS, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.num_channels = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_SAMPLE_RATE, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.sample_rate = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_APE_SEEK_TABLE_PRESENT, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->ape_dec.seek_table_present = atoi(value);
}
*sr = pal_snd_dec->ape_dec.sample_rate;
*ch = pal_snd_dec->ape_dec.num_channels;
AHAL_DBG("APE CSD values: compatibleVersion %d compressionLevel %d"
" formatFlags %d blocksPerFrame %d finalFrameBlocks %d"
" totalFrames %d bitsPerSample %d numChannels %d"
" sampleRate %d seekTablePresent %d",
pal_snd_dec->ape_dec.compatible_version,
pal_snd_dec->ape_dec.compression_level,
pal_snd_dec->ape_dec.format_flags,
pal_snd_dec->ape_dec.blocks_per_frame,
pal_snd_dec->ape_dec.final_frame_blocks,
pal_snd_dec->ape_dec.total_frames,
pal_snd_dec->ape_dec.bits_per_sample,
pal_snd_dec->ape_dec.num_channels,
pal_snd_dec->ape_dec.sample_rate,
pal_snd_dec->ape_dec.seek_table_present);
}
else if (config_->offload_info.format == AUDIO_FORMAT_VORBIS) {
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_VORBIS_BITSTREAM_FMT, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->vorbis_dec.bit_stream_fmt = atoi(value);
}
}
else if (config_->offload_info.format == AUDIO_FORMAT_WMA || config_->offload_info.format == AUDIO_FORMAT_WMA_PRO) {
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_FORMAT_TAG, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.fmt_tag = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.avg_bit_rate = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_BLOCK_ALIGN, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.super_block_align = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_BIT_PER_SAMPLE, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.bits_per_sample = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_CHANNEL_MASK, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.channelmask = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.encodeopt = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION1, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.encodeopt1 = atoi(value);
}
ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION2, value, sizeof(value));
if (ret >= 0) {
*isCompressMetadataAvail = true;
pal_snd_dec->wma_dec.encodeopt2 = atoi(value);
}
AHAL_DBG("WMA params: fmt %x, bit rate %x, balgn %x, sr %d, chmsk %x"
" encop %x, op1 %x, op2 %x",
pal_snd_dec->wma_dec.fmt_tag,
pal_snd_dec->wma_dec.avg_bit_rate,
pal_snd_dec->wma_dec.super_block_align,
pal_snd_dec->wma_dec.bits_per_sample,
pal_snd_dec->wma_dec.channelmask,
pal_snd_dec->wma_dec.encodeopt,
pal_snd_dec->wma_dec.encodeopt1,
pal_snd_dec->wma_dec.encodeopt2);
}
else if ((config_->offload_info.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC ||
(config_->offload_info.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC_ADTS ||
(config_->offload_info.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC_ADIF ||
(config_->offload_info.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AAC_LATM) {
*isCompressMetadataAvail = true;
pal_snd_dec->aac_dec.audio_obj_type = 29;
pal_snd_dec->aac_dec.pce_bits_size = 0;
AHAL_VERBOSE("AAC params: aot %d pce %d", pal_snd_dec->aac_dec.audio_obj_type, pal_snd_dec->aac_dec.pce_bits_size);
AHAL_VERBOSE("format %x", config_->offload_info.format);
}
return 0;
}
int AudioExtn::GetProxyParameters(std::shared_ptr<AudioDevice> adev __unused,
struct str_parms *query, struct str_parms *reply)
{
int ret, val = 0;
char value[32] = {0};
ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_CAN_OPEN_PROXY, value,
sizeof(value));
if (ret >= 0) {
val = 1;
str_parms_add_int(reply, AUDIO_PARAMETER_KEY_CAN_OPEN_PROXY, val);
}
AHAL_VERBOSE("called ... can_use_proxy %d", val);
return 0;
}
void AudioExtn::audio_extn_get_parameters(std::shared_ptr<AudioDevice> adev,
struct str_parms *query, struct str_parms *reply)
{
char *kv_pairs = NULL;
audio_extn_fm_get_parameters(adev, query, reply);
GetProxyParameters(adev, query, reply);
kv_pairs = str_parms_to_str(reply);
if (kv_pairs != NULL) {
AHAL_VERBOSE("returns %s", kv_pairs);
}
free(kv_pairs);
}
void AudioExtn::audio_extn_set_parameters(std::shared_ptr<AudioDevice> adev,
struct str_parms *params){
audio_extn_hfp_set_parameters(adev, params);
audio_extn_fm_set_parameters(adev, params);
}
int AudioExtn::get_controller_stream_from_params(struct str_parms *parms,
int *controller, int *stream) {
if ((str_parms_get_int(parms, "controller", controller) >= 0)
&& (str_parms_get_int(parms, "stream", stream) >=0 )) {
if (*controller < 0 || *controller >= MAX_CONTROLLERS ||
*stream < 0 || *stream >= MAX_STREAMS_PER_CONTROLLER) {
*controller = 0;
*stream = 0;
return -EINVAL;
}
} else {
*controller = -1;
*stream = -1;
}
return 0;
}
// START: BATTERY_LISTENER ==================================================
void AudioExtn::battery_listener_feature_init(bool is_feature_enabled) {
battery_listener_enabled = is_feature_enabled;
if (is_feature_enabled) {
batt_listener_lib_handle = dlopen(BATTERY_LISTENER_LIB_PATH, RTLD_NOW);
if (!batt_listener_lib_handle) {
AHAL_ERR("dlopen failed");
goto feature_disabled;
}
if (!(batt_listener_init = (batt_listener_init_t)dlsym(
batt_listener_lib_handle, "battery_properties_listener_init")) ||
!(batt_listener_deinit =
(batt_listener_deinit_t)dlsym(
batt_listener_lib_handle, "battery_properties_listener_deinit")) ||
!(batt_prop_is_charging =
(batt_prop_is_charging_t)dlsym(
batt_listener_lib_handle, "battery_properties_is_charging"))) {
AHAL_ERR("dlsym failed");
goto feature_disabled;
}
AHAL_DBG("---- Feature BATTERY_LISTENER is enabled ----");
return;
}
feature_disabled:
if (batt_listener_lib_handle) {
dlclose(batt_listener_lib_handle);
batt_listener_lib_handle = NULL;
}
batt_listener_init = NULL;
batt_listener_deinit = NULL;
batt_prop_is_charging = NULL;
AHAL_INFO("---- Feature BATTERY_LISTENER is disabled ----");
}
void AudioExtn::battery_properties_listener_init(battery_status_change_fn_t fn)
{
if(batt_listener_init)
batt_listener_init(fn);
}
void AudioExtn::battery_properties_listener_deinit()
{
if(batt_listener_deinit)
batt_listener_deinit();
}
bool AudioExtn::battery_properties_is_charging()
{
return (batt_prop_is_charging)? batt_prop_is_charging(): false;
}
// END: BATTERY_LISTENER ================================================================
// START: HFP ======================================================================
static void *hfp_lib_handle = NULL;
static hfp_init_t hfp_init;
static hfp_is_active_t hfp_is_active;
static hfp_get_usecase_t hfp_get_usecase;
static hfp_set_mic_mute_t hfp_set_mic_mute;
static set_parameters_t hfp_set_parameters;
static hfp_set_mic_mute2_t hfp_set_mic_mute2;
int AudioExtn::hfp_feature_init(bool is_feature_enabled)
{
AHAL_DBG("Called with feature %s",
is_feature_enabled ? "Enabled" : "NOT Enabled");
if (is_feature_enabled) {
// dlopen lib
hfp_lib_handle = dlopen(HFP_LIB_PATH, RTLD_NOW);
if (!hfp_lib_handle) {
AHAL_ERR("dlopen failed with: %s", dlerror());
goto feature_disabled;
}
if (!(hfp_init = (hfp_init_t)dlsym(
hfp_lib_handle, "hfp_init")) ||
!(hfp_is_active =
(hfp_is_active_t)dlsym(
hfp_lib_handle, "hfp_is_active")) ||
!(hfp_get_usecase =
(hfp_get_usecase_t)dlsym(
hfp_lib_handle, "hfp_get_usecase")) ||
!(hfp_set_mic_mute =
(hfp_set_mic_mute_t)dlsym(
hfp_lib_handle, "hfp_set_mic_mute")) ||
!(hfp_set_mic_mute2 =
(hfp_set_mic_mute2_t)dlsym(
hfp_lib_handle, "hfp_set_mic_mute2")) ||
!(hfp_set_parameters =
(set_parameters_t)dlsym(
hfp_lib_handle, "hfp_set_parameters"))) {
AHAL_ERR("dlsym failed");
goto feature_disabled;
}
AHAL_DBG("---- Feature HFP is Enabled ----");
return 0;
}
feature_disabled:
if (hfp_lib_handle) {
dlclose(hfp_lib_handle);
hfp_lib_handle = NULL;
}
hfp_init = NULL;
hfp_is_active = NULL;
hfp_get_usecase = NULL;
hfp_set_mic_mute = NULL;
hfp_set_mic_mute2 = NULL;
hfp_set_parameters = NULL;
AHAL_INFO("---- Feature HFP is disabled ----");
return -ENOSYS;
}
bool AudioExtn::audio_extn_hfp_is_active(std::shared_ptr<AudioDevice> adev)
{
return ((hfp_is_active) ?
hfp_is_active(adev) : false);
}
audio_usecase_t AudioExtn::audio_extn_hfp_get_usecase()
{
return ((hfp_get_usecase) ?
hfp_get_usecase() : -1);
}
int AudioExtn::audio_extn_hfp_set_mic_mute(bool state)
{
return ((hfp_set_mic_mute) ?
hfp_set_mic_mute(state) : -1);
}
static int reconfig_cb (tSESSION_TYPE session_type, int state)
{
int ret = 0;
pal_param_bta2dp_t param_bt_a2dp;
AHAL_DBG("reconfig_cb enter with state %s for %s", reconfigStateName.at(state).c_str(),
deviceNameLUT.at(SessionTypePalDevMap.at(session_type)).c_str());
if (session_type == LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
/* If reconfiguration is in progress state, we do a2dp suspend.
* If reconfiguration is in complete state, we do a2dp resume.
*/
if ((tRECONFIG_STATE)state == SESSION_SUSPEND) {
std::unique_lock<std::mutex> guard(reconfig_wait_mutex_);
param_bt_a2dp.a2dp_suspended = true;
param_bt_a2dp.dev_id = PAL_DEVICE_OUT_BLUETOOTH_BLE;
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_SUSPENDED, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
} else if ((tRECONFIG_STATE)state == SESSION_RESUME) {
param_bt_a2dp.a2dp_suspended = false;
param_bt_a2dp.dev_id = PAL_DEVICE_OUT_BLUETOOTH_BLE;
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_SUSPENDED, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
}
} else if (session_type == LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
if ((tRECONFIG_STATE)state == SESSION_SUSPEND) {
std::unique_lock<std::mutex> guard(reconfig_wait_mutex_);
param_bt_a2dp.a2dp_capture_suspended = true;
param_bt_a2dp.dev_id = PAL_DEVICE_IN_BLUETOOTH_BLE;
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_CAPTURE_SUSPENDED, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
} else if ((tRECONFIG_STATE)state == SESSION_RESUME) {
param_bt_a2dp.a2dp_capture_suspended = false;
param_bt_a2dp.dev_id = PAL_DEVICE_IN_BLUETOOTH_BLE;
ret = pal_set_param(PAL_PARAM_ID_BT_A2DP_CAPTURE_SUSPENDED, (void *)&param_bt_a2dp,
sizeof(pal_param_bta2dp_t));
}
}
AHAL_DBG("reconfig_cb exit with state %s for %s", reconfigStateName.at(state).c_str(),
deviceNameLUT.at(SessionTypePalDevMap.at(session_type)).c_str());
return ret;
}
void AudioExtn::audio_extn_hfp_set_parameters(std::shared_ptr<AudioDevice> adev,
struct str_parms *parms)
{
if (hfp_set_parameters)
hfp_set_parameters(adev, parms);
}
int AudioExtn::audio_extn_hfp_set_mic_mute2(std::shared_ptr<AudioDevice> adev, bool state)
{
return ((hfp_set_mic_mute2) ?
hfp_set_mic_mute2(adev, state) : -1);
}
// END: HFP ========================================================================
// START: A2DP ======================================================================
// Need to call this init for BT HIDL registration. It is expected that Audio HAL
// do need to do this initialization. Hence -
typedef void (*a2dp_bt_audio_pre_init_t)(void);
static void *a2dp_bt_lib_source_handle = NULL;
static a2dp_bt_audio_pre_init_t a2dp_bt_audio_pre_init = nullptr;
typedef void (*register_reconfig_cb_t)(int (*reconfig_cb)(tSESSION_TYPE, int));
static register_reconfig_cb_t register_reconfig_cb = nullptr;
int AudioExtn::a2dp_source_feature_init(bool is_feature_enabled)
{
AHAL_DBG("Called with feature %s",
is_feature_enabled ? "Enabled" : "NOT Enabled");
if (is_feature_enabled &&
(access(BT_IPC_SOURCE_LIB_NAME, R_OK) == 0)) {
// dlopen lib
a2dp_bt_lib_source_handle = dlopen(BT_IPC_SOURCE_LIB_NAME, RTLD_NOW);
if (!a2dp_bt_lib_source_handle) {
AHAL_ERR("dlopen %s failed with: %s", BT_IPC_SOURCE_LIB_NAME, dlerror());
goto feature_disabled;
}
if (!(a2dp_bt_audio_pre_init = (a2dp_bt_audio_pre_init_t)dlsym(
a2dp_bt_lib_source_handle, "bt_audio_pre_init")) ) {
AHAL_ERR("dlsym failed");
goto feature_disabled;
}
if (a2dp_bt_lib_source_handle && a2dp_bt_audio_pre_init) {
AHAL_DBG("calling BT module preinit");
// fwk related check's will be done in the BT layer
a2dp_bt_audio_pre_init();
}
if (!(register_reconfig_cb = (register_reconfig_cb_t)dlsym(
a2dp_bt_lib_source_handle, "register_reconfig_cb")) ) {
AHAL_ERR("dlsym failed");
goto feature_disabled;
}
if (a2dp_bt_lib_source_handle && register_reconfig_cb) {
AHAL_DBG("calling BT module register reconfig");
int (*reconfig_cb_ptr)(tSESSION_TYPE, int) = &reconfig_cb;
register_reconfig_cb(reconfig_cb_ptr);
}
AHAL_DBG("---- Feature A2DP offload is Enabled ----");
return 0;
}
feature_disabled:
if (a2dp_bt_lib_source_handle) {
dlclose(a2dp_bt_lib_source_handle);
a2dp_bt_lib_source_handle = NULL;
}
a2dp_bt_audio_pre_init = nullptr;
AHAL_INFO("---- Feature A2DP offload is disabled ----");
return -ENOSYS;
}
// END: A2DP
// START: DEVICE UTILS =============================================================
bool AudioExtn::audio_devices_cmp(const std::set<audio_devices_t>& devs, audio_device_cmp_fn_t fn) {
for(auto dev : devs)
if(fn(dev))
return true;
return false;
}
bool AudioExtn::audio_devices_cmp(const std::set<audio_devices_t>& devs, audio_devices_t dev) {
for(auto d : devs)
if(d == dev)
return true;
return false;
}
audio_devices_t AudioExtn::get_device_types(const std::set<audio_devices_t>& devs){
audio_devices_t device = AUDIO_DEVICE_NONE;
for(auto d : devs)
device = (audio_devices_t) (device | d);
return device;
}
bool AudioExtn::audio_devices_empty(const std::set<audio_devices_t>& devs){
return devs.empty();
}
// END: DEVICE UTILS ===============================================================
// START: KARAOKE ==================================================================
int AudioExtn::karaoke_open(pal_device_id_t device_out, pal_stream_callback pal_callback, pal_channel_info ch_info) {
std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance();
const int num_pal_devs = 2;
struct pal_device pal_devs[num_pal_devs];
karaoke_stream_handle = NULL;
pal_device_id_t device_in;
dynamic_media_config_t dynamic_media_config;
size_t payload_size = 0;
// Configuring Hostless Loopback
if (device_out == PAL_DEVICE_OUT_WIRED_HEADSET)
device_in = PAL_DEVICE_IN_WIRED_HEADSET;
else if (device_out == PAL_DEVICE_OUT_USB_HEADSET) {
device_in = PAL_DEVICE_IN_USB_HEADSET;
// get capability from device of USB
} else
return 0;
sattr.type = PAL_STREAM_LOOPBACK;
sattr.info.opt_stream_info.loopback_type = PAL_STREAM_LOOPBACK_KARAOKE;
sattr.direction = PAL_AUDIO_INPUT_OUTPUT;
sattr.in_media_config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
sattr.in_media_config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
sattr.in_media_config.ch_info = ch_info;
sattr.in_media_config.aud_fmt_id = PAL_AUDIO_FMT_DEFAULT_PCM;
sattr.out_media_config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
sattr.out_media_config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
sattr.out_media_config.ch_info = ch_info;
sattr.out_media_config.aud_fmt_id = PAL_AUDIO_FMT_DEFAULT_PCM;
for (int i = 0; i < num_pal_devs; ++i) {
pal_devs[i].id = i ? device_in : device_out;
if (device_out == PAL_DEVICE_OUT_USB_HEADSET || device_in == PAL_DEVICE_IN_USB_HEADSET) {
//Configure USB Digital Headset parameters
pal_param_device_capability_t *device_cap_query = (pal_param_device_capability_t *)
malloc(sizeof(pal_param_device_capability_t));
if (!device_cap_query) {
AHAL_ERR("Failed to allocate mem for device_cap_query");
return 0;
}
if (pal_devs[i].id == PAL_DEVICE_OUT_USB_HEADSET) {
device_cap_query->id = PAL_DEVICE_OUT_USB_DEVICE;
device_cap_query->is_playback = true;
} else {
device_cap_query->id = PAL_DEVICE_IN_USB_DEVICE;
device_cap_query->is_playback = false;
}
device_cap_query->addr.card_id = adevice->usb_card_id_;
device_cap_query->addr.device_num = adevice->usb_dev_num_;
device_cap_query->config = &dynamic_media_config;
pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY,
(void **)&device_cap_query,
&payload_size, nullptr);
pal_devs[i].address.card_id = adevice->usb_card_id_;
pal_devs[i].address.device_num = adevice->usb_dev_num_;
pal_devs[i].config.sample_rate = dynamic_media_config.sample_rate[0];
pal_devs[i].config.ch_info = ch_info;
pal_devs[i].config.aud_fmt_id = (pal_audio_fmt_t)dynamic_media_config.format[0];
free(device_cap_query);
} else {
pal_devs[i].config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
pal_devs[i].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
pal_devs[i].config.ch_info = ch_info;
pal_devs[i].config.aud_fmt_id = PAL_AUDIO_FMT_DEFAULT_PCM;
}
}
return pal_stream_open(&sattr,
num_pal_devs, pal_devs,
0,
NULL,
pal_callback,
(uint64_t) this,
&karaoke_stream_handle);
}
int AudioExtn::karaoke_start() {
return pal_stream_start(karaoke_stream_handle);
}
int AudioExtn::karaoke_stop() {
return pal_stream_stop(karaoke_stream_handle);
}
int AudioExtn::karaoke_close(){
return pal_stream_close(karaoke_stream_handle);
}
// END: KARAOKE ====================================================================
// START: PAL HIDL =================================================
int AudioExtn::audio_extn_hidl_init() {
std::lock_guard<std::mutex> lock(AudioExtn::sLock);
if (sServicesRegistered) {
AHAL_DBG("HIDLs are already registered");
return 0;
}
#ifdef PAL_HIDL_ENABLED
/* register audio PAL HIDL */
sp<IPAL> service = new PAL();
if(android::OK != service->registerAsService()) {
AHAL_ERR("Could not register PAL service");
return -EINVAL;
} else {
AHAL_DBG("successfully registered PAL service");
}
#endif
#ifdef AGM_HIDL_ENABLED
/* register AGM HIDL */
sp<IAGM> agm_service = new AGM();
AGM *temp = static_cast<AGM *>(agm_service.get());
if (temp->is_agm_initialized()) {
if(android::OK != agm_service->registerAsService()) {
AHAL_ERR("Could not register AGM service");
return -EINVAL;
} else {
AHAL_DBG("successfully registered AGM service");
}
}
#endif
/* to register other hidls */
sServicesRegistered = true;
return 0;
}
// END: PAL HIDL ===================================================
static set_parameters_t fm_set_params;
static get_parameters_t fm_get_params;
static void* libfm;
void AudioExtn::audio_extn_fm_init(bool enabled)
{
AHAL_DBG("Enter: enabled: %d", enabled);
if(enabled){
if(!libfm)
libfm = dlopen(FM_LIB_PATH, RTLD_NOW);
if (!libfm) {
AHAL_ERR("dlopen failed with: %s", dlerror());
return;
}
fm_set_params = (set_parameters_t) dlsym(libfm, "fm_set_parameters");
fm_get_params = (get_parameters_t) dlsym(libfm, "fm_get_parameters");
if(!fm_set_params || !fm_get_params){
AHAL_ERR("%s", dlerror());
dlclose(libfm);
}
}
AHAL_DBG("Exit");
}
void AudioExtn::audio_extn_fm_set_parameters(std::shared_ptr<AudioDevice> adev, struct str_parms *params){
if(fm_set_params)
fm_set_params(adev, params);
}
void AudioExtn::audio_extn_fm_get_parameters(std::shared_ptr<AudioDevice> adev, struct str_parms *query, struct str_parms *reply){
if(fm_get_params)
fm_get_params(adev, query, reply);
}
//START: KPI_OPTIMIZE =============================================================================
void AudioExtn::audio_extn_kpi_optimize_feature_init(bool is_feature_enabled)
{
audio_extn_kpi_optimize_feature_enabled = is_feature_enabled;
AHAL_DBG("---- Feature KPI_OPTIMIZE is %s ----", is_feature_enabled? "ENABLED": " NOT ENABLED");
}
typedef int (*perf_lock_acquire_t)(int, int, int*, int);
typedef int (*perf_lock_release_t)(int);
static void *qcopt_handle;
static perf_lock_acquire_t perf_lock_acq;
static perf_lock_release_t perf_lock_rel;
char opt_lib_path[512] = {0};
int AudioExtn::audio_extn_perf_lock_init(void)
{
int ret = 0;
//if feature is disabled, exit immediately
if(!audio_extn_kpi_optimize_feature_enabled)
goto err;
if (qcopt_handle == NULL) {
if (property_get("ro.vendor.extension_library",
opt_lib_path, NULL) <= 0) {
AHAL_ERR("Failed getting perf property");
ret = -EINVAL;
goto err;
}
if ((qcopt_handle = dlopen(opt_lib_path, RTLD_NOW)) == NULL) {
AHAL_ERR("Failed to open perf handle");
ret = -EINVAL;
goto err;
} else {
perf_lock_acq = (perf_lock_acquire_t)dlsym(qcopt_handle,
"perf_lock_acq");
if (perf_lock_acq == NULL) {
AHAL_ERR("Perf lock Acquire NULL");
dlclose(qcopt_handle);
ret = -EINVAL;
goto err;
}
perf_lock_rel = (perf_lock_release_t)dlsym(qcopt_handle,
"perf_lock_rel");
if (perf_lock_rel == NULL) {
AHAL_ERR("Perf lock Release NULL");
dlclose(qcopt_handle);
ret = -EINVAL;
goto err;
}
ALOGE("%s: Perf lock handles Success \n", __func__);
}
}
err:
return ret;
}
void AudioExtn::audio_extn_perf_lock_acquire(int *handle, int duration,
int *perf_lock_opts, int size)
{
if (audio_extn_kpi_optimize_feature_enabled)
{
if (!perf_lock_opts || !size || !perf_lock_acq || !handle) {
AHAL_ERR("Incorrect params, Failed to acquire perf lock, err ");
return;
}
/*
* Acquire performance lock for 1 sec during device path bringup.
* Lock will be released either after 1 sec or when perf_lock_release
* function is executed.
*/
*handle = perf_lock_acq(*handle, duration, perf_lock_opts, size);
if (*handle <= 0)
AHAL_ERR("Failed to acquire perf lock, err: %d\n", *handle);
}
}
void AudioExtn::audio_extn_perf_lock_release(int *handle)
{
if (audio_extn_kpi_optimize_feature_enabled) {
if (perf_lock_rel && handle && (*handle > 0)) {
perf_lock_rel(*handle);
*handle = 0;
} else
AHAL_ERR("Perf lock release error");
}
}
//END: KPI_OPTIMIZE =============================================================================
// START: compress_capture =====================================================
std::vector<uint32_t> CompressCapture::sAacSampleRates = {
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000};
std::unordered_map<uint32_t, int32_t>
CompressCapture::sSampleRateToDefaultBitRate = {
{8000, 36000}, {11025, 48000}, {12000, 56000},
{16000, 64000}, {22050, 82500}, {24000, 96000},
{32000, 110000}, {44100, 128000}, {48000, 156000}};
bool CompressCapture::parseMetadata(str_parms *parms,
struct audio_config *config,
int32_t &compressStreamAdjBitRate) {
bool ret = false;
int32_t value = 0;
if (config->format == AUDIO_FORMAT_AAC_LC ||
config->format == AUDIO_FORMAT_AAC_ADTS_LC ||
config->format == AUDIO_FORMAT_AAC_ADTS_HE_V1 ||
config->format == AUDIO_FORMAT_AAC_ADTS_HE_V2) {
/* query for AAC bitrate */
if (!str_parms_get_int(parms, kAudioParameterDSPAacBitRate,
&value)) {
uint32_t channelCount =
audio_channel_count_from_in_mask(config->channel_mask);
int32_t minValue = 0;
int32_t maxValue = UINT32_MAX;
auto it = std::find(sAacSampleRates.cbegin(),
sAacSampleRates.cend(), config->sample_rate);
if (it != sAacSampleRates.cend() &&
getAACMinBitrateValue(config->sample_rate, channelCount,
minValue) &&
getAACMaxBitrateValue(config->sample_rate, channelCount,
maxValue)) {
AHAL_INFO("client requested AAC bitrate: %d", value);
if (value < minValue) {
value = minValue;
AHAL_WARN("Adjusting AAC bitrate to minimum: %d", value);
} else if (value > maxValue) {
value = maxValue;
AHAL_WARN("Adjusting AAC bitrate to maximum: %d", value);
}
compressStreamAdjBitRate = value;
ret = true;
}
}
}
return ret;
}
bool CompressCapture::getAACMinBitrateValue(uint32_t sampleRate,
uint32_t channelCount,
int32_t &minValue) {
if (channelCount == 1) {
minValue = kAacMonoMinSupportedBitRate;
} else if (channelCount == 2) {
minValue = kAacStereoMinSupportedBitRate;
} else {
return false;
}
return true;
}
bool CompressCapture::getAACMaxBitrateValue(uint32_t sampleRate,
uint32_t channelCount,
int32_t &maxValue) {
if (channelCount == 1) {
maxValue = (int32_t)std::min((uint32_t)kAacMonoMaxSupportedBitRate,
6 * sampleRate);
} else if (channelCount == 2) {
maxValue = (int32_t)std::min((uint32_t)kAacStereoMaxSupportedBitRate,
12 * sampleRate);
} else {
return false;
}
return true;
}
bool CompressCapture::getAACMaxBufferSize(struct audio_config *config,
uint32_t &maxBufSize) {
int32_t maxBitRate = 0;
if (getAACMaxBitrateValue(
config->sample_rate,
audio_channel_count_from_in_mask(config->channel_mask),
maxBitRate)) {
/**
* AAC Encoder 1024 PCM samples => 1 compress AAC frame;
* 1 compress AAC frame => max possible length => max-bitrate bits;
* let's take example of 48K HZ;
* 1 second ==> 384000 bits ; 1 second ==> 48000 PCM samples;
* 1 AAC frame ==> 1024 PCM samples;
* Max buffer size possible;
* 48000/1024 = (8/375) seconds ==> ( 8/375 ) * 384000 bits
* ==> ( (8/375) * 384000 / 8 ) bytes;
**/
maxBufSize = (uint32_t)((((((double)kAacPCMSamplesPerFrame) /
config->sample_rate) *
((uint32_t)(maxBitRate))) /
8) +
/* Just in case; not to miss precision */ 1);
return true;
}
return false;
}
// END: compress_capture =======================================================