| Index: device/bluetooth/device.cc | 
| diff --git a/device/bluetooth/device.cc b/device/bluetooth/device.cc | 
| index 7e662802eabb5aed91c9859f782903049c866170..2094107538a48558234db191f8b076d5b2d18d33 100644 | 
| --- a/device/bluetooth/device.cc | 
| +++ b/device/bluetooth/device.cc | 
| @@ -3,6 +3,7 @@ | 
| // found in the LICENSE file. | 
|  | 
| #include <utility> | 
| +#include <vector> | 
|  | 
| #include "base/strings/utf_string_conversions.h" | 
| #include "device/bluetooth/device.h" | 
| @@ -10,11 +11,23 @@ | 
|  | 
| namespace bluetooth { | 
|  | 
| -Device::Device(const std::string& address, | 
| -               scoped_refptr<device::BluetoothAdapter> adapter) | 
| -    : address_(address), adapter_(std::move(adapter)) {} | 
| +Device::Device(scoped_refptr<device::BluetoothAdapter> adapter, | 
| +               std::unique_ptr<device::BluetoothGattConnection> connection, | 
| +               mojom::DeviceRequest request) | 
| +    : adapter_(std::move(adapter)), | 
| +      connection_(std::move(connection)), | 
| +      binding_(this, std::move(request)) { | 
| +  // Binding is owned by this Device, so base::Unretatined is safe. | 
| +  binding_.set_connection_error_handler( | 
| +      base::Bind(&Device::Disconnect, base::Unretained(this))); | 
|  | 
| -Device::~Device() {} | 
| +  adapter_->AddObserver(this); | 
| +} | 
| + | 
| +Device::~Device() { | 
| +  binding_.Close(); | 
| +  adapter_->RemoveObserver(this); | 
| +} | 
|  | 
| // static | 
| mojom::DeviceInfoPtr Device::ConstructDeviceInfoStruct( | 
| @@ -25,6 +38,7 @@ mojom::DeviceInfoPtr Device::ConstructDeviceInfoStruct( | 
| device_info->name_for_display = | 
| base::UTF16ToUTF8(device->GetNameForDisplay()); | 
| device_info->address = device->GetAddress(); | 
| +  device_info->is_gatt_connected = device->IsGattConnected(); | 
|  | 
| if (device->GetInquiryRSSI()) { | 
| device_info->rssi = mojom::RSSIWrapper::New(); | 
| @@ -34,14 +48,88 @@ mojom::DeviceInfoPtr Device::ConstructDeviceInfoStruct( | 
| return device_info; | 
| } | 
|  | 
| +mojo::Binding<mojom::Device>* Device::GetBindingForTesting() { | 
| +  return &binding_; | 
| +} | 
| + | 
| +void Device::DeviceChanged(device::BluetoothAdapter* adapter, | 
| +                           device::BluetoothDevice* device) { | 
| +  if (device->GetAddress() != GetAddress()) { | 
| +    return; | 
| +  } | 
| + | 
| +  if (!device->IsGattConnected()) { | 
| +    delete this; | 
| +  } | 
| +} | 
| + | 
| +void Device::GattServicesDiscovered(device::BluetoothAdapter* adapter, | 
| +                                    device::BluetoothDevice* device) { | 
| +  if (device->GetAddress() != GetAddress()) { | 
| +    return; | 
| +  } | 
| + | 
| +  std::vector<base::Closure> requests; | 
| +  requests.swap(pending_services_requests_); | 
| +  for (const base::Closure& request : requests) { | 
| +    request.Run(); | 
| +  } | 
| +} | 
| + | 
| +void Device::Disconnect() { | 
| +  delete this; | 
| +} | 
| + | 
| void Device::GetInfo(const GetInfoCallback& callback) { | 
| -  device::BluetoothDevice* device = adapter_->GetDevice(address_); | 
| -  if (device) { | 
| -    mojom::DeviceInfoPtr device_info = ConstructDeviceInfoStruct(device); | 
| -    callback.Run(std::move(device_info)); | 
| -  } else { | 
| -    callback.Run(nullptr); | 
| +  device::BluetoothDevice* device = adapter_->GetDevice(GetAddress()); | 
| +  DCHECK(device); | 
| + | 
| +  mojom::DeviceInfoPtr device_info = ConstructDeviceInfoStruct(device); | 
| +  callback.Run(std::move(device_info)); | 
| +} | 
| + | 
| +void Device::GetServices(const GetServicesCallback& callback) { | 
| +  device::BluetoothDevice* device = adapter_->GetDevice(GetAddress()); | 
| +  DCHECK(device); | 
| + | 
| +  if (device->IsGattServicesDiscoveryComplete()) { | 
| +    GetServicesImpl(callback); | 
| +    return; | 
| } | 
| + | 
| +  // pending_services_requests_ is owned by Device, so base::Unretatined is | 
| +  // safe. | 
| +  pending_services_requests_.push_back( | 
| +      base::Bind(&Device::GetServicesImpl, base::Unretained(this), callback)); | 
| +} | 
| + | 
| +void Device::GetServicesImpl(const GetServicesCallback& callback) { | 
| +  device::BluetoothDevice* device = adapter_->GetDevice(GetAddress()); | 
| +  DCHECK(device); | 
| + | 
| +  std::vector<mojom::ServiceInfoPtr> services; | 
| + | 
| +  for (const device::BluetoothRemoteGattService* service : | 
| +       device->GetGattServices()) { | 
| +    mojom::ServiceInfoPtr service_info = ConstructServiceInfoStruct(*service); | 
| +    services.push_back(std::move(service_info)); | 
| +  } | 
| + | 
| +  callback.Run(std::move(services)); | 
| +} | 
| + | 
| +mojom::ServiceInfoPtr Device::ConstructServiceInfoStruct( | 
| +    const device::BluetoothRemoteGattService& service) { | 
| +  mojom::ServiceInfoPtr service_info = mojom::ServiceInfo::New(); | 
| + | 
| +  service_info->uuid = service.GetUUID(); | 
| +  service_info->is_primary = service.IsPrimary(); | 
| + | 
| +  return service_info; | 
| +} | 
| + | 
| +const std::string& Device::GetAddress() { | 
| +  return connection_->GetDeviceAddress(); | 
| } | 
|  | 
| }  // namespace bluetooth | 
|  |