OLD | NEW |
(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 } // namespace |
| 19 |
| 20 const int64_t BleConnectionManager::kAdvertisingTimeoutMillis = 12000; |
| 21 const int64_t BleConnectionManager::kFailImmediatelyTimeoutMillis = 0; |
| 22 |
| 23 // static |
| 24 std::string BleConnectionManager::MessageTypeToString( |
| 25 const MessageType& reason) { |
| 26 switch (reason) { |
| 27 case MessageType::TETHER_AVAILABILITY_REQUEST: |
| 28 return "[TetherAvailabilityRequest]"; |
| 29 case MessageType::CONNECT_TETHERING_REQUEST: |
| 30 return "[ConnectTetheringRequest]"; |
| 31 case MessageType::KEEP_ALIVE_TICKLE: |
| 32 return "[KeepAliveTickle]"; |
| 33 case MessageType::DISCONNECT_TETHERING_REQUEST: |
| 34 return "[DisconnectTetheringRequest]"; |
| 35 default: |
| 36 return "[invalid MessageType]"; |
| 37 } |
| 38 } |
| 39 |
| 40 BleConnectionManager::ConnectionMetadata::ConnectionMetadata( |
| 41 const cryptauth::RemoteDevice remote_device, |
| 42 std::shared_ptr<base::Timer> timer, |
| 43 base::WeakPtr<BleConnectionManager> manager) |
| 44 : remote_device_(remote_device), |
| 45 connection_attempt_timeout_timer_(timer), |
| 46 manager_(manager), |
| 47 weak_ptr_factory_(this) {} |
| 48 |
| 49 BleConnectionManager::ConnectionMetadata::~ConnectionMetadata() {} |
| 50 |
| 51 void BleConnectionManager::ConnectionMetadata::RegisterConnectionReason( |
| 52 const MessageType& connection_reason) { |
| 53 active_connection_reasons_.insert(connection_reason); |
| 54 } |
| 55 |
| 56 void BleConnectionManager::ConnectionMetadata::UnregisterConnectionReason( |
| 57 const MessageType& connection_reason) { |
| 58 active_connection_reasons_.erase(connection_reason); |
| 59 } |
| 60 |
| 61 bool BleConnectionManager::ConnectionMetadata::HasReasonForConnection() const { |
| 62 return !active_connection_reasons_.empty(); |
| 63 } |
| 64 |
| 65 bool BleConnectionManager::ConnectionMetadata::HasEstablishedConnection() |
| 66 const { |
| 67 return secure_channel_.get(); |
| 68 } |
| 69 |
| 70 cryptauth::SecureChannel::Status |
| 71 BleConnectionManager::ConnectionMetadata::GetStatus() const { |
| 72 if (connection_attempt_timeout_timer_->IsRunning()) { |
| 73 // If the timer is running, a connection attempt is in progress but a |
| 74 // channel has not been established. |
| 75 return cryptauth::SecureChannel::Status::CONNECTING; |
| 76 } else if (!HasEstablishedConnection()) { |
| 77 // If there is no timer and a channel has not been established, the channel |
| 78 // is disconnected. |
| 79 return cryptauth::SecureChannel::Status::DISCONNECTED; |
| 80 } |
| 81 |
| 82 // If a channel has been established, return its status. |
| 83 return secure_channel_->status(); |
| 84 } |
| 85 |
| 86 void BleConnectionManager::ConnectionMetadata::StartConnectionAttemptTimer( |
| 87 bool fail_immediately) { |
| 88 DCHECK(!secure_channel_); |
| 89 DCHECK(!connection_attempt_timeout_timer_->IsRunning()); |
| 90 |
| 91 int64_t timeout_millis = fail_immediately ? kFailImmediatelyTimeoutMillis |
| 92 : kAdvertisingTimeoutMillis; |
| 93 |
| 94 connection_attempt_timeout_timer_->Start( |
| 95 FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_millis), |
| 96 base::Bind(&ConnectionMetadata::OnConnectionAttemptTimeout, |
| 97 weak_ptr_factory_.GetWeakPtr())); |
| 98 } |
| 99 |
| 100 void BleConnectionManager::ConnectionMetadata::OnConnectionAttemptTimeout() { |
| 101 manager_->OnConnectionAttemptTimeout(remote_device_); |
| 102 } |
| 103 |
| 104 void BleConnectionManager::ConnectionMetadata::SetSecureChannel( |
| 105 std::unique_ptr<cryptauth::SecureChannel> secure_channel) { |
| 106 DCHECK(!secure_channel_); |
| 107 |
| 108 // The connection has succeeded, so cancel the timeout. |
| 109 connection_attempt_timeout_timer_->Stop(); |
| 110 |
| 111 secure_channel_ = std::move(secure_channel); |
| 112 secure_channel_->AddObserver(this); |
| 113 secure_channel_->Initialize(); |
| 114 } |
| 115 |
| 116 void BleConnectionManager::ConnectionMetadata::SendMessage( |
| 117 const std::string& payload) { |
| 118 DCHECK(GetStatus() == cryptauth::SecureChannel::Status::AUTHENTICATED); |
| 119 secure_channel_->SendMessage(std::string(kTetherFeature), payload); |
| 120 } |
| 121 |
| 122 void BleConnectionManager::ConnectionMetadata::OnSecureChannelStatusChanged( |
| 123 cryptauth::SecureChannel* secure_channel, |
| 124 const cryptauth::SecureChannel::Status& old_status, |
| 125 const cryptauth::SecureChannel::Status& new_status) { |
| 126 DCHECK(secure_channel_.get() == secure_channel); |
| 127 |
| 128 if (new_status == cryptauth::SecureChannel::Status::CONNECTING) { |
| 129 // BleConnectionManager already broadcasts "disconnected => connecting" |
| 130 // status updates when a connection attempt begins, so there is no need to |
| 131 // handle this case. |
| 132 return; |
| 133 } |
| 134 |
| 135 // Make a copy of the two statuses. If |secure_channel_.reset()| is called |
| 136 // below, the SecureChannel instance will be destroyed and |old_status| and |
| 137 // |new_status| may refer to memory which has been deleted. |
| 138 const cryptauth::SecureChannel::Status old_status_copy = old_status; |
| 139 const cryptauth::SecureChannel::Status new_status_copy = new_status; |
| 140 |
| 141 if (new_status == cryptauth::SecureChannel::Status::DISCONNECTED) { |
| 142 secure_channel_->RemoveObserver(this); |
| 143 secure_channel_.reset(); |
| 144 } |
| 145 |
| 146 manager_->OnSecureChannelStatusChanged(remote_device_, old_status_copy, |
| 147 new_status_copy); |
| 148 } |
| 149 |
| 150 void BleConnectionManager::ConnectionMetadata::OnMessageReceived( |
| 151 cryptauth::SecureChannel* secure_channel, |
| 152 const std::string& feature, |
| 153 const std::string& payload) { |
| 154 DCHECK(secure_channel_.get() == secure_channel); |
| 155 if (feature != std::string(kTetherFeature)) { |
| 156 // If the message received was not a tether feature, ignore it. |
| 157 return; |
| 158 } |
| 159 |
| 160 manager_->SendMessageReceivedEvent(remote_device_, payload); |
| 161 } |
| 162 |
| 163 std::unique_ptr<base::Timer> BleConnectionManager::TimerFactory::CreateTimer() { |
| 164 return base::MakeUnique<base::OneShotTimer>(); |
| 165 } |
| 166 |
| 167 BleConnectionManager::BleConnectionManager( |
| 168 std::unique_ptr<Delegate> delegate, |
| 169 scoped_refptr<device::BluetoothAdapter> adapter, |
| 170 const LocalDeviceDataProvider* local_device_data_provider, |
| 171 const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher, |
| 172 cryptauth::BluetoothThrottler* bluetooth_throttler) |
| 173 : BleConnectionManager( |
| 174 std::move(delegate), |
| 175 adapter, |
| 176 // TODO(khorimoto): Inject |adapter| into |BleScanner|. |
| 177 base::MakeUnique<BleScanner>(local_device_data_provider), |
| 178 base::MakeUnique<BleAdvertiser>(adapter, |
| 179 local_device_data_provider, |
| 180 remote_beacon_seed_fetcher), |
| 181 base::MakeUnique<BleAdvertisementDeviceQueue>(), |
| 182 base::WrapUnique<TimerFactory>(new TimerFactory()), |
| 183 bluetooth_throttler) {} |
| 184 |
| 185 BleConnectionManager::BleConnectionManager( |
| 186 std::unique_ptr<Delegate> delegate, |
| 187 scoped_refptr<device::BluetoothAdapter> adapter, |
| 188 std::unique_ptr<BleScanner> ble_scanner, |
| 189 std::unique_ptr<BleAdvertiser> ble_advertiser, |
| 190 std::unique_ptr<BleAdvertisementDeviceQueue> device_queue, |
| 191 std::unique_ptr<TimerFactory> timer_factory, |
| 192 cryptauth::BluetoothThrottler* bluetooth_throttler) |
| 193 : delegate_(std::move(delegate)), |
| 194 adapter_(adapter), |
| 195 ble_scanner_(std::move(ble_scanner)), |
| 196 ble_advertiser_(std::move(ble_advertiser)), |
| 197 device_queue_(std::move(device_queue)), |
| 198 timer_factory_(std::move(timer_factory)), |
| 199 bluetooth_throttler_(bluetooth_throttler), |
| 200 weak_ptr_factory_(this) { |
| 201 ble_scanner_->AddObserver(this); |
| 202 } |
| 203 |
| 204 BleConnectionManager::~BleConnectionManager() { |
| 205 ble_scanner_->RemoveObserver(this); |
| 206 } |
| 207 |
| 208 void BleConnectionManager::RegisterRemoteDevice( |
| 209 const cryptauth::RemoteDevice& remote_device, |
| 210 const MessageType& connection_reason) { |
| 211 PA_LOG(INFO) << "Registering device with ID " |
| 212 << remote_device.GetTruncatedDeviceIdForLogs() << " for reason " |
| 213 << MessageTypeToString(connection_reason); |
| 214 |
| 215 std::shared_ptr<ConnectionMetadata> connection_metadata = |
| 216 GetConnectionMetadata(remote_device); |
| 217 if (!connection_metadata) { |
| 218 connection_metadata = AddMetadataForDevice(remote_device); |
| 219 } |
| 220 |
| 221 connection_metadata->RegisterConnectionReason(connection_reason); |
| 222 UpdateConnectionAttempts(); |
| 223 } |
| 224 |
| 225 void BleConnectionManager::UnregisterRemoteDevice( |
| 226 const cryptauth::RemoteDevice& remote_device, |
| 227 const MessageType& connection_reason) { |
| 228 PA_LOG(INFO) << "Unregistering device with ID " |
| 229 << remote_device.GetTruncatedDeviceIdForLogs() << " for reason " |
| 230 << MessageTypeToString(connection_reason); |
| 231 |
| 232 std::shared_ptr<ConnectionMetadata> connection_metadata = |
| 233 GetConnectionMetadata(remote_device); |
| 234 if (!connection_metadata) { |
| 235 // If the device was not registered, there is nothing to do. |
| 236 return; |
| 237 } |
| 238 |
| 239 connection_metadata->UnregisterConnectionReason(connection_reason); |
| 240 if (!connection_metadata->HasReasonForConnection()) { |
| 241 cryptauth::SecureChannel::Status status_before_disconnect = |
| 242 connection_metadata->GetStatus(); |
| 243 device_to_metadata_map_.erase(remote_device); |
| 244 if (status_before_disconnect == |
| 245 cryptauth::SecureChannel::Status::CONNECTING) { |
| 246 StopConnectionAttemptAndMoveToEndOfQueue(remote_device); |
| 247 } |
| 248 if (status_before_disconnect != |
| 249 cryptauth::SecureChannel::Status::DISCONNECTED) { |
| 250 // Send a status update for the disconnection. |
| 251 SendSecureChannelStatusChangeEvent( |
| 252 remote_device, status_before_disconnect, |
| 253 cryptauth::SecureChannel::Status::DISCONNECTED); |
| 254 } |
| 255 } |
| 256 |
| 257 UpdateConnectionAttempts(); |
| 258 } |
| 259 |
| 260 void BleConnectionManager::SendMessage( |
| 261 const cryptauth::RemoteDevice& remote_device, |
| 262 const std::string& message) { |
| 263 std::shared_ptr<ConnectionMetadata> connection_metadata = |
| 264 GetConnectionMetadata(remote_device); |
| 265 if (!connection_metadata || |
| 266 connection_metadata->GetStatus() != |
| 267 cryptauth::SecureChannel::Status::AUTHENTICATED) { |
| 268 PA_LOG(ERROR) << "Attempted to send a message to device with ID " |
| 269 << remote_device.GetTruncatedDeviceIdForLogs() << ". " |
| 270 << "Message: \"" << message << "\""; |
| 271 return; |
| 272 } |
| 273 |
| 274 PA_LOG(INFO) << "Sending message to device with ID " |
| 275 << remote_device.GetTruncatedDeviceIdForLogs() << ". " |
| 276 << "Message: \"" << message << "\""; |
| 277 connection_metadata->SendMessage(message); |
| 278 } |
| 279 |
| 280 void BleConnectionManager::AddObserver(Observer* observer) { |
| 281 observer_list_.AddObserver(observer); |
| 282 } |
| 283 |
| 284 void BleConnectionManager::RemoveObserver(Observer* observer) { |
| 285 observer_list_.RemoveObserver(observer); |
| 286 } |
| 287 |
| 288 void BleConnectionManager::OnReceivedAdvertisementFromDevice( |
| 289 const std::string& device_address, |
| 290 cryptauth::RemoteDevice remote_device) { |
| 291 std::shared_ptr<ConnectionMetadata> connection_metadata = |
| 292 GetConnectionMetadata(remote_device); |
| 293 if (!connection_metadata) { |
| 294 // If an advertisement is received from a device that is not registered, |
| 295 // ignore it. |
| 296 PA_LOG(WARNING) << "Received an advertisement from a device which is not " |
| 297 << "registered. Bluetooth address: " << device_address |
| 298 << ", Remote Device ID: " << remote_device.GetDeviceId(); |
| 299 return; |
| 300 } |
| 301 |
| 302 PA_LOG(INFO) << "Received advertisement from device with ID " |
| 303 << remote_device.GetTruncatedDeviceIdForLogs() << ". " |
| 304 << "Starting authentication handshake to that device."; |
| 305 |
| 306 // Create a connection to that device. |
| 307 std::unique_ptr<cryptauth::Connection> connection = |
| 308 cryptauth::weave::BluetoothLowEnergyWeaveClientConnection::Factory:: |
| 309 NewInstance(remote_device, device_address, adapter_, |
| 310 device::BluetoothUUID(std::string(kGattServerUuid)), |
| 311 bluetooth_throttler_); |
| 312 std::unique_ptr<cryptauth::SecureChannel> secure_channel = |
| 313 cryptauth::SecureChannel::Factory::NewInstance( |
| 314 std::move(connection), delegate_->CreateSecureChannelDelegate()); |
| 315 connection_metadata->SetSecureChannel(std::move(secure_channel)); |
| 316 |
| 317 // Stop trying to connect to that device, since a connection already exists. |
| 318 StopConnectionAttemptAndMoveToEndOfQueue(remote_device); |
| 319 UpdateConnectionAttempts(); |
| 320 } |
| 321 |
| 322 std::shared_ptr<BleConnectionManager::ConnectionMetadata> |
| 323 BleConnectionManager::GetConnectionMetadata( |
| 324 const cryptauth::RemoteDevice& remote_device) { |
| 325 const auto map_iter = device_to_metadata_map_.find(remote_device); |
| 326 if (map_iter == device_to_metadata_map_.end()) { |
| 327 return nullptr; |
| 328 } |
| 329 |
| 330 return map_iter->second; |
| 331 } |
| 332 |
| 333 std::shared_ptr<BleConnectionManager::ConnectionMetadata> |
| 334 BleConnectionManager::AddMetadataForDevice( |
| 335 const cryptauth::RemoteDevice& remote_device) { |
| 336 std::shared_ptr<ConnectionMetadata> existing_data = |
| 337 GetConnectionMetadata(remote_device); |
| 338 if (existing_data) { |
| 339 return existing_data; |
| 340 } |
| 341 |
| 342 std::unique_ptr<base::Timer> timer = timer_factory_->CreateTimer(); |
| 343 device_to_metadata_map_.insert( |
| 344 std::pair<cryptauth::RemoteDevice, std::shared_ptr<ConnectionMetadata>>( |
| 345 remote_device, |
| 346 std::shared_ptr<ConnectionMetadata>( |
| 347 new ConnectionMetadata(remote_device, std::move(timer), |
| 348 weak_ptr_factory_.GetWeakPtr())))); |
| 349 return device_to_metadata_map_.at(remote_device); |
| 350 } |
| 351 |
| 352 void BleConnectionManager::UpdateConnectionAttempts() { |
| 353 UpdateAdvertisementQueue(); |
| 354 |
| 355 std::vector<cryptauth::RemoteDevice> should_advertise_to = |
| 356 device_queue_->GetDevicesToWhichToAdvertise(); |
| 357 DCHECK(should_advertise_to.size() <= |
| 358 static_cast<size_t>(kMaxConcurrentAdvertisements)); |
| 359 |
| 360 for (const auto& remote_device : should_advertise_to) { |
| 361 std::shared_ptr<ConnectionMetadata> associated_data = |
| 362 GetConnectionMetadata(remote_device); |
| 363 if (associated_data->GetStatus() != |
| 364 cryptauth::SecureChannel::Status::CONNECTING) { |
| 365 // If there is no active attempt to connect to a device at the front of |
| 366 // the queue, start a connection attempt. |
| 367 StartConnectionAttempt(remote_device); |
| 368 } |
| 369 } |
| 370 } |
| 371 |
| 372 void BleConnectionManager::UpdateAdvertisementQueue() { |
| 373 std::vector<cryptauth::RemoteDevice> devices_for_queue; |
| 374 for (const auto& map_entry : device_to_metadata_map_) { |
| 375 if (map_entry.second->HasEstablishedConnection()) { |
| 376 // If there is already an active connection to the device, there is no |
| 377 // need to advertise to the device to bootstrap a connection. |
| 378 continue; |
| 379 } |
| 380 |
| 381 devices_for_queue.push_back(map_entry.first); |
| 382 } |
| 383 |
| 384 device_queue_->SetDevices(devices_for_queue); |
| 385 } |
| 386 |
| 387 void BleConnectionManager::StartConnectionAttempt( |
| 388 const cryptauth::RemoteDevice& remote_device) { |
| 389 std::shared_ptr<ConnectionMetadata> connection_metadata = |
| 390 GetConnectionMetadata(remote_device); |
| 391 DCHECK(connection_metadata); |
| 392 |
| 393 PA_LOG(INFO) << "Starting connection attempt to device with ID " |
| 394 << remote_device.GetTruncatedDeviceIdForLogs(); |
| 395 |
| 396 // Send a "disconnected => connecting" update to alert clients that a |
| 397 // connection attempt for |remote_device| is underway. |
| 398 SendSecureChannelStatusChangeEvent( |
| 399 remote_device, cryptauth::SecureChannel::Status::DISCONNECTED, |
| 400 cryptauth::SecureChannel::Status::CONNECTING); |
| 401 |
| 402 bool success = ble_scanner_->RegisterScanFilterForDevice(remote_device) && |
| 403 ble_advertiser_->StartAdvertisingToDevice(remote_device); |
| 404 |
| 405 // Start a timer; if a connection is unable to be created before the timer |
| 406 // fires, a timeout occurs. Note that if this class is unable to start both |
| 407 // the scanner and advertiser successfully (i.e., |success| is |false|), a |
| 408 // the connection fails immediately insetad of waiting for a timeout, which |
| 409 // has the effect of quickly sending out "disconnected => connecting => |
| 410 // disconnecting" status updates. The timer is used here instead of a special |
| 411 // case in order to route all connection failures through the same code path. |
| 412 connection_metadata->StartConnectionAttemptTimer( |
| 413 !success /* fail_immediately */); |
| 414 } |
| 415 |
| 416 void BleConnectionManager::StopConnectionAttemptAndMoveToEndOfQueue( |
| 417 const cryptauth::RemoteDevice& remote_device) { |
| 418 PA_LOG(INFO) << "Stopping connection attempt to device with ID " |
| 419 << remote_device.GetTruncatedDeviceIdForLogs(); |
| 420 |
| 421 ble_scanner_->UnregisterScanFilterForDevice(remote_device); |
| 422 ble_advertiser_->StopAdvertisingToDevice(remote_device); |
| 423 |
| 424 device_queue_->MoveDeviceToEnd(remote_device.GetDeviceId()); |
| 425 } |
| 426 |
| 427 void BleConnectionManager::OnConnectionAttemptTimeout( |
| 428 const cryptauth::RemoteDevice& remote_device) { |
| 429 PA_LOG(INFO) << "Connection attempt timed out for device with ID " |
| 430 << remote_device.GetTruncatedDeviceIdForLogs(); |
| 431 |
| 432 StopConnectionAttemptAndMoveToEndOfQueue(remote_device); |
| 433 |
| 434 // Send a "connecting => disconnected" update to alert clients that a |
| 435 // connection attempt for |remote_device| has failed. |
| 436 SendSecureChannelStatusChangeEvent( |
| 437 remote_device, cryptauth::SecureChannel::Status::CONNECTING, |
| 438 cryptauth::SecureChannel::Status::DISCONNECTED); |
| 439 |
| 440 UpdateConnectionAttempts(); |
| 441 } |
| 442 |
| 443 void BleConnectionManager::OnSecureChannelStatusChanged( |
| 444 const cryptauth::RemoteDevice& remote_device, |
| 445 const cryptauth::SecureChannel::Status& old_status, |
| 446 const cryptauth::SecureChannel::Status& new_status) { |
| 447 SendSecureChannelStatusChangeEvent(remote_device, old_status, new_status); |
| 448 UpdateConnectionAttempts(); |
| 449 } |
| 450 |
| 451 void BleConnectionManager::SendMessageReceivedEvent( |
| 452 const cryptauth::RemoteDevice& remote_device, |
| 453 const std::string& payload) { |
| 454 PA_LOG(INFO) << "Broadcasting message received event: " |
| 455 << "Device ID \"" << remote_device.GetTruncatedDeviceIdForLogs() |
| 456 << "\" received message \"" << payload << "\"."; |
| 457 for (auto& observer : observer_list_) { |
| 458 observer.OnMessageReceived(remote_device, payload); |
| 459 } |
| 460 } |
| 461 |
| 462 void BleConnectionManager::SendSecureChannelStatusChangeEvent( |
| 463 const cryptauth::RemoteDevice& remote_device, |
| 464 const cryptauth::SecureChannel::Status& old_status, |
| 465 const cryptauth::SecureChannel::Status& new_status) { |
| 466 PA_LOG(INFO) << "Broadcasting status change event: " |
| 467 << "Device ID \"" << remote_device.GetTruncatedDeviceIdForLogs() |
| 468 << "\": " << cryptauth::SecureChannel::StatusToString(old_status) |
| 469 << " => " |
| 470 << cryptauth::SecureChannel::StatusToString(new_status); |
| 471 for (auto& observer : observer_list_) { |
| 472 observer.OnSecureChannelStatusChanged(remote_device, old_status, |
| 473 new_status); |
| 474 } |
| 475 } |
| 476 |
| 477 } // namespace tether |
| 478 |
| 479 } // namespace chromeos |
OLD | NEW |