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

Side by Side Diff: chromeos/components/tether/ble_connection_manager.cc

Issue 2697763002: [CrOS Tether]: Create BleConnectionManager, which manages secure connections between the current de… (Closed)
Patch Set: Created 3 years, 10 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 2017 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 "chromeos/components/tether/ble_connection_manager.h"
6
7 #include "chromeos/components/tether/ble_constants.h"
8 #include "components/cryptauth/ble/bluetooth_low_energy_weave_client_connection. h"
9 #include "components/proximity_auth/logging/logging.h"
10 #include "device/bluetooth/bluetooth_uuid.h"
11
12 namespace chromeos {
13
14 namespace tether {
15
16 namespace {
17 const char kTetherFeature[] = "magic_tether";
18 const int64_t kAdvertisingTimeoutInSeconds = 12;
Ryan Hansberry 2017/02/14 18:27:28 Where'd this number come from?
Kyle Horimoto 2017/02/14 22:54:45 It was derived empirically during Android work.
19 const int64_t kShortErrorTimeoutInSeconds = 1;
Ryan Hansberry 2017/02/14 18:27:28 Assuming this is only used by the test, this can j
Kyle Horimoto 2017/02/14 22:54:45 This is used under non-test circumstances when the
20 } // namespace
21
22 // static
23 std::string BleConnectionManager::ConnectionReasonToString(
24 const ConnectionReason& reason) {
25 switch (reason) {
26 case ConnectionReason::TETHER_AVAILABILITY_REQUEST:
27 return "[TetherAvailabilityRequest]";
28 case ConnectionReason::CONNECT_TETHERING_REQUEST:
29 return "[ConnectTetheringRequest]";
30 case ConnectionReason::KEEP_ALIVE_REQUEST:
31 return "[KeepAliveRequest]";
32 case ConnectionReason::DISCONNECT_REQUEST:
33 return "[DisconnectRequest]";
34 default:
35 return "[invalid ConnectionReason]";
36 }
37 }
38
39 BleConnectionManager::ConnectionMetadata::ConnectionMetadata(
40 const cryptauth::RemoteDevice remote_device,
41 std::shared_ptr<base::Timer> timer)
42 : remote_device_(remote_device), timer_(timer), weak_ptr_factory_(this) {}
43
44 BleConnectionManager::ConnectionMetadata::~ConnectionMetadata() {}
45
46 void BleConnectionManager::ConnectionMetadata::RegisterConnectionReason(
47 const ConnectionReason& connection_reason) {
48 active_connection_reasons_.insert(connection_reason);
49 }
50
51 void BleConnectionManager::ConnectionMetadata::UnregisterConnectionReason(
52 const ConnectionReason& connection_reason) {
53 active_connection_reasons_.erase(connection_reason);
54 }
55
56 bool BleConnectionManager::ConnectionMetadata::HasReasonForConnection() const {
57 return !active_connection_reasons_.empty();
58 }
59
60 cryptauth::SecureChannel::Status
61 BleConnectionManager::ConnectionMetadata::GetStatus() const {
Ryan Hansberry 2017/02/14 18:27:28 Is this method really necessary if there are alrea
Kyle Horimoto 2017/02/14 22:54:45 Yes. When a device is unregistered, we need to sen
Ryan Hansberry 2017/02/15 18:56:02 IMO the convenience methods just make this class's
Kyle Horimoto 2017/02/15 22:24:14 Done, except I kept the HasEstablishedConnection()
62 if (IsActivelyConnecting()) {
63 return cryptauth::SecureChannel::Status::CONNECTING;
64 } else if (!HasEstablishedConnection()) {
65 return cryptauth::SecureChannel::Status::DISCONNECTED;
66 }
67
68 return secure_channel_->status();
69 }
70
71 bool BleConnectionManager::ConnectionMetadata::IsActivelyConnecting() const {
72 return timer_->IsRunning();
73 }
74
75 bool BleConnectionManager::ConnectionMetadata::HasEstablishedConnection()
76 const {
77 return secure_channel_.get();
78 }
79
80 bool BleConnectionManager::ConnectionMetadata::CanSendMessage() const {
81 return secure_channel_ &&
82 secure_channel_->status() ==
83 cryptauth::SecureChannel::Status::AUTHENTICATED;
84 }
85
86 void BleConnectionManager::ConnectionMetadata::StartConnectionAttemptTimeout(
87 const ConnectionAttemptTimeoutHandler& handler,
88 bool use_short_error_timeout) {
89 DCHECK(!IsActivelyConnecting());
90 DCHECK(!secure_channel_);
91 DCHECK(timeout_handler_.is_null());
92
93 int64_t timeout_sec = use_short_error_timeout ? kShortErrorTimeoutInSeconds
Ryan Hansberry 2017/02/14 18:27:28 It would be much better if timeout_sec were a prop
Kyle Horimoto 2017/02/14 22:54:45 This isn't a constant value, though. If scanning/a
94 : kAdvertisingTimeoutInSeconds;
95
96 timeout_handler_ = handler;
97 timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(timeout_sec),
98 base::Bind(&ConnectionMetadata::OnConnectionAttemptTimeout,
99 weak_ptr_factory_.GetWeakPtr()));
100 }
101
102 void BleConnectionManager::ConnectionMetadata::OnConnectionAttemptTimeout() {
103 // First, store |timeout_handler_| to a temporary variable and reset
104 // |timeout_handler_|.
105 ConnectionAttemptTimeoutHandler handler = timeout_handler_;
106 timeout_handler_.Reset();
107
108 // Run the handler after |timeout_handler_| has already been reset to ensure
109 // that if the callback references this instance, internal state is correct.
110 handler.Run(remote_device_);
111 }
112
113 void BleConnectionManager::ConnectionMetadata::SetSecureChannel(
114 std::unique_ptr<cryptauth::SecureChannel> secure_channel,
115 const SecureChannelStatusChangeHandler& status_change_handler,
116 const MessageReceivedHandler message_received_handler) {
117 DCHECK(!secure_channel_);
118 DCHECK(status_change_handler_.is_null());
119 DCHECK(message_received_handler_.is_null());
120
121 // The connection has succeeded, so cancel the timeout.
122 timer_->Stop();
Ryan Hansberry 2017/02/14 18:27:28 Please name this more descriptively than just time
Kyle Horimoto 2017/02/14 22:54:45 Done.
123
124 status_change_handler_ = status_change_handler;
125 message_received_handler_ = message_received_handler;
126
127 secure_channel_ = std::move(secure_channel);
128 secure_channel_->AddObserver(this);
129 secure_channel_->Initialize();
130 }
131
132 void BleConnectionManager::ConnectionMetadata::SendMessage(
133 const std::string& payload) {
134 DCHECK(CanSendMessage());
135 secure_channel_->SendMessage(std::string(kTetherFeature), payload);
136 }
137
138 void BleConnectionManager::ConnectionMetadata::OnSecureChannelStatusChanged(
139 cryptauth::SecureChannel* secure_channel,
140 const cryptauth::SecureChannel::Status& old_status,
141 const cryptauth::SecureChannel::Status& new_status) {
142 DCHECK(secure_channel_.get() == secure_channel);
143
144 if (new_status == cryptauth::SecureChannel::Status::CONNECTING) {
145 // BleConnectionManager already broadcasts "disconnected => connecting"
146 // status updates when a connection attempt begins, so there is no need to
147 // handle this case.
148 return;
149 }
150
151 if (new_status == cryptauth::SecureChannel::Status::DISCONNECTED) {
152 // First, store |status_change_handler_| to a temporary variable and reset
153 // |status_change_handler_|.
154 SecureChannelStatusChangeHandler handler = status_change_handler_;
155 secure_channel_->RemoveObserver(this);
156 secure_channel_.reset();
157 timeout_handler_.Reset();
158 status_change_handler_.Reset();
159 message_received_handler_.Reset();
160
161 // Run the handler after the other two handlers have already been reset to
Ryan Hansberry 2017/02/15 18:56:02 s/handler/callback
162 // ensure that if the callback references this instance, internal state is
Ryan Hansberry 2017/02/14 18:27:28 Why wouldn't the callback reference this instance?
Kyle Horimoto 2017/02/14 22:54:45 Not sure what you mean. The callback being run her
163 // correct.
164 handler.Run(remote_device_, old_status,
165 cryptauth::SecureChannel::Status::DISCONNECTED);
166 return;
167 }
168
169 status_change_handler_.Run(remote_device_, old_status, new_status);
170 }
171
172 void BleConnectionManager::ConnectionMetadata::OnMessageReceived(
173 cryptauth::SecureChannel* secure_channel,
174 const std::string& feature,
175 const std::string& payload) {
176 DCHECK(secure_channel_.get() == secure_channel);
177 if (feature != std::string(kTetherFeature)) {
178 // If the message received was not a tether feature, ignore it.
179 return;
180 }
181
182 message_received_handler_.Run(remote_device_, payload);
183 }
184
185 std::unique_ptr<base::Timer> BleConnectionManager::TimerFactory::CreateTimer() {
186 return base::MakeUnique<base::OneShotTimer>();
187 }
188
189 BleConnectionManager::BleConnectionManager(
190 std::unique_ptr<Delegate> delegate,
191 scoped_refptr<device::BluetoothAdapter> adapter,
192 const LocalDeviceDataProvider* local_device_data_provider,
193 const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
194 cryptauth::BluetoothThrottler* bluetooth_throttler)
195 : BleConnectionManager(
196 std::move(delegate),
197 adapter,
198 base::MakeUnique<BleScanner>(local_device_data_provider),
Ryan Hansberry 2017/02/14 18:27:28 BleScanner should be changed to be injected with a
Kyle Horimoto 2017/02/14 22:54:45 You're absolutely correct. That was a mistake on m
Ryan Hansberry 2017/02/15 18:56:02 sgtm
199 base::MakeUnique<BleAdvertiser>(adapter,
200 local_device_data_provider,
201 remote_beacon_seed_fetcher),
202 base::MakeUnique<BleAdvertisementDeviceQueue>(),
203 base::WrapUnique<TimerFactory>(new TimerFactory()),
204 bluetooth_throttler) {}
205
206 BleConnectionManager::BleConnectionManager(
207 std::unique_ptr<Delegate> delegate,
208 scoped_refptr<device::BluetoothAdapter> adapter,
209 std::unique_ptr<BleScanner> ble_scanner,
210 std::unique_ptr<BleAdvertiser> ble_advertiser,
211 std::unique_ptr<BleAdvertisementDeviceQueue> device_queue,
212 std::unique_ptr<TimerFactory> timer_factory,
213 cryptauth::BluetoothThrottler* bluetooth_throttler)
214 : delegate_(std::move(delegate)),
215 adapter_(adapter),
216 ble_scanner_(std::move(ble_scanner)),
217 ble_advertiser_(std::move(ble_advertiser)),
218 device_queue_(std::move(device_queue)),
219 timer_factory_(std::move(timer_factory)),
220 bluetooth_throttler_(bluetooth_throttler),
221 weak_ptr_factory_(this) {
222 ble_scanner_->AddObserver(this);
223 }
224
225 BleConnectionManager::~BleConnectionManager() {
226 ble_scanner_->RemoveObserver(this);
227 }
228
229 void BleConnectionManager::RegisterRemoteDevice(
230 const cryptauth::RemoteDevice& remote_device,
231 const ConnectionReason& connection_reason) {
232 PA_LOG(INFO) << "Registering device with ID "
233 << remote_device.GetTruncatedDeviceIdForLogs() << " for reason "
234 << ConnectionReasonToString(connection_reason);
235
236 std::shared_ptr<ConnectionMetadata> data =
237 GetConnectionMetadata(remote_device);
238 if (!data) {
239 data = AddMetadataForDevice(remote_device);
240 }
241
242 data->RegisterConnectionReason(connection_reason);
243 UpdateConnectionAttempts();
244 }
245
246 void BleConnectionManager::UnregisterRemoteDevice(
247 const cryptauth::RemoteDevice& remote_device,
248 const ConnectionReason& connection_reason) {
249 PA_LOG(INFO) << "Unregistering device with ID "
250 << remote_device.GetTruncatedDeviceIdForLogs() << " for reason "
251 << ConnectionReasonToString(connection_reason);
252
253 std::shared_ptr<ConnectionMetadata> data =
254 GetConnectionMetadata(remote_device);
255 if (!data) {
256 // If the device was not registered, there is nothing to do.
257 return;
258 }
259
260 data->UnregisterConnectionReason(connection_reason);
261 if (!data->HasReasonForConnection()) {
262 cryptauth::SecureChannel::Status status_before_disconnect =
263 data->GetStatus();
264 device_to_metadata_map_.erase(remote_device);
265 if (status_before_disconnect ==
266 cryptauth::SecureChannel::Status::CONNECTING) {
267 StopConnectionAttemptAndMoveToEndOfQueue(remote_device);
268 }
269 if (status_before_disconnect !=
270 cryptauth::SecureChannel::Status::DISCONNECTED) {
271 // Send a status update for the disconnection.
272 SendSecureChannelStatusChangeEvent(
273 remote_device, status_before_disconnect,
274 cryptauth::SecureChannel::Status::DISCONNECTED);
275 }
276 }
277
278 UpdateConnectionAttempts();
279 }
280
281 void BleConnectionManager::SendMessage(
Ryan Hansberry 2017/02/14 18:27:28 This API doesn't make sense to me: if we registere
Kyle Horimoto 2017/02/14 22:54:45 Sorry - I probably didn't make things clear enough
282 const cryptauth::RemoteDevice& remote_device,
283 const std::string& message) {
284 std::shared_ptr<ConnectionMetadata> data =
285 GetConnectionMetadata(remote_device);
286 if (!data || !data->CanSendMessage()) {
287 PA_LOG(ERROR) << "Attempted to send a message to device with ID "
288 << remote_device.GetTruncatedDeviceIdForLogs() << ". "
289 << "Message: \"" << message << "\"";
290 return;
291 }
292
293 PA_LOG(INFO) << "Sending message to device with ID "
294 << remote_device.GetTruncatedDeviceIdForLogs() << ". "
295 << "Message: \"" << message << "\"";
296 data->SendMessage(message);
297 }
298
299 void BleConnectionManager::AddObserver(Observer* observer) {
300 observer_list_.AddObserver(observer);
301 }
302
303 void BleConnectionManager::RemoveObserver(Observer* observer) {
304 observer_list_.RemoveObserver(observer);
305 }
306
307 void BleConnectionManager::OnReceivedAdvertisementFromDevice(
308 const std::string& device_address,
309 cryptauth::RemoteDevice remote_device) {
310 std::shared_ptr<ConnectionMetadata> data =
311 GetConnectionMetadata(remote_device);
312 if (!data) {
313 // If an advertisement is received from a device that is not registered,
314 // ignore it.
315 PA_LOG(WARNING) << "Received an advertisement from a device which is not "
316 << "registered. Bluetooth address: " << device_address
317 << ", Remote Device ID: " << remote_device.GetDeviceId();
318 return;
319 }
320
321 PA_LOG(INFO) << "Received advertisement from device with ID "
322 << remote_device.GetTruncatedDeviceIdForLogs() << ". "
323 << "Starting authentication handshake to that device.";
324
325 // Create a connection to that device.
326 std::unique_ptr<cryptauth::Connection> connection =
327 cryptauth::weave::BluetoothLowEnergyWeaveClientConnection::Factory::
328 NewInstance(remote_device, device_address, adapter_,
329 device::BluetoothUUID(std::string(kGattServerUuid)),
330 bluetooth_throttler_);
331 std::unique_ptr<cryptauth::SecureChannel> secure_channel =
332 cryptauth::SecureChannel::Factory::NewInstance(
333 std::move(connection), delegate_->CreateSecureChannelDelegate());
334 data->SetSecureChannel(
335 std::move(secure_channel),
336 base::Bind(&BleConnectionManager::OnSecureChannelStatusChanged,
337 weak_ptr_factory_.GetWeakPtr()),
338 base::Bind(&BleConnectionManager::SendMessageReceivedEvent,
339 weak_ptr_factory_.GetWeakPtr()));
340
341 // Stop trying to connect to that device, since a connection already exists.
342 StopConnectionAttemptAndMoveToEndOfQueue(remote_device);
343 UpdateConnectionAttempts();
344 }
345
346 std::shared_ptr<BleConnectionManager::ConnectionMetadata>
347 BleConnectionManager::GetConnectionMetadata(
348 const cryptauth::RemoteDevice& remote_device) {
349 const auto map_iter = device_to_metadata_map_.find(remote_device);
350 if (map_iter == device_to_metadata_map_.end()) {
351 return nullptr;
352 }
353
354 return map_iter->second;
355 }
356
357 std::shared_ptr<BleConnectionManager::ConnectionMetadata>
358 BleConnectionManager::AddMetadataForDevice(
359 const cryptauth::RemoteDevice& remote_device) {
360 std::shared_ptr<ConnectionMetadata> existing_data =
361 GetConnectionMetadata(remote_device);
362 if (existing_data) {
363 return existing_data;
364 }
365
366 std::unique_ptr<base::Timer> timer = timer_factory_->CreateTimer();
367 device_to_metadata_map_.insert(
368 std::pair<cryptauth::RemoteDevice, std::shared_ptr<ConnectionMetadata>>(
369 remote_device,
370 std::shared_ptr<ConnectionMetadata>(
371 new ConnectionMetadata(remote_device, std::move(timer)))));
372 return device_to_metadata_map_.at(remote_device);
373 }
374
375 void BleConnectionManager::UpdateConnectionAttempts() {
376 UpdateAdvertisementQueue();
377
378 std::vector<cryptauth::RemoteDevice> should_advertise_to =
379 device_queue_->GetDevicesToWhichToAdvertise();
380 DCHECK(should_advertise_to.size() <=
381 static_cast<size_t>(kMaxConcurrentAdvertisements));
382
383 for (const auto& remote_device : should_advertise_to) {
384 std::shared_ptr<ConnectionMetadata> associated_data =
385 GetConnectionMetadata(remote_device);
386 if (!associated_data->IsActivelyConnecting()) {
387 // If there is no active attempt to connect to a device at the front of
388 // the queue, start a connection attempt.
389 StartConnectionAttempt(remote_device);
390 }
391 }
392 }
393
394 void BleConnectionManager::UpdateAdvertisementQueue() {
395 std::vector<cryptauth::RemoteDevice> devices_for_queue;
396 for (const auto& map_entry : device_to_metadata_map_) {
397 if (map_entry.second->HasEstablishedConnection()) {
398 // If there is already an active connection to the device, there is no
399 // need to advertise to the device to bootstrap a connection.
400 continue;
401 }
402
403 devices_for_queue.push_back(map_entry.first);
404 }
405
406 device_queue_->SetDevices(devices_for_queue);
407 }
408
409 void BleConnectionManager::StartConnectionAttempt(
410 const cryptauth::RemoteDevice& remote_device) {
411 std::shared_ptr<ConnectionMetadata> data =
412 GetConnectionMetadata(remote_device);
413 DCHECK(data);
414
415 PA_LOG(INFO) << "Starting connection attempt to device with ID "
416 << remote_device.GetTruncatedDeviceIdForLogs();
417
418 // Send a "disconnected => connecting" update to alert clients that a
419 // connection attempt for |remote_device| is underway.
420 SendSecureChannelStatusChangeEvent(
421 remote_device, cryptauth::SecureChannel::Status::DISCONNECTED,
422 cryptauth::SecureChannel::Status::CONNECTING);
423
424 bool success = ble_scanner_->RegisterScanFilterForDevice(remote_device) &&
425 ble_advertiser_->StartAdvertisingToDevice(remote_device);
426
427 data->StartConnectionAttemptTimeout(
428 base::Bind(&BleConnectionManager::OnConnectionAttemptTimeout,
429 weak_ptr_factory_.GetWeakPtr()),
430 !success);
431 }
432
433 void BleConnectionManager::StopConnectionAttemptAndMoveToEndOfQueue(
434 const cryptauth::RemoteDevice& remote_device) {
435 PA_LOG(INFO) << "Stopping connection attempt to device with ID "
436 << remote_device.GetTruncatedDeviceIdForLogs();
437
438 ble_scanner_->UnregisterScanFilterForDevice(remote_device);
439 ble_advertiser_->StopAdvertisingToDevice(remote_device);
440
441 device_queue_->MoveDeviceToEnd(remote_device.GetDeviceId());
442 }
443
444 void BleConnectionManager::OnConnectionAttemptTimeout(
445 const cryptauth::RemoteDevice& remote_device) {
446 PA_LOG(INFO) << "Connection attempt timed out for device with ID "
447 << remote_device.GetTruncatedDeviceIdForLogs();
448
449 StopConnectionAttemptAndMoveToEndOfQueue(remote_device);
450
451 // Send a "connecting => disconnected" update to alert clients that a
452 // connection attempt for |remote_device| has failed.
453 SendSecureChannelStatusChangeEvent(
454 remote_device, cryptauth::SecureChannel::Status::CONNECTING,
455 cryptauth::SecureChannel::Status::DISCONNECTED);
456
457 UpdateConnectionAttempts();
458 }
459
460 void BleConnectionManager::OnSecureChannelStatusChanged(
461 const cryptauth::RemoteDevice& remote_device,
462 const cryptauth::SecureChannel::Status& old_status,
463 const cryptauth::SecureChannel::Status& new_status) {
464 SendSecureChannelStatusChangeEvent(remote_device, old_status, new_status);
465 UpdateConnectionAttempts();
466 }
467
468 void BleConnectionManager::SendMessageReceivedEvent(
469 const cryptauth::RemoteDevice& remote_device,
470 const std::string& payload) {
471 PA_LOG(INFO) << "Broadcasting message received event: "
472 << "Device ID \"" << remote_device.GetTruncatedDeviceIdForLogs()
473 << "\" received message \"" << payload << "\".";
474 for (auto& observer : observer_list_) {
475 observer.OnMessageReceived(remote_device, payload);
476 }
477 }
478
479 void BleConnectionManager::SendSecureChannelStatusChangeEvent(
480 const cryptauth::RemoteDevice& remote_device,
481 const cryptauth::SecureChannel::Status& old_status,
482 const cryptauth::SecureChannel::Status& new_status) {
483 PA_LOG(INFO) << "Broadcasting status change event: "
484 << "Device ID \"" << remote_device.GetTruncatedDeviceIdForLogs()
485 << "\": " << cryptauth::SecureChannel::StatusToString(old_status)
486 << " => "
487 << cryptauth::SecureChannel::StatusToString(new_status);
488 for (auto& observer : observer_list_) {
489 observer.OnSecureChannelStatusChanged(remote_device, old_status,
490 new_status);
491 }
492 }
493
494 } // namespace tether
495
496 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698