/* * Copyright 2018 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/bind.h> #include <base/logging.h> #include <base/message_loop/message_loop.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include "avrcp_internal.h" #include "avrcp_test_helper.h" #include "connection_handler.h" using ::testing::_; using ::testing::Return; using ::testing::SaveArg; using ::testing::SaveArgPointee; using ::testing::SetArgPointee; using ::testing::MockFunction; using ::testing::NiceMock; using ::testing::StrictMock; namespace bluetooth { namespace avrcp { using device_ptr = std::shared_ptr<Device>; class AvrcpConnectionHandlerTest : public testing::Test { public: void SetUp() override { ON_CALL(mock_avrcp_, Close(_)).WillByDefault(Return(0)); } void SetUpSdp(tAVRC_FIND_CBACK* sdp_cb, bool browsing, bool absolute_volume) { EXPECT_CALL(mock_avrcp_, FindService(_, _, _, _)) .Times(1) .WillOnce(DoAll(SaveArg<3>(sdp_cb), Return(0))); static tSDP_DISC_ATTR fake_features; fake_features = { .p_next_attr = nullptr, .attr_id = 0, .attr_len_type = 0, .attr_value.v.u16 = 0, }; if (browsing) fake_features.attr_value.v.u16 |= AVRC_SUPF_CT_BROWSE; if (absolute_volume) fake_features.attr_value.v.u16 |= AVRC_SUPF_CT_CAT2; EXPECT_CALL(mock_sdp_, FindAttributeInRec(_, _)) .Times(4) .WillRepeatedly(Return(&fake_features)); EXPECT_CALL(mock_sdp_, FindServiceInDb(_, _, _)) .Times(2) .WillOnce(Return((tSDP_DISC_REC*)0x01)) // Return any non null pointer .WillOnce(Return((tSDP_DISC_REC*)0x01)); // Return any non null pointer EXPECT_CALL(mock_sdp_, FindProfileVersionInRec(_, _, _)) .Times(2) .WillRepeatedly(DoAll(SetArgPointee<2>(AVRC_REV_1_6), Return(true))); } protected: ConnectionHandler* connection_handler_ = nullptr; // We use NiceMock's here because each function of this code does quite a few // operations. This way it is much easier to write a higher number of smaller // tests without having a large amount of warnings. NiceMock<MockFunction<void(device_ptr)>> device_cb; NiceMock<MockAvrcpInterface> mock_avrcp_; NiceMock<MockSdpInterface> mock_sdp_; NiceMock<MockVolumeInterface> mock_volume_; }; TEST_F(AvrcpConnectionHandlerTest, initializeTest) { // Set an Expectation that Open will be called as an acceptor and save the // connection callback once it is called tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(1) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))); auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Check that the callback was sent with us as the acceptor ASSERT_EQ(conn_cb.conn, 1); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } // Check that disconnecting without an active connection TEST_F(AvrcpConnectionHandlerTest, notConnectedDisconnectTest) { // Set an Expectation that Open will be called twice as an acceptor and save // the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(1) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Call the callback with a message saying the connection has closed conn_cb.ctrl_cback.Run(1, AVRC_CLOSE_IND_EVT, 0, &RawAddress::kAny); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); }; // Test calling the connection callback after the handler is cleaned up TEST_F(AvrcpConnectionHandlerTest, disconnectAfterCleanupTest) { // Set an Expectation that Open will be called twice as an acceptor and save // the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(1) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); // Call the callback with a message saying the connection has closed conn_cb.ctrl_cback.Run(1, AVRC_CLOSE_IND_EVT, 0, &RawAddress::kAny); }; // Check that we can handle having a remote device connect to us, start SDP, and // open another acceptor connection TEST_F(AvrcpConnectionHandlerTest, remoteDeviceConnectionTest) { // Set an Expectation that Open will be called twice as an acceptor and save // the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(2) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce( DoAll(SetArgPointee<0>(2), SaveArgPointee<1>(&conn_cb), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Check that the callback was sent with us as the acceptor ASSERT_EQ(conn_cb.conn, 1); // Set an Expectations that SDP will be performed tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, false, false); // Set an expectation that a device will be created EXPECT_CALL(device_cb, Call(_)).Times(1); // Set an Expectation that OpenBrowse will be called in acceptor mode when the // device connects. EXPECT_CALL(mock_avrcp_, OpenBrowse(1, AVCT_ACP)).Times(1); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); // Run the SDP callback with status success sdp_cb.Run(0); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } // Check that when a device does not support absolute volume, that the // handler reports that via the volume interface. TEST_F(AvrcpConnectionHandlerTest, noAbsoluteVolumeTest) { // Set an Expectation that Open will be called twice as an acceptor and save // the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(2) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce( DoAll(SetArgPointee<0>(2), SaveArgPointee<1>(&conn_cb), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Set an Expectations that SDP will be performed tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, false, false); EXPECT_CALL(mock_volume_, DeviceConnected(RawAddress::kAny)).Times(1); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); // Run the SDP callback with status success sdp_cb.Run(0); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } // Check that when a device does support absolute volume, that the handler // doesn't report it. Instead that will be left up to the device. TEST_F(AvrcpConnectionHandlerTest, absoluteVolumeTest) { // Set an Expectation that Open will be called twice as an acceptor and save // the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(2) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce( DoAll(SetArgPointee<0>(2), SaveArgPointee<1>(&conn_cb), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); StrictMock<MockVolumeInterface> strict_volume; ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &strict_volume)); connection_handler_ = ConnectionHandler::Get(); // Set an Expectations that SDP will be performed with absolute volume // supported tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, false, true); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); // Run the SDP callback with status success sdp_cb.Run(0); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } TEST_F(AvrcpConnectionHandlerTest, disconnectTest) { // Set an Expectation that Open will be called twice as an acceptor and save // the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(2) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce(DoAll(SetArgPointee<0>(2), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); // Set up the expectation that Close will be called EXPECT_CALL(mock_avrcp_, Close(1)).Times(1); // Call the callback with a message saying that a remote device has // disconnected conn_cb.ctrl_cback.Run(1, AVRC_CLOSE_IND_EVT, 0, &RawAddress::kAny); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } // Check that we can handle having a remote device connect to us, start SDP, and // open another acceptor connection TEST_F(AvrcpConnectionHandlerTest, multipleRemoteDeviceConnectionTest) { // Set an Expectation that Open will be called three times as an acceptor and // save the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(3) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce( DoAll(SetArgPointee<0>(2), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce( DoAll(SetArgPointee<0>(3), SaveArgPointee<1>(&conn_cb), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Check that the callback was sent with us as the acceptor ASSERT_EQ(conn_cb.conn, 1); // Set an Expectations that SDP will be performed tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, false, false); // Set an expectation that a device will be created EXPECT_CALL(device_cb, Call(_)).Times(1); // Set an Expectation that OpenBrowse will be called in acceptor mode when the // device connects on handle 1 EXPECT_CALL(mock_avrcp_, OpenBrowse(1, AVCT_ACP)).Times(1); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); // Run the SDP callback with status success sdp_cb.Run(0); // Set an Expectations that SDP will be performed again SetUpSdp(&sdp_cb, false, false); // Set an expectation that a device will be created again EXPECT_CALL(device_cb, Call(_)).Times(1); // Set an Expectation that OpenBrowse will be called in acceptor mode when the // device connects on handle 2 EXPECT_CALL(mock_avrcp_, OpenBrowse(2, AVCT_ACP)).Times(1); // Call the callback with a message saying that a remote device has connected // with a different address conn_cb.ctrl_cback.Run(2, AVRC_OPEN_IND_EVT, 0, &RawAddress::kEmpty); // Run the SDP callback with status success sdp_cb.Run(0); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } TEST_F(AvrcpConnectionHandlerTest, cleanupTest) { // Set Up Expectations for Initialize tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, _)) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce( DoAll(SetArgPointee<0>(2), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce( DoAll(SetArgPointee<0>(3), SaveArgPointee<1>(&conn_cb), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Call the callback twice with a message saying that a remote device has // connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); conn_cb.ctrl_cback.Run(2, AVRC_OPEN_IND_EVT, 0, &RawAddress::kEmpty); // Set an Expectation that Close will be called twice with handles 1 and 2 EXPECT_CALL(mock_avrcp_, Close(1)); EXPECT_CALL(mock_avrcp_, Close(2)); // Cleanup the object causing all open connections to be closed connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } TEST_F(AvrcpConnectionHandlerTest, connectToRemoteDeviceTest) { // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Set an Expectation that SDP will be performed tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, false, false); // Connect to the device which starts SDP connection_handler_->ConnectDevice(RawAddress::kEmpty); // Set an expectation that the handler will try to open an AVRCP connection // after doing SDP tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kEmpty)) .Times(1) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))); // Complete SDP sdp_cb.Run(0); // Check that the callback was sent with us as the initiator ASSERT_EQ(conn_cb.conn, 0); // Set an expectation that a device will be created EXPECT_CALL(device_cb, Call(_)).Times(1); // Set an Expectation that OpenBrowse will NOT be called since the SDP entry // didn't list browsing as a feature EXPECT_CALL(mock_avrcp_, OpenBrowse(_, _)).Times(0); // Call the callback with a message saying that a remote device has connected // with a different address conn_cb.ctrl_cback.Run(2, AVRC_OPEN_IND_EVT, 0, &RawAddress::kEmpty); // Cleanup the object causing all open connections to be closed connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } TEST_F(AvrcpConnectionHandlerTest, connectToBrowsableRemoteDeviceTest) { // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Set an Expectation that SDP will be performed tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, true, false); // Connect to the device which starts SDP connection_handler_->ConnectDevice(RawAddress::kEmpty); // Set an expectation that the handler will try to open an AVRCP connection // after doing SDP tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kEmpty)) .Times(1) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))); // Complete SDP sdp_cb.Run(0); // Check that the callback was sent with us as the initiator ASSERT_EQ(conn_cb.conn, 0); // Set an expectation that a device will be created EXPECT_CALL(device_cb, Call(_)).Times(1); // Set an Expectation that OpenBrowse will be called since browsing is listed // as supported in SDP EXPECT_CALL(mock_avrcp_, OpenBrowse(1, AVCT_INT)).Times(1); // Call the callback with a message saying that a remote device has connected // with a different address conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kEmpty); // Cleanup the object causing all open connections to be closed connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } TEST_F(AvrcpConnectionHandlerTest, disconnectWhileDoingSdpTest) { // Set an Expectation that Open will be called twice as an acceptor and save // the connection callback once it is called. tAVRC_CONN_CB conn_cb; EXPECT_CALL(mock_avrcp_, Open(_, _, RawAddress::kAny)) .Times(2) .WillOnce( DoAll(SetArgPointee<0>(1), SaveArgPointee<1>(&conn_cb), Return(0))) .WillOnce(DoAll(SetArgPointee<0>(2), Return(0))); // Initialize the interface auto bound_callback = base::Bind(&MockFunction<void(device_ptr)>::Call, base::Unretained(&device_cb)); ASSERT_TRUE(ConnectionHandler::Initialize(bound_callback, &mock_avrcp_, &mock_sdp_, &mock_volume_)); connection_handler_ = ConnectionHandler::Get(); // Set an Expectation that SDP will be performed tAVRC_FIND_CBACK sdp_cb; SetUpSdp(&sdp_cb, true, false); // Call the callback with a message saying that a remote device has connected conn_cb.ctrl_cback.Run(1, AVRC_OPEN_IND_EVT, 0, &RawAddress::kAny); // Call the callback with a message saying that a remote device has // disconnected conn_cb.ctrl_cback.Run(1, AVRC_CLOSE_IND_EVT, 0, &RawAddress::kAny); // Signal that SDP has completed sdp_cb.Run(0); connection_handler_ = nullptr; ConnectionHandler::CleanUp(); } } // namespace avrcp } // namespace bluetooth