Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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 "components/proximity_auth/ble/bluetooth_low_energy_connection.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/memory/ref_counted.h" | |
| 9 #include "base/memory/weak_ptr.h" | |
| 10 #include "components/proximity_auth/ble/fake_wire_message.h" | |
| 11 #include "components/proximity_auth/connection_finder.h" | |
| 12 #include "components/proximity_auth/wire_message.h" | |
| 13 #include "device/bluetooth/bluetooth_adapter.h" | |
| 14 #include "device/bluetooth/bluetooth_device.h" | |
| 15 #include "device/bluetooth/bluetooth_gatt_characteristic.h" | |
| 16 #include "device/bluetooth/bluetooth_gatt_connection.h" | |
| 17 #include "device/bluetooth/bluetooth_gatt_notify_session.h" | |
| 18 #include "device/bluetooth/bluetooth_uuid.h" | |
| 19 | |
| 20 using device::BluetoothAdapter; | |
| 21 using device::BluetoothDevice; | |
| 22 using device::BluetoothGattConnection; | |
| 23 using device::BluetoothGattService; | |
| 24 using device::BluetoothGattCharacteristic; | |
| 25 using device::BluetoothGattNotifySession; | |
| 26 using device::BluetoothUUID; | |
| 27 | |
| 28 namespace proximity_auth { | |
| 29 | |
| 30 BluetoothLowEnergyConnection::BluetoothLowEnergyConnection( | |
| 31 const RemoteDevice& device, | |
| 32 scoped_refptr<device::BluetoothAdapter> adapter, | |
| 33 BluetoothUUID remote_service_uuid, | |
| 34 const std::string& to_peripheral_char_uuid, | |
| 35 const std::string& from_peripheral_char_uuid, | |
| 36 scoped_ptr<BluetoothGattConnection> gatt_connection) | |
| 37 : Connection(device), | |
| 38 adapter_(adapter), | |
| 39 remote_service_uuid_(remote_service_uuid), | |
| 40 to_peripheral_char_uuid_(to_peripheral_char_uuid), | |
| 41 from_peripheral_char_uuid_(from_peripheral_char_uuid), | |
| 42 connection_(gatt_connection.Pass()), | |
| 43 notify_session_pending_(false), | |
| 44 connect_signal_response_pending_(false), | |
| 45 weak_ptr_factory_(this) { | |
| 46 DCHECK(connection_); | |
| 47 DCHECK(adapter_); | |
| 48 DCHECK(adapter_->IsInitialized()); | |
| 49 | |
| 50 SetStatus(IN_PROGRESS); | |
| 51 adapter_->AddObserver(this); | |
|
Tim Song
2015/05/11 08:02:39
Are all services going to be discovered as soon as
sacomoto
2015/05/11 12:04:04
Yes, you are right. We should scan the services a
| |
| 52 } | |
| 53 | |
| 54 BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() { | |
| 55 Disconnect(); | |
| 56 if (adapter_) { | |
| 57 adapter_->RemoveObserver(this); | |
| 58 adapter_ = NULL; | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 void BluetoothLowEnergyConnection::Connect() { | |
| 63 NOTREACHED(); | |
| 64 } | |
| 65 | |
| 66 // This actually forgets the remote BLE device. This is safe as long as we only | |
| 67 // connect to BLE devices advertising the SmartLock service (assuming this | |
| 68 // device has no other being used). | |
| 69 void BluetoothLowEnergyConnection::Disconnect() { | |
| 70 StopNotifySession(); | |
| 71 SetStatus(DISCONNECTED); | |
| 72 if (connection_) { | |
| 73 connection_.reset(); | |
| 74 BluetoothDevice* device = GetRemoteDevice(); | |
| 75 if (device) { | |
| 76 VLOG(1) << "Forget device " << device->GetAddress(); | |
| 77 device->Forget(base::Bind(&base::DoNothing)); | |
| 78 } | |
| 79 } | |
| 80 } | |
| 81 | |
| 82 // TODO(sacomoto): Send a SmartLock BLE socket incoming signal. Implement a | |
| 83 // sender with full support for messages larger than a single characteristic | |
| 84 // value. | |
| 85 void BluetoothLowEnergyConnection::SendMessageImpl( | |
| 86 scoped_ptr<WireMessage> message) { | |
| 87 DCHECK(!GetGattCharacteristic(to_peripheral_char_id_)); | |
| 88 VLOG(1) << "Sending message " << message->Serialize(); | |
| 89 | |
| 90 std::string serialized_message = message->Serialize(); | |
| 91 std::vector<uint8> bytes(serialized_message.begin(), | |
| 92 serialized_message.end()); | |
| 93 | |
| 94 GetGattCharacteristic(to_peripheral_char_id_) | |
| 95 ->WriteRemoteCharacteristic( | |
| 96 bytes, base::Bind(&base::DoNothing), | |
| 97 base::Bind( | |
| 98 &BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError, | |
| 99 weak_ptr_factory_.GetWeakPtr())); | |
| 100 } | |
| 101 | |
| 102 void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter* adapter, | |
| 103 BluetoothDevice* device) { | |
| 104 if (device && device->GetAddress() == GetRemoteDeviceAddress()) { | |
| 105 VLOG(1) << "Device removed " << GetRemoteDeviceAddress(); | |
| 106 Disconnect(); | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 void BluetoothLowEnergyConnection::GattDiscoveryCompleteForService( | |
| 111 BluetoothAdapter* adapter, | |
| 112 BluetoothGattService* service) { | |
| 113 if (service && service->GetUUID() == remote_service_uuid_) { | |
| 114 VLOG(1) << "All characteristics discovered for " | |
| 115 << remote_service_uuid_.canonical_value(); | |
| 116 | |
| 117 std::vector<device::BluetoothGattCharacteristic*> characteristics = | |
| 118 service->GetCharacteristics(); | |
| 119 for (auto iter = characteristics.begin(); iter != characteristics.end(); | |
| 120 iter++) { | |
| 121 HandleCharacteristicUpdate(*iter); | |
| 122 } | |
| 123 | |
| 124 if (to_peripheral_char_id_.empty() || from_peripheral_char_id_.empty()) { | |
| 125 VLOG(1) << "Connection error, missing characteristics for SmartLock " | |
| 126 "service.\n" | |
| 127 << (to_peripheral_char_id_.empty() ? to_peripheral_char_uuid_ | |
| 128 : "") | |
| 129 << (from_peripheral_char_id_.empty() | |
| 130 ? ", " + from_peripheral_char_uuid_ | |
| 131 : "") << " not found."; | |
| 132 Disconnect(); | |
| 133 } | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 void BluetoothLowEnergyConnection::GattCharacteristicAdded( | |
| 138 BluetoothAdapter* adapter, | |
| 139 BluetoothGattCharacteristic* characteristic) { | |
| 140 VLOG(1) << "new char found: " << characteristic->GetUUID().canonical_value(); | |
| 141 HandleCharacteristicUpdate(characteristic); | |
|
Tim Song
2015/05/11 08:02:39
Is it correct to call HandleCharacteristicUpdate()
sacomoto
2015/05/11 12:04:04
This was intentional. The idea was to have the con
Tim Song
2015/05/12 03:02:46
Thanks for the explanation. In this case, do you e
sacomoto
2015/05/12 07:23:19
Yes, we still need to override GattDiscoveryComple
Tim Song
2015/05/13 02:36:55
Can we at least get rid of the for loop iterating
| |
| 142 } | |
| 143 | |
| 144 // TODO(sacomoto): Parse the SmartLock BLE socket incoming signal. Implement a | |
| 145 // receiver with full suport for messages larger than a single characteristic | |
| 146 // value. | |
| 147 void BluetoothLowEnergyConnection::GattCharacteristicValueChanged( | |
| 148 BluetoothAdapter* adapter, | |
| 149 BluetoothGattCharacteristic* characteristic, | |
| 150 const std::vector<uint8>& value) { | |
| 151 DCHECK_EQ(adapter, adapter_.get()); | |
| 152 | |
| 153 VLOG(1) << "Characteristic value changed: " | |
| 154 << characteristic->GetUUID().canonical_value(); | |
| 155 | |
| 156 if (characteristic->GetIdentifier() == from_peripheral_char_id_) { | |
| 157 const ControlSignal signal = | |
| 158 static_cast<ControlSignal>(ExtractUint32(value)); | |
| 159 | |
| 160 switch (signal) { | |
| 161 case kInvitationResponseSignal: | |
| 162 connect_signal_response_pending_ = false; | |
| 163 CompleteConnection(); | |
| 164 break; | |
| 165 case kInviteToConnectSignal: | |
| 166 case kSendSignal: | |
| 167 // TODO(sacomoto): Actually handle the message and call OnBytesReceived | |
| 168 // when complete. | |
| 169 case kDisconnectSignal: | |
| 170 Disconnect(); | |
| 171 break; | |
| 172 } | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 scoped_ptr<WireMessage> BluetoothLowEnergyConnection::DeserializeWireMessage( | |
| 177 bool* is_incomplete_message) { | |
| 178 return FakeWireMessage::Deserialize(received_bytes(), is_incomplete_message); | |
| 179 } | |
| 180 | |
| 181 void BluetoothLowEnergyConnection::HandleCharacteristicUpdate( | |
| 182 BluetoothGattCharacteristic* characteristic) { | |
| 183 // Checks if |characteristic| is equal to |from_peripheral_char_uuid_| or | |
| 184 // |to_peripheral_char_uuid_|. | |
| 185 UpdateCharacteristicsStatus(characteristic); | |
| 186 | |
| 187 // Starts a notify session for |from_peripheral_char_uuid_|. | |
| 188 if (characteristic->GetIdentifier() == from_peripheral_char_id_) | |
| 189 StartNotifySession(); | |
| 190 | |
| 191 // Sends a invite to connect signal if ready. | |
| 192 SendInviteToConnectSignal(); | |
| 193 } | |
| 194 | |
| 195 void BluetoothLowEnergyConnection::CompleteConnection() { | |
| 196 if (status() == IN_PROGRESS && !connect_signal_response_pending_ && | |
| 197 !to_peripheral_char_id_.empty() && !from_peripheral_char_id_.empty() && | |
| 198 notify_session_) { | |
| 199 VLOG(1) << "Connection completed"; | |
| 200 SetStatus(CONNECTED); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 void BluetoothLowEnergyConnection::StartNotifySession() { | |
| 205 BluetoothGattCharacteristic* characteristic = | |
| 206 GetGattCharacteristic(from_peripheral_char_id_); | |
| 207 if (!characteristic) { | |
| 208 VLOG(1) << "Characteristic " << from_peripheral_char_uuid_ << " not found."; | |
| 209 return; | |
| 210 } | |
| 211 | |
| 212 if (notify_session_ || notify_session_pending_) { | |
| 213 VLOG(1) << "Notify session already started."; | |
| 214 return; | |
| 215 } | |
| 216 | |
| 217 notify_session_pending_ = true; | |
| 218 characteristic->StartNotifySession( | |
| 219 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionStarted, | |
| 220 weak_ptr_factory_.GetWeakPtr()), | |
| 221 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionError, | |
| 222 weak_ptr_factory_.GetWeakPtr())); | |
| 223 } | |
| 224 | |
| 225 void BluetoothLowEnergyConnection::OnNotifySessionError( | |
| 226 BluetoothGattService::GattErrorCode error) { | |
| 227 VLOG(1) << "Error starting notification session: " << error; | |
| 228 notify_session_pending_ = false; | |
| 229 } | |
| 230 | |
| 231 void BluetoothLowEnergyConnection::OnNotifySessionStarted( | |
| 232 scoped_ptr<BluetoothGattNotifySession> notify_session) { | |
| 233 VLOG(1) << "Notification session started " | |
| 234 << notify_session->GetCharacteristicIdentifier(); | |
| 235 notify_session_ = notify_session.Pass(); | |
| 236 notify_session_pending_ = false; | |
| 237 | |
| 238 // Sends an invite to connect signal if ready. | |
| 239 SendInviteToConnectSignal(); | |
| 240 } | |
| 241 | |
| 242 void BluetoothLowEnergyConnection::StopNotifySession() { | |
| 243 if (notify_session_) { | |
| 244 notify_session_->Stop(base::Bind(&base::DoNothing)); | |
| 245 notify_session_.reset(); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 void BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError( | |
| 250 BluetoothGattService::GattErrorCode error) { | |
| 251 VLOG(1) << "Error writing characteristic" << to_peripheral_char_uuid_; | |
| 252 } | |
| 253 | |
| 254 void BluetoothLowEnergyConnection::SendInviteToConnectSignal() { | |
| 255 if (status() == IN_PROGRESS && !connect_signal_response_pending_ && | |
| 256 !to_peripheral_char_id_.empty() && !from_peripheral_char_id_.empty() && | |
| 257 notify_session_) { | |
| 258 VLOG(1) << "Sending invite to connect signal"; | |
| 259 connect_signal_response_pending_ = true; | |
| 260 const char connect_signal[4] = {}; | |
|
Tim Song
2015/05/11 08:02:39
You should use use the kInviteToConnectSignal valu
sacomoto
2015/05/11 12:04:04
Done.
| |
| 261 | |
| 262 // The connection status is not CONNECTED yet, so in order to bypass the | |
| 263 // check in SendMessage implementation we need to send the message using the | |
| 264 // private implementation. | |
| 265 SendMessageImpl( | |
| 266 scoped_ptr<FakeWireMessage>(new FakeWireMessage(connect_signal))); | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 void BluetoothLowEnergyConnection::UpdateCharacteristicsStatus( | |
| 271 BluetoothGattCharacteristic* characteristic) { | |
| 272 if (characteristic) { | |
| 273 std::string canonical_value = characteristic->GetUUID().canonical_value(); | |
| 274 VLOG(1) << canonical_value; | |
| 275 if (to_peripheral_char_uuid_ == canonical_value) { | |
| 276 to_peripheral_char_id_ = characteristic->GetIdentifier(); | |
| 277 } | |
| 278 if (from_peripheral_char_uuid_ == canonical_value) { | |
|
Tim Song
2015/05/11 08:02:39
nit : please be consistent and remove the braces f
sacomoto
2015/05/11 12:04:03
Done.
| |
| 279 from_peripheral_char_id_ = characteristic->GetIdentifier(); | |
| 280 } | |
| 281 BluetoothGattService* service = characteristic->GetService(); | |
| 282 if (service && service->GetUUID() == remote_service_uuid_) | |
| 283 remote_service_id_ = service->GetIdentifier(); | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 const std::string& BluetoothLowEnergyConnection::GetRemoteDeviceAddress() { | |
| 288 return remote_device().bluetooth_address; | |
| 289 } | |
| 290 | |
| 291 BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() { | |
| 292 if (!adapter_ || !adapter_->IsInitialized()) { | |
| 293 VLOG(1) << "adapter not ready"; | |
| 294 return NULL; | |
| 295 } | |
| 296 return adapter_->GetDevice(GetRemoteDeviceAddress()); | |
| 297 } | |
| 298 | |
| 299 BluetoothGattService* BluetoothLowEnergyConnection::GetRemoteService() { | |
| 300 BluetoothDevice* remote_device = GetRemoteDevice(); | |
| 301 if (!remote_device) { | |
| 302 VLOG(1) << "device not found"; | |
| 303 return NULL; | |
| 304 } | |
| 305 return remote_device->GetGattService(remote_service_id_); | |
| 306 } | |
| 307 | |
| 308 BluetoothGattCharacteristic* | |
| 309 BluetoothLowEnergyConnection::GetGattCharacteristic( | |
| 310 const std::string& gatt_characteristic) { | |
| 311 BluetoothGattService* remote_service = GetRemoteService(); | |
| 312 if (!remote_service) { | |
| 313 VLOG(1) << "service not found"; | |
| 314 return NULL; | |
| 315 } | |
| 316 return remote_service->GetCharacteristic(gatt_characteristic); | |
| 317 } | |
| 318 | |
| 319 // TODO(sacomoto): make this robust to byte ordering in both sides of the | |
| 320 // SmartLock BLE socket. | |
| 321 uint32 BluetoothLowEnergyConnection::ExtractUint32( | |
| 322 const std::vector<uint8>& bytes) { | |
| 323 return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); | |
| 324 } | |
| 325 | |
| 326 } // namespace proximity_auth | |
| OLD | NEW |