/* * 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. */ #undef LOG_TAG // Undefine the LOG_TAG by this compilation unit #include "btif/src/btif_rc.cc" #include #include #include #include #include "bta/include/bta_av_api.h" #include "btif/avrcp/avrcp_service.h" #include "btif/include/btif_common.h" #include "common/message_loop_thread.h" #include "device/include/interop.h" #include "include/hardware/bt_rc.h" #include "stack/include/main_thread.h" #include "test/common/mock_functions.h" #include "test/mock/mock_osi_alarm.h" #include "test/mock/mock_osi_allocator.h" #include "test/mock/mock_osi_list.h" #include "types/raw_address.h" namespace bluetooth { namespace avrcp { int VolChanged = 0; AvrcpService* AvrcpService::instance_ = nullptr; void AvrcpService::SendMediaUpdate(bool /*track_changed*/, bool /*play_state*/, bool /*queue*/) {} void AvrcpService::SendFolderUpdate(bool /*available_players*/, bool /*addressed_players*/, bool /*uids*/) {} void AvrcpService::SendPlayerSettingsChanged(std::vector /*attributes*/, std::vector /*values*/) {} void AvrcpService::ServiceInterfaceImpl::Init( MediaInterface* /*media_interface*/, VolumeInterface* /*volume_interface*/, PlayerSettingsInterface* /*player_settings_interface*/) {} void AvrcpService::ServiceInterfaceImpl::RegisterBipServer(int /*psm*/) {} void AvrcpService::ServiceInterfaceImpl::UnregisterBipServer() {} bool AvrcpService::ServiceInterfaceImpl::ConnectDevice(const RawAddress& /*bdaddr*/) { return true; } bool AvrcpService::ServiceInterfaceImpl::DisconnectDevice(const RawAddress& /*bdaddr*/) { return true; } void AvrcpService::ServiceInterfaceImpl::SetBipClientStatus(const RawAddress& /*bdaddr*/, bool /*connected*/) {} bool AvrcpService::ServiceInterfaceImpl::Cleanup() { return true; } AvrcpService* AvrcpService::Get() { EXPECT_EQ(instance_, nullptr); instance_ = new AvrcpService(); return instance_; } void AvrcpService::RegisterVolChanged(const RawAddress& /*bdaddr*/) { VolChanged++; } } // namespace avrcp } // namespace bluetooth namespace { const RawAddress kDeviceAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66}); const uint8_t kRcHandle = 123; } // namespace void btif_av_clear_remote_suspend_flag(const A2dpType /*local_a2dp_type*/) {} bool btif_av_is_connected(const A2dpType /*local_a2dp_type*/) { return true; } bool btif_av_is_sink_enabled(void) { return true; } RawAddress btif_av_sink_active_peer(void) { return RawAddress(); } RawAddress btif_av_source_active_peer(void) { return RawAddress(); } bool btif_av_stream_started_ready(const A2dpType /*local_a2dp_type*/) { 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; } bool btif_av_src_sink_coexist_enabled() { return true; } bool btif_av_is_connected_addr(const RawAddress& /*peer_address*/, const A2dpType /*local_a2dp_type*/) { return true; } bool btif_av_peer_is_connected_sink(const RawAddress& /*peer_address*/) { return false; } bool btif_av_peer_is_connected_source(const RawAddress& /*peer_address*/) { return true; } bool btif_av_peer_is_sink(const RawAddress& /*peer_address*/) { return false; } bool btif_av_peer_is_source(const RawAddress& /*peer_address*/) { return true; } bool btif_av_both_enable(void) { return true; } static bluetooth::common::MessageLoopThread jni_thread("bt_jni_thread"); bt_status_t do_in_jni_thread(base::OnceClosure task) { if (!jni_thread.DoInThread(FROM_HERE, std::move(task))) { log::error("Post task to task runner failed!"); return BT_STATUS_JNI_THREAD_ATTACH_ERROR; } return BT_STATUS_SUCCESS; } bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; } bool interop_match_addr(const interop_feature_t /*feature*/, const RawAddress* /*addr*/) { return false; } /** * Test class to test selected functionality in hci/src/hci_layer.cc */ class BtifRcTest : public ::testing::Test { protected: void SetUp() override { reset_mock_function_count_map(); } void TearDown() override {} }; TEST_F(BtifRcTest, get_element_attr_rsp) { btif_rc_cb.rc_multi_cb[0].rc_addr = kDeviceAddress; 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; ASSERT_EQ(get_element_attr_rsp(kDeviceAddress, num_attr, p_attrs), BT_STATUS_SUCCESS); ASSERT_EQ(1, get_func_call_count("AVRC_BldResponse")); } TEST_F(BtifRcTest, btif_rc_get_addr_by_handle) { RawAddress bd_addr; btif_rc_cb.rc_multi_cb[0].rc_addr = kDeviceAddress; btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED; btif_rc_cb.rc_multi_cb[0].rc_handle = 0; btif_rc_get_addr_by_handle(0, bd_addr); ASSERT_EQ(kDeviceAddress, bd_addr); } static btrc_ctrl_callbacks_t default_btrc_ctrl_callbacks = { .size = sizeof(btrc_ctrl_callbacks_t), .passthrough_rsp_cb = [](const RawAddress& /* bd_addr */, int /* id */, int /* key_state */) { FAIL(); }, .groupnavigation_rsp_cb = [](int /* id */, int /* key_state */) { FAIL(); }, .connection_state_cb = [](bool /* rc_connect */, bool /* bt_connect */, const RawAddress& /* bd_addr */) { FAIL(); }, .getrcfeatures_cb = [](const RawAddress& /* bd_addr */, int /* features */) { FAIL(); }, .setplayerappsetting_rsp_cb = [](const RawAddress& /* bd_addr */, uint8_t /* accepted */) { FAIL(); }, .playerapplicationsetting_cb = [](const RawAddress& /* bd_addr */, uint8_t /* num_attr */, btrc_player_app_attr_t* /* app_attrs */, uint8_t /* num_ext_attr */, btrc_player_app_ext_attr_t* /* ext_attrs */) { FAIL(); }, .playerapplicationsetting_changed_cb = [](const RawAddress& /* bd_addr */, const btrc_player_settings_t& /* vals */) { FAIL(); }, .setabsvol_cmd_cb = [](const RawAddress& /* bd_addr */, uint8_t /* abs_vol */, uint8_t /* label */) { FAIL(); }, .registernotification_absvol_cb = [](const RawAddress& /* bd_addr */, uint8_t /* label */) { FAIL(); }, .track_changed_cb = [](const RawAddress& /* bd_addr */, uint8_t /* num_attr */, btrc_element_attr_val_t* /* p_attrs */) { FAIL(); }, .play_position_changed_cb = [](const RawAddress& /* bd_addr */, uint32_t /* song_len */, uint32_t /* song_pos */) { FAIL(); }, .play_status_changed_cb = [](const RawAddress& /* bd_addr */, btrc_play_status_t /* play_status */) { FAIL(); }, .get_folder_items_cb = [](const RawAddress& /* bd_addr */, btrc_status_t /* status */, const btrc_folder_items_t* /* folder_items */, uint8_t /* count */) { FAIL(); }, .change_folder_path_cb = [](const RawAddress& /* bd_addr */, uint32_t /* count */) { FAIL(); }, .set_browsed_player_cb = [](const RawAddress& /* bd_addr */, uint8_t /* num_items */, uint8_t /* depth */) { FAIL(); }, .set_addressed_player_cb = [](const RawAddress& /* bd_addr */, uint8_t /* status */) { FAIL(); }, .addressed_player_changed_cb = [](const RawAddress& /* bd_addr */, uint16_t /* id */) { FAIL(); }, .now_playing_contents_changed_cb = [](const RawAddress& /* bd_addr */) { FAIL(); }, .available_player_changed_cb = [](const RawAddress& /* bd_addr */) { FAIL(); }, .get_cover_art_psm_cb = [](const RawAddress& /* bd_addr */, const uint16_t /* psm */) { FAIL(); }, }; static btrc_ctrl_callbacks_t btrc_ctrl_callbacks = default_btrc_ctrl_callbacks; struct rc_connection_state_cb_t { bool rc_state; bool bt_state; RawAddress raw_address; }; struct rc_feature_cb_t { int feature; RawAddress raw_address; }; static std::promise g_btrc_connection_state_promise; static std::promise g_btrc_feature; class BtifRcWithCallbacksTest : public BtifRcTest { protected: void SetUp() override { BtifRcTest::SetUp(); btrc_ctrl_callbacks = default_btrc_ctrl_callbacks; init_ctrl(&btrc_ctrl_callbacks); jni_thread.StartUp(); btrc_ctrl_callbacks.getrcfeatures_cb = [](const RawAddress& bd_addr, int features) { rc_feature_cb_t rc_feature = { .feature = features, .raw_address = bd_addr, }; g_btrc_feature.set_value(rc_feature); }; } void TearDown() override { jni_thread.ShutDown(); bt_rc_ctrl_callbacks->getrcfeatures_cb = [](const RawAddress& /*bd_addr*/, int /*features*/) {}; btrc_ctrl_callbacks = default_btrc_ctrl_callbacks; BtifRcTest::TearDown(); } }; TEST_F(BtifRcWithCallbacksTest, handle_rc_ctrl_features) { g_btrc_feature = std::promise(); std::future future = g_btrc_feature.get_future(); btif_rc_device_cb_t p_dev; p_dev.peer_tg_features = (BTA_AV_FEAT_RCTG | BTA_AV_FEAT_ADV_CTRL | BTA_AV_FEAT_RCCT | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_BROWSE | BTA_AV_FEAT_COVER_ARTWORK); p_dev.rc_connected = true; handle_rc_ctrl_features(&p_dev); ASSERT_EQ(1, get_func_call_count("AVRC_BldCommand")); ASSERT_EQ(std::future_status::ready, future.wait_for(std::chrono::seconds(2))); auto res = future.get(); log::info("FEATURES:{}", res.feature); ASSERT_EQ(res.feature, (BTRC_FEAT_ABSOLUTE_VOLUME | BTRC_FEAT_METADATA | BTRC_FEAT_BROWSE | BTRC_FEAT_COVER_ARTWORK)); } class BtifRcBrowseConnectionTest : public BtifRcTest { protected: void SetUp() override { BtifRcTest::SetUp(); init_ctrl(&btrc_ctrl_callbacks); jni_thread.StartUp(); btrc_ctrl_callbacks.connection_state_cb = [](bool rc_state, bool bt_state, const RawAddress& bd_addr) { rc_connection_state_cb_t rc_connection_state = { .rc_state = rc_state, .bt_state = bt_state, .raw_address = bd_addr, }; g_btrc_connection_state_promise.set_value(rc_connection_state); }; } void TearDown() override { jni_thread.ShutDown(); bt_rc_ctrl_callbacks->connection_state_cb = [](bool /*rc_state*/, bool /*bt_state*/, const RawAddress& /*bd_addr*/) {}; BtifRcTest::TearDown(); } }; TEST_F(BtifRcBrowseConnectionTest, handle_rc_browse_connect) { g_btrc_connection_state_promise = std::promise(); std::future future = g_btrc_connection_state_promise.get_future(); tBTA_AV_RC_BROWSE_OPEN browse_data = { .rc_handle = 0, .peer_addr = {}, .status = BTA_AV_SUCCESS, }; btif_rc_cb.rc_multi_cb[0].rc_handle = 0; btif_rc_cb.rc_multi_cb[0].rc_addr = RawAddress::kEmpty; btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED; btif_rc_cb.rc_multi_cb[0].rc_connected = false; /* process unit test handle_rc_browse_connect */ handle_rc_browse_connect(&browse_data); ASSERT_EQ(std::future_status::ready, future.wait_for(std::chrono::seconds(2))); auto res = future.get(); ASSERT_TRUE(res.bt_state); } class BtifRcConnectionTest : public BtifRcTest { protected: void SetUp() override { BtifRcTest::SetUp(); init_ctrl(&btrc_ctrl_callbacks); jni_thread.StartUp(); g_btrc_connection_state_promise = std::promise(); g_btrc_connection_state_future = g_btrc_connection_state_promise.get_future(); btrc_ctrl_callbacks.connection_state_cb = [](bool rc_state, bool bt_state, const RawAddress& bd_addr) { rc_connection_state_cb_t rc_connection_state = { .rc_state = rc_state, .bt_state = bt_state, .raw_address = bd_addr, }; g_btrc_connection_state_promise.set_value(rc_connection_state); }; } void TearDown() override { jni_thread.ShutDown(); bt_rc_ctrl_callbacks->connection_state_cb = [](bool /*rc_state*/, bool /*bt_state*/, const RawAddress& /*bd_addr*/) {}; BtifRcTest::TearDown(); } std::future g_btrc_connection_state_future; }; TEST_F(BtifRcConnectionTest, btif_rc_connection_test) {} TEST_F(BtifRcConnectionTest, handle_rc_browse_connect) { tBTA_AV_RC_BROWSE_OPEN browse_data = { .rc_handle = 0, .peer_addr = {}, .status = BTA_AV_SUCCESS, }; btif_rc_cb.rc_multi_cb[0].rc_handle = 0; btif_rc_cb.rc_multi_cb[0].rc_addr = RawAddress::kEmpty; btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED; btif_rc_cb.rc_multi_cb[0].rc_connected = false; /* process unit test handle_rc_browse_connect */ handle_rc_browse_connect(&browse_data); ASSERT_EQ(std::future_status::ready, g_btrc_connection_state_future.wait_for(std::chrono::seconds(2))); auto res = g_btrc_connection_state_future.get(); ASSERT_TRUE(res.bt_state); } TEST_F(BtifRcConnectionTest, btif_rc_check_pending_cmd) { btif_rc_cb.rc_multi_cb[0].rc_handle = 0xff; btif_rc_cb.rc_multi_cb[0].rc_addr = kDeviceAddress; btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED; btif_rc_cb.rc_multi_cb[0].rc_connected = true; btif_rc_cb.rc_multi_cb[0].launch_cmd_pending |= (RC_PENDING_ACT_REG_VOL | RC_PENDING_ACT_GET_CAP | RC_PENDING_ACT_REPORT_CONN); btif_rc_check_pending_cmd(kDeviceAddress); ASSERT_EQ(1, get_func_call_count("AVRC_BldCommand")); ASSERT_EQ(std::future_status::ready, g_btrc_connection_state_future.wait_for(std::chrono::seconds(3))); auto res = g_btrc_connection_state_future.get(); ASSERT_TRUE(res.rc_state); } TEST_F(BtifRcConnectionTest, bt_av_rc_open_evt) { btrc_ctrl_callbacks.get_cover_art_psm_cb = [](const RawAddress& /* bd_addr */, const uint16_t /* psm */) {}; btrc_ctrl_callbacks.getrcfeatures_cb = [](const RawAddress& /* bd_addr */, int /* features */) {}; /* handle_rc_connect */ tBTA_AV data = { .rc_open = { .rc_handle = 0, .cover_art_psm = 0, .peer_features = 0, .peer_ct_features = 0, .peer_tg_features = (BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_RCTG | BTA_AV_FEAT_RCCT), .peer_addr = kDeviceAddress, .status = BTA_AV_SUCCESS, }, }; btif_rc_cb.rc_multi_cb[0].rc_handle = 0; btif_rc_cb.rc_multi_cb[0].rc_addr = RawAddress::kEmpty; btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_DISCONNECTED; btif_rc_cb.rc_multi_cb[0].rc_connected = false; btif_rc_handler(BTA_AV_RC_OPEN_EVT, &data); ASSERT_TRUE(btif_rc_cb.rc_multi_cb[data.rc_open.rc_handle].rc_connected); ASSERT_EQ(btif_rc_cb.rc_multi_cb[data.rc_open.rc_handle].rc_state, BTRC_CONNECTION_STATE_CONNECTED); ASSERT_EQ(std::future_status::ready, g_btrc_connection_state_future.wait_for(std::chrono::seconds(2))); auto res = g_btrc_connection_state_future.get(); ASSERT_TRUE(res.rc_state); } class BtifTrackChangeCBTest : public BtifRcTest { protected: void SetUp() override { BtifRcTest::SetUp(); init_ctrl(&btrc_ctrl_callbacks); jni_thread.StartUp(); btrc_ctrl_callbacks.track_changed_cb = [](const RawAddress& bd_addr, uint8_t /*num_attr*/, btrc_element_attr_val_t* /*p_attrs*/) { btif_rc_cb.rc_multi_cb[0].rc_addr = bd_addr; }; } void TearDown() override { jni_thread.ShutDown(); btrc_ctrl_callbacks.track_changed_cb = [](const RawAddress& /*bd_addr*/, uint8_t /*num_attr*/, btrc_element_attr_val_t* /*p_attrs*/) {}; BtifRcTest::TearDown(); } }; TEST_F(BtifTrackChangeCBTest, handle_get_metadata_attr_response) { // Setup an already connected device btif_rc_cb.rc_multi_cb[0].rc_connected = true; btif_rc_cb.rc_multi_cb[0].br_connected = false; btif_rc_cb.rc_multi_cb[0].rc_handle = kRcHandle; btif_rc_cb.rc_multi_cb[0].rc_features = {}; btif_rc_cb.rc_multi_cb[0].rc_cover_art_psm = 0; btif_rc_cb.rc_multi_cb[0].rc_state = BTRC_CONNECTION_STATE_CONNECTED; btif_rc_cb.rc_multi_cb[0].rc_addr = kDeviceAddress; btif_rc_cb.rc_multi_cb[0].rc_pending_play = 0; btif_rc_cb.rc_multi_cb[0].rc_volume = 0; btif_rc_cb.rc_multi_cb[0].rc_vol_label = 0; btif_rc_cb.rc_multi_cb[0].rc_supported_event_list = nullptr; btif_rc_cb.rc_multi_cb[0].rc_app_settings = {}; btif_rc_cb.rc_multi_cb[0].rc_play_status_timer = nullptr; btif_rc_cb.rc_multi_cb[0].rc_features_processed = false; btif_rc_cb.rc_multi_cb[0].rc_playing_uid = 0; btif_rc_cb.rc_multi_cb[0].rc_procedure_complete = false; btif_rc_cb.rc_multi_cb[0].peer_ct_features = {}; btif_rc_cb.rc_multi_cb[0].peer_tg_features = {}; btif_rc_cb.rc_multi_cb[0].launch_cmd_pending = 0; ASSERT_TRUE(btif_rc_get_device_by_handle(kRcHandle)); tBTA_AV_META_MSG meta_msg = { .rc_handle = kRcHandle, .len = 0, .label = 0, .code{}, .company_id = 0, .p_data = {}, .p_msg = nullptr, }; tAVRC_GET_ATTRS_RSP rsp = { .pdu = 0, .status = AVRC_STS_NO_ERROR, .opcode = 0, .num_attrs = 0, .p_attrs = nullptr, }; handle_get_metadata_attr_response(&meta_msg, &rsp); ASSERT_EQ(1, get_func_call_count("osi_free_and_reset")); }