Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: components/proximity_auth/ble/bluetooth_low_energy_connection.cc

Issue 1116963002: Bluetooth low energy connection. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "base/time/time.h"
11 #include "components/proximity_auth/ble/fake_wire_message.h"
12 #include "components/proximity_auth/connection_finder.h"
13 #include "components/proximity_auth/wire_message.h"
14 #include "device/bluetooth/bluetooth_adapter.h"
15 #include "device/bluetooth/bluetooth_device.h"
16 #include "device/bluetooth/bluetooth_gatt_characteristic.h"
17 #include "device/bluetooth/bluetooth_gatt_connection.h"
18 #include "device/bluetooth/bluetooth_gatt_notify_session.h"
19 #include "device/bluetooth/bluetooth_uuid.h"
20
21 using device::BluetoothAdapter;
22 using device::BluetoothDevice;
23 using device::BluetoothGattConnection;
24 using device::BluetoothGattService;
25 using device::BluetoothGattCharacteristic;
26 using device::BluetoothGattNotifySession;
27 using device::BluetoothUUID;
28
29 namespace proximity_auth {
30
31 BluetoothLowEnergyConnection::BluetoothLowEnergyConnection(
32 const RemoteDevice& device,
33 scoped_refptr<device::BluetoothAdapter> adapter,
34 const BluetoothUUID remote_service_uuid,
35 const BluetoothUUID to_peripheral_char_uuid,
36 const BluetoothUUID from_peripheral_char_uuid,
37 scoped_ptr<BluetoothGattConnection> gatt_connection)
38 : Connection(device),
39 adapter_(adapter),
40 remote_service_({remote_service_uuid, ""}),
41 to_peripheral_char_({to_peripheral_char_uuid, ""}),
42 from_peripheral_char_({from_peripheral_char_uuid, ""}),
43 connection_(gatt_connection.Pass()),
44 notify_session_pending_(false),
45 connect_signal_response_pending_(false),
46 weak_ptr_factory_(this) {
47 DCHECK(connection_);
48 DCHECK(adapter_);
49 DCHECK(adapter_->IsInitialized());
50
51 start_time_ = base::TimeTicks::Now();
52 SetStatus(IN_PROGRESS);
53
54 // We should set the status to IN_PROGRESS before calling this function, as we
55 // can set the status to CONNECTED by an inner call of
56 // HandleCharacteristicUpdate().
57 ScanRemoteCharacteristics(GetRemoteService());
58
59 adapter_->AddObserver(this);
60 }
61
62 BluetoothLowEnergyConnection::~BluetoothLowEnergyConnection() {
63 Disconnect();
64 if (adapter_) {
65 adapter_->RemoveObserver(this);
66 adapter_ = NULL;
67 }
68 }
69
70 void BluetoothLowEnergyConnection::Connect() {
71 NOTREACHED();
72 }
73
74 // This actually forgets the remote BLE device. This is safe as long as we only
75 // connect to BLE devices advertising the SmartLock service (assuming this
76 // device has no other being used).
77 void BluetoothLowEnergyConnection::Disconnect() {
78 StopNotifySession();
79 SetStatus(DISCONNECTED);
80 if (connection_) {
81 connection_.reset();
82 BluetoothDevice* device = GetRemoteDevice();
83 if (device) {
84 VLOG(1) << "Forget device " << device->GetAddress();
85 device->Forget(base::Bind(&base::DoNothing));
86 }
87 }
88 }
89
90 // TODO(sacomoto): Send a SmartLock BLE socket incoming signal. Implement a
91 // sender with full support for messages larger than a single characteristic
92 // value.
93 void BluetoothLowEnergyConnection::SendMessageImpl(
94 scoped_ptr<WireMessage> message) {
95 DCHECK(!GetGattCharacteristic(to_peripheral_char_.id));
96 VLOG(1) << "Sending message " << message->Serialize();
97
98 std::string serialized_message = message->Serialize();
99 std::vector<uint8> bytes(serialized_message.begin(),
100 serialized_message.end());
101
102 GetGattCharacteristic(to_peripheral_char_.id)
103 ->WriteRemoteCharacteristic(
104 bytes, base::Bind(&base::DoNothing),
105 base::Bind(
106 &BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError,
107 weak_ptr_factory_.GetWeakPtr()));
108 }
109
110 void BluetoothLowEnergyConnection::DeviceRemoved(BluetoothAdapter* adapter,
111 BluetoothDevice* device) {
112 if (device && device->GetAddress() == GetRemoteDeviceAddress()) {
113 VLOG(1) << "Device removed " << GetRemoteDeviceAddress();
114 Disconnect();
115 }
116 }
117
118 void BluetoothLowEnergyConnection::GattDiscoveryCompleteForService(
119 BluetoothAdapter* adapter,
120 BluetoothGattService* service) {
121 if (service && service->GetUUID() == remote_service_.uuid) {
122 VLOG(1) << "All characteristics discovered for "
123 << remote_service_.uuid.canonical_value();
124
125 if (to_peripheral_char_.id.empty() || from_peripheral_char_.id.empty()) {
126 VLOG(1) << "Connection error, missing characteristics for SmartLock "
127 "service.\n"
128 << (to_peripheral_char_.id.empty()
129 ? to_peripheral_char_.uuid.canonical_value()
130 : "")
131 << (from_peripheral_char_.id.empty()
132 ? ", " + from_peripheral_char_.uuid.canonical_value()
133 : "") << " not found.";
134 Disconnect();
135 }
136 }
137 }
138
139 void BluetoothLowEnergyConnection::GattCharacteristicAdded(
140 BluetoothAdapter* adapter,
141 BluetoothGattCharacteristic* characteristic) {
142 VLOG(1) << "New char found: " << characteristic->GetUUID().canonical_value();
143 HandleCharacteristicUpdate(characteristic);
144 }
145
146 // TODO(sacomoto): Parse the SmartLock BLE socket incoming signal. Implement a
147 // receiver with full suport for messages larger than a single characteristic
148 // value.
149 void BluetoothLowEnergyConnection::GattCharacteristicValueChanged(
150 BluetoothAdapter* adapter,
151 BluetoothGattCharacteristic* characteristic,
152 const std::vector<uint8>& value) {
153 DCHECK_EQ(adapter, adapter_.get());
154
155 VLOG(1) << "Characteristic value changed: "
156 << characteristic->GetUUID().canonical_value();
157
158 if (characteristic->GetIdentifier() == from_peripheral_char_.id) {
159 const ControlSignal signal = static_cast<ControlSignal>(ToUint32(value));
160
161 switch (signal) {
162 case ControlSignal::kInvitationResponseSignal:
163 connect_signal_response_pending_ = false;
164 CompleteConnection();
165 break;
166 case ControlSignal::kInviteToConnectSignal:
167 case ControlSignal::kSendSignal:
168 // TODO(sacomoto): Actually handle the message and call OnBytesReceived
169 // when complete.
170 case ControlSignal::kDisconnectSignal:
171 Disconnect();
172 break;
173 }
174 }
175 }
176
177 scoped_ptr<WireMessage> BluetoothLowEnergyConnection::DeserializeWireMessage(
178 bool* is_incomplete_message) {
179 return FakeWireMessage::Deserialize(received_bytes(), is_incomplete_message);
180 }
181
182 void BluetoothLowEnergyConnection::ScanRemoteCharacteristics(
183 BluetoothGattService* service) {
184 if (service) {
185 std::vector<device::BluetoothGattCharacteristic*> characteristics =
186 service->GetCharacteristics();
187 for (auto iter = characteristics.begin(); iter != characteristics.end();
188 iter++) {
189 HandleCharacteristicUpdate(*iter);
190 }
191 }
192 }
193
194 void BluetoothLowEnergyConnection::HandleCharacteristicUpdate(
195 BluetoothGattCharacteristic* characteristic) {
196 // Checks if |characteristic| is equal to |from_peripheral_char_| or
197 // |to_peripheral_char_|.
198 UpdateCharacteristicsStatus(characteristic);
199
200 // Starts a notify session for |from_peripheral_char_|.
201 if (characteristic->GetIdentifier() == from_peripheral_char_.id)
202 StartNotifySession();
203
204 // Sends a invite to connect signal if ready.
205 SendInviteToConnectSignal();
206 }
207
208 void BluetoothLowEnergyConnection::CompleteConnection() {
209 if (status() == IN_PROGRESS && !connect_signal_response_pending_ &&
210 !to_peripheral_char_.id.empty() && !from_peripheral_char_.id.empty() &&
211 notify_session_) {
212 VLOG(1) << "Connection completed";
213 VLOG(1) << "Time elapsed: " << base::TimeTicks::Now() - start_time_;
214 SetStatus(CONNECTED);
215 }
216 }
217
218 void BluetoothLowEnergyConnection::StartNotifySession() {
219 BluetoothGattCharacteristic* characteristic =
220 GetGattCharacteristic(from_peripheral_char_.id);
221 if (!characteristic) {
222 VLOG(1) << "Characteristic " << from_peripheral_char_.uuid.canonical_value()
223 << " not found.";
224 return;
225 }
226
227 if (notify_session_ || notify_session_pending_) {
228 VLOG(1) << "Notify session already started.";
229 return;
230 }
231
232 notify_session_pending_ = true;
233 characteristic->StartNotifySession(
234 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionStarted,
235 weak_ptr_factory_.GetWeakPtr()),
236 base::Bind(&BluetoothLowEnergyConnection::OnNotifySessionError,
237 weak_ptr_factory_.GetWeakPtr()));
238 }
239
240 void BluetoothLowEnergyConnection::OnNotifySessionError(
241 BluetoothGattService::GattErrorCode error) {
242 VLOG(1) << "Error starting notification session: " << error;
243 notify_session_pending_ = false;
244 }
245
246 void BluetoothLowEnergyConnection::OnNotifySessionStarted(
247 scoped_ptr<BluetoothGattNotifySession> notify_session) {
248 VLOG(1) << "Notification session started "
249 << notify_session->GetCharacteristicIdentifier();
250 notify_session_ = notify_session.Pass();
251 notify_session_pending_ = false;
252
253 // Sends an invite to connect signal if ready.
254 SendInviteToConnectSignal();
255 }
256
257 void BluetoothLowEnergyConnection::StopNotifySession() {
258 if (notify_session_) {
259 notify_session_->Stop(base::Bind(&base::DoNothing));
260 notify_session_.reset();
261 }
262 }
263
264 void BluetoothLowEnergyConnection::OnWriteRemoteCharacteristicError(
265 BluetoothGattService::GattErrorCode error) {
266 VLOG(1) << "Error writing characteristic"
267 << to_peripheral_char_.uuid.canonical_value();
268 }
269
270 void BluetoothLowEnergyConnection::SendInviteToConnectSignal() {
271 if (status() == IN_PROGRESS && !connect_signal_response_pending_ &&
272 !to_peripheral_char_.id.empty() && !from_peripheral_char_.id.empty() &&
273 notify_session_) {
274 VLOG(1) << "Sending invite to connect signal";
275 connect_signal_response_pending_ = true;
276
277 // The connection status is not CONNECTED yet, so in order to bypass the
278 // check in SendMessage implementation we need to send the message using the
279 // private implementation.
280 SendMessageImpl(scoped_ptr<FakeWireMessage>(new FakeWireMessage(
281 ToString(static_cast<uint32>(ControlSignal::kInviteToConnectSignal)))));
282 }
283 }
284
285 void BluetoothLowEnergyConnection::UpdateCharacteristicsStatus(
286 BluetoothGattCharacteristic* characteristic) {
287 if (characteristic) {
288 BluetoothUUID uuid = characteristic->GetUUID();
289 if (to_peripheral_char_.uuid == uuid)
290 to_peripheral_char_.id = characteristic->GetIdentifier();
291 if (from_peripheral_char_.uuid == uuid)
292 from_peripheral_char_.id = characteristic->GetIdentifier();
293
294 BluetoothGattService* service = characteristic->GetService();
295 if (service && service->GetUUID() == remote_service_.uuid)
296 remote_service_.id = service->GetIdentifier();
297 }
298 }
299
300 const std::string& BluetoothLowEnergyConnection::GetRemoteDeviceAddress() {
301 return remote_device().bluetooth_address;
302 }
303
304 BluetoothDevice* BluetoothLowEnergyConnection::GetRemoteDevice() {
305 if (!adapter_ || !adapter_->IsInitialized()) {
306 VLOG(1) << "adapter not ready";
307 return NULL;
308 }
309 return adapter_->GetDevice(GetRemoteDeviceAddress());
310 }
311
312 BluetoothGattService* BluetoothLowEnergyConnection::GetRemoteService() {
313 BluetoothDevice* remote_device = GetRemoteDevice();
314 if (!remote_device) {
315 VLOG(1) << "device not found";
316 return NULL;
317 }
318 if (remote_service_.id.empty()) {
319 std::vector<BluetoothGattService*> services =
320 remote_device->GetGattServices();
321 for (auto iter = services.begin(); iter != services.end(); ++iter)
322 if ((*iter)->GetUUID() == remote_service_.uuid) {
323 remote_service_.id = (*iter)->GetIdentifier();
324 break;
325 }
326 }
327 return remote_device->GetGattService(remote_service_.id);
328 }
329
330 BluetoothGattCharacteristic*
331 BluetoothLowEnergyConnection::GetGattCharacteristic(
332 const std::string& gatt_characteristic) {
333 BluetoothGattService* remote_service = GetRemoteService();
334 if (!remote_service) {
335 VLOG(1) << "service not found";
336 return NULL;
337 }
338 return remote_service->GetCharacteristic(gatt_characteristic);
339 }
340
341 // TODO(sacomoto): make this robust to byte ordering in both sides of the
342 // SmartLock BLE socket.
343 uint32 BluetoothLowEnergyConnection::ToUint32(const std::vector<uint8>& bytes) {
344 return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
345 }
346
347 // TODO(sacomoto): make this robust to byte ordering in both sides of the
348 // SmartLock BLE socket.
349 const std::string BluetoothLowEnergyConnection::ToString(const uint32 value) {
350 char bytes[4] = {};
351
352 bytes[0] = static_cast<uint8>(value);
353 bytes[1] = static_cast<uint8>(value >> 8);
354 bytes[2] = static_cast<uint8>(value >> 12);
355 bytes[3] = static_cast<uint8>(value >> 24);
356
357 return std::string(bytes);
358 }
359
360 } // namespace proximity_auth
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698