| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "bluetooth/newblued/newblue.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <base/message_loop/message_loop.h> |
| #include <base/run_loop.h> |
| #include <gtest/gtest.h> |
| |
| #include "bluetooth/newblued/mock_libnewblue.h" |
| |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::ElementsAre; |
| using ::testing::Pair; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| |
| namespace bluetooth { |
| |
| namespace { |
| |
| constexpr uniq_t kDiscoveryHandle = 11; |
| // A random handle value. |
| constexpr uniq_t kPairStateChangeHandle = 3; |
| constexpr uniq_t kPasskeyDisplayObserverHandle = 4; |
| |
| } // namespace |
| |
| class TestPairingAgent : public PairingAgent { |
| public: |
| void DisplayPasskey(const std::string& device_address, |
| uint32_t passkey) override { |
| displayed_passkeys.push_back({device_address, passkey}); |
| } |
| std::vector<std::pair<std::string, uint32_t>> displayed_passkeys; |
| }; |
| |
| class NewblueTest : public ::testing::Test { |
| public: |
| // A dummy struct that hosts the device information from discovery callback. |
| struct MockDevice { |
| std::string address; |
| uint8_t address_type; |
| int8_t rssi; |
| uint8_t reply_type; |
| std::vector<uint8_t> eir; |
| PairState pair_state; |
| PairError pair_error; |
| }; |
| |
| void SetUp() override { |
| auto libnewblue = std::make_unique<MockLibNewblue>(); |
| libnewblue_ = libnewblue.get(); |
| newblue_ = std::make_unique<Newblue>(std::move(libnewblue)); |
| } |
| |
| bool StubHciUp(const uint8_t* address, |
| hciReadyForUpCbk callback, |
| void* callback_data) { |
| callback(callback_data); |
| return true; |
| } |
| |
| void OnReadyForUp() { is_ready_for_up_ = true; } |
| |
| void OnDeviceDiscovered(const std::string& address, |
| uint8_t address_type, |
| int8_t rssi, |
| uint8_t reply_type, |
| const std::vector<uint8_t>& eir) { |
| discovered_devices_.push_back( |
| {address, address_type, rssi, reply_type, eir}); |
| } |
| |
| void OnPairStateChanged(const std::string& address, |
| PairState pair_state, |
| PairError pair_error) { |
| for (auto& dev : discovered_devices_) { |
| if (dev.address == address) { |
| dev.pair_state = pair_state; |
| dev.pair_error = pair_error; |
| } |
| } |
| } |
| |
| void ExpectBringUp() { |
| newblue_->Init(); |
| EXPECT_CALL(*libnewblue_, HciIsUp()).WillOnce(Return(false)); |
| EXPECT_FALSE(newblue_->BringUp()); |
| |
| EXPECT_CALL(*libnewblue_, HciIsUp()).WillOnce(Return(true)); |
| EXPECT_CALL(*libnewblue_, L2cInit()).WillOnce(Return(0)); |
| EXPECT_CALL(*libnewblue_, AttInit()).WillOnce(Return(true)); |
| EXPECT_CALL(*libnewblue_, GattProfileInit()).WillOnce(Return(true)); |
| EXPECT_CALL(*libnewblue_, GattBuiltinInit()).WillOnce(Return(true)); |
| EXPECT_CALL(*libnewblue_, SmInit()).WillOnce(Return(true)); |
| EXPECT_CALL(*libnewblue_, SmRegisterPairStateObserver(_, _)) |
| .WillOnce(DoAll(SaveArg<0>(&pair_state_changed_callback_data_), |
| SaveArg<1>(&pair_state_changed_callback_), |
| Return(kPairStateChangeHandle))); |
| EXPECT_CALL(*libnewblue_, SmRegisterPasskeyDisplayObserver(_, _)) |
| .WillOnce(DoAll(SaveArg<0>(&passkey_display_callback_data_), |
| SaveArg<1>(&passkey_display_callback_), |
| Return(kPasskeyDisplayObserverHandle))); |
| EXPECT_TRUE(newblue_->BringUp()); |
| } |
| |
| protected: |
| base::MessageLoop message_loop_; |
| bool is_ready_for_up_ = false; |
| std::unique_ptr<Newblue> newblue_; |
| MockLibNewblue* libnewblue_; |
| std::vector<MockDevice> discovered_devices_; |
| smPairStateChangeCbk pair_state_changed_callback_; |
| void* pair_state_changed_callback_data_ = nullptr; |
| smPasskeyDisplayCbk passkey_display_callback_; |
| void* passkey_display_callback_data_ = nullptr; |
| }; |
| |
| TEST_F(NewblueTest, ListenReadyForUp) { |
| newblue_->Init(); |
| |
| hciReadyForUpCbk up_callback; |
| EXPECT_CALL(*libnewblue_, HciUp(_, _, _)) |
| .WillOnce(DoAll(SaveArg<1>(&up_callback), |
| Invoke(this, &NewblueTest::StubHciUp))); |
| bool success = newblue_->ListenReadyForUp( |
| base::Bind(&NewblueTest::OnReadyForUp, base::Unretained(this))); |
| EXPECT_TRUE(success); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(is_ready_for_up_); |
| |
| // If libnewblue says the stack is ready for up again, ignore that. |
| // We shouldn't bring up the stack more than once. |
| is_ready_for_up_ = false; |
| up_callback(newblue_.get()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(is_ready_for_up_); |
| } |
| |
| TEST_F(NewblueTest, ListenReadyForUpFailed) { |
| newblue_->Init(); |
| |
| EXPECT_CALL(*libnewblue_, HciUp(_, _, _)).WillOnce(Return(false)); |
| bool success = newblue_->ListenReadyForUp( |
| base::Bind(&NewblueTest::OnReadyForUp, base::Unretained(this))); |
| EXPECT_FALSE(success); |
| } |
| |
| TEST_F(NewblueTest, BringUp) { |
| ExpectBringUp(); |
| } |
| |
| TEST_F(NewblueTest, StartDiscovery) { |
| ExpectBringUp(); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, true, false)) |
| .WillOnce(DoAll(SaveArg<0>(&inquiry_response_callback), |
| SaveArg<1>(&inquiry_response_callback_data), |
| Return(kDiscoveryHandle))); |
| newblue_->StartDiscovery( |
| base::Bind(&NewblueTest::OnDeviceDiscovered, base::Unretained(this))); |
| |
| // 2 devices discovered. |
| struct bt_addr addr1 = {.type = BT_ADDR_TYPE_LE_RANDOM, |
| .addr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}; |
| uint8_t eir1[] = { |
| 6, static_cast<uint8_t>(EirType::NAME_SHORT), 'a', 'l', 'i', 'c', 'e'}; |
| inquiry_response_callback(inquiry_response_callback_data, &addr1, -101, |
| HCI_ADV_TYPE_SCAN_RSP, &eir1, arraysize(eir1)); |
| struct bt_addr addr2 = {.type = BT_ADDR_TYPE_LE_PUBLIC, |
| .addr = {0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}; |
| uint8_t eir2[] = { |
| 5, static_cast<uint8_t>(EirType::NAME_SHORT), 'b', 'o', 'b', '\0'}; |
| inquiry_response_callback(inquiry_response_callback_data, &addr2, -102, |
| HCI_ADV_TYPE_ADV_IND, &eir2, arraysize(eir2)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(2, discovered_devices_.size()); |
| EXPECT_EQ("06:05:04:03:02:01", discovered_devices_[0].address); |
| EXPECT_EQ(BT_ADDR_TYPE_LE_RANDOM, discovered_devices_[0].address_type); |
| EXPECT_EQ(-101, discovered_devices_[0].rssi); |
| EXPECT_EQ(HCI_ADV_TYPE_SCAN_RSP, discovered_devices_[0].reply_type); |
| EXPECT_EQ(std::vector<uint8_t>(eir1, eir1 + arraysize(eir1)), |
| discovered_devices_[0].eir); |
| |
| EXPECT_EQ("07:06:05:04:03:02", discovered_devices_[1].address); |
| EXPECT_EQ(-102, discovered_devices_[1].rssi); |
| EXPECT_EQ(BT_ADDR_TYPE_LE_PUBLIC, discovered_devices_[1].address_type); |
| EXPECT_EQ(-102, discovered_devices_[1].rssi); |
| EXPECT_EQ(HCI_ADV_TYPE_ADV_IND, discovered_devices_[1].reply_type); |
| EXPECT_EQ(std::vector<uint8_t>(eir2, eir2 + arraysize(eir2)), |
| discovered_devices_[1].eir); |
| |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(kDiscoveryHandle)) |
| .WillOnce(Return(true)); |
| newblue_->StopDiscovery(); |
| // Any inquiry response after StopDiscovery should be ignored. |
| inquiry_response_callback(inquiry_response_callback_data, &addr1, -101, |
| HCI_ADV_TYPE_SCAN_RSP, &eir1, arraysize(eir1)); |
| base::RunLoop().RunUntilIdle(); |
| // Check that discovered_devices_ is still the same. |
| EXPECT_EQ(2, discovered_devices_.size()); |
| } |
| |
| TEST_F(NewblueTest, PairStateChanged) { |
| ExpectBringUp(); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, true, false)) |
| .WillOnce(DoAll(SaveArg<0>(&inquiry_response_callback), |
| SaveArg<1>(&inquiry_response_callback_data), |
| Return(kDiscoveryHandle))); |
| newblue_->StartDiscovery( |
| base::Bind(&NewblueTest::OnDeviceDiscovered, base::Unretained(this))); |
| |
| // 1 device discovered. |
| struct bt_addr addr1 = {.type = BT_ADDR_TYPE_LE_RANDOM, |
| .addr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}; |
| uint8_t eir1[] = { |
| 6, static_cast<uint8_t>(EirType::NAME_SHORT), 'a', 'l', 'i', 'c', 'e'}; |
| inquiry_response_callback(inquiry_response_callback_data, &addr1, -101, |
| HCI_ADV_TYPE_SCAN_RSP, &eir1, arraysize(eir1)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(1, discovered_devices_.size()); |
| EXPECT_EQ("06:05:04:03:02:01", discovered_devices_[0].address); |
| EXPECT_EQ(-101, discovered_devices_[0].rssi); |
| EXPECT_EQ(PairState::NOT_PAIRED, discovered_devices_[0].pair_state); |
| |
| // Register as a pairing state observer. |
| UniqueId pair_observer_handle = newblue_->RegisterAsPairObserver( |
| base::Bind(&NewblueTest::OnPairStateChanged, base::Unretained(this))); |
| EXPECT_NE(kInvalidUniqueId, pair_observer_handle); |
| |
| // Pairing started. |
| struct smPairStateChange state_change = {.pairState = SM_PAIR_STATE_START, |
| .pairErr = SM_PAIR_ERR_NONE, |
| .peerAddr = addr1}; |
| pair_state_changed_callback_(pair_state_changed_callback_data_, &state_change, |
| kPairStateChangeHandle); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(1, discovered_devices_.size()); |
| EXPECT_EQ("06:05:04:03:02:01", discovered_devices_[0].address); |
| EXPECT_EQ(-101, discovered_devices_[0].rssi); |
| EXPECT_EQ(PairState::STARTED, discovered_devices_[0].pair_state); |
| EXPECT_EQ(PairError::NONE, discovered_devices_[0].pair_error); |
| |
| // Pairing failed with SM_PAIR_ERR_L2C_CONN error. |
| state_change.pairState = SM_PAIR_STATE_FAILED; |
| state_change.pairErr = SM_PAIR_ERR_L2C_CONN; |
| |
| pair_state_changed_callback_(pair_state_changed_callback_data_, &state_change, |
| kPairStateChangeHandle); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(1, discovered_devices_.size()); |
| EXPECT_EQ("06:05:04:03:02:01", discovered_devices_[0].address); |
| EXPECT_EQ(-101, discovered_devices_[0].rssi); |
| EXPECT_EQ(PairState::FAILED, discovered_devices_[0].pair_state); |
| EXPECT_EQ(PairError::L2C_CONN, discovered_devices_[0].pair_error); |
| } |
| |
| TEST_F(NewblueTest, Pair) { |
| ExpectBringUp(); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, true, false)) |
| .WillOnce(DoAll(SaveArg<0>(&inquiry_response_callback), |
| SaveArg<1>(&inquiry_response_callback_data), |
| Return(kDiscoveryHandle))); |
| newblue_->StartDiscovery( |
| base::Bind(&NewblueTest::OnDeviceDiscovered, base::Unretained(this))); |
| |
| // 1 device discovered. |
| struct bt_addr addr = {.type = BT_ADDR_TYPE_LE_RANDOM, |
| .addr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}; |
| std::string device_addr("06:05:04:03:02:01"); |
| uint8_t eir[] = { |
| // Flag |
| 3, static_cast<uint8_t>(EirType::FLAGS), 0xAA, 0xBB, |
| // Name |
| 6, static_cast<uint8_t>(EirType::NAME_SHORT), 'm', 'o', 'u', 's', 'e', |
| // Appearance |
| 3, static_cast<uint8_t>(EirType::GAP_APPEARANCE), 0xc2, 0x03}; |
| |
| inquiry_response_callback(inquiry_response_callback_data, &addr, -101, |
| HCI_ADV_TYPE_SCAN_RSP, &eir, arraysize(eir)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(1, discovered_devices_.size()); |
| EXPECT_EQ(device_addr, discovered_devices_[0].address); |
| EXPECT_EQ(-101, discovered_devices_[0].rssi); |
| EXPECT_EQ(PairState::NOT_PAIRED, discovered_devices_[0].pair_state); |
| |
| EXPECT_CALL(*libnewblue_, SmPair(_, _)).WillOnce(Return()); |
| |
| EXPECT_TRUE( |
| newblue_->Pair(device_addr, true, {.bond = false, .mitm = false})); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(NewblueTest, CancelPairing) { |
| ExpectBringUp(); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, true, false)) |
| .WillOnce(DoAll(SaveArg<0>(&inquiry_response_callback), |
| SaveArg<1>(&inquiry_response_callback_data), |
| Return(kDiscoveryHandle))); |
| newblue_->StartDiscovery( |
| base::Bind(&NewblueTest::OnDeviceDiscovered, base::Unretained(this))); |
| |
| // 1 device discovered. |
| struct bt_addr addr = {.type = BT_ADDR_TYPE_LE_RANDOM, |
| .addr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}; |
| std::string device_addr("06:05:04:03:02:01"); |
| uint8_t eir[] = { |
| // Flag |
| 3, static_cast<uint8_t>(EirType::FLAGS), 0xAA, 0xBB, |
| // Name |
| 6, static_cast<uint8_t>(EirType::NAME_SHORT), 'm', 'o', 'u', 's', 'e', |
| // Appearance |
| 3, static_cast<uint8_t>(EirType::GAP_APPEARANCE), 0xc2, 0x03}; |
| |
| inquiry_response_callback(inquiry_response_callback_data, &addr, -101, |
| HCI_ADV_TYPE_SCAN_RSP, &eir, arraysize(eir)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(1, discovered_devices_.size()); |
| EXPECT_EQ(device_addr, discovered_devices_[0].address); |
| EXPECT_EQ(-101, discovered_devices_[0].rssi); |
| EXPECT_EQ(PairState::NOT_PAIRED, discovered_devices_[0].pair_state); |
| |
| // Register as a pairing state observer. |
| UniqueId pair_observer_handle = newblue_->RegisterAsPairObserver( |
| base::Bind(&NewblueTest::OnPairStateChanged, base::Unretained(this))); |
| EXPECT_NE(kInvalidUniqueId, pair_observer_handle); |
| |
| EXPECT_CALL(*libnewblue_, SmPair(_, _)).WillOnce(Return()); |
| EXPECT_TRUE( |
| newblue_->Pair(device_addr, true, {.bond = false, .mitm = false})); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Pairing started. |
| struct smPairStateChange state_change = {.pairState = SM_PAIR_STATE_START, |
| .pairErr = SM_PAIR_ERR_NONE, |
| .peerAddr = addr}; |
| pair_state_changed_callback_(pair_state_changed_callback_data_, &state_change, |
| kPairStateChangeHandle); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(1, discovered_devices_.size()); |
| EXPECT_EQ("06:05:04:03:02:01", discovered_devices_[0].address); |
| EXPECT_EQ(-101, discovered_devices_[0].rssi); |
| EXPECT_EQ(PairState::STARTED, discovered_devices_[0].pair_state); |
| |
| // Cancel pairing. |
| EXPECT_CALL(*libnewblue_, SmUnpair(_)).WillOnce(Return()); |
| EXPECT_TRUE(newblue_->CancelPair(device_addr, true)); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(NewblueTest, PasskeyDisplayObserver) { |
| ExpectBringUp(); |
| |
| TestPairingAgent pairing_agent; |
| newblue_->RegisterPairingAgent(&pairing_agent); |
| |
| struct bt_addr peer_addr = {.type = BT_ADDR_TYPE_LE_RANDOM, |
| .addr = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}}; |
| struct smPasskeyDisplay passkey_display = { |
| .valid = true, .passkey = 123456, .peerAddr = peer_addr}; |
| passkey_display_callback_(passkey_display_callback_data_, &passkey_display, |
| kPasskeyDisplayObserverHandle); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(pairing_agent.displayed_passkeys, |
| ElementsAre(Pair("06:05:04:03:02:01", 123456))); |
| } |
| |
| } // namespace bluetooth |