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 20 using device::BluetoothAdapter; 21 using device::BluetoothDevice; 22 23 namespace chromeos { 24 25 BluetoothAdapterChromeOS::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 40 BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() { 41 DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this); 42 DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this); 43 DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this); 44 } 45 46 void BluetoothAdapterChromeOS::AddObserver( 47 BluetoothAdapter::Observer* observer) { 48 DCHECK(observer); 49 observers_.AddObserver(observer); 50 } 51 52 void BluetoothAdapterChromeOS::RemoveObserver( 53 BluetoothAdapter::Observer* observer) { 54 DCHECK(observer); 55 observers_.RemoveObserver(observer); 56 } 57 58 std::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 70 std::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 82 bool BluetoothAdapterChromeOS::IsInitialized() const { 83 return true; 84 } 85 86 bool BluetoothAdapterChromeOS::IsPresent() const { 87 return !object_path_.value().empty(); 88 } 89 90 bool 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 101 void 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 114 bool 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 125 void 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 142 void 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 157 void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData( 158 const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, 159 const ErrorCallback& error_callback) { 160 error_callback.Run(); 161 } 162 163 void 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 170 void BluetoothAdapterChromeOS::AdapterRemoved( 171 const dbus::ObjectPath& object_path) { 172 if (object_path == object_path_) 173 RemoveAdapter(); 174 } 175 176 void 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 192 void 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 210 void 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 227 void 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 261 void 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 280 BluetoothDeviceChromeOS* 281 BluetoothAdapterChromeOS::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 294 void 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 329 void 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 347 void BluetoothAdapterChromeOS::OnSetAlias(bool success) { 348 LOG_IF(WARNING, !success) << object_path_.value() 349 << ": Failed to set adapter alias"; 350 } 351 352 void 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 382 void BluetoothAdapterChromeOS::PoweredChanged(bool powered) { 383 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 384 AdapterPoweredChanged(this, powered)); 385 } 386 387 void BluetoothAdapterChromeOS::DiscoveringChanged( 388 bool discovering) { 389 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 390 AdapterDiscoveringChanged(this, discovering)); 391 } 392 393 void BluetoothAdapterChromeOS::PresentChanged(bool present) { 394 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 395 AdapterPresentChanged(this, present)); 396 } 397 398 void BluetoothAdapterChromeOS::NotifyDeviceChanged( 399 BluetoothDeviceChromeOS* device) { 400 DCHECK(device->adapter_ == this); 401 402 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, 403 DeviceChanged(this, device)); 404 } 405 406 void 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 415 void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) { 416 callback.Run(); 417 } 418 419 void 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 428 void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) { 429 callback.Run(); 430 } 431 432 void 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