| // 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 <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include <base/memory/ref_counted.h> |
| #include <base/message_loop/message_loop.h> |
| #include <base/run_loop.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 <gtest/gtest.h> |
| |
| #include "bluetooth/common/util.h" |
| #include "bluetooth/newblued/mock_libnewblue.h" |
| |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::Invoke; |
| using ::testing::Return; |
| using ::testing::SaveArg; |
| |
| 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 kTestDeviceObjectPath[] = |
| "/org/bluez/hci0/dev_06_05_04_03_02_01"; |
| |
| constexpr uniq_t kTestDiscoveryId = 7; |
| |
| 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) { |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock(dbus::kPropertiesInterface, |
| dbus::kPropertiesGet, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock(dbus::kPropertiesInterface, |
| dbus::kPropertiesSet, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*exported_object, |
| ExportMethodAndBlock(dbus::kPropertiesInterface, |
| dbus::kPropertiesGetAll, _)) |
| .WillOnce(Return(true)); |
| } |
| |
| // Expects that the methods on org.bluez.Device1 interface are exported. |
| void ExpectDeviceMethodsExported( |
| scoped_refptr<dbus::MockExportedObject> exported_object) { |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kPair, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kCancelPairing, _)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL( |
| *exported_object, |
| ExportMethodAndBlock(bluetooth_device::kBluetoothDeviceInterface, |
| bluetooth_device::kConnect, _)) |
| .WillOnce(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)); |
| } |
| |
| // 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) { |
| scoped_refptr<dbus::MockExportedObject> exported_dev_object = |
| AddOrGetMockExportedObject(device_object_path); |
| ExpectDeviceMethodsExported(exported_dev_object); |
| ExpectPropertiesMethodsExported(exported_dev_object); |
| 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); |
| } |
| |
| 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 object_path( |
| bluez_object_manager::kBluezObjectManagerServicePath); |
| bluez_object_proxy_ = new dbus::MockObjectProxy( |
| bus_.get(), bluez_object_manager::kBluezObjectManagerServiceName, |
| object_path); |
| EXPECT_CALL(*bus_, GetObjectProxy( |
| bluez_object_manager::kBluezObjectManagerServiceName, |
| object_path)) |
| .WillRepeatedly(Return(bluez_object_proxy_.get())); |
| EXPECT_CALL(*bluez_object_proxy_, SetNameOwnerChangedCallback(_)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*bluez_object_proxy_, ConnectToSignal(_, _, _, _)) |
| .Times(AnyNumber()); |
| } |
| |
| void SetupBluezObjectManager() { |
| dbus::ObjectPath object_path( |
| bluez_object_manager::kBluezObjectManagerServicePath); |
| bluez_object_manager_ = new dbus::MockObjectManager( |
| bus_.get(), bluez_object_manager::kBluezObjectManagerServiceName, |
| object_path); |
| EXPECT_CALL(*bus_, GetObjectManager( |
| bluez_object_manager::kBluezObjectManagerServiceName, |
| object_path)) |
| .WillRepeatedly(Return(bluez_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() { |
| exported_root_object_ = SetupExportedRootObject(); |
| exported_agent_manager_object_ = SetupExportedAgentManagerObject(); |
| exported_adapter_object_ = SetupExportedAdapterObject(); |
| ExpectPropertiesMethodsExported(exported_adapter_object_); |
| ExpectAdvertisingManagerMethodsExported(exported_adapter_object_); |
| ExpectPropertiesMethodsExported(exported_agent_manager_object_); |
| 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(); |
| } |
| |
| void TestAdapterBringUp( |
| scoped_refptr<dbus::MockExportedObject> exported_adapter_object, |
| MethodHandlerMap adapter_method_handlers) { |
| // org.bluez.Adapter1 methods |
| 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(*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(Return(true)); |
| |
| // 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")); |
| struct smKnownDevNode* 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)); |
| } |
| |
| base::MessageLoop message_loop_; |
| scoped_refptr<dbus::MockBus> bus_; |
| scoped_refptr<dbus::MockObjectProxy> bluez_object_proxy_; |
| scoped_refptr<dbus::MockObjectManager> bluez_object_manager_; |
| // 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) { |
| scoped_refptr<dbus::MockExportedObject> exported_root_object = |
| SetupExportedRootObject(); |
| scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object = |
| SetupExportedAgentManagerObject(); |
| scoped_refptr<dbus::MockExportedObject> exported_adapter_object = |
| SetupExportedAdapterObject(); |
| ExpectPropertiesMethodsExported(exported_adapter_object); |
| ExpectAdvertisingManagerMethodsExported(exported_adapter_object); |
| ExpectPropertiesMethodsExported(exported_agent_manager_object); |
| 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) { |
| TestInit(); |
| |
| MethodHandlerMap adapter_method_handlers; |
| TestAdapterBringUp(exported_adapter_object_, adapter_method_handlers); |
| |
| TestDeinit(); |
| } |
| |
| TEST_F(NewblueDaemonTest, DiscoveryAPI) { |
| TestInit(); |
| |
| dbus::ExportedObject::MethodCallCallback start_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback stop_discovery_handler; |
| dbus::ExportedObject::MethodCallCallback remove_device_handler; |
| MethodHandlerMap adapter_method_handlers; |
| adapter_method_handlers[bluetooth_adapter::kStartDiscovery] = |
| &start_discovery_handler; |
| adapter_method_handlers[bluetooth_adapter::kStopDiscovery] = |
| &stop_discovery_handler; |
| adapter_method_handlers[bluetooth_adapter::kRemoveDevice] = |
| &remove_device_handler; |
| TestAdapterBringUp(exported_adapter_object_, adapter_method_handlers); |
| |
| ASSERT_FALSE(start_discovery_handler.is_null()); |
| ASSERT_FALSE(stop_discovery_handler.is_null()); |
| ASSERT_FALSE(remove_device_handler.is_null()); |
| |
| // 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; |
| hciDeviceDiscoveredLeCbk inquiry_response_callback; |
| void* inquiry_response_callback_data; |
| EXPECT_CALL(*libnewblue_, HciDiscoverLeStart(_, _, /* active */ true, |
| /* use_random_addr */ false)) |
| .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()); |
| |
| // Device discovered. |
| ExpectDeviceObjectExported(dbus::ObjectPath(kTestDeviceObjectPath)); |
| struct bt_addr address; |
| ConvertToBtAddr(false, kTestDeviceAddress, &address); |
| inquiry_response_callback(inquiry_response_callback_data, &address, |
| /* rssi */ -101, HCI_ADV_TYPE_SCAN_RSP, |
| /* eir */ {}, |
| /* eir_len*/ 0); |
| // Trigger the queued inquiry_response_callback task. |
| base::RunLoop().RunUntilIdle(); |
| |
| // RemoveDevice failed (unknown device address). |
| 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(bluetooth_adapter::kErrorFailed, |
| 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)); |
| |
| // 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()); |
| |
| 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(); |
| } |
| |
| } // namespace bluetooth |