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