| // 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_daemon.h" |
| |
| #include <cstdlib> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/memory/ref_counted.h> |
| #include <base/message_loop/message_loop.h> |
| #include <base/run_loop.h> |
| #include <base/values.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/mock_bus.h> |
| #include <dbus/mock_exported_object.h> |
| #include <dbus/mock_object_manager.h> |
| #include <dbus/mock_object_proxy.h> |
| #include <dbus/object_manager.h> |
| #include <dbus/values_util.h> |
| #include <gtest/gtest.h> |
| |
| #include "bluetooth/newblued/mock_libnewblue.h" |
| #include "bluetooth/newblued/util.h" |
| |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::Invoke; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| using ::testing::SaveArgPointee; |
| |
| namespace bluetooth { |
| |
| namespace { |
| |
| constexpr char kTestSender[] = ":1.1"; |
| constexpr char kTestSender2[] = ":1.2"; |
| constexpr int kTestSerial = 10; |
| constexpr char kTestDeviceAddress[] = "06:05:04:03:02:01"; |
| constexpr char kTestDeviceAddress2[] = "06:05:04:03:02:02"; |
| constexpr char kLatestAddress[] = "16:15:14:13:12:11"; |
| constexpr char kTestDeviceObjectPath[] = |
| "/org/bluez/hci0/dev_06_05_04_03_02_01"; |
| constexpr char kTestDeviceObjectPath2[] = |
| "/org/bluez/hci0/dev_06_05_04_03_02_02"; |
| constexpr char kUnknownDeviceObjectPath[] = |
| "/org/bluez/hci0/dev_FF_FF_FF_FF_FF_FF"; |
| |
| constexpr uniq_t kTestDiscoveryId = 7; |
| |
| constexpr gatt_client_conn_t kTestGattClientConnectionId = 3; |
| |
| const std::map<std::string, brillo::VariantDictionary> filters = { |
| {"clear", {}}, |
| {"classic_loose", |
| { |
| {"Transport", std::string{"bredr"}}, |
| {"RSSI", int16_t{-120}}, |
| {"Pathloss", uint16_t{120}}, |
| }}, |
| {"tight", |
| {{"Transport", std::string{"auto"}}, |
| {"RSSI", int16_t{-80}}, |
| {"Pathloss", uint16_t{20}}, |
| {"UUIDs", |
| std::vector<std::string>{"0000181e-0000-1000-8000-00805f9b34fb"}}}}, |
| {"loose", |
| { |
| {"Transport", std::string{"auto"}}, |
| {"RSSI", int16_t{-120}}, |
| {"Pathloss", uint16_t{120}}, |
| }}, |
| {"looser_rssi", |
| {{"Transport", std::string{"le"}}, |
| {"RSSI", int16_t{-100}}, |
| {"Pathloss", uint16_t{20}}, |
| {"UUIDs", |
| std::vector<std::string>{"0000181e-0000-1000-8000-00805f9b34fb"}}}}, |
| {"uuid", |
| {{"Transport", std::string{"le"}}, |
| {"RSSI", int16_t{-100}}, |
| {"Pathloss", uint16_t{20}}, |
| {"UUIDs", |
| std::vector<std::string>{"0000181f-0000-1000-8000-00805f9b34fb"}}}}}; |
| |
| constexpr uint8_t eir[] = { |
| // Flag |
| 3, static_cast<uint8_t>(EirType::FLAGS), 0xAA, 0xBB, |
| // UUID16_COMPLETE - Battery Service |
| 3, static_cast<uint8_t>(EirType::UUID16_COMPLETE), 0x0F, 0x18, |
| // UUID32_INCOMPLETE - Blood Pressure |
| 5, static_cast<uint8_t>(EirType::UUID32_INCOMPLETE), 0x10, 0x18, 0x00, 0x00, |
| // UUID128_COMPLETE |
| 17, static_cast<uint8_t>(EirType::UUID128_COMPLETE), 0xFB, 0x34, 0x9B, 0x5F, |
| 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x1E, 0x18, 0x00, 0x00, |
| // Name |
| 4, static_cast<uint8_t>(EirType::NAME_SHORT), 'f', 'o', 'o', |
| // TX Power |
| 2, static_cast<uint8_t>(EirType::TX_POWER), 0x0A, |
| // Class |
| 4, static_cast<uint8_t>(EirType::CLASS_OF_DEV), 0x01, 0x02, 0x03, |
| // Service data associated with 16-bit Battery Service UUID |
| 5, static_cast<uint8_t>(EirType::SVC_DATA16), 0x0F, 0x18, 0x22, 0x11, |
| // Service data associate with 32-bit Bond Management Service UUID |
| 7, static_cast<uint8_t>(EirType::SVC_DATA32), 0x1E, 0x18, 0x00, 0x00, 0x44, |
| 0x33, |
| // Appearance |
| 3, static_cast<uint8_t>(EirType::GAP_APPEARANCE), 0x01, 0x02, |
| // Manufacturer data |
| 5, static_cast<uint8_t>(EirType::MANUFACTURER_DATA), 0x0E, 0x00, 0x55, |
| 0x66}; |
| void SaveResponse(std::unique_ptr<dbus::Response>* saved_response, |
| std::unique_ptr<dbus::Response> response) { |
| *saved_response = std::move(response); |
| } |
| |
| } // namespace |
| |
| class NewblueDaemonTest : public ::testing::Test { |
| public: |
| using MethodHandlerMap = |
| std::map<std::string, dbus::ExportedObject::MethodCallCallback*>; |
| |
| void SetUp() override { |
| bus_ = new dbus::MockBus(dbus::Bus::Options()); |
| EXPECT_CALL(*bus_, GetDBusTaskRunner()) |
| .WillRepeatedly(Return(message_loop_.task_runner().get())); |
| EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber()); |
| EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber()); |
| EXPECT_CALL(*bus_, Connect()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*bus_, SetUpAsyncOperations()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*bus_, SendWithReplyAndBlock(_, _, _)).Times(AnyNumber()); |
| EXPECT_CALL(*bus_, AddFilterFunction(_, _)).Times(AnyNumber()); |
| EXPECT_CALL(*bus_, RemoveFilterFunction(_, _)).Times(AnyNumber()); |
| EXPECT_CALL(*bus_, AddMatch(_, _)).Times(AnyNumber()); |
| EXPECT_CALL(*bus_, RemoveMatch(_, _)).Times(AnyNumber()); |
| auto libnewblue = std::make_unique<MockLibNewblue>(); |
| libnewblue_ = libnewblue.get(); |
| auto newblue = std::make_unique<Newblue>(std::move(libnewblue)); |
| newblue_daemon_ = std::make_unique<NewblueDaemon>(std::move(newblue), |
| false /* is_idle_mode */); |
| SetupBluezObjectProxy(); |
| SetupBluezObjectManager(); |
| // Force MessageLoop to run all pending tasks as an effect of instantiating |
| // MockObjectManager. This is needed to avoid memory leak as pending tasks |
| // hold pointers. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| protected: |
| dbus::ExportedObject::MethodCallCallback* GetMethodHandler( |
| MethodHandlerMap method_handlers, const std::string& method_name) { |
| return base::ContainsKey(method_handlers, method_name) |
| ? method_handlers[method_name] |
| : &dummy_method_handler_; |
| } |
| |
| // Expects that the standard methods on org.freedesktop.DBus.Properties |
| // interface are exported. |
| void ExpectPropertiesMethodsExportedAsync( |
| scoped_refptr<dbus::MockExportedObject> exported_object) { |
| EXPECT_CALL(*exported_object, ExportMethod(dbus::kPropertiesInterface, |
| dbus::kPropertiesGet, _, _)) |
| .Times(1); |
| EXPECT_CALL(*exported_object, ExportMethod(dbus::kPropertiesInterface, |
| dbus::kPropertiesSet, _, _)) |
| .Times(1); |
| EXPECT_CALL(*exported_object, ExportMethod(dbus::kPropertiesInterface, |
| dbus::kPropertiesGetAll, _, _)) |
| .Times(1); |
| } |
| |
| // Expects that the standard methods on org.freedesktop.DBus.Properties |
| // interface are exported. |
| void ExpectPropertiesMethodsExported( |
| scoped_refptr<dbus::MockExportedObject> exported_object, |
| MethodHandlerMap method_handlers) { |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock(dbus::kPropertiesInterface, |
| dbus::kPropertiesGet, _)) |
| .WillOnce(DoAll( |
| SaveArg<2>(GetMethodHandler(method_handlers, dbus::kPropertiesGet)), |
| Return(true))); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock(dbus::kPropertiesInterface, |
| dbus::kPropertiesSet, _)) |
| .WillOnce(DoAll( |
| SaveArg<2>(GetMethodHandler(method_handlers, dbus::kPropertiesSet)), |
| Return(true))); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock(dbus::kPropertiesInterface, |
| dbus::kPropertiesGetAll, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler(method_handlers, |
| dbus::kPropertiesGetAll)), |
| Return(true))); |
| } |
| |
| // Expects that the methods on org.bluez.Device1 interface are exported. |
| void ExpectDeviceMethodsExported( |
| scoped_refptr<dbus::MockExportedObject> exported_object, |
| MethodHandlerMap method_handlers) { |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kPair, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler(method_handlers, |
| bluetooth_device::kPair)), |
| Return(true))); |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kCancelPairing, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler( |
| method_handlers, bluetooth_device::kCancelPairing)), |
| Return(true))); |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kConnect, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler( |
| method_handlers, bluetooth_device::kConnect)), |
| Return(true))); |
| |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kDisconnect, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler( |
| method_handlers, bluetooth_device::kDisconnect)), |
| Return(true))); |
| |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kExecuteWrite, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler( |
| method_handlers, bluetooth_device::kExecuteWrite)), |
| Return(true))); |
| } |
| |
| // Expects that the methods on org.bluez.Device1 interface are unexported. |
| void ExpectDeviceMethodsUnexported( |
| scoped_refptr<dbus::MockExportedObject> exported_object) { |
| EXPECT_CALL( |
| *exported_object, |
| UnexportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kPair)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| *exported_object, |
| UnexportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kCancelPairing)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| *exported_object, |
| UnexportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kConnect)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| *exported_object, |
| UnexportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kDisconnect)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| *exported_object, |
| UnexportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kExecuteWrite)) |
| .WillOnce(Return(true)); |
| } |
| |
| // Expects that the methods on org.bluez.AdvertisingManager1 interface are |
| // exported. |
| void ExpectAdvertisingManagerMethodsExported( |
| scoped_refptr<dbus::MockExportedObject> exported_object) { |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock( |
| bluetooth_advertising_manager:: |
| kBluetoothAdvertisingManagerInterface, |
| bluetooth_advertising_manager::kRegisterAdvertisement, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock( |
| bluetooth_advertising_manager:: |
| kBluetoothAdvertisingManagerInterface, |
| bluetooth_advertising_manager::kUnregisterAdvertisement, _)) |
| .WillOnce(Return(true)); |
| } |
| |
| // Expects that the methods on org.bluez.AgentManager1 interface are |
| // exported. |
| void ExpectAgentManagerMethodsExported( |
| scoped_refptr<dbus::MockExportedObject> exported_object) { |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock( |
| bluetooth_agent_manager::kBluetoothAgentManagerInterface, |
| bluetooth_agent_manager::kRegisterAgent, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock( |
| bluetooth_agent_manager::kBluetoothAgentManagerInterface, |
| bluetooth_agent_manager::kUnregisterAgent, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock( |
| bluetooth_agent_manager::kBluetoothAgentManagerInterface, |
| bluetooth_agent_manager::kRequestDefaultAgent, _)) |
| .WillOnce(Return(true)); |
| } |
| |
| scoped_refptr<dbus::MockExportedObject> AddOrGetMockExportedObject( |
| const dbus::ObjectPath& object_path) { |
| if (base::ContainsKey(mock_exported_objects_, object_path)) |
| return mock_exported_objects_[object_path]; |
| |
| scoped_refptr<dbus::MockExportedObject> exported_object = |
| new dbus::MockExportedObject(bus_.get(), object_path); |
| mock_exported_objects_[object_path] = exported_object; |
| return exported_object; |
| } |
| |
| void RemoveMockExportedObject(const dbus::ObjectPath& object_path) { |
| if (base::ContainsKey(mock_exported_objects_, object_path)) |
| mock_exported_objects_.erase(object_path); |
| } |
| |
| void ExpectDeviceObjectExported(const dbus::ObjectPath& device_object_path, |
| MethodHandlerMap method_handlers) { |
| scoped_refptr<dbus::MockExportedObject> exported_dev_object = |
| AddOrGetMockExportedObject(device_object_path); |
| ExpectDeviceMethodsExported(exported_dev_object, method_handlers); |
| ExpectPropertiesMethodsExported(exported_dev_object, method_handlers); |
| EXPECT_CALL(*bus_, GetExportedObject(device_object_path)) |
| .WillOnce(Return(exported_dev_object.get())); |
| EXPECT_CALL(*exported_dev_object, SendSignal(_)).Times(AnyNumber()); |
| } |
| |
| void ExpectDeviceObjectUnexported( |
| const dbus::ObjectPath& device_object_path) { |
| scoped_refptr<dbus::MockExportedObject> exported_dev_object = |
| AddOrGetMockExportedObject(device_object_path); |
| ExpectDeviceMethodsUnexported(exported_dev_object); |
| EXPECT_CALL(*exported_dev_object, Unregister()).Times(1); |
| } |
| |
| void ExpectDeviceObjectNotExported( |
| const dbus::ObjectPath& device_object_path) { |
| scoped_refptr<dbus::MockExportedObject> exported_dev_object = |
| AddOrGetMockExportedObject(device_object_path); |
| EXPECT_CALL(*bus_, GetExportedObject(device_object_path)).Times(0); |
| EXPECT_CALL(*exported_dev_object, SendSignal(_)).Times(0); |
| } |
| |
| scoped_refptr<dbus::MockExportedObject> SetupExportedRootObject() { |
| dbus::ObjectPath root_path( |
| newblue_object_manager::kNewblueObjectManagerServicePath); |
| scoped_refptr<dbus::MockExportedObject> exported_root_object = |
| new dbus::MockExportedObject(bus_.get(), root_path); |
| EXPECT_CALL(*bus_, GetExportedObject(root_path)) |
| .WillRepeatedly(Return(exported_root_object.get())); |
| EXPECT_CALL(*exported_root_object, SendSignal(_)).Times(AnyNumber()); |
| return exported_root_object; |
| } |
| |
| scoped_refptr<dbus::MockExportedObject> SetupExportedAgentManagerObject() { |
| dbus::ObjectPath agent_manager_path( |
| bluetooth_agent_manager::kBluetoothAgentManagerServicePath); |
| scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object = |
| new dbus::MockExportedObject(bus_.get(), agent_manager_path); |
| EXPECT_CALL(*bus_, GetExportedObject(agent_manager_path)) |
| .WillRepeatedly(Return(exported_agent_manager_object.get())); |
| return exported_agent_manager_object; |
| } |
| |
| scoped_refptr<dbus::MockExportedObject> SetupExportedAdapterObject() { |
| dbus::ObjectPath adapter_object_path(kAdapterObjectPath); |
| scoped_refptr<dbus::MockExportedObject> exported_adapter_object = |
| new dbus::MockExportedObject(bus_.get(), adapter_object_path); |
| EXPECT_CALL(*bus_, GetExportedObject(adapter_object_path)) |
| .WillRepeatedly(Return(exported_adapter_object.get())); |
| return exported_adapter_object; |
| } |
| |
| void SetupBluezObjectProxy() { |
| dbus::ObjectPath bluez_object_path( |
| bluez_object_manager::kBluezObjectManagerServicePath); |
| bluez_object_proxy_ = new dbus::MockObjectProxy( |
| bus_.get(), bluez_object_manager::kBluezObjectManagerServiceName, |
| bluez_object_path); |
| EXPECT_CALL(*bus_, GetObjectProxy( |
| bluez_object_manager::kBluezObjectManagerServiceName, |
| bluez_object_path)) |
| .WillRepeatedly(Return(bluez_object_proxy_.get())); |
| EXPECT_CALL(*bluez_object_proxy_, SetNameOwnerChangedCallback(_)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*bluez_object_proxy_, ConnectToSignal(_, _, _, _)) |
| .Times(AnyNumber()); |
| |
| dbus::ObjectPath bluetooth_object_path( |
| bluez_object_manager::kBluezObjectManagerServicePath); |
| bluetooth_object_proxy_ = new dbus::MockObjectProxy( |
| bus_.get(), |
| bluetooth_object_manager::kBluetoothObjectManagerServiceName, |
| bluetooth_object_path); |
| EXPECT_CALL( |
| *bus_, GetObjectProxy( |
| bluetooth_object_manager::kBluetoothObjectManagerServiceName, |
| bluetooth_object_path)) |
| .WillRepeatedly(Return(bluetooth_object_proxy_.get())); |
| } |
| |
| void SetupBluezObjectManager() { |
| dbus::ObjectPath bluez_object_path( |
| bluez_object_manager::kBluezObjectManagerServicePath); |
| bluez_object_manager_ = new dbus::MockObjectManager( |
| bus_.get(), bluez_object_manager::kBluezObjectManagerServiceName, |
| bluez_object_path); |
| EXPECT_CALL(*bus_, GetObjectManager( |
| bluez_object_manager::kBluezObjectManagerServiceName, |
| bluez_object_path)) |
| .WillRepeatedly(Return(bluez_object_manager_.get())); |
| |
| dbus::ObjectPath bluetooth_object_path( |
| bluetooth_object_manager::kBluetoothObjectManagerServicePath); |
| bluetooth_object_manager_ = new dbus::MockObjectManager( |
| bus_.get(), |
| bluetooth_object_manager::kBluetoothObjectManagerServiceName, |
| bluetooth_object_path); |
| EXPECT_CALL( |
| *bus_, GetObjectManager( |
| bluetooth_object_manager::kBluetoothObjectManagerServiceName, |
| bluetooth_object_path)) |
| .WillRepeatedly(Return(bluetooth_object_manager_.get())); |
| } |
| |
| struct smKnownDevNode* CreateSmKnownDeviceNode(const std::string& address, |
| bool is_random_address, |
| bool is_paired, |
| std::string name) { |
| struct smKnownDevNode* node = |
| (struct smKnownDevNode*)calloc(1, sizeof(struct smKnownDevNode)); |
| ConvertToBtAddr(is_random_address, address, &node->addr); |
| node->isPaired = is_paired; |
| node->name = strdup(name.c_str()); |
| return node; |
| } |
| |
| struct smKnownDevNode* StubGetKnownDevices() { |
| struct smKnownDevNode* node1 = CreateSmKnownDeviceNode( |
| "01:AA:BB:CC:DD:EE", /* is_random_address */ true, |
| /* is_paired */ true, "Test Device 1"); |
| struct smKnownDevNode* node2 = CreateSmKnownDeviceNode( |
| "02:AA:BB:CC:DD:EE", /* is_random_address */ true, |
| /* is_paired */ false, "Test Device 2"); |
| struct smKnownDevNode* node3 = CreateSmKnownDeviceNode( |
| "03:AA:BB:CC:DD:EE", /* is_random_address */ false, |
| /* is_paired */ true, "Test Device 3"); |
| node1->next = node2; |
| node2->next = node3; |
| node3->next = nullptr; |
| return node1; |
| } |
| |
| void ExpectTestInit( |
| scoped_refptr<dbus::MockExportedObject> exported_root_object) { |
| EXPECT_CALL(*bus_, |
| RequestOwnershipAndBlock( |
| newblue_object_manager::kNewblueObjectManagerServiceName, |
| dbus::Bus::REQUIRE_PRIMARY)) |
| .WillOnce(Return(true)); |
| |
| // Standard methods on org.freedesktop.DBus.ObjectManager interface should |
| // be exported. |
| EXPECT_CALL(*exported_root_object, |
| ExportMethod(dbus::kObjectManagerInterface, |
| dbus::kObjectManagerGetManagedObjects, _, _)) |
| .Times(1); |
| // Standard methods on org.freedesktop.DBus.Properties interface should be |
| // exported. |
| ExpectPropertiesMethodsExportedAsync(exported_root_object); |
| } |
| |
| void TestInit(MethodHandlerMap adapter_method_handlers) { |
| exported_root_object_ = SetupExportedRootObject(); |
| exported_agent_manager_object_ = SetupExportedAgentManagerObject(); |
| exported_adapter_object_ = SetupExportedAdapterObject(); |
| ExpectPropertiesMethodsExported(exported_adapter_object_, |
| adapter_method_handlers); |
| ExpectAdvertisingManagerMethodsExported(exported_adapter_object_); |
| ExpectPropertiesMethodsExported(exported_agent_manager_object_, |
| adapter_method_handlers); |
| ExpectAgentManagerMethodsExported(exported_agent_manager_object_); |
| |
| ExpectTestInit(exported_root_object_); |
| |
| EXPECT_CALL(*libnewblue_, HciUp(_, _, _)).WillOnce(Return(true)); |
| EXPECT_TRUE(newblue_daemon_->Init( |
| bus_, nullptr /* no need to access the delegator */)); |
| } |
| |
| void TestDeinit() { |
| EXPECT_CALL(*exported_root_object_, Unregister()).Times(1); |
| EXPECT_CALL(*exported_adapter_object_, Unregister()).Times(1); |
| EXPECT_CALL(*exported_agent_manager_object_, Unregister()).Times(1); |
| // Expect that all the exported objects are unregistered. |
| for (const auto it : mock_exported_objects_) { |
| scoped_refptr<dbus::MockExportedObject> mock_exported_object = it.second; |
| EXPECT_CALL(*mock_exported_object, Unregister()).Times(1); |
| } |
| // Shutdown now to make sure ExportedObjectManagerWrapper is destructed |
| // first before the mocked objects. |
| newblue_daemon_->Shutdown(); |
| } |
| |
| // |with_saved_devices| controls whether there is paired devices saved in |
| // persist. Useful for some tests that want to start with a clean device list. |
| void TestAdapterBringUp( |
| scoped_refptr<dbus::MockExportedObject> exported_adapter_object, |
| MethodHandlerMap adapter_method_handlers, |
| bool with_saved_devices) { |
| // org.bluez.Adapter1 methods |
| EXPECT_CALL( |
| *exported_adapter_object, |
| ExportMethodAndBlock(bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kSetDiscoveryFilter, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler( |
| adapter_method_handlers, |
| bluetooth_adapter::kSetDiscoveryFilter)), |
| Return(true))); |
| |
| EXPECT_CALL( |
| *exported_adapter_object, |
| ExportMethodAndBlock(bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kStartDiscovery, _)) |
| .WillOnce(DoAll( |
| SaveArg<2>(GetMethodHandler(adapter_method_handlers, |
| bluetooth_adapter::kStartDiscovery)), |
| Return(true))); |
| |
| EXPECT_CALL( |
| *exported_adapter_object, |
| ExportMethodAndBlock(bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kStopDiscovery, _)) |
| .WillOnce(DoAll( |
| SaveArg<2>(GetMethodHandler(adapter_method_handlers, |
| bluetooth_adapter::kStopDiscovery)), |
| Return(true))); |
| |
| EXPECT_CALL( |
| *exported_adapter_object, |
| ExportMethodAndBlock(bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kRemoveDevice, _)) |
| .WillOnce(DoAll( |
| SaveArg<2>(GetMethodHandler(adapter_method_handlers, |
| bluetooth_adapter::kRemoveDevice)), |
| Return(true))); |
| |
| EXPECT_CALL( |
| *exported_adapter_object, |
| ExportMethodAndBlock(bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kHandleSuspendImminent, _)) |
| .WillOnce(DoAll(SaveArg<2>(GetMethodHandler( |
| adapter_method_handlers, |
| bluetooth_adapter::kHandleSuspendImminent)), |
| Return(true))); |
| |
| EXPECT_CALL( |
| *exported_adapter_object, |
| ExportMethodAndBlock(bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kHandleSuspendDone, _)) |
| .WillOnce(DoAll( |
| SaveArg<2>(GetMethodHandler(adapter_method_handlers, |
| bluetooth_adapter::kHandleSuspendDone)), |
| Return(true))); |
| |
| 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_, SmRegisterPasskeyDisplayObserver(_, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*libnewblue_, SmRegisterPairStateObserver(_, _)) |
| .WillOnce(DoAll(SaveArg<0>(&pair_state_callback_data_), |
| SaveArg<1>(&pair_state_callback_), Return(true))); |
| EXPECT_CALL(*libnewblue_, BtleHidInit(_, _)).Times(1); |
| |
| struct smKnownDevNode* known_devices = nullptr; |
| if (with_saved_devices) { |
| // At initialization, newblued should export the saved paired devices. |
| ExpectDeviceObjectExported( |
| dbus::ObjectPath("/org/bluez/hci0/dev_01_AA_BB_CC_DD_EE"), {}); |
| ExpectDeviceObjectExported( |
| dbus::ObjectPath("/org/bluez/hci0/dev_03_AA_BB_CC_DD_EE"), {}); |
| known_devices = StubGetKnownDevices(); |
| } |
| EXPECT_CALL(*libnewblue_, SmGetKnownDevices()) |
| .WillOnce(Return(known_devices)); |
| EXPECT_CALL(*libnewblue_, SmKnownDevicesFree(known_devices)) |
| .WillOnce(Invoke(&smKnownDevicesFree)); |
| |
| // Listens to BlueZ's Adapter1 interface for monitoring StackSyncQuitting. |
| EXPECT_CALL( |
| *bluez_object_manager_, |
| RegisterInterface(bluetooth_adapter::kBluetoothAdapterInterface, _)) |
| .Times(1); |
| |
| newblue_daemon_->OnHciReadyForUp(); |
| } |
| |
| void ConstructRemoveDeviceMethodCall( |
| dbus::MethodCall* remove_device_method_call, |
| const std::string device_object_path) { |
| remove_device_method_call->SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| remove_device_method_call->SetSender(kTestSender); |
| remove_device_method_call->SetSerial(kTestSerial); |
| dbus::MessageWriter writer(remove_device_method_call); |
| writer.AppendObjectPath(dbus::ObjectPath(device_object_path)); |
| } |
| |
| void CallSetDiscoveryFilterMethod( |
| dbus::ExportedObject::MethodCallCallback set_discovery_filter_handler, |
| std::string sender, |
| std::string filter_type) { |
| // Initialization for Set Discovery Filter method. |
| dbus::MethodCall set_discovery_filter_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kSetDiscoveryFilter); |
| std::unique_ptr<dbus::Response> set_discovery_filter_response; |
| set_discovery_filter_method_call.SetPath( |
| dbus::ObjectPath(kAdapterObjectPath)); |
| set_discovery_filter_method_call.SetSender(sender); |
| set_discovery_filter_method_call.SetSerial(kTestSerial); |
| |
| dbus::MessageWriter writer(&set_discovery_filter_method_call); |
| brillo::dbus_utils::AppendValueToWriter(&writer, |
| filters.find(filter_type)->second); |
| |
| set_discovery_filter_handler.Run( |
| &set_discovery_filter_method_call, |
| base::Bind(&SaveResponse, &set_discovery_filter_response)); |
| } |
| |
| // Tests org.bluez.Adapter1.StartDiscovery |
| void TestStartDiscovery( |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler, |
| hciDeviceDiscoveredLeCbk* inquiry_response_callback, |
| void** inquiry_response_callback_data) { |
| // StartDiscovery by the first client, it should return D-Bus success and |
| // should trigger NewBlue StartDiscovery. |
| dbus::MethodCall start_discovery_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kStartDiscovery); |
| start_discovery_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| start_discovery_method_call.SetSender(kTestSender); |
| start_discovery_method_call.SetSerial(kTestSerial); |
| std::unique_ptr<dbus::Response> start_discovery_response; |
| Newblue::DeviceDiscoveredCallback on_device_discovered; |
| EXPECT_CALL(*libnewblue_, |
| HciDiscoverLeStart(_, _, /* active */ true, _, _, _, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(inquiry_response_callback), |
| SaveArg<1>(inquiry_response_callback_data), |
| Return(kTestDiscoveryId))); |
| start_discovery_handler.Run( |
| &start_discovery_method_call, |
| base::Bind(&SaveResponse, &start_discovery_response)); |
| EXPECT_EQ("", start_discovery_response->GetErrorName()); |
| ASSERT_NE(nullptr, *inquiry_response_callback); |
| ASSERT_NE(nullptr, *inquiry_response_callback_data); |
| // StartDiscovery again by the same client, it should return D-Bus error and |
| // should not affect NewBlue discovery state. |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, _, _, _, _, _, _)) |
| .Times(0); |
| start_discovery_handler.Run( |
| &start_discovery_method_call, |
| base::Bind(&SaveResponse, &start_discovery_response)); |
| EXPECT_EQ(bluetooth_adapter::kErrorInProgress, |
| start_discovery_response->GetErrorName()); |
| // StartDiscovery by a different client, it should return D-Bus success and |
| // should not affect NewBlue discovery state since it has already been |
| // started. |
| start_discovery_method_call.SetSender(kTestSender2); |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, _, _, _, _, _, _)) |
| .Times(0); |
| start_discovery_handler.Run( |
| &start_discovery_method_call, |
| base::Bind(&SaveResponse, &start_discovery_response)); |
| EXPECT_EQ("", start_discovery_response->GetErrorName()); |
| } |
| |
| void TestStopDiscovery( |
| dbus::ExportedObject::MethodCallCallback stop_discovery_handler) { |
| // StopDiscovery by the first client, it should return D-Bus success and |
| // should not affect NewBlue discovery state since there is still another |
| // client having discovery session. |
| dbus::MethodCall stop_discovery_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kStopDiscovery); |
| stop_discovery_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| stop_discovery_method_call.SetSender(kTestSender); |
| stop_discovery_method_call.SetSerial(kTestSerial); |
| std::unique_ptr<dbus::Response> stop_discovery_response; |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(_)).Times(0); |
| stop_discovery_handler.Run( |
| &stop_discovery_method_call, |
| base::Bind(&SaveResponse, &stop_discovery_response)); |
| EXPECT_EQ("", stop_discovery_response->GetErrorName()); |
| // StopDiscovery again by the same client, it should return D-Bus error, and |
| // should not affect the NewBlue discovery state. |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(_)).Times(0); |
| stop_discovery_handler.Run( |
| &stop_discovery_method_call, |
| base::Bind(&SaveResponse, &stop_discovery_response)); |
| EXPECT_EQ(bluetooth_adapter::kErrorFailed, |
| stop_discovery_response->GetErrorName()); |
| // StopDiscovery by the other client, it should return D-Bus success, and |
| // should trigger NewBlue's StopDiscovery since there is no more client |
| // having a discovery session. |
| stop_discovery_method_call.SetSender(kTestSender2); |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(kTestDiscoveryId)) |
| .WillOnce(Return(true)); |
| stop_discovery_handler.Run( |
| &stop_discovery_method_call, |
| base::Bind(&SaveResponse, &stop_discovery_response)); |
| EXPECT_EQ("", stop_discovery_response->GetErrorName()); |
| } |
| std::unique_ptr<dbus::Response> ReadProperty( |
| const std::string& interface_name, |
| const std::string& property_name, |
| dbus::ExportedObject::MethodCallCallback get_property_handler, |
| const char* sender, |
| int serial) { |
| std::unique_ptr<dbus::Response> get_property_response; |
| dbus::MethodCall get_property_method_call(dbus::kPropertiesInterface, |
| dbus::kPropertiesGet); |
| get_property_method_call.SetSender(sender); |
| get_property_method_call.SetSerial(serial); |
| |
| dbus::MessageWriter get_property_message_writer(&get_property_method_call); |
| get_property_message_writer.AppendString(interface_name); |
| get_property_message_writer.AppendString(property_name); |
| |
| get_property_handler.Run(&get_property_method_call, |
| base::Bind(&SaveResponse, &get_property_response)); |
| |
| EXPECT_TRUE(get_property_response != nullptr); |
| return get_property_response; |
| } |
| |
| // Tests org.bluez.Device1.Connect() and org.bluez.Device1.Disconnect() |
| void TestConnectDisconnect( |
| dbus::ExportedObject::MethodCallCallback connect_handler, |
| dbus::ExportedObject::MethodCallCallback disconnect_handler, |
| dbus::ExportedObject::MethodCallCallback get_property_handler, |
| const struct bt_addr* address) { |
| dbus::MethodCall connect_method_call( |
| bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kConnect); |
| connect_method_call.SetSender(kTestSender); |
| connect_method_call.SetSerial(kTestSerial); |
| |
| // Unknown device path |
| std::unique_ptr<dbus::Response> failed_connect_response; |
| connect_method_call.SetPath(dbus::ObjectPath(kUnknownDeviceObjectPath)); |
| EXPECT_CALL(*libnewblue_, GattClientConnect(_, _, _)).Times(0); |
| connect_handler.Run(&connect_method_call, |
| base::Bind(&SaveResponse, &failed_connect_response)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(bluetooth_device::kErrorDoesNotExist, |
| failed_connect_response->GetErrorName()); |
| |
| // GattClientConnect() fails. |
| connect_method_call.SetPath(dbus::ObjectPath(kTestDeviceObjectPath)); |
| EXPECT_CALL(*libnewblue_, GattClientConnect(_, _, _)).WillOnce(Return(0)); |
| connect_handler.Run(&connect_method_call, |
| base::Bind(&SaveResponse, &failed_connect_response)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(bluetooth_device::kErrorFailed, |
| failed_connect_response->GetErrorName()); |
| |
| // GattClientConnect() succeeds. |
| void* data; |
| struct bt_addr addr; |
| gattCliConnectResultCbk gatt_client_connect_callback; |
| EXPECT_CALL(*libnewblue_, GattClientConnect(_, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&data), SaveArgPointee<1>(&addr), |
| SaveArg<2>(&gatt_client_connect_callback), |
| Return(kTestGattClientConnectionId))); |
| std::unique_ptr<dbus::Response> success_connect_response; |
| connect_handler.Run(&connect_method_call, |
| base::Bind(&SaveResponse, &success_connect_response)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_FALSE(success_connect_response.get()); |
| EXPECT_EQ(ConvertBtAddrToString(addr), ConvertBtAddrToString(*address)); |
| |
| // Callback for a different connection id should be ignored. |
| gatt_client_connect_callback(data, kTestGattClientConnectionId + 10, |
| static_cast<uint8_t>(ConnectState::CONNECTED)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_FALSE(success_connect_response.get()); |
| // Callback for the pending id should update the connection status and send |
| // the D-Bus reply. |
| gatt_client_connect_callback(data, kTestGattClientConnectionId, |
| static_cast<uint8_t>(ConnectState::CONNECTED)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_TRUE(success_connect_response.get()); |
| EXPECT_EQ("", success_connect_response->GetErrorName()); |
| // Check "connected" property value after connected. |
| std::unique_ptr<dbus::Response> get_property_response = |
| ReadProperty(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kConnectedProperty, get_property_handler, |
| kTestSender, kTestSerial); |
| dbus::MessageReader connect_message_reader(get_property_response.get()); |
| bool connected = false; |
| EXPECT_TRUE(connect_message_reader.PopVariantOfBool(&connected)); |
| EXPECT_TRUE(connected); |
| get_property_response.reset(); |
| |
| // Disconnect |
| dbus::MethodCall disconnect_method_call( |
| bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kDisconnect); |
| disconnect_method_call.SetSender(kTestSender); |
| disconnect_method_call.SetSerial(kTestSerial); |
| |
| // Unknown device path |
| std::unique_ptr<dbus::Response> failed_disconnect_response; |
| disconnect_method_call.SetPath(dbus::ObjectPath(kUnknownDeviceObjectPath)); |
| disconnect_handler.Run( |
| &disconnect_method_call, |
| base::Bind(&SaveResponse, &failed_disconnect_response)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(bluetooth_device::kErrorDoesNotExist, |
| failed_disconnect_response->GetErrorName()); |
| |
| // Disconnect succeeds by client |
| std::unique_ptr<dbus::Response> success_disconnect_by_client_response; |
| disconnect_method_call.SetPath(dbus::ObjectPath(kTestDeviceObjectPath)); |
| disconnect_handler.Run( |
| &disconnect_method_call, |
| base::Bind(&SaveResponse, &success_disconnect_by_client_response)); |
| gatt_client_connect_callback( |
| data, kTestGattClientConnectionId, |
| static_cast<uint8_t>(ConnectState::DISCONNECTED_BY_US)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_TRUE(success_disconnect_by_client_response.get()); |
| EXPECT_EQ("", success_disconnect_by_client_response->GetErrorName()); |
| // Check "connected" property value after disconnected. |
| get_property_response = |
| ReadProperty(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kConnectedProperty, get_property_handler, |
| kTestSender, kTestSerial); |
| dbus::MessageReader disconnect_message_reader(get_property_response.get()); |
| EXPECT_TRUE(disconnect_message_reader.PopVariantOfBool(&connected)); |
| EXPECT_FALSE(connected); |
| } |
| |
| void TestPair(dbus::ExportedObject::MethodCallCallback pair_handler) { |
| dbus::MethodCall pair_method_call( |
| bluetooth_device::kBluetoothDeviceInterface, bluetooth_device::kPair); |
| pair_method_call.SetSender(kTestSender); |
| pair_method_call.SetSerial(kTestSerial); |
| |
| // Pair() to unknown device. |
| std::unique_ptr<dbus::Response> failed_pair_response; |
| pair_method_call.SetPath(dbus::ObjectPath(kUnknownDeviceObjectPath)); |
| pair_handler.Run(&pair_method_call, |
| base::Bind(&SaveResponse, &failed_pair_response)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_TRUE(failed_pair_response.get()); |
| EXPECT_EQ(bluetooth_adapter::kErrorFailed, |
| failed_pair_response->GetErrorName()); |
| |
| // Pair() succeeds. |
| std::unique_ptr<dbus::Response> success_pair_response; |
| pair_method_call.SetPath(dbus::ObjectPath(kTestDeviceObjectPath)); |
| EXPECT_CALL(*libnewblue_, SmPair(_, _)).Times(1); |
| pair_handler.Run(&pair_method_call, |
| base::Bind(&SaveResponse, &success_pair_response)); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_FALSE(success_pair_response.get()); |
| struct bt_addr address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| struct smPairStateChange pair_state_change = {SM_PAIR_STATE_PAIRED, |
| SM_PAIR_ERR_NONE, address}; |
| (*pair_state_callback_)(pair_state_callback_data_, &pair_state_change, 1); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ("", success_pair_response->GetErrorName()); |
| } |
| |
| base::MessageLoop message_loop_; |
| scoped_refptr<dbus::MockBus> bus_; |
| scoped_refptr<dbus::MockObjectProxy> bluez_object_proxy_; |
| scoped_refptr<dbus::MockObjectProxy> bluetooth_object_proxy_; |
| scoped_refptr<dbus::MockObjectManager> bluez_object_manager_; |
| scoped_refptr<dbus::MockObjectManager> bluetooth_object_manager_; |
| void* pair_state_callback_data_; |
| smPairStateChangeCbk pair_state_callback_; |
| // Declare MockExportedObject's before newblue_daemon_ to make sure the |
| // MockExportedObject-s are destroyed after newblue_daemon_. |
| std::map<dbus::ObjectPath, scoped_refptr<dbus::MockExportedObject>> |
| mock_exported_objects_; |
| scoped_refptr<dbus::MockExportedObject> exported_root_object_; |
| scoped_refptr<dbus::MockExportedObject> exported_adapter_object_; |
| scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object_; |
| std::unique_ptr<NewblueDaemon> newblue_daemon_; |
| MockLibNewblue* libnewblue_; |
| dbus::ExportedObject::MethodCallCallback dummy_method_handler_; |
| }; |
| |
| TEST_F(NewblueDaemonTest, InitFailed) { |
| MethodHandlerMap adapter_method_handlers; |
| scoped_refptr<dbus::MockExportedObject> exported_root_object = |
| SetupExportedRootObject(); |
| scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object = |
| SetupExportedAgentManagerObject(); |
| ExpectPropertiesMethodsExported(exported_agent_manager_object, |
| adapter_method_handlers); |
| ExpectAgentManagerMethodsExported(exported_agent_manager_object); |
| |
| ExpectTestInit(exported_root_object); |
| |
| EXPECT_CALL(*libnewblue_, HciUp(_, _, _)).WillOnce(Return(false)); |
| EXPECT_FALSE(newblue_daemon_->Init( |
| bus_, nullptr /* no need to access the delegator */)); |
| |
| // Shutdown now to make sure ExportedObjectManagerWrapper is destructed first |
| // before the mocked objects. |
| newblue_daemon_->Shutdown(); |
| } |
| |
| TEST_F(NewblueDaemonTest, InitSuccessAndBringUp) { |
| MethodHandlerMap adapter_method_handlers; |
| |
| TestInit(adapter_method_handlers); |
| TestAdapterBringUp(exported_adapter_object_, adapter_method_handlers, |
| /* with_saved_devices */ true); |
| |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, DiscoveryAPI) { |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback stop_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback remove_device_handler; |
| MethodHandlerMap adapter_method_handlers = { |
| {bluetooth_adapter::kStartDiscovery, &start_discovery_handler}, |
| {bluetooth_adapter::kStopDiscovery, &stop_discovery_handler}, |
| {bluetooth_adapter::kRemoveDevice, &remove_device_handler}, |
| }; |
| |
| TestInit(adapter_method_handlers); |
| TestAdapterBringUp(exported_adapter_object_, adapter_method_handlers, |
| /* with_saved_devices */ false); |
| |
| ASSERT_FALSE(start_discovery_handler.is_null()); |
| ASSERT_FALSE(stop_discovery_handler.is_null()); |
| ASSERT_FALSE(remove_device_handler.is_null()); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| TestStartDiscovery(start_discovery_handler, &inquiry_response_callback, |
| &inquiry_response_callback_data); |
| |
| // Device discovered. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), {}); |
| struct bt_addr address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| // Trigger the queued inquiry_response_callback task. |
| base::RunLoop().RunUntilIdle(); |
| |
| // RemoveDevice for unknown device address should do no-op and succeed. |
| dbus::MethodCall remove_device_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kRemoveDevice); |
| ConstructRemoveDeviceMethodCall(&remove_device_method_call, |
| "/org/bluez/hci0/dev_11_11_11_11_11_11"); |
| std::unique_ptr<dbus::Response> remove_device_response; |
| remove_device_handler.Run(&remove_device_method_call, |
| base::Bind(&SaveResponse, &remove_device_response)); |
| EXPECT_EQ("", remove_device_response->GetErrorName()); |
| // RemoveDevice successful. |
| dbus::MethodCall remove_device_method_call2( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kRemoveDevice); |
| ConstructRemoveDeviceMethodCall(&remove_device_method_call2, |
| kTestDeviceObjectPath); |
| ExpectDeviceObjectUnexported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| std::unique_ptr<dbus::Response> remove_device_response2; |
| remove_device_handler.Run( |
| &remove_device_method_call2, |
| base::Bind(&SaveResponse, &remove_device_response2)); |
| EXPECT_EQ("", remove_device_response2->GetErrorName()); |
| RemoveMockExportedObject(dbus::ObjectPath(kTestDeviceObjectPath)); |
| |
| TestStopDiscovery(stop_discovery_handler); |
| |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, IdleMode) { |
| EXPECT_CALL(*bus_, |
| RequestOwnershipAndBlock( |
| newblue_object_manager::kNewblueObjectManagerServiceName, |
| dbus::Bus::REQUIRE_PRIMARY)) |
| .WillOnce(Return(true)); |
| |
| auto libnewblue = std::make_unique<MockLibNewblue>(); |
| libnewblue_ = libnewblue.get(); |
| auto newblue = std::make_unique<Newblue>(std::move(libnewblue)); |
| newblue_daemon_ = std::make_unique<NewblueDaemon>(std::move(newblue), |
| true /* is_idle_mode */); |
| |
| // In idle mode, the daemon shouldn't try to bring up the LE stack. |
| EXPECT_CALL(*libnewblue_, HciUp(_, _, _)).Times(0); |
| EXPECT_TRUE(newblue_daemon_->Init( |
| bus_, nullptr /* no need to access the delegator */)); |
| |
| // Shutdown now to make sure ExportedObjectManagerWrapper is destructed first |
| // before the mocked objects. |
| newblue_daemon_->Shutdown(); |
| } |
| |
| TEST_F(NewblueDaemonTest, Pair) { |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback pair_handler; |
| MethodHandlerMap method_handlers = { |
| {bluetooth_adapter::kStartDiscovery, &start_discovery_handler}, |
| {bluetooth_device::kPair, &pair_handler}, |
| }; |
| |
| TestInit(method_handlers); |
| TestAdapterBringUp(exported_adapter_object_, method_handlers, |
| /* with_saved_devices */ false); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| TestStartDiscovery(start_discovery_handler, &inquiry_response_callback, |
| &inquiry_response_callback_data); |
| |
| // Device discovered. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| struct bt_addr address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| // Trigger the queued inquiry_response_callback task. |
| base::RunLoop().RunUntilIdle(); |
| |
| TestPair(pair_handler); |
| |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, Connection) { |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback connect_handler; |
| dbus::ExportedObject::MethodCallCallback disconnect_handler; |
| dbus::ExportedObject::MethodCallCallback get_property_handler; |
| MethodHandlerMap method_handlers = { |
| {bluetooth_adapter::kStartDiscovery, &start_discovery_handler}, |
| {bluetooth_device::kConnect, &connect_handler}, |
| {bluetooth_device::kDisconnect, &disconnect_handler}, |
| {dbus::kPropertiesGet, &get_property_handler}, |
| }; |
| |
| TestInit(method_handlers); |
| TestAdapterBringUp(exported_adapter_object_, method_handlers, |
| /* with_saved_devices */ true); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| TestStartDiscovery(start_discovery_handler, &inquiry_response_callback, |
| &inquiry_response_callback_data); |
| |
| // Device discovered. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| struct bt_addr address; |
| struct bt_addr latest_address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| ConvertToBtAddr(false, kLatestAddress, &latest_address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &latest_address, |
| &address, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| // Trigger the queued inquiry_response_callback task. |
| base::RunLoop().RunUntilIdle(); |
| |
| TestConnectDisconnect(connect_handler, disconnect_handler, |
| get_property_handler, &latest_address); |
| |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, BackgroundScan) { |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback stop_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback connect_handler; |
| dbus::ExportedObject::MethodCallCallback disconnect_handler; |
| dbus::ExportedObject::MethodCallCallback pair_handler; |
| MethodHandlerMap method_handlers = { |
| {bluetooth_adapter::kStartDiscovery, &start_discovery_handler}, |
| {bluetooth_adapter::kStopDiscovery, &stop_discovery_handler}, |
| {bluetooth_device::kConnect, &connect_handler}, |
| {bluetooth_device::kDisconnect, &disconnect_handler}, |
| {bluetooth_device::kPair, &pair_handler}, |
| }; |
| |
| TestInit(method_handlers); |
| TestAdapterBringUp(exported_adapter_object_, method_handlers, |
| /* with_saved_devices */ false); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| TestStartDiscovery(start_discovery_handler, &inquiry_response_callback, |
| &inquiry_response_callback_data); |
| |
| // Device discovered. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| struct bt_addr address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| // Trigger the queued inquiry_response_callback task. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Stop all discovery by clients so we can test background scan in isolation. |
| TestStopDiscovery(stop_discovery_handler); |
| |
| // After the pairing is done, we should start background scan to look for the |
| // unconnected paired device. |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, _, _, _, _, _, _)) |
| .WillOnce(Return(kTestDiscoveryId)); |
| TestPair(pair_handler); |
| |
| // Upon receiving an advertisement from a paired device, connection should be |
| // initiated. |
| void* data; |
| struct bt_addr addr; |
| gattCliConnectResultCbk gatt_client_connect_callback; |
| EXPECT_CALL(*libnewblue_, GattClientConnect(_, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&data), SaveArgPointee<1>(&addr), |
| SaveArg<2>(&gatt_client_connect_callback), |
| Return(kTestGattClientConnectionId))); |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(ConvertBtAddrToString(addr), ConvertBtAddrToString(address)); |
| // The connection succeeds, the background scan should stop |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(kTestDiscoveryId)) |
| .WillOnce(Return(true)); |
| gatt_client_connect_callback(data, kTestGattClientConnectionId, |
| static_cast<uint8_t>(ConnectState::CONNECTED)); |
| base::RunLoop().RunUntilIdle(); |
| |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, BackgroundScanWithRandomResolvableDevice) { |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback stop_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback connect_handler; |
| dbus::ExportedObject::MethodCallCallback disconnect_handler; |
| dbus::ExportedObject::MethodCallCallback pair_handler; |
| MethodHandlerMap method_handlers = { |
| {bluetooth_adapter::kStartDiscovery, &start_discovery_handler}, |
| {bluetooth_adapter::kStopDiscovery, &stop_discovery_handler}, |
| {bluetooth_device::kConnect, &connect_handler}, |
| {bluetooth_device::kDisconnect, &disconnect_handler}, |
| {bluetooth_device::kPair, &pair_handler}, |
| }; |
| |
| TestInit(method_handlers); |
| TestAdapterBringUp(exported_adapter_object_, method_handlers, |
| /* with_saved_devices */ false); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| TestStartDiscovery(start_discovery_handler, &inquiry_response_callback, |
| &inquiry_response_callback_data); |
| |
| // Device discovered. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| struct bt_addr address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| // Trigger the queued inquiry_response_callback task. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Stop all discovery by clients so we can test background scan in isolation. |
| TestStopDiscovery(stop_discovery_handler); |
| |
| // After the pairing is done, we should start background scan to look for the |
| // unconnected paired device. |
| EXPECT_CALL(*libnewblue_, |
| HciDiscoverLeStart(_, _, /* active */ false, _, _, _, _, _)) |
| .WillOnce(Return(kTestDiscoveryId)); |
| TestPair(pair_handler); |
| |
| // Upon receiving an advertisement from a paired device, connection should be |
| // initiated. |
| void* data; |
| struct bt_addr addr; |
| struct bt_addr latest_address; |
| ConvertToBtAddr(false, kLatestAddress, &latest_address); |
| gattCliConnectResultCbk gatt_client_connect_callback; |
| EXPECT_CALL(*libnewblue_, GattClientConnect(_, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&data), SaveArgPointee<1>(&addr), |
| SaveArg<2>(&gatt_client_connect_callback), |
| Return(kTestGattClientConnectionId))); |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &latest_address, |
| &address, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(ConvertBtAddrToString(addr), ConvertBtAddrToString(latest_address)); |
| // The connection succeeds, the background scan should stop |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(kTestDiscoveryId)) |
| .WillOnce(Return(true)); |
| gatt_client_connect_callback(data, kTestGattClientConnectionId, |
| static_cast<uint8_t>(ConnectState::CONNECTED)); |
| base::RunLoop().RunUntilIdle(); |
| |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, ScanState) { |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback stop_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback suspend_imminent_handler; |
| dbus::ExportedObject::MethodCallCallback suspend_done_handler; |
| dbus::ExportedObject::MethodCallCallback pair_handler; |
| MethodHandlerMap method_handlers = { |
| {bluetooth_adapter::kStartDiscovery, &start_discovery_handler}, |
| {bluetooth_adapter::kStopDiscovery, &stop_discovery_handler}, |
| {bluetooth_adapter::kHandleSuspendImminent, &suspend_imminent_handler}, |
| {bluetooth_adapter::kHandleSuspendDone, &suspend_done_handler}, |
| {bluetooth_device::kPair, &pair_handler}, |
| }; |
| |
| TestInit(method_handlers); |
| // With previously paired device, background scan should start |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, _, _, _, _, _, _)) |
| .Times(0); |
| TestAdapterBringUp(exported_adapter_object_, method_handlers, |
| /* with_saved_devices */ false); |
| |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| |
| // Two clients will request start discovery. |
| TestStartDiscovery(start_discovery_handler, &inquiry_response_callback, |
| &inquiry_response_callback_data); |
| |
| // Device discovered. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| struct bt_addr address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| // Paired with the device |
| TestPair(pair_handler); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Trigger suspend imminent, all discovery activities should stop |
| dbus::MethodCall suspend_imminent_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kHandleSuspendImminent); |
| suspend_imminent_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| suspend_imminent_method_call.SetSender(kTestSender); |
| suspend_imminent_method_call.SetSerial(kTestSerial); |
| // Add action (empty string) |
| dbus::MessageWriter suspendWriter(&suspend_imminent_method_call); |
| suspendWriter.AppendString(""); |
| |
| std::unique_ptr<dbus::Response> suspend_imminent_response; |
| |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(kTestDiscoveryId)) |
| .WillOnce(Return(true)); |
| suspend_imminent_handler.Run( |
| &suspend_imminent_method_call, |
| base::Bind(&SaveResponse, &suspend_imminent_response)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // System wake up, expect active discovery to be resumed |
| dbus::MethodCall suspend_done_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kHandleSuspendDone); |
| suspend_done_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| suspend_done_method_call.SetSender(kTestSender); |
| suspend_done_method_call.SetSerial(kTestSerial); |
| // Add action (empty string) |
| dbus::MessageWriter doneWriter(&suspend_done_method_call); |
| doneWriter.AppendString(""); |
| std::unique_ptr<dbus::Response> suspend_done_response; |
| |
| EXPECT_CALL(*libnewblue_, |
| HciDiscoverLeStart(_, _, /* active */ true, _, _, _, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&inquiry_response_callback), |
| SaveArg<1>(&inquiry_response_callback_data), |
| Return(kTestDiscoveryId))); |
| suspend_done_handler.Run(&suspend_done_method_call, |
| base::Bind(&SaveResponse, &suspend_done_response)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Stop discovery, however passive scan should resume because not all paired |
| // device is connected. There two clients having discovery session now. |
| dbus::MethodCall stop_discovery_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kStopDiscovery); |
| stop_discovery_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| stop_discovery_method_call.SetSender(kTestSender); |
| stop_discovery_method_call.SetSerial(kTestSerial); |
| std::unique_ptr<dbus::Response> stop_discovery_response; |
| // Stop the first clients. |
| stop_discovery_handler.Run( |
| &stop_discovery_method_call, |
| base::Bind(&SaveResponse, &stop_discovery_response)); |
| // Stop discovery for second clients. |
| stop_discovery_method_call.SetSender(kTestSender2); |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(kTestDiscoveryId)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*libnewblue_, |
| HciDiscoverLeStart(_, _, /* active */ false, _, _, _, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&inquiry_response_callback), |
| SaveArg<1>(&inquiry_response_callback_data), |
| Return(kTestDiscoveryId))); |
| stop_discovery_handler.Run( |
| &stop_discovery_method_call, |
| base::Bind(&SaveResponse, &stop_discovery_response)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Upon receiving an advertisement from a paired device, connection should be |
| // initiated. |
| void* data; |
| gattCliConnectResultCbk gatt_client_connect_callback; |
| EXPECT_CALL(*libnewblue_, GattClientConnect(_, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&data), |
| SaveArg<2>(&gatt_client_connect_callback), |
| Return(kTestGattClientConnectionId))); |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| // The connection succeeds, the background scan should stop |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStop(kTestDiscoveryId)) |
| .WillOnce(Return(true)); |
| gatt_client_connect_callback(data, kTestGattClientConnectionId, |
| static_cast<uint8_t>(ConnectState::CONNECTED)); |
| base::RunLoop().RunUntilIdle(); |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, DiscoveryFilter) { |
| dbus::ExportedObject::MethodCallCallback set_discovery_filter_handler; |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback stop_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback remove_device_handler; |
| MethodHandlerMap method_handlers = { |
| {bluetooth_adapter::kSetDiscoveryFilter, &set_discovery_filter_handler}, |
| {bluetooth_adapter::kStartDiscovery, &start_discovery_handler}, |
| {bluetooth_adapter::kStopDiscovery, &stop_discovery_handler}, |
| {bluetooth_adapter::kRemoveDevice, &remove_device_handler}, |
| }; |
| |
| TestInit(method_handlers); |
| TestAdapterBringUp(exported_adapter_object_, method_handlers, |
| /* with_saved_devices */ false); |
| |
| // Initialization. |
| dbus::MethodCall start_discovery_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kStartDiscovery); |
| start_discovery_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| start_discovery_method_call.SetSender(kTestSender); |
| start_discovery_method_call.SetSerial(kTestSerial); |
| std::unique_ptr<dbus::Response> start_discovery_response; |
| dbus::MethodCall stop_discovery_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kStopDiscovery); |
| stop_discovery_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath)); |
| stop_discovery_method_call.SetSender(kTestSender); |
| stop_discovery_method_call.SetSerial(kTestSerial); |
| std::unique_ptr<dbus::Response> stop_discovery_response; |
| Newblue::DeviceDiscoveredCallback on_device_discovered; |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| struct bt_addr address, address2; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| ConvertToBtAddr(false, kTestDeviceAddress2, &address2); |
| |
| // Setup the discovery filter to filter out low RSSI devices. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender, |
| "tight"); |
| |
| // Start discovery |
| EXPECT_CALL(*libnewblue_, |
| HciDiscoverLeStart(_, _, /* active */ true, _, _, _, _, _)) |
| .WillOnce(DoAll(SaveArg<0>(&inquiry_response_callback), |
| SaveArg<1>(&inquiry_response_callback_data), |
| Return(kTestDiscoveryId))); |
| start_discovery_handler.Run( |
| &start_discovery_method_call, |
| base::Bind(&SaveResponse, &start_discovery_response)); |
| |
| // Both devices are blocked by the "tight" filter. |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -90, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Update the filter for the same client. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender, |
| "classic_loose"); |
| |
| // Both devices are still blocked because the latest filter is for classic |
| // only and ignored by NewBlue. |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -90, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Setup the discovery filter to have slightly lower RSSI threshold. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender, |
| "looser_rssi"); |
| // One device with higher RSSI shall pass, but not the second one. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -90, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Clear the filter for the client by sending an empty filter dict. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender, |
| "clear"); |
| // The second device with lower RSSI shall pass now. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath2), |
| method_handlers); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Remove both discovered devices for the following tests. |
| dbus::MethodCall remove_device_method_call( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kRemoveDevice); |
| ConstructRemoveDeviceMethodCall(&remove_device_method_call, |
| kTestDeviceObjectPath); |
| ExpectDeviceObjectUnexported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| std::unique_ptr<dbus::Response> remove_device_response; |
| remove_device_handler.Run(&remove_device_method_call, |
| base::Bind(&SaveResponse, &remove_device_response)); |
| RemoveMockExportedObject(dbus::ObjectPath(kTestDeviceObjectPath)); |
| dbus::MethodCall remove_device_method_call2( |
| bluetooth_adapter::kBluetoothAdapterInterface, |
| bluetooth_adapter::kRemoveDevice); |
| ConstructRemoveDeviceMethodCall(&remove_device_method_call2, |
| kTestDeviceObjectPath2); |
| ExpectDeviceObjectUnexported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| std::unique_ptr<dbus::Response> remove_device_response2; |
| remove_device_handler.Run( |
| &remove_device_method_call2, |
| base::Bind(&SaveResponse, &remove_device_response2)); |
| RemoveMockExportedObject(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| |
| // Setup the discovery filter to looking for a wrong UUID. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender, |
| "uuid"); |
| // Both devices are blocked by the "uuid filter". |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -90, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Setup a looser RSSI with correct uuid filter by second client. However, |
| // since second client have not started a scan session. No effect on filters. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender2, |
| "looser_rssi"); |
| // Both devices are still blocked by the "uuid filter". |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -90, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Try start discovery by the second client. Now the filters will merge to |
| // become a "looser RSSI filter" to allow one device to pass. |
| start_discovery_method_call.SetSender(kTestSender2); |
| start_discovery_handler.Run( |
| &start_discovery_method_call, |
| base::Bind(&SaveResponse, &start_discovery_response)); |
| // One device with higher RSSI shall pass, but not the second one. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -90, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Update the filter for first client to be loose. The other device should |
| // pass now. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender, |
| "loose"); |
| // The second device with lower RSSI shall pass now. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath2), |
| method_handlers); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Remove both discovered devices for the following tests. |
| ExpectDeviceObjectUnexported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| remove_device_handler.Run(&remove_device_method_call, |
| base::Bind(&SaveResponse, &remove_device_response)); |
| RemoveMockExportedObject(dbus::ObjectPath(kTestDeviceObjectPath)); |
| ExpectDeviceObjectUnexported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| remove_device_handler.Run( |
| &remove_device_method_call2, |
| base::Bind(&SaveResponse, &remove_device_response2)); |
| RemoveMockExportedObject(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| |
| // Try stop discovery by the first client. Now the merged filter will back to |
| // what second client holds: a "looser_rssi" filter. |
| stop_discovery_handler.Run( |
| &stop_discovery_method_call, |
| base::Bind(&SaveResponse, &stop_discovery_response)); |
| // One device with higher RSSI shall pass, but not the second one. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath), |
| method_handlers); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address, |
| /* resolved_address */ nullptr, |
| /* rssi */ -90, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ sizeof(eir)); |
| base::RunLoop().RunUntilIdle(); |
| ExpectDeviceObjectNotExported(dbus::ObjectPath(kTestDeviceObjectPath2)); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Clear the filter for the second client by sending an empty filter dict. |
| CallSetDiscoveryFilterMethod(set_discovery_filter_handler, kTestSender2, |
| "clear"); |
| // The second device with lower RSSI shall pass now. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath2), |
| method_handlers); |
| (*inquiry_response_callback)(inquiry_response_callback_data, &address2, |
| /* resolved_address */ nullptr, |
| /* rssi */ -110, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ eir, |
| /* eir_len*/ 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| TestDeinit(); |
| } |
| // TODO(mcchou): Add a test case where the connection is terminated by remote |
| // device. |
| |
| } // namespace bluetooth |