#endif
#ifndef AVRCP_DEFAULT_VERSION
-#define AVRCP_DEFAULT_VERSION AVRCP_1_4_STRING
+#define AVRCP_DEFAULT_VERSION AVRCP_1_5_STRING
#endif
/* state machine states */
// The public address of these devices are same as the Random address in ADV.
// Then will get name by LE_Create_connection, actually fails,
// but will block pairing.
- INTEROP_DISABLE_NAME_REQUEST
+ INTEROP_DISABLE_NAME_REQUEST,
+
+ // Respond AVRCP profile version only 1.4 for some device.
+ INTEROP_AVRCP_1_4_ONLY,
+
+ // Disable sniff mode for headsets/car-kits
+ // Some car kits supports sniff mode but when DUT initiates sniff req
+ // Remote will go to bad state and its leads to LMP time out.
+ INTEROP_DISABLE_SNIFF
} interop_feature_t;
// Check if a given |addr| matches a known interoperability workaround as
// for skip name request,
// because BR/EDR address and ADV random address are the same
{{{0xd4, 0x7a, 0xe2, 0, 0, 0}}, 3, INTEROP_DISABLE_NAME_REQUEST},
+
+ // Lexus Carkit
+ {{{0x64, 0xd4, 0xbd, 0, 0, 0}}, 3, INTEROP_AVRCP_1_4_ONLY},
+
+ // Mazda Carkit
+ {{{0xfc, 0x35, 0xe6, 0, 0, 0}}, 3, INTEROP_AVRCP_1_4_ONLY},
+
+ // Toyota Car Audio
+ {{{0x00, 0x17, 0x53, 0, 0, 0}}, 3, INTEROP_AVRCP_1_4_ONLY},
+
+ // Honda High End Carkit
+ {{{0x9c, 0x8d, 0x7c, 0, 0, 0}}, 3, INTEROP_AVRCP_1_4_ONLY},
+
+ // Honda Civic Carkit
+ {{{0x0c, 0xd9, 0xc1, 0, 0, 0}}, 3, INTEROP_AVRCP_1_4_ONLY},
+
+ // KDDI Carkit
+ {{{0x44, 0xea, 0xd8, 0, 0, 0}}, 3, INTEROP_DISABLE_SNIFF}
};
typedef struct {
CASE_RETURN_STR(INTEROP_DISABLE_ROLE_SWITCH)
CASE_RETURN_STR(INTEROP_HID_HOST_LIMIT_SNIFF_INTERVAL)
CASE_RETURN_STR(INTEROP_DISABLE_NAME_REQUEST)
+ CASE_RETURN_STR(INTEROP_AVRCP_1_4_ONLY)
+ CASE_RETURN_STR(INTEROP_DISABLE_SNIFF)
}
return "UNKNOWN";
std::vector<uint8_t> set_browsed_player_request = {0x70, 0x00, 0x02, 0x00,
0x02};
+// AVRCP Set Browsed Player Request with player_id = 0
+std::vector<uint8_t> set_browsed_player_id_0_request = {0x70, 0x00, 0x02, 0x00,
+ 0x00};
+
// AVRCP Set Browsed Player Response with num items = 4 and depth = 0
std::vector<uint8_t> set_browsed_player_response = {
0x70, 0x00, 0x0a, 0x04, 0x00, 0x00, 0x00,
return;
}
+ if (pkt->GetPlayerId() == 0 && num_items == 0) {
+ // Response fail if no browsable player in Bluetooth Player
+ auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(
+ Status::PLAYER_NOT_BROWSABLE, 0x0000, num_items, 0, "");
+ send_message(label, true, std::move(response));
+ return;
+ }
+
curr_browsed_player_id_ = pkt->GetPlayerId();
// Clear the path and push the new root.
SendMessage(1, request);
}
+TEST_F(AvrcpDeviceTest, setBrowsedPlayerTest) {
+ MockMediaInterface interface;
+ NiceMock<MockA2dpInterface> a2dp_interface;
+
+ test_device->RegisterInterfaces(&interface, &a2dp_interface, nullptr);
+
+ EXPECT_CALL(interface, SetBrowsedPlayer(_, _))
+ .Times(3)
+ .WillOnce(InvokeCb<1>(true, "", 0))
+ .WillOnce(InvokeCb<1>(false, "", 0))
+ .WillOnce(InvokeCb<1>(true, "", 2));
+
+ auto not_browsable_rsp = SetBrowsedPlayerResponseBuilder::MakeBuilder(
+ Status::PLAYER_NOT_BROWSABLE, 0x0000, 0, 0, "");
+ EXPECT_CALL(response_cb,
+ Call(1, true, matchPacket(std::move(not_browsable_rsp))))
+ .Times(1);
+
+ auto player_id_0_request =
+ TestBrowsePacket::Make(set_browsed_player_id_0_request);
+ SendBrowseMessage(1, player_id_0_request);
+
+ auto invalid_id_rsp = SetBrowsedPlayerResponseBuilder::MakeBuilder(
+ Status::INVALID_PLAYER_ID, 0x0000, 0, 0, "");
+ EXPECT_CALL(response_cb,
+ Call(2, true, matchPacket(std::move(invalid_id_rsp))))
+ .Times(1);
+
+ SendBrowseMessage(2, player_id_0_request);
+
+ auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(
+ Status::NO_ERROR, 0x0000, 2, 0, "");
+ EXPECT_CALL(response_cb, Call(3, true, matchPacket(std::move(response))))
+ .Times(1);
+
+ SendBrowseMessage(3, player_id_0_request);
+}
+
TEST_F(AvrcpDeviceTest, volumeChangedTest) {
MockMediaInterface interface;
NiceMock<MockA2dpInterface> a2dp_interface;
#include "bt_utils.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
+#include "osi/include/properties.h"
#define A2DP_AAC_DEFAULT_BITRATE 320000 // 320 kbps
#define A2DP_AAC_MIN_BITRATE 64000 // 64 kbps
btav_a2dp_codec_bits_per_sample_t bits_per_sample;
} tA2DP_AAC_CIE;
+static bool aac_source_caps_configured = false;
+static tA2DP_AAC_CIE a2dp_aac_source_caps = {};
+
/* AAC Source codec capabilities */
-static const tA2DP_AAC_CIE a2dp_aac_source_caps = {
+static const tA2DP_AAC_CIE a2dp_aac_cbr_source_caps = {
// objectType
A2DP_AAC_OBJECT_TYPE_MPEG2_LC,
// sampleRate
// bits_per_sample
BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16};
+/* AAC Source codec capabilities */
+static const tA2DP_AAC_CIE a2dp_aac_vbr_source_caps = {
+ // objectType
+ A2DP_AAC_OBJECT_TYPE_MPEG2_LC,
+ // sampleRate
+ // TODO: AAC 48.0kHz sampling rate should be added back - see b/62301376
+ A2DP_AAC_SAMPLING_FREQ_44100,
+ // channelMode
+ A2DP_AAC_CHANNEL_MODE_STEREO,
+ // variableBitRateSupport
+ A2DP_AAC_VARIABLE_BIT_RATE_ENABLED,
+ // bitRate
+ A2DP_AAC_DEFAULT_BITRATE,
+ // bits_per_sample
+ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16};
+
/* AAC Sink codec capabilities */
static const tA2DP_AAC_CIE a2dp_aac_sink_caps = {
// objectType
const char* A2DP_CodecIndexStrAacSink(void) { return "AAC SINK"; }
+void aac_source_caps_initialize() {
+ if (aac_source_caps_configured) {
+ return;
+ }
+ a2dp_aac_source_caps =
+ osi_property_get_bool("persist.bluetooth.a2dp_aac.vbr_supported", false)
+ ? a2dp_aac_vbr_source_caps
+ : a2dp_aac_cbr_source_caps;
+ aac_source_caps_configured = true;
+}
+
bool A2DP_InitCodecConfigAac(AvdtpSepConfig* p_cfg) {
+ aac_source_caps_initialize();
if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_aac_source_caps,
p_cfg->codec_info) != A2DP_SUCCESS) {
return false;
btav_a2dp_codec_priority_t codec_priority)
: A2dpCodecConfigAacBase(BTAV_A2DP_CODEC_INDEX_SOURCE_AAC,
A2DP_CodecIndexStrAac(), codec_priority, true) {
+ aac_source_caps_initialize();
// Compute the local capability
if (a2dp_aac_source_caps.sampleRate & A2DP_AAC_SAMPLING_FREQ_44100) {
codec_local_capability_.sample_rate |= BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
result_config_cie.variableBitRateSupport =
p_a2dp_aac_caps->variableBitRateSupport &
peer_info_cie.variableBitRateSupport;
+ if (result_config_cie.variableBitRateSupport !=
+ A2DP_AAC_VARIABLE_BIT_RATE_DISABLED) {
+ codec_config_.codec_specific_1 =
+ static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+ } else {
+ codec_config_.codec_specific_1 =
+ static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_CBR);
+ }
// Set the bit rate as follows:
// 1. If the remote device reports a bogus bit rate
goto fail;
}
- if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
- p_result_codec_config) != A2DP_SUCCESS) {
- goto fail;
- }
-
//
// Copy the codec-specific fields if they are not zero
//
- if (codec_user_config_.codec_specific_1 != 0)
- codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+ if (codec_user_config_.codec_specific_1 != 0) {
+ if (result_config_cie.variableBitRateSupport !=
+ A2DP_AAC_VARIABLE_BIT_RATE_DISABLED) {
+ auto user_bitrate_mode = codec_user_config_.codec_specific_1;
+ switch (static_cast<AacEncoderBitrateMode>(user_bitrate_mode)) {
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_C:
+ // VBR is supported, and is disabled by the user preference
+ result_config_cie.variableBitRateSupport =
+ A2DP_AAC_VARIABLE_BIT_RATE_DISABLED;
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_1:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_2:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_3:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_4:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5:
+ codec_config_.codec_specific_1 = codec_user_config_.codec_specific_1;
+ break;
+ default:
+ codec_config_.codec_specific_1 =
+ static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+ }
+ } else {
+ // It is no needed to check the user preference when Variable Bitrate
+ // unsupported by one of source or sink
+ codec_config_.codec_specific_1 =
+ static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_CBR);
+ }
+ }
if (codec_user_config_.codec_specific_2 != 0)
codec_config_.codec_specific_2 = codec_user_config_.codec_specific_2;
if (codec_user_config_.codec_specific_3 != 0)
if (codec_user_config_.codec_specific_4 != 0)
codec_config_.codec_specific_4 = codec_user_config_.codec_specific_4;
+ if (A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &result_config_cie,
+ p_result_codec_config) != A2DP_SUCCESS) {
+ goto fail;
+ }
+
// Create a local copy of the peer codec capability/config, and the
// result codec config.
if (is_capability) {
tA2DP_AAC_CIE peer_info_cie;
uint8_t channelMode;
uint16_t sampleRate;
+ uint8_t variableBitRateSupport;
const tA2DP_AAC_CIE* p_a2dp_aac_caps =
(is_source_) ? &a2dp_aac_source_caps : &a2dp_aac_sink_caps;
BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
}
+ // Compute the selectable capability - variable bitrate mode
+ variableBitRateSupport = p_a2dp_aac_caps->variableBitRateSupport &
+ peer_info_cie.variableBitRateSupport;
+ if (variableBitRateSupport != A2DP_AAC_VARIABLE_BIT_RATE_DISABLED) {
+ codec_selectable_capability_.codec_specific_1 =
+ static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+ } else {
+ codec_selectable_capability_.codec_specific_1 =
+ static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_CBR);
+ }
+
status = A2DP_BuildInfoAac(AVDT_MEDIA_TYPE_AUDIO, &peer_info_cie,
ota_codec_peer_capability_);
CHECK(status == A2DP_SUCCESS);
"invalid codec bit rate mode",
__func__);
return; // TODO: Return an error?
+ } else if (aac_param_value == A2DP_AAC_VARIABLE_BIT_RATE_ENABLED) {
+ // VBR has 5 modes defined in external/aac/libAACenc/src/aacenc.h
+ // A2DP_AAC_VARIABLE_BIT_RATE_DISABLED is equal to AACENC_BR_MODE_CBR
+ auto bitrate_mode = a2dp_codec_config->getCodecConfig().codec_specific_1;
+ switch (static_cast<AacEncoderBitrateMode>(bitrate_mode)) {
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_1:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_2:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_3:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_4:
+ [[fallthrough]];
+ case AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5:
+ break;
+ default:
+ bitrate_mode =
+ static_cast<int64_t>(AacEncoderBitrateMode::AACENC_BR_MODE_VBR_5);
+ }
+ aac_param_value =
+ static_cast<uint8_t>(bitrate_mode) & ~A2DP_AAC_VARIABLE_BIT_RATE_MASK;
}
+ LOG_INFO(LOG_TAG, "%s: AACENC_BITRATEMODE: %d", __func__, aac_param_value);
aac_error = aacEncoder_SetParam(a2dp_aac_encoder_cb.aac_handle,
AACENC_BITRATEMODE, aac_param_value);
if (aac_error != AACENC_OK) {
A2dpCodecConfig::debug_codec_dump(fd);
+ auto codec_specific_1 = getCodecConfig().codec_specific_1;
+ dprintf(
+ fd,
+ " AAC bitrate mode : %s "
+ "(0x%" PRIx64 ")\n",
+ ((codec_specific_1 & ~A2DP_AAC_VARIABLE_BIT_RATE_MASK) == 0 ? "Constant"
+ : "Variable"),
+ codec_specific_1);
dprintf(fd,
" Packet counts (expected/dropped) : %zu / "
"%zu\n",
*settings);
}
if ((*settings & HCI_ENABLE_SNIFF_MODE) &&
- (!HCI_SNIFF_MODE_SUPPORTED(localFeatures))) {
+ ((!HCI_SNIFF_MODE_SUPPORTED(localFeatures)) ||
+ interop_match_addr(INTEROP_DISABLE_SNIFF, &remote_bda))) {
*settings &= (~HCI_ENABLE_SNIFF_MODE);
BTM_TRACE_API("BTM_SetLinkPolicy sniff not supported (settings: 0x%04x)",
*settings);
#include "btm_int.h"
#include "btm_int_types.h"
#include "btu.h"
+#include "device/include/interop.h"
#include "hcidefs.h"
#include "hcimsgs.h"
#include "l2c_int.h"
/* check if the requested mode is supported */
ind = mode - BTM_PM_MD_HOLD; /* make it base 0 */
p_features = BTM_ReadLocalFeatures();
- if (!(p_features[btm_pm_mode_off[ind]] & btm_pm_mode_msk[ind])) {
+ if (!(p_features[btm_pm_mode_off[ind]] & btm_pm_mode_msk[ind]) ||
+ interop_match_addr(INTEROP_DISABLE_SNIFF, &remote_bda)) {
LOG(ERROR) << __func__ << ": pm_id " << unsigned(pm_id) << " mode "
<< unsigned(mode) << " is not supported for " << remote_bda;
return BTM_MODE_UNSUPPORTED;
#ifndef A2DP_AAC_ENCODER_H
#define A2DP_AAC_ENCODER_H
+#include "a2dp_aac_constants.h"
#include "a2dp_codec_api.h"
+// Is used in btav_a2dp_codec_config_t.codec_specific_1 when codec is AAC
+enum class AacEncoderBitrateMode : int64_t {
+ // Variable bitrate mode unsupported when used in a codec report, and upper
+ // layer can use this value as system default (keep current settings)
+ AACENC_BR_MODE_CBR = A2DP_AAC_VARIABLE_BIT_RATE_DISABLED,
+ // Constant bitrate mode when Variable bitrate mode is supported. This can
+ // also be used to disable Variable bitrate mode by upper layer
+ AACENC_BR_MODE_VBR_C = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x00),
+ // Variable bitrate mode (very low bitrate for software encoding).
+ AACENC_BR_MODE_VBR_1 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x01),
+ // Variable bitrate mode (low bitrate for software encoding).
+ AACENC_BR_MODE_VBR_2 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x02),
+ // Variable bitrate mode (medium bitrate for software encoding).
+ AACENC_BR_MODE_VBR_3 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x03),
+ // Variable bitrate mode (high bitrate for software encoding).
+ AACENC_BR_MODE_VBR_4 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x04),
+ // Variable bitrate mode (very high bitrate for software encoding).
+ AACENC_BR_MODE_VBR_5 = (A2DP_AAC_VARIABLE_BIT_RATE_ENABLED | 0x05),
+};
+
// Loads the A2DP AAC encoder.
// Return true on success, otherwise false.
bool A2DP_LoadEncoderAac(void);
#include "bt_common.h"
#include "bt_types.h"
+#include "avrc_defs.h"
+#include "device/include/interop.h"
#include "osi/include/osi.h"
#include "sdp_api.h"
#include "sdpint.h"
tSDP_RECORD* p_rec;
tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
tSDP_ATTRIBUTE* p_attr;
+ tSDP_ATTRIBUTE attr_sav;
bool maxxed_out = false, is_cont = false;
uint8_t* p_seq_start;
uint16_t seq_len, attr_len;
attr_seq.attr_entry[xx].end);
if (p_attr) {
+ // Check if the attribute contain AVRCP profile description list
+ uint16_t avrcp_version = sdpu_is_avrcp_profile_description_list(p_attr);
+ if (avrcp_version > AVRC_REV_1_4 &&
+ interop_match_addr(INTEROP_AVRCP_1_4_ONLY,
+ &(p_ccb->device_address))) {
+ SDP_TRACE_DEBUG(
+ "%s, device=%s is only accept AVRCP 1.4, reply AVRCP 1.4 "
+ "instead.",
+ __func__, p_ccb->device_address.ToString().c_str());
+ memcpy(&attr_sav, p_attr, sizeof(tSDP_ATTRIBUTE));
+ attr_sav.value_ptr[attr_sav.len - 1] = 0x04;
+ p_attr = &attr_sav;
+ }
/* Check if attribute fits. Assume 3-byte value type/length */
rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
/* Send the buffer through L2CAP */
L2CA_DataWrite(p_ccb->connection_id, p_buf);
}
-
#endif /* SDP_SERVER_ENABLED == TRUE */
#include "bt_types.h"
#include "btif_config.h"
+#include "avrc_defs.h"
#include "sdp_api.h"
#include "sdpint.h"
osi_free(p_attr_buff);
return p_out;
}
+/*******************************************************************************
+ *
+ * Function sdpu_is_avrcp_profile_description_list
+ *
+ * Description This function is to check if attirbute contain AVRCP profile
+ * description list
+ *
+ * p_attr: attibute to be check
+ *
+ * Returns AVRCP profile version if matched, else 0
+ *
+ ******************************************************************************/
+uint16_t sdpu_is_avrcp_profile_description_list(tSDP_ATTRIBUTE* p_attr) {
+ if (p_attr->id != ATTR_ID_BT_PROFILE_DESC_LIST || p_attr->len != 8) {
+ return 0;
+ }
+
+ uint8_t* p_uuid = p_attr->value_ptr + 3;
+ // Check if AVRCP profile UUID
+ if (p_uuid[0] != 0x11 || p_uuid[1] != 0xe) {
+ return 0;
+ }
+ uint8_t p_version = *(p_uuid + 4);
+ switch (p_version) {
+ case 0x0:
+ return AVRC_REV_1_0;
+ case 0x3:
+ return AVRC_REV_1_3;
+ case 0x4:
+ return AVRC_REV_1_4;
+ case 0x5:
+ return AVRC_REV_1_5;
+ case 0x6:
+ return AVRC_REV_1_6;
+ default:
+ return 0;
+ }
+}
extern uint8_t* sdpu_build_partial_attrib_entry(uint8_t* p_out,
tSDP_ATTRIBUTE* p_attr,
uint16_t len, uint16_t* offset);
+extern uint16_t sdpu_is_avrcp_profile_description_list(tSDP_ATTRIBUTE* p_attr);
/* Functions provided by sdp_db.cc
*/
tSMP_BR_STATE curr_state = p_cb->br_state;
tSMP_BR_SM_TBL state_table;
uint8_t action, entry;
- tSMP_BR_ENTRY_TBL entry_table = smp_br_entry_table[p_cb->role];
SMP_TRACE_EVENT("main %s", __func__);
if (curr_state >= SMP_BR_STATE_MAX) {
return;
}
+ tSMP_BR_ENTRY_TBL entry_table = smp_br_entry_table[p_cb->role];
+
SMP_TRACE_DEBUG("SMP Role: %s State: [%s (%d)], Event: [%s (%d)]",
(p_cb->role == HCI_ROLE_SLAVE) ? "Slave" : "Master",
smp_get_br_state_name(p_cb->br_state), p_cb->br_state,