Merge branch android10-qpr3-release
Change-Id: I691b239eb1f526001707ee968c295a05853a6a4c
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 5f6e378..8b05056 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -76,6 +76,10 @@
{
"name" : "net_test_types",
"host" : true
+ },
+ {
+ "name" : "net_test_btif_rc",
+ "host" : true
}
]
}
diff --git a/binder/android/bluetooth/IBluetoothManager.aidl b/binder/android/bluetooth/IBluetoothManager.aidl
index 2e12700..8a80d49 100644
--- a/binder/android/bluetooth/IBluetoothManager.aidl
+++ b/binder/android/bluetooth/IBluetoothManager.aidl
@@ -46,6 +46,8 @@
String getAddress();
String getName();
+ boolean onFactoryReset();
+
boolean isBleScanAlwaysAvailable();
int updateBleAppCount(IBinder b, boolean enable, String packageName);
boolean isBleAppPresent();
diff --git a/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index b022b43..7cd7056 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -1246,7 +1246,6 @@
open.chnl = p_scb->chnl;
open.hndl = p_scb->hndl;
open.status = BTA_AV_SUCCESS;
- open.starting = bta_av_chk_start(p_scb);
open.edr = 0;
p = BTM_ReadRemoteFeatures(p_scb->PeerAddress());
if (p != NULL) {
@@ -1262,8 +1261,10 @@
bta_ar_avdt_conn(BTA_ID_AV, open.bd_addr, p_scb->hdi);
#endif
if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC) {
+ open.starting = false;
open.sep = AVDT_TSEP_SNK;
} else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK) {
+ open.starting = bta_av_chk_start(p_scb);
open.sep = AVDT_TSEP_SRC;
}
diff --git a/bta/av/bta_av_ssm.cc b/bta/av/bta_av_ssm.cc
index a1a94b0..80effa3 100644
--- a/bta/av/bta_av_ssm.cc
+++ b/bta/av/bta_av_ssm.cc
@@ -329,7 +329,7 @@
/* STR_RECONFIG_CFM_EVT */
{BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
/* AVRC_TIMER_EVT */
- {BTA_AV_OPEN_RC, BTA_AV_CHK_2ND_START, BTA_AV_OPEN_SST},
+ {BTA_AV_OPEN_RC, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
/* AVDT_CONNECT_EVT */ {BTA_AV_SIGNORE, BTA_AV_SIGNORE, BTA_AV_OPEN_SST},
/* AVDT_DISCONNECT_EVT */
{BTA_AV_STR_CLOSED, BTA_AV_SIGNORE, BTA_AV_INIT_SST},
diff --git a/bta/hf_client/bta_hf_client_rfc.cc b/bta/hf_client/bta_hf_client_rfc.cc
index f3e0947..535371f 100644
--- a/bta/hf_client/bta_hf_client_rfc.cc
+++ b/bta/hf_client/bta_hf_client_rfc.cc
@@ -127,6 +127,7 @@
} else {
APPL_TRACE_ERROR("%s: PORT_SUCCESS, ignoring handle = %d", __func__,
port_handle);
+ osi_free(p_buf);
return;
}
} else if (client_cb != NULL &&
@@ -136,6 +137,10 @@
RFCOMM_RemoveServer(port_handle);
p_buf->hdr.event = BTA_HF_CLIENT_RFC_CLOSE_EVT;
+ } else if (client_cb == NULL) {
+ // client_cb is already cleaned due to hfp client disabled.
+ // Assigned a valid event value to header and send this message anyway.
+ p_buf->hdr.event = BTA_HF_CLIENT_RFC_CLOSE_EVT;
}
p_buf->hdr.layer_specific = client_cb != NULL ? client_cb->handle : 0;
diff --git a/btif/Android.bp b/btif/Android.bp
index f33d174..0ecf207 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -208,3 +208,36 @@
],
cflags: ["-DBUILDCFG"],
}
+
+// btif rc unit tests for target
+// ========================================================
+cc_test {
+ name: "net_test_btif_rc",
+ defaults: ["fluoride_defaults"],
+ test_suites: ["device-tests"],
+ host_supported: true,
+ include_dirs: btifCommonIncludes,
+ srcs: [
+ "test/btif_rc_test.cc",
+ ],
+ header_libs: ["libbluetooth_headers"],
+ shared_libs: [
+ "libcrypto",
+ "libcutils",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+ static_libs: [
+ "libbluetooth-types",
+ "libbt-common",
+ "libbt-protos-lite",
+ "libosi",
+ "libosi-AllocationTestHarness",
+ ],
+ cflags: ["-DBUILDCFG"],
+ sanitize: {
+ address: true,
+ cfi: true,
+ misc_undefined: ["bounds"],
+ },
+}
diff --git a/btif/src/btif_a2dp_sink.cc b/btif/src/btif_a2dp_sink.cc
index bb1bc49..aa75d21 100644
--- a/btif/src/btif_a2dp_sink.cc
+++ b/btif/src/btif_a2dp_sink.cc
@@ -667,7 +667,6 @@
LOG_INFO(LOG_TAG, "%s: state=%d", __func__, state);
LockGuard lock(g_mutex);
- if (!btif_av_is_connected()) return;
APPL_TRACE_DEBUG("%s: setting focus state to %d", __func__, state);
btif_a2dp_sink_cb.rx_focus_state = state;
if (btif_a2dp_sink_cb.rx_focus_state == BTIF_A2DP_SINK_FOCUS_NOT_GRANTED) {
diff --git a/btif/src/btif_rc.cc b/btif/src/btif_rc.cc
index 9919a7b..575d83e 100644
--- a/btif/src/btif_rc.cc
+++ b/btif/src/btif_rc.cc
@@ -1836,6 +1836,12 @@
BTIF_TRACE_DEBUG("%s", __func__);
CHECK_RC_CONNECTED(p_dev);
+ if (num_attr > BTRC_MAX_ELEM_ATTR_SIZE) {
+ LOG(WARNING) << __func__
+ << " Exceeded number attributes:" << static_cast<int>(num_attr)
+ << " max:" << BTRC_MAX_ELEM_ATTR_SIZE;
+ num_attr = BTRC_MAX_ELEM_ATTR_SIZE;
+ }
memset(element_attrs, 0, sizeof(tAVRC_ATTR_ENTRY) * num_attr);
if (num_attr == 0) {
@@ -1844,7 +1850,8 @@
for (i = 0; i < num_attr; i++) {
element_attrs[i].attr_id = p_attrs[i].attr_id;
element_attrs[i].name.charset_id = AVRC_CHARSET_ID_UTF8;
- element_attrs[i].name.str_len = (uint16_t)strlen((char*)p_attrs[i].text);
+ element_attrs[i].name.str_len =
+ (uint16_t)strnlen((char*)p_attrs[i].text, BTRC_MAX_ATTR_STR_LEN);
element_attrs[i].name.p_str = p_attrs[i].text;
BTIF_TRACE_DEBUG(
"%s: attr_id: 0x%x, charset_id: 0x%x, str_len: %d, str: %s", __func__,
@@ -2939,11 +2946,12 @@
return;
}
// interval is only valid for AVRC_EVT_PLAY_POS_CHANGED
- uint32_t interval = 0;
+ uint32_t interval_in_seconds = 0;
if (p_event->event_id == AVRC_EVT_PLAY_POS_CHANGED) {
- interval = 2000;
+ interval_in_seconds = 2;
}
- status = register_notification_cmd(p_transaction->lbl, p_event->event_id, interval, p_dev);
+ status = register_notification_cmd(p_transaction->lbl, p_event->event_id,
+ interval_in_seconds, p_dev);
if (status != BT_STATUS_SUCCESS) {
BTIF_TRACE_ERROR("%s: Error in Notification registration: %d", __func__,
status);
@@ -3152,11 +3160,10 @@
break;
} else {
uint8_t* p_data = p_rsp->param.track;
- /* Update the UID for current track
- * Attributes will be fetched after the AVRCP procedure
- */
BE_STREAM_TO_UINT64(p_dev->rc_playing_uid, p_data);
get_play_status_cmd(p_dev);
+ get_element_attribute_cmd(AVRC_MAX_NUM_MEDIA_ATTR_ID, attr_list,
+ p_dev);
}
break;
@@ -3336,6 +3343,8 @@
rc_ctrl_procedure_complete(p_dev);
return;
}
+ p_dev->rc_app_settings.num_attrs = 0;
+ p_dev->rc_app_settings.num_ext_attrs = 0;
for (xx = 0; xx < p_rsp->num_attr; xx++) {
uint8_t st_index;
@@ -3809,6 +3818,10 @@
if (p_rsp->status == AVRC_STS_NO_ERROR) {
do_in_jni_thread(
FROM_HERE,
+ base::Bind(bt_rc_ctrl_callbacks->play_status_changed_cb, p_dev->rc_addr,
+ (btrc_play_status_t)p_rsp->play_status));
+ do_in_jni_thread(
+ FROM_HERE,
base::Bind(bt_rc_ctrl_callbacks->play_position_changed_cb,
p_dev->rc_addr, p_rsp->song_len, p_rsp->song_pos));
} else {
@@ -3907,6 +3920,12 @@
/* We want to make the ownership explicit in native */
btrc_items, item_count));
+ if (item_count > 0) {
+ if (btrc_items[0].item_type == AVRC_ITEM_PLAYER &&
+ (p_dev->rc_features & BTA_AV_FEAT_APP_SETTING)) {
+ list_player_app_setting_attrib_cmd(p_dev);
+ }
+ }
/* Release the memory block for items and attributes allocated here.
* Since the executor for do_in_jni_thread is a Single Thread Task Runner it
* is okay to queue up the cleanup of btrc_items */
diff --git a/btif/test/btif_rc_test.cc b/btif/test/btif_rc_test.cc
new file mode 100644
index 0000000..6271881
--- /dev/null
+++ b/btif/test/btif_rc_test.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/logging.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <cstdint>
+
+#include "bta/include/bta_av_api.h"
+#include "btif/include/btif_common.h"
+#include "device/include/interop.h"
+#include "include/hardware/bt_rc.h"
+#include "osi/test/AllocationTestHarness.h"
+#include "stack/include/btm_api_types.h"
+#include "types/raw_address.h"
+#undef LOG_TAG
+#include "btif/src/btif_rc.cc"
+
+extern void allocation_tracker_uninit(void);
+
+namespace {
+int AVRC_BldResponse_ = 0;
+} // namespace
+
+uint8_t appl_trace_level = BT_TRACE_LEVEL_WARNING;
+uint8_t btif_trace_level = BT_TRACE_LEVEL_WARNING;
+
+tAVRC_STS AVRC_BldCommand(tAVRC_COMMAND* p_cmd, BT_HDR** pp_pkt) { return 0; }
+tAVRC_STS AVRC_BldResponse(uint8_t handle, tAVRC_RESPONSE* p_rsp,
+ BT_HDR** pp_pkt) {
+ AVRC_BldResponse_++;
+ return 0;
+}
+tAVRC_STS AVRC_Ctrl_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result) {
+ return 0;
+}
+tAVRC_STS AVRC_Ctrl_ParsResponse(tAVRC_MSG* p_msg, tAVRC_RESPONSE* p_result,
+ uint8_t* p_buf, uint16_t* buf_len) {
+ return 0;
+}
+tAVRC_STS AVRC_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result,
+ uint8_t* p_buf, uint16_t buf_len) {
+ return 0;
+}
+tAVRC_STS AVRC_ParsResponse(tAVRC_MSG* p_msg, tAVRC_RESPONSE* p_result,
+ UNUSED_ATTR uint8_t* p_buf,
+ UNUSED_ATTR uint16_t buf_len) {
+ return 0;
+}
+void BTA_AvCloseRc(uint8_t rc_handle) {}
+void BTA_AvMetaCmd(uint8_t rc_handle, uint8_t label, tBTA_AV_CMD cmd_code,
+ BT_HDR* p_pkt) {}
+void BTA_AvMetaRsp(uint8_t rc_handle, uint8_t label, tBTA_AV_CODE rsp_code,
+ BT_HDR* p_pkt) {}
+void BTA_AvRemoteCmd(uint8_t rc_handle, uint8_t label, tBTA_AV_RC rc_id,
+ tBTA_AV_STATE key_state) {}
+void BTA_AvRemoteVendorUniqueCmd(uint8_t rc_handle, uint8_t label,
+ tBTA_AV_STATE key_state, uint8_t* p_msg,
+ uint8_t buf_len) {}
+void BTA_AvVendorCmd(uint8_t rc_handle, uint8_t label, tBTA_AV_CODE cmd_code,
+ uint8_t* p_data, uint16_t len) {}
+void BTA_AvVendorRsp(uint8_t rc_handle, uint8_t label, tBTA_AV_CODE rsp_code,
+ uint8_t* p_data, uint16_t len, uint32_t company_id) {}
+void btif_av_clear_remote_suspend_flag(void) {}
+bool btif_av_is_connected(void) { return false; }
+bool btif_av_is_sink_enabled(void) { return false; }
+RawAddress btif_av_sink_active_peer(void) { return RawAddress(); }
+RawAddress btif_av_source_active_peer(void) { return RawAddress(); }
+bool btif_av_stream_started_ready(void) { return false; }
+bt_status_t btif_transfer_context(tBTIF_CBACK* p_cback, uint16_t event,
+ char* p_params, int param_len,
+ tBTIF_COPY_CBACK* p_copy_cback) {
+ return BT_STATUS_SUCCESS;
+}
+const char* dump_rc_event(uint8_t event) { return nullptr; }
+const char* dump_rc_notification_event_id(uint8_t event_id) { return nullptr; }
+const char* dump_rc_pdu(uint8_t pdu) { return nullptr; }
+bt_status_t do_in_jni_thread(const base::Location& from_here,
+ base::OnceClosure task) {
+ return BT_STATUS_SUCCESS;
+}
+base::MessageLoop* get_main_message_loop() { return nullptr; }
+bool interop_match_addr(const interop_feature_t feature,
+ const RawAddress* addr) {
+ return false;
+}
+void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+
+/**
+ * Test class to test selected functionality in hci/src/hci_layer.cc
+ */
+class BtifRcTest : public AllocationTestHarness {
+ protected:
+ void SetUp() override {
+ AllocationTestHarness::SetUp();
+ // Disable our allocation tracker to allow ASAN full range
+ allocation_tracker_uninit();
+ }
+
+ void TearDown() override { AllocationTestHarness::TearDown(); }
+};
+
+TEST_F(BtifRcTest, get_element_attr_rsp) {
+ RawAddress bd_addr;
+
+ btif_rc_cb.rc_multi_cb[0].rc_addr = bd_addr;
+ btif_rc_cb.rc_multi_cb[0].rc_connected = true;
+ btif_rc_cb.rc_multi_cb[0]
+ .rc_pdu_info[IDX_GET_ELEMENT_ATTR_RSP]
+ .is_rsp_pending = true;
+ btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED;
+
+ btrc_element_attr_val_t p_attrs[BTRC_MAX_ELEM_ATTR_SIZE];
+ uint8_t num_attr = BTRC_MAX_ELEM_ATTR_SIZE + 1;
+
+ CHECK(get_element_attr_rsp(bd_addr, num_attr, p_attrs) == BT_STATUS_SUCCESS);
+ CHECK(AVRC_BldResponse_ == 1);
+}
diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h
index 67a67c5..d122234 100644
--- a/internal_include/bt_target.h
+++ b/internal_include/bt_target.h
@@ -108,7 +108,7 @@
#endif
#ifndef BTA_DM_SDP_DB_SIZE
-#define BTA_DM_SDP_DB_SIZE 8000
+#define BTA_DM_SDP_DB_SIZE 20000
#endif
#ifndef HL_INCLUDED
diff --git a/profile/avrcp/device.cc b/profile/avrcp/device.cc
index 4ca624b..264eaf0 100644
--- a/profile/avrcp/device.cc
+++ b/profile/avrcp/device.cc
@@ -98,6 +98,19 @@
case CommandPdu::REGISTER_NOTIFICATION: {
auto register_notification =
Packet::Specialize<RegisterNotificationResponse>(pkt);
+
+ if (!register_notification->IsValid()) {
+ DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
+ auto response =
+ RejectBuilder::MakeBuilder(pkt->GetCommandPdu(),
+ Status::INVALID_PARAMETER);
+ send_message(label, false, std::move(response));
+ active_labels_.erase(label);
+ volume_interface_ = nullptr;
+ volume_ = VOL_REGISTRATION_FAILED;
+ return;
+ }
+
if (register_notification->GetEvent() != Event::VOLUME_CHANGED) {
DEVICE_LOG(WARNING)
<< __func__ << ": Unhandled register notification received: "
@@ -336,16 +349,6 @@
uint8_t label, const std::shared_ptr<RegisterNotificationResponse>& pkt) {
DEVICE_VLOG(1) << __func__ << ": interim=" << pkt->IsInterim();
- if (!pkt->IsValid()) {
- DEVICE_LOG(WARNING) << __func__ << ": Request packet is not valid";
- auto response = RejectBuilder::MakeBuilder(pkt->GetCommandPdu(), Status::INVALID_PARAMETER);
- send_message(label, false, std::move(response));
- active_labels_.erase(label);
- volume_interface_ = nullptr;
- volume_ = VOL_REGISTRATION_FAILED;
- return;
- }
-
if (volume_interface_ == nullptr) return;
if (pkt->GetCType() == CType::REJECTED) {
diff --git a/service/gatt_server.cc b/service/gatt_server.cc
index 52fd1ed..f46927d 100644
--- a/service/gatt_server.cc
+++ b/service/gatt_server.cc
@@ -18,6 +18,7 @@
#include <base/logging.h>
+#include "osi/include/log.h"
#include "service/logging_helpers.h"
#include "stack/include/bt_types.h"
@@ -116,6 +117,12 @@
return false;
}
+ if (offset < 0) {
+ android_errorWriteLog(0x534e4554, "143231677");
+ LOG(ERROR) << "Offset is less than 0 offset: " << offset;
+ return false;
+ }
+
if (value.size() + offset > BTGATT_MAX_ATTR_LEN) {
LOG(ERROR) << "Value is too large";
return false;
diff --git a/stack/Android.bp b/stack/Android.bp
index f7f5a45..f9d11e2 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -200,6 +200,7 @@
],
srcs: [
"test/stack_a2dp_test.cc",
+ "test/stack_avrcp_test.cc",
],
shared_libs: [
"libcrypto",
diff --git a/stack/BUILD.gn b/stack/BUILD.gn
index 00680c8..87dc726 100644
--- a/stack/BUILD.gn
+++ b/stack/BUILD.gn
@@ -202,6 +202,7 @@
testonly = true
sources = [
"test/stack_a2dp_test.cc",
+ "test/stack_avrcp_test.cc",
]
include_dirs = [
diff --git a/stack/a2dp/a2dp_aac_decoder.cc b/stack/a2dp/a2dp_aac_decoder.cc
index d9cd85d..d998d7d 100644
--- a/stack/a2dp/a2dp_aac_decoder.cc
+++ b/stack/a2dp/a2dp_aac_decoder.cc
@@ -30,7 +30,7 @@
typedef struct {
HANDLE_AACDECODER aac_handle;
bool has_aac_handle; // True if aac_handle is valid
- INT_PCM* decode_buf;
+ INT_PCM* decode_buf = nullptr;
decoded_data_callback_t decode_callback;
} tA2DP_AAC_DECODER_CB;
@@ -58,7 +58,7 @@
void a2dp_aac_decoder_cleanup(void) {
if (a2dp_aac_decoder_cb.has_aac_handle)
aacDecoder_Close(a2dp_aac_decoder_cb.aac_handle);
- free(a2dp_aac_decoder_cb.decode_buf);
+ osi_free(a2dp_aac_decoder_cb.decode_buf);
memset(&a2dp_aac_decoder_cb, 0, sizeof(a2dp_aac_decoder_cb));
}
diff --git a/stack/avdt/avdt_msg.cc b/stack/avdt/avdt_msg.cc
index 853f369..33fbfa7 100644
--- a/stack/avdt/avdt_msg.cc
+++ b/stack/avdt/avdt_msg.cc
@@ -985,18 +985,30 @@
* Returns Error code or zero if no error.
*
******************************************************************************/
-static uint8_t avdt_msg_prs_rej(tAVDT_MSG* p_msg, uint8_t* p, uint8_t sig) {
- if ((sig == AVDT_SIG_SETCONFIG) || (sig == AVDT_SIG_RECONFIG)) {
- p_msg->hdr.err_param = *p++;
- p_msg->hdr.err_code = *p;
- } else if ((sig == AVDT_SIG_START) || (sig == AVDT_SIG_SUSPEND)) {
- AVDT_MSG_PRS_SEID(p, p_msg->hdr.err_param);
- p_msg->hdr.err_code = *p;
+static uint8_t avdt_msg_prs_rej(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len,
+ uint8_t sig) {
+ uint8_t error = 0;
+
+ if (len > 0) {
+ if ((sig == AVDT_SIG_SETCONFIG) || (sig == AVDT_SIG_RECONFIG)) {
+ p_msg->hdr.err_param = *p++;
+ len--;
+ } else if ((sig == AVDT_SIG_START) || (sig == AVDT_SIG_SUSPEND)) {
+ AVDT_MSG_PRS_SEID(p, p_msg->hdr.err_param);
+ len--;
+ }
+ }
+
+ if (len < 1) {
+ char error_info[] = "AVDT rejected response length mismatch";
+ android_errorWriteWithInfoLog(0x534e4554, "79702484", -1, error_info,
+ strlen(error_info));
+ error = AVDT_ERR_LENGTH;
} else {
p_msg->hdr.err_code = *p;
}
- return 0;
+ return error;
}
/*******************************************************************************
@@ -1604,7 +1616,7 @@
evt = avdt_msg_rsp_2_evt[sig - 1];
} else /* msg_type == AVDT_MSG_TYPE_REJ */
{
- err = avdt_msg_prs_rej(&msg, p, sig);
+ err = avdt_msg_prs_rej(&msg, p, p_buf->len, sig);
evt = avdt_msg_rej_2_evt[sig - 1];
}
diff --git a/stack/avrc/avrc_pars_ct.cc b/stack/avrc/avrc_pars_ct.cc
index 80dc882..39ed921 100644
--- a/stack/avrc/avrc_pars_ct.cc
+++ b/stack/avrc/avrc_pars_ct.cc
@@ -806,7 +806,7 @@
if (len < min_len) goto length_error;
BE_STREAM_TO_UINT32(p_result->get_play_status.song_len, p);
BE_STREAM_TO_UINT32(p_result->get_play_status.song_pos, p);
- BE_STREAM_TO_UINT8(p_result->get_play_status.status, p);
+ BE_STREAM_TO_UINT8(p_result->get_play_status.play_status, p);
break;
case AVRC_PDU_SET_ADDRESSED_PLAYER:
diff --git a/stack/avrc/avrc_pars_tg.cc b/stack/avrc/avrc_pars_tg.cc
index 22471bd..fe1db3d 100644
--- a/stack/avrc/avrc_pars_tg.cc
+++ b/stack/avrc/avrc_pars_tg.cc
@@ -363,7 +363,7 @@
*
* Description This function is used to parse cmds received for CTRL
* Currently it is for SetAbsVolume and Volume Change
- * Notification..
+ * Notification.
*
* Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed
* successfully.
@@ -390,6 +390,12 @@
return status;
}
+#define RETURN_STATUS_IF_FALSE(_status_, _b_, _msg_, ...) \
+ if (!(_b_)) { \
+ AVRC_TRACE_DEBUG(_msg_, ##__VA_ARGS__); \
+ return _status_; \
+ }
+
/*******************************************************************************
*
* Function avrc_pars_browsing_cmd
@@ -409,6 +415,10 @@
uint8_t* p = p_msg->p_browse_data;
int count;
+ uint16_t min_len = 3;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
p_result->pdu = *p++;
AVRC_TRACE_DEBUG("avrc_pars_browsing_cmd() pdu:0x%x", p_result->pdu);
/* skip over len */
@@ -416,11 +426,20 @@
switch (p_result->pdu) {
case AVRC_PDU_SET_BROWSED_PLAYER: /* 0x70 */
+ min_len += 2;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
// For current implementation all players are browsable.
BE_STREAM_TO_UINT16(p_result->br_player.player_id, p);
break;
case AVRC_PDU_GET_FOLDER_ITEMS: /* 0x71 */
+
+ min_len += 10;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
STREAM_TO_UINT8(p_result->get_items.scope, p);
// To be modified later here (Scope) when all browsing commands are
// supported
@@ -441,12 +460,21 @@
if (buf_len < (count << 2))
p_result->get_items.attr_count = count = (buf_len >> 2);
for (int idx = 0; idx < count; idx++) {
+ min_len += 4;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD,
+ (p_msg->browse_len >= min_len),
+ "msg too short");
+
BE_STREAM_TO_UINT32(p_result->get_items.p_attr_list[idx], p);
}
}
break;
case AVRC_PDU_CHANGE_PATH: /* 0x72 */
+ min_len += 11;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
BE_STREAM_TO_UINT16(p_result->chg_path.uid_counter, p);
BE_STREAM_TO_UINT8(p_result->chg_path.direction, p);
if (p_result->chg_path.direction != AVRC_DIR_UP &&
@@ -457,7 +485,12 @@
break;
case AVRC_PDU_GET_ITEM_ATTRIBUTES: /* 0x73 */
+ min_len += 12;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
BE_STREAM_TO_UINT8(p_result->get_attrs.scope, p);
+
if (p_result->get_attrs.scope > AVRC_SCOPE_NOW_PLAYING) {
status = AVRC_STS_BAD_SCOPE;
break;
@@ -473,6 +506,11 @@
p_result->get_attrs.attr_count = count = (buf_len >> 2);
for (int idx = 0, count = 0; idx < p_result->get_attrs.attr_count;
idx++) {
+ min_len += 4;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD,
+ (p_msg->browse_len >= min_len),
+ "msg too short");
+
BE_STREAM_TO_UINT32(p_result->get_attrs.p_attr_list[count], p);
if (AVRC_IS_VALID_MEDIA_ATTRIBUTE(
p_result->get_attrs.p_attr_list[count])) {
@@ -488,6 +526,10 @@
break;
case AVRC_PDU_GET_TOTAL_NUM_OF_ITEMS: /* 0x75 */
+ ++min_len;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
BE_STREAM_TO_UINT8(p_result->get_num_of_items.scope, p);
if (p_result->get_num_of_items.scope > AVRC_SCOPE_NOW_PLAYING) {
status = AVRC_STS_BAD_SCOPE;
@@ -495,6 +537,10 @@
break;
case AVRC_PDU_SEARCH: /* 0x80 */
+ min_len += 4;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
BE_STREAM_TO_UINT16(p_result->search.string.charset_id, p);
BE_STREAM_TO_UINT16(p_result->search.string.str_len, p);
p_result->search.string.p_str = p_buf;
@@ -504,6 +550,10 @@
} else {
android_errorWriteLog(0x534e4554, "63146237");
}
+ min_len += p_result->search.string.str_len;
+ RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
+ "msg too short");
+
BE_STREAM_TO_ARRAY(p, p_buf, p_result->search.string.str_len);
} else {
status = AVRC_STS_INTERNAL_ERR;
diff --git a/stack/btm/btm_ble_multi_adv.cc b/stack/btm/btm_ble_multi_adv.cc
index 22d2e17..b425c09 100644
--- a/stack/btm/btm_ble_multi_adv.cc
+++ b/stack/btm/btm_ble_multi_adv.cc
@@ -728,16 +728,13 @@
data.insert(data.begin(), flags.begin(), flags.end());
}
- // Find and fill TX Power with the correct value
- if (data.size()) {
- size_t i = 0;
- while (i < data.size()) {
- uint8_t type = data[i + 1];
- if (type == HCI_EIR_TX_POWER_LEVEL_TYPE) {
- data[i + 2] = adv_inst[inst_id].tx_power;
- }
- i += data[i] + 1;
+ // Find and fill TX Power with the correct value.
+ // The TX Power section is a 3 byte section.
+ for (size_t i = 0; (i + 2) < data.size();) {
+ if (data[i + 1] == HCI_EIR_TX_POWER_LEVEL_TYPE) {
+ data[i + 2] = adv_inst[inst_id].tx_power;
}
+ i += data[i] + 1;
}
VLOG(1) << "data is: " << base::HexEncode(data.data(), data.size());
diff --git a/stack/btm/btm_inq.cc b/stack/btm/btm_inq.cc
index ef16359..f56369b 100644
--- a/stack/btm/btm_inq.cc
+++ b/stack/btm/btm_inq.cc
@@ -915,10 +915,9 @@
/* Make sure there is not already one in progress */
if (p_inq->remname_active) {
if (BTM_UseLeLink(p_inq->remname_bda)) {
- if (btm_ble_cancel_remote_name(p_inq->remname_bda))
- return (BTM_CMD_STARTED);
- else
- return (BTM_UNKNOWN_ADDR);
+ /* Cancel remote name request for LE device, and process remote name
+ * callback. */
+ btm_inq_rmt_name_failed_cancelled();
} else
btsnd_hcic_rmt_name_req_cancel(p_inq->remname_bda);
return (BTM_CMD_STARTED);
@@ -2091,22 +2090,22 @@
}
void btm_inq_remote_name_timer_timeout(UNUSED_ATTR void* data) {
- btm_inq_rmt_name_failed();
+ btm_inq_rmt_name_failed_cancelled();
}
/*******************************************************************************
*
- * Function btm_inq_rmt_name_failed
+ * Function btm_inq_rmt_name_failed_cancelled
*
- * Description This function is if timeout expires while getting remote
- * name. This is done for devices that incorrectly do not
- * report operation failure
+ * Description This function is if timeout expires or request is cancelled
+ * while getting remote name. This is done for devices that
+ * incorrectly do not report operation failure
*
* Returns void
*
******************************************************************************/
-void btm_inq_rmt_name_failed(void) {
- BTM_TRACE_ERROR("btm_inq_rmt_name_failed() remname_active=%d",
+void btm_inq_rmt_name_failed_cancelled(void) {
+ BTM_TRACE_ERROR("btm_inq_rmt_name_failed_cancelled() remname_active=%d",
btm_cb.btm_inq_vars.remname_active);
if (btm_cb.btm_inq_vars.remname_active)
diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h
index 88cb724..05180db 100644
--- a/stack/btm/btm_int.h
+++ b/stack/btm/btm_int.h
@@ -59,7 +59,7 @@
extern void btm_process_remote_name(const RawAddress* bda, BD_NAME name,
uint16_t evt_len, uint8_t hci_status);
-extern void btm_inq_rmt_name_failed(void);
+extern void btm_inq_rmt_name_failed_cancelled(void);
extern void btm_inq_remote_name_timer_timeout(void* data);
/* Inquiry related functions */
diff --git a/stack/btu/btu_hcif.cc b/stack/btu/btu_hcif.cc
index 245c537..52d5d60 100644
--- a/stack/btu/btu_hcif.cc
+++ b/stack/btu/btu_hcif.cc
@@ -48,6 +48,7 @@
#include "btu.h"
#include "common/metrics.h"
#include "device/include/controller.h"
+#include "hci_evt_length.h"
#include "hci_layer.h"
#include "hcimsgs.h"
#include "l2c_int.h"
@@ -257,6 +258,13 @@
STREAM_TO_UINT8(hci_evt_code, p);
STREAM_TO_UINT8(hci_evt_len, p);
+ // validate event size
+ if (hci_evt_len < hci_event_parameters_minimum_length[hci_evt_code]) {
+ HCI_TRACE_WARNING("%s: evt:0x%2X, malformed event of size %hhd", __func__,
+ hci_evt_code, hci_evt_len);
+ return;
+ }
+
btu_hcif_log_event_metrics(hci_evt_code, p);
switch (hci_evt_code) {
diff --git a/stack/crypto_toolbox/aes_cmac.cc b/stack/crypto_toolbox/aes_cmac.cc
index 8b8246e..c90b80c 100644
--- a/stack/crypto_toolbox/aes_cmac.cc
+++ b/stack/crypto_toolbox/aes_cmac.cc
@@ -180,7 +180,8 @@
* length - length of the input in byte.
*/
Octet16 aes_cmac(const Octet16& key, const uint8_t* input, uint16_t length) {
- uint16_t len, diff;
+ uint32_t len;
+ uint16_t diff;
/* n is number of rounds */
uint16_t n = (length + OCTET16_LEN - 1) / OCTET16_LEN;
diff --git a/stack/include/hci_evt_length.h b/stack/include/hci_evt_length.h
new file mode 100644
index 0000000..ea8dfdf
--- /dev/null
+++ b/stack/include/hci_evt_length.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2020 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.
+ */
+
+/*
+ * Definitions for HCI Event Parameter Minimum Length
+ */
+static const uint8_t hci_event_parameters_minimum_length[] = {
+ 0, // 0x00 - N/A
+ 1, // 0x01 - HCI_Inquiry_Complete Event
+ 15, // 0x02 - HCI_Inquiry_Result Event (Num_Responses = 1)
+ 11, // 0x03 - HCI_Connection_Complete Event
+ 10, // 0x04 - HCI_Connection_Request Event
+ 4, // 0x05 - HCI_Disconnection_Complete Event
+ 3, // 0x06 - HCI_Authentication_Complete Event
+ 255, // 0x07 - HCI_Remote_Name_Request_Complete Event
+ 4, // 0x08 - HCI_Encryption_Change Event
+ 3, // 0x09 - HCI_Change_Connection_Link_Key_Complete Event
+ 4, // 0x0A - HCI_Master_Link_Key_Complete Event
+ 11, // 0x0B - HCI_Read_Remote_Supported_Features_Complete Event
+ 8, // 0x0C - HCI_Read_Remote_Version_Information_Complete Event
+ 21, // 0x0D - HCI_QoS_Setup_Complete Event
+ 3, // 0x0E - HCI_Command_Complete Event (Depends on command)
+ 4, // 0x0F - HCI_Command_Status Event
+ 1, // 0x10 - HCI_Hardware_Error Event
+ 2, // 0x11 - HCI_Flush_Occurred Event
+ 8, // 0x12 - HCI_Role_Change Event
+ 5, // 0x13 - HCI_Number_Of_Completed_Packets Event (Num_Handles = 1)
+ 6, // 0x14 - HCI_Mode_Change Event
+ 23, // 0x15 - HCI_Return_Link_Keys Event (Num_Keys = 1)
+ 6, // 0x16 - HCI_PIN_Code_Request Event
+ 6, // 0x17 - HCI_Link_Key_Request Event
+ 23, // 0x18 - HCI_Link_Key_Notification Event
+ 3, // 0x19 - HCI_Loopback_Command Event (Depends on command)
+ 1, // 0x1A - HCI_Data_Buffer_Overflow Event
+ 3, // 0x1B - HCI_Max_Slots_Change Event
+ 5, // 0x1C - HCI_Read_Clock_Offset_Complete Event
+ 5, // 0x1D - HCI_Connection_Packet_Type_Changed Event
+ 2, // 0x1E - HCI_QoS_Violation Event
+ 7, // 0x1F - HCI_Page_Scan_Mode_Change Event (Deprecated)
+ 7, // 0x20 - HCI_Page_Scan_Repetition_Mode_Change Event
+ 22, // 0x21 - HCI_Flow_Specification_Complet Event
+ 15, // 0x22 - HCI_Inquiry_Result_with_RSSI Event (Num_Responses = 1)
+ 13, // 0x23 - HCI_Read_Remote_Extended_Features_Complete Event
+ 0, // 0x24 - N/A
+ 0, // 0x25 - N/A
+ 0, // 0x26 - N/A
+ 0, // 0x27 - N/A
+ 0, // 0x28 - N/A
+ 0, // 0x29 - N/A
+ 0, // 0x2A - N/A
+ 0, // 0x2B - N/A
+ 17, // 0x2C - HCI_Synchronous_Connection_Complete Event
+ 9, // 0x2D - HCI_Synchronous_Connection_Changed Event
+ 11, // 0x2E - HCI_Sniff_Subrating Event
+ 255, // 0x2F - HCI_Extended_Inquiry_Result Event
+ 3, // 0x30 - HCI_Encryption_Key_Refresh_Complete Event
+ 6, // 0x31 - HCI_IO_Capability_Request Event
+ 9, // 0x32 - HCI_IO_Capability_Response Event
+ 10, // 0x33 - HCI_User_Confirmation_Request Event
+ 6, // 0x34 - HCI_User_Passkey_Request Event
+ 6, // 0x35 - HCI_Remote_OOB_Data_Request Event
+ 7, // 0x36 - HCI_Simple_Pairing_Complete Event
+ 0, // 0x37 - N/A
+ 4, // 0x38 - HCI_Link_Supervision_Timeout_Changed Event
+ 2, // 0x39 - HCI_Enhanced_Flush_Complete Event
+ 0, // 0x3A - N/A
+ 10, // 0x3B - HCI_User_Passkey_Notification Event
+ 7, // 0x3C - HCI_Keypress_Notification Event
+ 14, // 0x3D - HCI_Remote_Host_Supported_Features_Notification Event
+ 0, // 0x3E - LE Meta event
+ 0, // 0x3F - N/A
+ 2, // 0x40 - HCI_Physical_Link_Complete Event
+ 1, // 0x41 - HCI_Channel_Selected Event
+ 3, // 0x42 - HCI_Disconnection_Physical_Link_Complete Event
+ 2, // 0x43 - HCI_Physical_Link_Loss_Early_Warning Event
+ 1, // 0x44 - HCI_Physical_Link_Recovery Event
+ 5, // 0x45 - HCI_Logical_Link_Complete Event
+ 4, // 0x46 - HCI_Disconnection_Logical_Link_Complete Event
+ 3, // 0x47 - HCI_Flow_Spec_Modify_Complete Event
+ 9, // 0x48 - HCI_Number_Of_Completed_Data_Blocks Event (Num_Handles = 1)
+ 2, // 0x49 - HCI_AMP_Start_Test Event
+ 2, // 0x4A - HCI_AMP_Test_End Event
+ 18, // 0x4B - HCI_AMP_Receiver_Report Event
+ 3, // 0x4C - HCI_Short_Range_Mode_Change_Complete Event
+ 2, // 0x4D - HCI_AMP_Status_Change Event
+ 9, // 0x4E - HCI_Triggered_Clock_Capture Event
+ 1, // 0x4F - HCI_Synchronization_Train_Complete Event
+ 29, // 0x50 - HCI_Synchronization_Train_Received Event
+ 18, // 0x51 - HCI_Connectionless_Slave_Broadcast_Receive Event
+ // (Data_Length = 0)
+ 7, // 0x52 - HCI_Connectionless_Slave_Broadcast_Timeout Event
+ 7, // 0x53 - HCI_Truncated_Page_Complete Event
+ 0, // 0x54 - HCI_Slave_Page_Response_Timeout Event
+ 10, // 0x55 - HCI_Connectionless_Slave_Broadcast_Channel_Map_Change Event
+ 4, // 0x56 - HCI_Inquiry_Response_Notification Event
+ 2, // 0x57 - HCI_Authenticated_Payload_Timeout_Expired Event
+ 8, // 0x58 - HCI_SAM_Status_Change Event
+ 0, // 0x59 - N/A
+ 0, // 0x5A - N/A
+ 0, // 0x5B - N/A
+ 0, // 0x5C - N/A
+ 0, // 0x5D - N/A
+ 0, // 0x5E - N/A
+ 0, // 0x5F - N/A
+ 0, // 0x60 - N/A
+ 0, // 0x61 - N/A
+ 0, // 0x62 - N/A
+ 0, // 0x63 - N/A
+ 0, // 0x64 - N/A
+ 0, // 0x65 - N/A
+ 0, // 0x66 - N/A
+ 0, // 0x67 - N/A
+ 0, // 0x68 - N/A
+ 0, // 0x69 - N/A
+ 0, // 0x6A - N/A
+ 0, // 0x6B - N/A
+ 0, // 0x6C - N/A
+ 0, // 0x6D - N/A
+ 0, // 0x6E - N/A
+ 0, // 0x6F - N/A
+ 0, // 0x70 - N/A
+ 0, // 0x71 - N/A
+ 0, // 0x72 - N/A
+ 0, // 0x73 - N/A
+ 0, // 0x74 - N/A
+ 0, // 0x75 - N/A
+ 0, // 0x76 - N/A
+ 0, // 0x77 - N/A
+ 0, // 0x78 - N/A
+ 0, // 0x79 - N/A
+ 0, // 0x7A - N/A
+ 0, // 0x7B - N/A
+ 0, // 0x7C - N/A
+ 0, // 0x7D - N/A
+ 0, // 0x7E - N/A
+ 0, // 0x7F - N/A
+ 0, // 0x80 - N/A
+ 0, // 0x81 - N/A
+ 0, // 0x82 - N/A
+ 0, // 0x83 - N/A
+ 0, // 0x84 - N/A
+ 0, // 0x85 - N/A
+ 0, // 0x86 - N/A
+ 0, // 0x87 - N/A
+ 0, // 0x88 - N/A
+ 0, // 0x89 - N/A
+ 0, // 0x8A - N/A
+ 0, // 0x8B - N/A
+ 0, // 0x8C - N/A
+ 0, // 0x8D - N/A
+ 0, // 0x8E - N/A
+ 0, // 0x8F - N/A
+ 0, // 0x90 - N/A
+ 0, // 0x91 - N/A
+ 0, // 0x92 - N/A
+ 0, // 0x93 - N/A
+ 0, // 0x94 - N/A
+ 0, // 0x95 - N/A
+ 0, // 0x96 - N/A
+ 0, // 0x97 - N/A
+ 0, // 0x98 - N/A
+ 0, // 0x99 - N/A
+ 0, // 0x9A - N/A
+ 0, // 0x9B - N/A
+ 0, // 0x9C - N/A
+ 0, // 0x9D - N/A
+ 0, // 0x9E - N/A
+ 0, // 0x9F - N/A
+ 0, // 0xA0 - N/A
+ 0, // 0xA1 - N/A
+ 0, // 0xA2 - N/A
+ 0, // 0xA3 - N/A
+ 0, // 0xA4 - N/A
+ 0, // 0xA5 - N/A
+ 0, // 0xA6 - N/A
+ 0, // 0xA7 - N/A
+ 0, // 0xA8 - N/A
+ 0, // 0xA9 - N/A
+ 0, // 0xAA - N/A
+ 0, // 0xAB - N/A
+ 0, // 0xAC - N/A
+ 0, // 0xAD - N/A
+ 0, // 0xAE - N/A
+ 0, // 0xAF - N/A
+ 0, // 0xB0 - N/A
+ 0, // 0xB1 - N/A
+ 0, // 0xB2 - N/A
+ 0, // 0xB3 - N/A
+ 0, // 0xB4 - N/A
+ 0, // 0xB5 - N/A
+ 0, // 0xB6 - N/A
+ 0, // 0xB7 - N/A
+ 0, // 0xB8 - N/A
+ 0, // 0xB9 - N/A
+ 0, // 0xBA - N/A
+ 0, // 0xBB - N/A
+ 0, // 0xBC - N/A
+ 0, // 0xBD - N/A
+ 0, // 0xBE - N/A
+ 0, // 0xBF - N/A
+ 0, // 0xC0 - N/A
+ 0, // 0xC1 - N/A
+ 0, // 0xC2 - N/A
+ 0, // 0xC3 - N/A
+ 0, // 0xC4 - N/A
+ 0, // 0xC5 - N/A
+ 0, // 0xC6 - N/A
+ 0, // 0xC7 - N/A
+ 0, // 0xC8 - N/A
+ 0, // 0xC9 - N/A
+ 0, // 0xCA - N/A
+ 0, // 0xCB - N/A
+ 0, // 0xCC - N/A
+ 0, // 0xCD - N/A
+ 0, // 0xCE - N/A
+ 0, // 0xCF - N/A
+ 0, // 0xD0 - N/A
+ 0, // 0xD1 - N/A
+ 0, // 0xD2 - N/A
+ 0, // 0xD3 - N/A
+ 0, // 0xD4 - N/A
+ 0, // 0xD5 - N/A
+ 0, // 0xD6 - N/A
+ 0, // 0xD7 - N/A
+ 0, // 0xD8 - N/A
+ 0, // 0xD9 - N/A
+ 0, // 0xDA - N/A
+ 0, // 0xDB - N/A
+ 0, // 0xDC - N/A
+ 0, // 0xDD - N/A
+ 0, // 0xDE - N/A
+ 0, // 0xDF - N/A
+ 0, // 0xE0 - N/A
+ 0, // 0xE1 - N/A
+ 0, // 0xE2 - N/A
+ 0, // 0xE3 - N/A
+ 0, // 0xE4 - N/A
+ 0, // 0xE5 - N/A
+ 0, // 0xE6 - N/A
+ 0, // 0xE7 - N/A
+ 0, // 0xE8 - N/A
+ 0, // 0xE9 - N/A
+ 0, // 0xEA - N/A
+ 0, // 0xEB - N/A
+ 0, // 0xEC - N/A
+ 0, // 0xED - N/A
+ 0, // 0xEE - N/A
+ 0, // 0xEF - N/A
+ 0, // 0xF0 - N/A
+ 0, // 0xF1 - N/A
+ 0, // 0xF2 - N/A
+ 0, // 0xF3 - N/A
+ 0, // 0xF4 - N/A
+ 0, // 0xF5 - N/A
+ 0, // 0xF6 - N/A
+ 0, // 0xF7 - N/A
+ 0, // 0xF8 - N/A
+ 0, // 0xF9 - N/A
+ 0, // 0xFA - N/A
+ 0, // 0xFB - N/A
+ 0, // 0xFC - N/A
+ 0, // 0xFD - N/A
+ 0, // 0xFE - N/A
+ 0, // 0xFF - HCI_Vendor_Specific Event
+};
diff --git a/stack/test/stack_avrcp_test.cc b/stack/test/stack_avrcp_test.cc
new file mode 100644
index 0000000..ad1cc9e
--- /dev/null
+++ b/stack/test/stack_avrcp_test.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <gtest/gtest.h>
+
+#include "stack/include/avrc_api.h"
+
+class StackAvrcpTest : public ::testing::Test {
+ protected:
+ StackAvrcpTest() = default;
+
+ virtual ~StackAvrcpTest() = default;
+};
+
+TEST_F(StackAvrcpTest, test_avrcp_parse_browse_cmd) {
+ uint8_t scratch_buf[512]{};
+ tAVRC_MSG msg{};
+ tAVRC_COMMAND result{};
+ uint8_t browse_cmd_buf[512]{};
+
+ msg.hdr.opcode = AVRC_OP_BROWSE;
+ msg.browse.p_browse_data = browse_cmd_buf;
+ msg.browse.browse_len = 2;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_BAD_CMD);
+
+ memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+ browse_cmd_buf[0] = AVRC_PDU_SET_BROWSED_PLAYER;
+ msg.browse.browse_len = 3;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_BAD_CMD);
+
+ msg.browse.browse_len = 5;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_NO_ERROR);
+
+ memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+ browse_cmd_buf[0] = AVRC_PDU_GET_FOLDER_ITEMS;
+ msg.browse.browse_len = 3;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_BAD_CMD);
+
+ msg.browse.browse_len = 13;
+ uint8_t* p = &browse_cmd_buf[3];
+ UINT8_TO_STREAM(p, AVRC_SCOPE_NOW_PLAYING); // scope
+ UINT32_TO_STREAM(p, 0x00000001); // start_item
+ UINT32_TO_STREAM(p, 0x00000002); // end_item
+ browse_cmd_buf[12] = 0; // attr_count
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_NO_ERROR);
+
+ memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+ browse_cmd_buf[0] = AVRC_PDU_CHANGE_PATH;
+ msg.browse.browse_len = 3;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_BAD_CMD);
+
+ msg.browse.browse_len = 14;
+ p = &browse_cmd_buf[3];
+ UINT16_TO_STREAM(p, 0x1234); // uid_counter
+ UINT8_TO_STREAM(p, AVRC_DIR_UP); // direction
+ UINT8_TO_STREAM(p, 0); // attr_count
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_NO_ERROR);
+
+ memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+ browse_cmd_buf[0] = AVRC_PDU_GET_ITEM_ATTRIBUTES;
+ msg.browse.browse_len = 3;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_BAD_CMD);
+
+ msg.browse.browse_len = 15;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_NO_ERROR);
+
+ memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+ browse_cmd_buf[0] = AVRC_PDU_GET_TOTAL_NUM_OF_ITEMS;
+ msg.browse.browse_len = 3;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_BAD_CMD);
+
+ msg.browse.browse_len = 4;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_NO_ERROR);
+
+ memset(browse_cmd_buf, 0, sizeof(browse_cmd_buf));
+ browse_cmd_buf[0] = AVRC_PDU_SEARCH;
+ msg.browse.browse_len = 3;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_BAD_CMD);
+
+ p = &browse_cmd_buf[3];
+ UINT16_TO_STREAM(p, 0x0000); // charset_id
+ UINT16_TO_STREAM(p, 0x0000); // str_len
+ msg.browse.browse_len = 7;
+ EXPECT_EQ(AVRC_ParsCommand(&msg, &result, scratch_buf, sizeof(scratch_buf)),
+ AVRC_STS_NO_ERROR);
+}