| // 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/adapter_interface_handler.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include <base/stl_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/errors/error.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/object_path.h> |
| |
| #include "bluetooth/common/exported_object_manager_wrapper.h" |
| #include "bluetooth/common/util.h" |
| #include "bluetooth/newblued/newblue.h" |
| |
| namespace bluetooth { |
| |
| AdapterInterfaceHandler::AdapterInterfaceHandler( |
| scoped_refptr<dbus::Bus> bus, |
| Newblue* newblue, |
| ExportedObjectManagerWrapper* exported_object_manager_wrapper) |
| : bus_(bus), |
| newblue_(newblue), |
| exported_object_manager_wrapper_(exported_object_manager_wrapper), |
| weak_ptr_factory_(this) {} |
| |
| void AdapterInterfaceHandler::Init( |
| Newblue::DeviceDiscoveredCallback device_discovered_callback, |
| DeviceInterfaceHandler* device_interface_handler) { |
| device_discovered_callback_ = device_discovered_callback; |
| device_interface_handler_ = device_interface_handler; |
| dbus::ObjectPath adapter_object_path(kAdapterObjectPath); |
| exported_object_manager_wrapper_->AddExportedInterface( |
| adapter_object_path, bluetooth_adapter::kBluetoothAdapterInterface); |
| ExportedInterface* adapter_interface = |
| exported_object_manager_wrapper_->GetExportedInterface( |
| adapter_object_path, bluetooth_adapter::kBluetoothAdapterInterface); |
| |
| // Expose the "Powered" property of the adapter. This property is only |
| // controlled by BlueZ, so newblued's "Powered" property is ignored by |
| // btdispatch. However, it is useful to have the dummy "Powered" property |
| // for testing when Chrome (or any client) connects directly to newblued |
| // instead of via btdispatch. |
| adapter_interface |
| ->EnsureExportedPropertyRegistered<bool>( |
| bluetooth_adapter::kPoweredProperty) |
| ->SetValue(true); |
| adapter_interface |
| ->EnsureExportedPropertyRegistered<bool>( |
| bluetooth_adapter::kStackSyncQuittingProperty) |
| ->SetValue(false); |
| |
| adapter_interface->AddSimpleMethodHandlerWithErrorAndMessage( |
| bluetooth_adapter::kStartDiscovery, base::Unretained(this), |
| &AdapterInterfaceHandler::HandleStartDiscovery); |
| adapter_interface->AddSimpleMethodHandlerWithErrorAndMessage( |
| bluetooth_adapter::kStopDiscovery, base::Unretained(this), |
| &AdapterInterfaceHandler::HandleStopDiscovery); |
| adapter_interface->AddSimpleMethodHandlerWithErrorAndMessage( |
| bluetooth_adapter::kRemoveDevice, base::Unretained(this), |
| &AdapterInterfaceHandler::HandleRemoveDevice); |
| |
| adapter_interface->ExportAndBlock(); |
| } |
| |
| bool AdapterInterfaceHandler::HandleStartDiscovery(brillo::ErrorPtr* error, |
| dbus::Message* message) { |
| VLOG(1) << __func__; |
| |
| const std::string& client_address = message->GetSender(); |
| |
| if (base::ContainsKey(discovery_clients_, client_address)) { |
| brillo::Error::AddTo( |
| error, FROM_HERE, brillo::errors::dbus::kDomain, |
| bluetooth_adapter::kErrorInProgress, |
| base::StringPrintf("Client already has a discovery session: %s", |
| client_address.c_str())); |
| return false; |
| } |
| |
| if (!UpdateDiscovery(discovery_clients_.size() + 1)) { |
| brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| bluetooth_adapter::kErrorFailed, |
| "Failed to start discovery"); |
| return false; |
| } |
| |
| discovery_clients_[client_address] = |
| std::make_unique<DBusClient>(bus_, client_address); |
| discovery_clients_[client_address]->WatchClientUnavailable( |
| base::Bind(&AdapterInterfaceHandler::OnClientUnavailable, |
| weak_ptr_factory_.GetWeakPtr(), client_address)); |
| |
| return true; |
| } |
| |
| bool AdapterInterfaceHandler::HandleStopDiscovery(brillo::ErrorPtr* error, |
| dbus::Message* message) { |
| VLOG(1) << __func__; |
| |
| const std::string& client_address = message->GetSender(); |
| |
| if (!base::ContainsKey(discovery_clients_, client_address)) { |
| brillo::Error::AddTo( |
| error, FROM_HERE, brillo::errors::dbus::kDomain, |
| bluetooth_adapter::kErrorFailed, |
| base::StringPrintf("Client doesn't have a discovery session: %s", |
| client_address.c_str())); |
| return false; |
| } |
| |
| if (!UpdateDiscovery(discovery_clients_.size() - 1)) { |
| brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| bluetooth_adapter::kErrorFailed, |
| "Failed to stop discovery"); |
| return false; |
| } |
| |
| discovery_clients_.erase(client_address); |
| |
| return true; |
| } |
| |
| bool AdapterInterfaceHandler::HandleRemoveDevice( |
| brillo::ErrorPtr* error, |
| dbus::Message* message, |
| const dbus::ObjectPath& device_path) { |
| VLOG(1) << __func__; |
| |
| std::string device_address = |
| ConvertDeviceObjectPathToAddress(device_path.value()); |
| |
| if (!device_interface_handler_->RemoveDevice(device_address)) { |
| brillo::Error::AddTo(error, FROM_HERE, brillo::errors::dbus::kDomain, |
| bluetooth_adapter::kErrorFailed, |
| "Device does not exist"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AdapterInterfaceHandler::UpdateDiscovery(int n_discovery_clients) { |
| VLOG(1) << "Updating discovery for would be " << n_discovery_clients |
| << " clients."; |
| if (n_discovery_clients > 0 && !is_discovering_) { |
| // There is at least one client requesting for discovery, and it's not |
| // currently discovering. |
| VLOG(1) << "Trying to start discovery"; |
| if (!newblue_->StartDiscovery(device_discovered_callback_)) { |
| LOG(ERROR) << "Failed to start discovery"; |
| return false; |
| } |
| is_discovering_ = true; |
| } else if (n_discovery_clients == 0 && is_discovering_) { |
| // There is no client requesting for discovery, and it's currently |
| // discovering. |
| VLOG(1) << "Trying to stop discovery"; |
| if (!newblue_->StopDiscovery()) { |
| LOG(ERROR) << "Failed to stop discovery"; |
| return false; |
| } |
| is_discovering_ = false; |
| } else { |
| VLOG(1) << "No need to change discovery state"; |
| } |
| |
| return true; |
| } |
| |
| void AdapterInterfaceHandler::OnClientUnavailable( |
| const std::string& client_address) { |
| VLOG(1) << "Discovery client becomes unavailable, address " << client_address; |
| discovery_clients_.erase(client_address); |
| UpdateDiscovery(discovery_clients_.size()); |
| } |
| |
| } // namespace bluetooth |