1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "device/bluetooth/bluetooth_adapter_chromeos.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/logging.h" 11#include "base/metrics/histogram.h" 12#include "base/sys_info.h" 13#include "chromeos/dbus/bluetooth_adapter_client.h" 14#include "chromeos/dbus/bluetooth_device_client.h" 15#include "chromeos/dbus/bluetooth_input_client.h" 16#include "chromeos/dbus/dbus_thread_manager.h" 17#include "device/bluetooth/bluetooth_device.h" 18#include "device/bluetooth/bluetooth_device_chromeos.h" 19 20using device::BluetoothAdapter; 21using device::BluetoothDevice; 22 23namespace chromeos { 24 25BluetoothAdapterChromeOS::BluetoothAdapterChromeOS() 26 : weak_ptr_factory_(this) { 27 DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this); 28 DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this); 29 DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this); 30 31 std::vector<dbus::ObjectPath> object_paths = 32 DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters(); 33 34 if (!object_paths.empty()) { 35 VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available."; 36 SetAdapter(object_paths[0]); 37 } 38} 39 40BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() { 41 DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this); 42 DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this); 43 DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this); 44} 45 46void BluetoothAdapterChromeOS::AddObserver( 47 BluetoothAdapter::Observer* observer) { 48 DCHECK(observer); 49 observers_.AddObserver(observer); 50} 51 52void BluetoothAdapterChromeOS::RemoveObserver( 53 BluetoothAdapter::Observer* observer) { 54 DCHECK(observer); 55 observers_.RemoveObserver(observer); 56} 57 58std::string BluetoothAdapterChromeOS::GetAddress() const { 59 if (!IsPresent()) 60 return std::string(); 61 62 BluetoothAdapterClient::Properties* properties = 63 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 64 GetProperties(object_path_); 65 DCHECK(properties); 66 67 return properties->address.value(); 68} 69 70std::string BluetoothAdapterChromeOS::GetName() const { 71 if (!IsPresent()) 72 return std::string(); 73 74 BluetoothAdapterClient::Properties* properties = 75 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 76 GetProperties(object_path_); 77 DCHECK(properties); 78 79 return properties->alias.value(); 80} 81 82bool BluetoothAdapterChromeOS::IsInitialized() const { 83 return true; 84} 85 86bool BluetoothAdapterChromeOS::IsPresent() const { 87 return !object_path_.value().empty(); 88} 89 90bool BluetoothAdapterChromeOS::IsPowered() const { 91 if (!IsPresent()) 92 return false; 93 94 BluetoothAdapterClient::Properties* properties = 95 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 96 GetProperties(object_path_); 97 98 return properties->powered.value(); 99} 100 101void BluetoothAdapterChromeOS::SetPowered( 102 bool powered, 103 const base::Closure& callback, 104 const ErrorCallback& error_callback) { 105 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 106 GetProperties(object_path_)->powered.Set( 107 powered, 108 base::Bind(&BluetoothAdapterChromeOS::OnSetPowered, 109 weak_ptr_factory_.GetWeakPtr(), 110 callback, 111 error_callback)); 112} 113 114bool BluetoothAdapterChromeOS::IsDiscovering() const { 115 if (!IsPresent()) 116 return false; 117 118 BluetoothAdapterClient::Properties* properties = 119 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 120 GetProperties(object_path_); 121 122 return properties->discovering.value(); 123} 124 125void BluetoothAdapterChromeOS::StartDiscovering( 126 const base::Closure& callback, 127 const ErrorCallback& error_callback) { 128 // BlueZ counts discovery sessions, and permits multiple sessions for a 129 // single connection, so issue a StartDiscovery() call for every use 130 // within Chromium for the right behavior. 131 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 132 StartDiscovery( 133 object_path_, 134 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery, 135 weak_ptr_factory_.GetWeakPtr(), 136 callback), 137 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError, 138 weak_ptr_factory_.GetWeakPtr(), 139 error_callback)); 140} 141 142void BluetoothAdapterChromeOS::StopDiscovering( 143 const base::Closure& callback, 144 const ErrorCallback& error_callback) { 145 // Inform BlueZ to stop one of our open discovery sessions. 146 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 147 StopDiscovery( 148 object_path_, 149 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery, 150 weak_ptr_factory_.GetWeakPtr(), 151 callback), 152 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError, 153 weak_ptr_factory_.GetWeakPtr(), 154 error_callback)); 155} 156 157void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData( 158 const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, 159 const ErrorCallback& error_callback) { 160 error_callback.Run(); 161} 162 163void BluetoothAdapterChromeOS::AdapterAdded( 164 const dbus::ObjectPath& object_path) { 165 // Set the adapter to the newly added adapter only if no adapter is present. 166 if (!IsPresent()) 167 SetAdapter(object_path); 168} 169 170void BluetoothAdapterChromeOS::AdapterRemoved( 171 const dbus::ObjectPath& object_path) { 172 if (object_path == object_path_) 173 RemoveAdapter(); 174} 175 176void BluetoothAdapterChromeOS::AdapterPropertyChanged( 177 const dbus::ObjectPath& object_path, 178 const std::string& property_name) { 179 if (object_path != object_path_) 180 return; 181 182 BluetoothAdapterClient::Properties* properties = 183 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 184 GetProperties(object_path_); 185 186 if (property_name == properties->powered.name()) 187 PoweredChanged(properties->powered.value()); 188 else if (property_name == properties->discovering.name()) 189 DiscoveringChanged(properties->discovering.value()); 190} 191 192void BluetoothAdapterChromeOS::DeviceAdded( 193 const dbus::ObjectPath& object_path) { 194 BluetoothDeviceClient::Properties* properties = 195 DBusThreadManager::Get()->GetBluetoothDeviceClient()-> 196 GetProperties(object_path); 197 if (properties->adapter.value() != object_path_) 198 return; 199 200 BluetoothDeviceChromeOS* device_chromeos = 201 new BluetoothDeviceChromeOS(this, object_path); 202 DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end()); 203 204 devices_[device_chromeos->GetAddress()] = device_chromeos; 205 206 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 207 DeviceAdded(this, device_chromeos)); 208} 209 210void BluetoothAdapterChromeOS::DeviceRemoved( 211 const dbus::ObjectPath& object_path) { 212 for (DevicesMap::iterator iter = devices_.begin(); 213 iter != devices_.end(); ++iter) { 214 BluetoothDeviceChromeOS* device_chromeos = 215 static_cast<BluetoothDeviceChromeOS*>(iter->second); 216 if (device_chromeos->object_path() == object_path) { 217 devices_.erase(iter); 218 219 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 220 DeviceRemoved(this, device_chromeos)); 221 delete device_chromeos; 222 return; 223 } 224 } 225} 226 227void BluetoothAdapterChromeOS::DevicePropertyChanged( 228 const dbus::ObjectPath& object_path, 229 const std::string& property_name) { 230 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path); 231 if (!device_chromeos) 232 return; 233 234 BluetoothDeviceClient::Properties* properties = 235 DBusThreadManager::Get()->GetBluetoothDeviceClient()-> 236 GetProperties(object_path); 237 238 if (property_name == properties->bluetooth_class.name() || 239 property_name == properties->address.name() || 240 property_name == properties->alias.name() || 241 property_name == properties->paired.name() || 242 property_name == properties->trusted.name() || 243 property_name == properties->connected.name() || 244 property_name == properties->uuids.name()) 245 NotifyDeviceChanged(device_chromeos); 246 247 // UMA connection counting 248 if (property_name == properties->connected.name()) { 249 int count = 0; 250 251 for (DevicesMap::iterator iter = devices_.begin(); 252 iter != devices_.end(); ++iter) { 253 if (iter->second->IsPaired() && iter->second->IsConnected()) 254 ++count; 255 } 256 257 UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count); 258 } 259} 260 261void BluetoothAdapterChromeOS::InputPropertyChanged( 262 const dbus::ObjectPath& object_path, 263 const std::string& property_name) { 264 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path); 265 if (!device_chromeos) 266 return; 267 268 BluetoothInputClient::Properties* properties = 269 DBusThreadManager::Get()->GetBluetoothInputClient()-> 270 GetProperties(object_path); 271 272 // Properties structure can be removed, which triggers a change in the 273 // BluetoothDevice::IsConnectable() property, as does a change in the 274 // actual reconnect_mode property. 275 if (!properties || 276 property_name == properties->reconnect_mode.name()) 277 NotifyDeviceChanged(device_chromeos); 278} 279 280BluetoothDeviceChromeOS* 281BluetoothAdapterChromeOS::GetDeviceWithPath( 282 const dbus::ObjectPath& object_path) { 283 for (DevicesMap::iterator iter = devices_.begin(); 284 iter != devices_.end(); ++iter) { 285 BluetoothDeviceChromeOS* device_chromeos = 286 static_cast<BluetoothDeviceChromeOS*>(iter->second); 287 if (device_chromeos->object_path() == object_path) 288 return device_chromeos; 289 } 290 291 return NULL; 292} 293 294void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) { 295 DCHECK(!IsPresent()); 296 object_path_ = object_path; 297 298 VLOG(1) << object_path_.value() << ": using adapter."; 299 300 SetAdapterName(); 301 302 BluetoothAdapterClient::Properties* properties = 303 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 304 GetProperties(object_path_); 305 306 PresentChanged(true); 307 308 if (properties->powered.value()) 309 PoweredChanged(true); 310 if (properties->discovering.value()) 311 DiscoveringChanged(true); 312 313 std::vector<dbus::ObjectPath> device_paths = 314 DBusThreadManager::Get()->GetBluetoothDeviceClient()-> 315 GetDevicesForAdapter(object_path_); 316 317 for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin(); 318 iter != device_paths.end(); ++iter) { 319 BluetoothDeviceChromeOS* device_chromeos = 320 new BluetoothDeviceChromeOS(this, *iter); 321 322 devices_[device_chromeos->GetAddress()] = device_chromeos; 323 324 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 325 DeviceAdded(this, device_chromeos)); 326 } 327} 328 329void BluetoothAdapterChromeOS::SetAdapterName() { 330 std::string board = base::SysInfo::GetLsbReleaseBoard(); 331 std::string alias; 332 if (board.substr(0, 6) == "stumpy") { 333 alias = "Chromebox"; 334 } else if (board.substr(0, 4) == "link") { 335 alias = "Chromebook Pixel"; 336 } else { 337 alias = "Chromebook"; 338 } 339 340 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 341 GetProperties(object_path_)->alias.Set( 342 alias, 343 base::Bind(&BluetoothAdapterChromeOS::OnSetAlias, 344 weak_ptr_factory_.GetWeakPtr())); 345} 346 347void BluetoothAdapterChromeOS::OnSetAlias(bool success) { 348 LOG_IF(WARNING, !success) << object_path_.value() 349 << ": Failed to set adapter alias"; 350} 351 352void BluetoothAdapterChromeOS::RemoveAdapter() { 353 DCHECK(IsPresent()); 354 VLOG(1) << object_path_.value() << ": adapter removed."; 355 356 BluetoothAdapterClient::Properties* properties = 357 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> 358 GetProperties(object_path_); 359 360 object_path_ = dbus::ObjectPath(""); 361 362 if (properties->powered.value()) 363 PoweredChanged(false); 364 if (properties->discovering.value()) 365 DiscoveringChanged(false); 366 367 // Copy the devices list here and clear the original so that when we 368 // send DeviceRemoved(), GetDevices() returns no devices. 369 DevicesMap devices = devices_; 370 devices_.clear(); 371 372 for (DevicesMap::iterator iter = devices.begin(); 373 iter != devices.end(); ++iter) { 374 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 375 DeviceRemoved(this, iter->second)); 376 delete iter->second; 377 } 378 379 PresentChanged(false); 380} 381 382void BluetoothAdapterChromeOS::PoweredChanged(bool powered) { 383 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 384 AdapterPoweredChanged(this, powered)); 385} 386 387void BluetoothAdapterChromeOS::DiscoveringChanged( 388 bool discovering) { 389 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 390 AdapterDiscoveringChanged(this, discovering)); 391} 392 393void BluetoothAdapterChromeOS::PresentChanged(bool present) { 394 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 395 AdapterPresentChanged(this, present)); 396} 397 398void BluetoothAdapterChromeOS::NotifyDeviceChanged( 399 BluetoothDeviceChromeOS* device) { 400 DCHECK(device->adapter_ == this); 401 402 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 403 DeviceChanged(this, device)); 404} 405 406void BluetoothAdapterChromeOS::OnSetPowered(const base::Closure& callback, 407 const ErrorCallback& error_callback, 408 bool success) { 409 if (success) 410 callback.Run(); 411 else 412 error_callback.Run(); 413} 414 415void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) { 416 callback.Run(); 417} 418 419void BluetoothAdapterChromeOS::OnStartDiscoveryError( 420 const ErrorCallback& error_callback, 421 const std::string& error_name, 422 const std::string& error_message) { 423 LOG(WARNING) << object_path_.value() << ": Failed to start discovery: " 424 << error_name << ": " << error_message; 425 error_callback.Run(); 426} 427 428void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) { 429 callback.Run(); 430} 431 432void BluetoothAdapterChromeOS::OnStopDiscoveryError( 433 const ErrorCallback& error_callback, 434 const std::string& error_name, 435 const std::string& error_message) { 436 LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: " 437 << error_name << ": " << error_message; 438 error_callback.Run(); 439} 440 441} // namespace chromeos 442