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 |