| OLD | NEW | 
|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "device/bluetooth/bluetooth_adapter_mac.h" | 5 #include "device/bluetooth/bluetooth_adapter_mac.h" | 
| 6 | 6 | 
| 7 #import <IOBluetooth/objc/IOBluetoothDevice.h> | 7 #import <IOBluetooth/objc/IOBluetoothDevice.h> | 
| 8 #import <IOBluetooth/objc/IOBluetoothHostController.h> | 8 #import <IOBluetooth/objc/IOBluetoothHostController.h> | 
| 9 | 9 | 
| 10 #include <string> | 10 #include <string> | 
| 11 | 11 | 
| 12 #include "base/bind.h" | 12 #include "base/bind.h" | 
| 13 #include "base/compiler_specific.h" | 13 #include "base/compiler_specific.h" | 
| 14 #include "base/containers/hash_tables.h" | 14 #include "base/containers/hash_tables.h" | 
| 15 #include "base/location.h" | 15 #include "base/location.h" | 
| 16 #include "base/mac/sdk_forward_declarations.h" | 16 #include "base/mac/sdk_forward_declarations.h" | 
| 17 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" | 
| 18 #include "base/profiler/scoped_tracker.h" | 18 #include "base/profiler/scoped_tracker.h" | 
| 19 #include "base/sequenced_task_runner.h" | 19 #include "base/sequenced_task_runner.h" | 
| 20 #include "base/single_thread_task_runner.h" | 20 #include "base/single_thread_task_runner.h" | 
| 21 #include "base/strings/sys_string_conversions.h" | 21 #include "base/strings/sys_string_conversions.h" | 
| 22 #include "base/thread_task_runner_handle.h" | 22 #include "base/thread_task_runner_handle.h" | 
| 23 #include "base/time/time.h" | 23 #include "base/time/time.h" | 
| 24 #include "device/bluetooth/bluetooth_device_mac.h" | 24 #include "device/bluetooth/bluetooth_device_mac.h" | 
|  | 25 #include "device/bluetooth/bluetooth_discovery_session.h" | 
| 25 #include "device/bluetooth/bluetooth_socket_mac.h" | 26 #include "device/bluetooth/bluetooth_socket_mac.h" | 
| 26 #include "device/bluetooth/bluetooth_uuid.h" | 27 #include "device/bluetooth/bluetooth_uuid.h" | 
| 27 | 28 | 
| 28 namespace { | 29 namespace { | 
| 29 | 30 | 
| 30 // The frequency with which to poll the adapter for updates. | 31 // The frequency with which to poll the adapter for updates. | 
| 31 const int kPollIntervalMs = 500; | 32 const int kPollIntervalMs = 500; | 
| 32 | 33 | 
| 33 // The length of time that must elapse since the last Inquiry response before a | 34 // The length of time that must elapse since the last Inquiry response before a | 
| 34 // discovered Classic device is considered to be no longer available. | 35 // discovered Classic device is considered to be no longer available. | 
| (...skipping 15 matching lines...) Expand all  Loading... | 
| 50   adapter->Init(); | 51   adapter->Init(); | 
| 51   return adapter->weak_ptr_factory_.GetWeakPtr(); | 52   return adapter->weak_ptr_factory_.GetWeakPtr(); | 
| 52 } | 53 } | 
| 53 | 54 | 
| 54 BluetoothAdapterMac::BluetoothAdapterMac() | 55 BluetoothAdapterMac::BluetoothAdapterMac() | 
| 55     : BluetoothAdapter(), | 56     : BluetoothAdapter(), | 
| 56       powered_(false), | 57       powered_(false), | 
| 57       num_discovery_sessions_(0), | 58       num_discovery_sessions_(0), | 
| 58       classic_discovery_manager_( | 59       classic_discovery_manager_( | 
| 59           BluetoothDiscoveryManagerMac::CreateClassic(this)), | 60           BluetoothDiscoveryManagerMac::CreateClassic(this)), | 
|  | 61       low_energy_discovery_manager_( | 
|  | 62           BluetoothLowEnergyDiscoveryManagerMac::Create(this)), | 
| 60       weak_ptr_factory_(this) { | 63       weak_ptr_factory_(this) { | 
| 61   DCHECK(classic_discovery_manager_.get()); | 64   DCHECK(classic_discovery_manager_.get()); | 
| 62 } | 65 } | 
| 63 | 66 | 
| 64 BluetoothAdapterMac::~BluetoothAdapterMac() { | 67 BluetoothAdapterMac::~BluetoothAdapterMac() { | 
| 65 } | 68 } | 
| 66 | 69 | 
| 67 std::string BluetoothAdapterMac::GetAddress() const { | 70 std::string BluetoothAdapterMac::GetAddress() const { | 
| 68   return address_; | 71   return address_; | 
| 69 } | 72 } | 
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 102 } | 105 } | 
| 103 | 106 | 
| 104 void BluetoothAdapterMac::SetDiscoverable( | 107 void BluetoothAdapterMac::SetDiscoverable( | 
| 105     bool discoverable, | 108     bool discoverable, | 
| 106     const base::Closure& callback, | 109     const base::Closure& callback, | 
| 107     const ErrorCallback& error_callback) { | 110     const ErrorCallback& error_callback) { | 
| 108   NOTIMPLEMENTED(); | 111   NOTIMPLEMENTED(); | 
| 109 } | 112 } | 
| 110 | 113 | 
| 111 bool BluetoothAdapterMac::IsDiscovering() const { | 114 bool BluetoothAdapterMac::IsDiscovering() const { | 
| 112   return classic_discovery_manager_->IsDiscovering(); | 115   return (classic_discovery_manager_->IsDiscovering() || | 
|  | 116           low_energy_discovery_manager_->IsDiscovering()); | 
| 113 } | 117 } | 
| 114 | 118 | 
| 115 void BluetoothAdapterMac::CreateRfcommService( | 119 void BluetoothAdapterMac::CreateRfcommService( | 
| 116     const BluetoothUUID& uuid, | 120     const BluetoothUUID& uuid, | 
| 117     const ServiceOptions& options, | 121     const ServiceOptions& options, | 
| 118     const CreateServiceCallback& callback, | 122     const CreateServiceCallback& callback, | 
| 119     const CreateServiceErrorCallback& error_callback) { | 123     const CreateServiceErrorCallback& error_callback) { | 
| 120   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket(); | 124   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket(); | 
| 121   socket->ListenUsingRfcomm( | 125   socket->ListenUsingRfcomm( | 
| 122       this, uuid, options, base::Bind(callback, socket), error_callback); | 126       this, uuid, options, base::Bind(callback, socket), error_callback); | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 141 } | 145 } | 
| 142 | 146 | 
| 143 void BluetoothAdapterMac::RegisterAdvertisement( | 147 void BluetoothAdapterMac::RegisterAdvertisement( | 
| 144     scoped_ptr<BluetoothAdvertisement::Data> advertisement_data, | 148     scoped_ptr<BluetoothAdvertisement::Data> advertisement_data, | 
| 145     const CreateAdvertisementCallback& callback, | 149     const CreateAdvertisementCallback& callback, | 
| 146     const CreateAdvertisementErrorCallback& error_callback) { | 150     const CreateAdvertisementErrorCallback& error_callback) { | 
| 147   NOTIMPLEMENTED(); | 151   NOTIMPLEMENTED(); | 
| 148   error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM); | 152   error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM); | 
| 149 } | 153 } | 
| 150 | 154 | 
| 151 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) { | 155 void BluetoothAdapterMac::ClassicDeviceFound(IOBluetoothDevice* device) { | 
| 152   DeviceAdded(device); | 156   ClassicDeviceAdded(device); | 
| 153 } | 157 } | 
| 154 | 158 | 
| 155 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) { | 159 void BluetoothAdapterMac::ClassicDiscoveryStopped(bool unexpected) { | 
| 156   if (unexpected) { | 160   if (unexpected) { | 
| 157     DVLOG(1) << "Discovery stopped unexpectedly"; | 161     DVLOG(1) << "Discovery stopped unexpectedly"; | 
| 158     num_discovery_sessions_ = 0; | 162     num_discovery_sessions_ = 0; | 
| 159     MarkDiscoverySessionsAsInactive(); | 163     MarkDiscoverySessionsAsInactive(); | 
| 160   } | 164   } | 
| 161   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, | 165   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, | 
| 162                     observers_, | 166                     observers_, | 
| 163                     AdapterDiscoveringChanged(this, false)); | 167                     AdapterDiscoveringChanged(this, false)); | 
| 164 } | 168 } | 
| 165 | 169 | 
| 166 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) { | 170 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) { | 
| 167   // TODO(isherman): Investigate whether this method can be replaced with a call | 171   // TODO(isherman): Investigate whether this method can be replaced with a call | 
| 168   // to +registerForConnectNotifications:selector:. | 172   // to +registerForConnectNotifications:selector:. | 
| 169   DVLOG(1) << "Adapter registered a new connection from device with address: " | 173   DVLOG(1) << "Adapter registered a new connection from device with address: " | 
| 170            << BluetoothDeviceMac::GetDeviceAddress(device); | 174            << BluetoothDeviceMac::GetDeviceAddress(device); | 
| 171   DeviceAdded(device); | 175   ClassicDeviceAdded(device); | 
|  | 176 } | 
|  | 177 | 
|  | 178 void BluetoothAdapterMac::RemovePairingDelegateInternal( | 
|  | 179     BluetoothDevice::PairingDelegate* pairing_delegate) { | 
| 172 } | 180 } | 
| 173 | 181 | 
| 174 void BluetoothAdapterMac::AddDiscoverySession( | 182 void BluetoothAdapterMac::AddDiscoverySession( | 
| 175     BluetoothDiscoveryFilter* discovery_filter, | 183     BluetoothDiscoveryFilter* discovery_filter, | 
| 176     const base::Closure& callback, | 184     const base::Closure& callback, | 
| 177     const ErrorCallback& error_callback) { | 185     const ErrorCallback& error_callback) { | 
| 178   DVLOG(1) << __func__; | 186   DVLOG(1) << __func__; | 
| 179   if (num_discovery_sessions_ > 0) { | 187   if (num_discovery_sessions_ > 0) { | 
| 180     DCHECK(IsDiscovering()); | 188     DCHECK(IsDiscovering()); | 
| 181     num_discovery_sessions_++; | 189     num_discovery_sessions_++; | 
|  | 190     // We are already running a discovery session, notify the system if the | 
|  | 191     // filter has changed. | 
|  | 192     if (!StartDiscovery(discovery_filter)) { | 
|  | 193       error_callback.Run(); | 
|  | 194       return; | 
|  | 195     } | 
| 182     callback.Run(); | 196     callback.Run(); | 
| 183     return; | 197     return; | 
| 184   } | 198   } | 
| 185 | 199 | 
| 186   DCHECK_EQ(0, num_discovery_sessions_); | 200   DCHECK_EQ(0, num_discovery_sessions_); | 
| 187 | 201 | 
| 188   if (!classic_discovery_manager_->StartDiscovery()) { | 202   if (!StartDiscovery(discovery_filter)) { | 
| 189     DVLOG(1) << "Failed to add a discovery session"; |  | 
| 190     error_callback.Run(); | 203     error_callback.Run(); | 
| 191     return; | 204     return; | 
| 192   } | 205   } | 
| 193 | 206 | 
| 194   DVLOG(1) << "Added a discovery session"; | 207   DVLOG(1) << "Added a discovery session"; | 
| 195   num_discovery_sessions_++; | 208   num_discovery_sessions_++; | 
| 196   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, | 209   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, | 
| 197                     observers_, | 210                     observers_, | 
| 198                     AdapterDiscoveringChanged(this, true)); | 211                     AdapterDiscoveringChanged(this, true)); | 
| 199   callback.Run(); | 212   callback.Run(); | 
| (...skipping 12 matching lines...) Expand all  Loading... | 
| 212     callback.Run(); | 225     callback.Run(); | 
| 213     return; | 226     return; | 
| 214   } | 227   } | 
| 215 | 228 | 
| 216   if (num_discovery_sessions_ == 0) { | 229   if (num_discovery_sessions_ == 0) { | 
| 217     DVLOG(1) << "No active discovery sessions. Returning error."; | 230     DVLOG(1) << "No active discovery sessions. Returning error."; | 
| 218     error_callback.Run(); | 231     error_callback.Run(); | 
| 219     return; | 232     return; | 
| 220   } | 233   } | 
| 221 | 234 | 
| 222   if (!classic_discovery_manager_->StopDiscovery()) { | 235   // Default to dual discovery if |discovery_filter| is NULL. | 
| 223     DVLOG(1) << "Failed to stop discovery"; | 236   BluetoothDiscoveryFilter::TransportMask transport = | 
| 224     error_callback.Run(); | 237       BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL; | 
| 225     return; | 238   if (discovery_filter) | 
|  | 239     transport = discovery_filter->GetTransport(); | 
|  | 240 | 
|  | 241   if (transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC) { | 
|  | 242     if (!classic_discovery_manager_->StopDiscovery()) { | 
|  | 243       DVLOG(1) << "Failed to stop classic discovery"; | 
|  | 244       error_callback.Run(); | 
|  | 245       return; | 
|  | 246     } | 
|  | 247   } | 
|  | 248   if (transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_LE) { | 
|  | 249     low_energy_discovery_manager_->StopDiscovery(); | 
| 226   } | 250   } | 
| 227 | 251 | 
| 228   DVLOG(1) << "Discovery stopped"; | 252   DVLOG(1) << "Discovery stopped"; | 
| 229   num_discovery_sessions_--; | 253   num_discovery_sessions_--; | 
| 230   callback.Run(); | 254   callback.Run(); | 
| 231 } | 255 } | 
| 232 | 256 | 
| 233 void BluetoothAdapterMac::SetDiscoveryFilter( | 257 void BluetoothAdapterMac::SetDiscoveryFilter( | 
| 234     scoped_ptr<BluetoothDiscoveryFilter> discovery_filter, | 258     scoped_ptr<BluetoothDiscoveryFilter> discovery_filter, | 
| 235     const base::Closure& callback, | 259     const base::Closure& callback, | 
| 236     const ErrorCallback& error_callback) { | 260     const ErrorCallback& error_callback) { | 
| 237   NOTIMPLEMENTED(); | 261   NOTIMPLEMENTED(); | 
| 238   error_callback.Run(); | 262   error_callback.Run(); | 
| 239 } | 263 } | 
| 240 | 264 | 
| 241 void BluetoothAdapterMac::RemovePairingDelegateInternal( | 265 bool BluetoothAdapterMac::StartDiscovery( | 
| 242     BluetoothDevice::PairingDelegate* pairing_delegate) { | 266     BluetoothDiscoveryFilter* discovery_filter) { | 
|  | 267   // Default to dual discovery if |discovery_filter| is NULL.  IOBluetooth seems | 
|  | 268   // allow starting low energy and classic discovery at once. | 
|  | 269   BluetoothDiscoveryFilter::TransportMask transport = | 
|  | 270       BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL; | 
|  | 271   if (discovery_filter) | 
|  | 272     transport = discovery_filter->GetTransport(); | 
|  | 273 | 
|  | 274   if ((transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC) && | 
|  | 275       !classic_discovery_manager_->IsDiscovering()) { | 
|  | 276     // TODO(krstnmnlsn): If a classic discovery session is already running then | 
|  | 277     // we should update its filter. crbug.com/498056 | 
|  | 278     if (!classic_discovery_manager_->StartDiscovery()) { | 
|  | 279       DVLOG(1) << "Failed to add a classic discovery session"; | 
|  | 280       return false; | 
|  | 281     } | 
|  | 282   } | 
|  | 283   if (transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_LE) { | 
|  | 284     // Begin a low energy discovery session or update it if one is already | 
|  | 285     // running. | 
|  | 286     low_energy_discovery_manager_->StartDiscovery(BluetoothDevice::UUIDList()); | 
|  | 287   } | 
|  | 288   return true; | 
| 243 } | 289 } | 
| 244 | 290 | 
| 245 void BluetoothAdapterMac::Init() { | 291 void BluetoothAdapterMac::Init() { | 
| 246   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 292   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 
| 247   PollAdapter(); | 293   PollAdapter(); | 
| 248 } | 294 } | 
| 249 | 295 | 
| 250 void BluetoothAdapterMac::InitForTest( | 296 void BluetoothAdapterMac::InitForTest( | 
| 251     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) { | 297     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) { | 
| 252   ui_task_runner_ = ui_task_runner; | 298   ui_task_runner_ = ui_task_runner; | 
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 316           "461181 BluetoothAdapterMac::PollAdapter::UpdateDevices")); | 362           "461181 BluetoothAdapterMac::PollAdapter::UpdateDevices")); | 
| 317   UpdateDevices(); | 363   UpdateDevices(); | 
| 318 | 364 | 
| 319   ui_task_runner_->PostDelayedTask( | 365   ui_task_runner_->PostDelayedTask( | 
| 320       FROM_HERE, | 366       FROM_HERE, | 
| 321       base::Bind(&BluetoothAdapterMac::PollAdapter, | 367       base::Bind(&BluetoothAdapterMac::PollAdapter, | 
| 322                  weak_ptr_factory_.GetWeakPtr()), | 368                  weak_ptr_factory_.GetWeakPtr()), | 
| 323       base::TimeDelta::FromMilliseconds(kPollIntervalMs)); | 369       base::TimeDelta::FromMilliseconds(kPollIntervalMs)); | 
| 324 } | 370 } | 
| 325 | 371 | 
| 326 void BluetoothAdapterMac::DeviceAdded(IOBluetoothDevice* device) { | 372 void BluetoothAdapterMac::ClassicDeviceAdded(IOBluetoothDevice* device) { | 
| 327   std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device); | 373   std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device); | 
| 328 | 374 | 
| 329   // Only notify observers once per device. | 375   // Only notify observers once per device. | 
| 330   if (devices_.count(device_address)) | 376   if (devices_.count(device_address)) | 
| 331     return; | 377     return; | 
| 332 | 378 | 
| 333   devices_[device_address] = new BluetoothDeviceMac(device); | 379   devices_[device_address] = new BluetoothDeviceMac(device); | 
| 334   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, | 380   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, | 
| 335                     observers_, | 381                     observers_, | 
| 336                     DeviceAdded(this, devices_[device_address])); | 382                     DeviceAdded(this, devices_[device_address])); | 
| 337 } | 383 } | 
| 338 | 384 | 
|  | 385 // TODO(krstnmnlsn): This method to be implemented as soon as UpdateDevices can | 
|  | 386 // handle instances of LowEnergyBluetoothDevice in |devices_|. crbug.com/498009 | 
|  | 387 void BluetoothAdapterMac::LowEnergyDeviceUpdated( | 
|  | 388     CBPeripheral* peripheral, | 
|  | 389     NSDictionary* advertisementData, | 
|  | 390     int rssi) { | 
|  | 391 } | 
|  | 392 | 
|  | 393 // TODO(krstnmnlsn): This method assumes all BluetoothDevices in devices_ are | 
|  | 394 // instances of BluetoothDeviceMac.  Add support for low energy devices. | 
|  | 395 // crbug.com/498009 | 
| 339 void BluetoothAdapterMac::UpdateDevices() { | 396 void BluetoothAdapterMac::UpdateDevices() { | 
| 340   // Notify observers if any previously seen devices are no longer available, | 397   // Notify observers if any previously seen devices are no longer available, | 
| 341   // i.e. if they are no longer paired, connected, nor recently discovered via | 398   // i.e. if they are no longer paired, connected, nor recently discovered via | 
| 342   // an inquiry. | 399   // an inquiry. | 
| 343   std::set<std::string> removed_devices; | 400   std::set<std::string> removed_devices; | 
| 344   for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 401   for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) { | 
| 345     BluetoothDevice* device = it->second; | 402     BluetoothDevice* device = it->second; | 
| 346     if (device->IsPaired() || device->IsConnected()) | 403     if (device->IsPaired() || device->IsConnected()) | 
| 347       continue; | 404       continue; | 
| 348 | 405 | 
| 349     NSDate* last_inquiry_update = | 406     NSDate* last_inquiry_update = | 
| 350         static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate(); | 407         static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate(); | 
| 351     if (last_inquiry_update && | 408     if (last_inquiry_update && | 
| 352         -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec) | 409         -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec) | 
| 353       continue; | 410       continue; | 
| 354 | 411 | 
| 355     FOR_EACH_OBSERVER( | 412     FOR_EACH_OBSERVER( | 
| 356         BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device)); | 413         BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device)); | 
| 357     delete device; | 414     delete device; | 
| 358     removed_devices.insert(it->first); | 415     removed_devices.insert(it->first); | 
| 359     // The device will be erased from the map in the loop immediately below. | 416     // The device will be erased from the map in the loop immediately below. | 
| 360   } | 417   } | 
| 361   for (const std::string& device_address : removed_devices) { | 418   for (const std::string& device_address : removed_devices) { | 
| 362     size_t num_removed = devices_.erase(device_address); | 419     size_t num_removed = devices_.erase(device_address); | 
| 363     DCHECK_EQ(num_removed, 1U); | 420     DCHECK_EQ(num_removed, 1U); | 
| 364   } | 421   } | 
| 365 | 422 | 
| 366   // Add any new paired devices. | 423   // Add any new paired devices. | 
| 367   for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) { | 424   for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) { | 
| 368     DeviceAdded(device); | 425     ClassicDeviceAdded(device); | 
| 369   } | 426   } | 
| 370 } | 427 } | 
| 371 | 428 | 
| 372 }  // namespace device | 429 }  // namespace device | 
| OLD | NEW | 
|---|