OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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_scanner.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/memory/ptr_util.h" |
| 9 #include "base/strings/string_util.h" |
| 10 #include "chromeos/components/tether/ble_constants.h" |
| 11 #include "components/cryptauth/proto/cryptauth_api.pb.h" |
| 12 #include "components/cryptauth/remote_device.h" |
| 13 #include "components/proximity_auth/logging/logging.h" |
| 14 #include "device/bluetooth/bluetooth_device.h" |
| 15 #include "device/bluetooth/bluetooth_discovery_session.h" |
| 16 #include "device/bluetooth/bluetooth_uuid.h" |
| 17 |
| 18 namespace chromeos { |
| 19 |
| 20 namespace tether { |
| 21 |
| 22 namespace { |
| 23 |
| 24 // Minimum RSSI value to use for discovery. The -90 value was determined |
| 25 // empirically and is borrowed from |
| 26 // |proximity_auth::BluetoothLowEnergyConnectionFinder|. |
| 27 const int kMinDiscoveryRSSI = -90; |
| 28 |
| 29 // Valid service data must include at least 4 bytes: 2 bytes associated with the |
| 30 // scanning device (used as a scan filter) and 2 bytes which identify the |
| 31 // advertising device to the scanning device. |
| 32 const size_t kMinNumBytesInServiceData = 4; |
| 33 |
| 34 // Returns out |string|s data as a hex string. |
| 35 std::string StringToHexOfContents(const std::string& string) { |
| 36 std::stringstream ss; |
| 37 ss << "0x" << std::hex; |
| 38 |
| 39 for (size_t i = 0; i < string.size(); i++) { |
| 40 ss << static_cast<int>(string.data()[i]); |
| 41 } |
| 42 |
| 43 return ss.str(); |
| 44 } |
| 45 |
| 46 } // namespace |
| 47 |
| 48 BleScanner::DelegateImpl::DelegateImpl() {} |
| 49 |
| 50 BleScanner::DelegateImpl::~DelegateImpl() {} |
| 51 |
| 52 bool BleScanner::DelegateImpl::IsBluetoothAdapterAvailable() const { |
| 53 return device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable(); |
| 54 } |
| 55 |
| 56 void BleScanner::DelegateImpl::GetAdapter( |
| 57 const device::BluetoothAdapterFactory::AdapterCallback& callback) { |
| 58 device::BluetoothAdapterFactory::GetAdapter(callback); |
| 59 } |
| 60 |
| 61 const std::vector<uint8_t>* BleScanner::DelegateImpl::GetServiceDataForUUID( |
| 62 const device::BluetoothUUID& service_uuid, |
| 63 device::BluetoothDevice* bluetooth_device) { |
| 64 return bluetooth_device->GetServiceDataForUUID(service_uuid); |
| 65 } |
| 66 |
| 67 BleScanner::BleScanner( |
| 68 const LocalDeviceDataProvider* local_device_data_provider) |
| 69 : BleScanner(base::MakeUnique<DelegateImpl>(), |
| 70 cryptauth::EidGenerator::GetInstance(), |
| 71 local_device_data_provider) {} |
| 72 |
| 73 BleScanner::~BleScanner() {} |
| 74 |
| 75 BleScanner::BleScanner( |
| 76 std::unique_ptr<Delegate> delegate, |
| 77 const cryptauth::EidGenerator* eid_generator, |
| 78 const LocalDeviceDataProvider* local_device_data_provider) |
| 79 : delegate_(std::move(delegate)), |
| 80 eid_generator_(eid_generator), |
| 81 local_device_data_provider_(local_device_data_provider), |
| 82 is_initializing_adapter_(false), |
| 83 is_initializing_discovery_session_(false), |
| 84 discovery_session_(nullptr), |
| 85 weak_ptr_factory_(this) {} |
| 86 |
| 87 bool BleScanner::RegisterScanFilterForDevice( |
| 88 const cryptauth::RemoteDevice& remote_device) { |
| 89 if (!delegate_->IsBluetoothAdapterAvailable()) { |
| 90 PA_LOG(ERROR) << "Bluetooth is not supported on this platform."; |
| 91 return false; |
| 92 } |
| 93 |
| 94 if (registered_remote_devices_.size() >= kMaxConcurrentAdvertisements) { |
| 95 // Each scan filter corresponds to an advertisement. Thus, the number of |
| 96 // concurrent advertisements cannot exceed the maximum number of concurrent |
| 97 // advertisements. |
| 98 return false; |
| 99 } |
| 100 |
| 101 std::vector<cryptauth::BeaconSeed> local_device_beacon_seeds; |
| 102 if (!local_device_data_provider_->GetLocalDeviceData( |
| 103 nullptr, &local_device_beacon_seeds)) { |
| 104 // If the local device's beacon seeds could not be fetched, a scan filter |
| 105 // cannot be generated. |
| 106 return false; |
| 107 } |
| 108 |
| 109 std::unique_ptr<cryptauth::EidGenerator::EidData> scan_filters = |
| 110 eid_generator_->GenerateBackgroundScanFilter(local_device_beacon_seeds); |
| 111 if (!scan_filters) { |
| 112 // If a background scan filter cannot be generated, give up. |
| 113 return false; |
| 114 } |
| 115 |
| 116 registered_remote_devices_.push_back(remote_device); |
| 117 UpdateDiscoveryStatus(); |
| 118 |
| 119 return true; |
| 120 } |
| 121 |
| 122 bool BleScanner::UnregisterScanFilterForDevice( |
| 123 const cryptauth::RemoteDevice& remote_device) { |
| 124 for (auto it = registered_remote_devices_.begin(); |
| 125 it != registered_remote_devices_.end(); ++it) { |
| 126 if (it->GetDeviceId() == remote_device.GetDeviceId()) { |
| 127 registered_remote_devices_.erase(it); |
| 128 UpdateDiscoveryStatus(); |
| 129 return true; |
| 130 } |
| 131 } |
| 132 |
| 133 return false; |
| 134 } |
| 135 |
| 136 bool BleScanner::IsDeviceRegistered(const std::string& device_id) { |
| 137 for (auto it = registered_remote_devices_.begin(); |
| 138 it != registered_remote_devices_.end(); ++it) { |
| 139 if (it->GetDeviceId() == device_id) { |
| 140 return true; |
| 141 } |
| 142 } |
| 143 |
| 144 return false; |
| 145 } |
| 146 |
| 147 void BleScanner::AddObserver(Observer* observer) { |
| 148 observer_list_.AddObserver(observer); |
| 149 } |
| 150 |
| 151 void BleScanner::RemoveObserver(Observer* observer) { |
| 152 observer_list_.RemoveObserver(observer); |
| 153 } |
| 154 |
| 155 void BleScanner::AdapterPoweredChanged(device::BluetoothAdapter* adapter, |
| 156 bool powered) { |
| 157 DCHECK_EQ(adapter_.get(), adapter); |
| 158 PA_LOG(INFO) << "Adapter power changed. Powered = " << powered; |
| 159 UpdateDiscoveryStatus(); |
| 160 } |
| 161 |
| 162 void BleScanner::DeviceAdded(device::BluetoothAdapter* adapter, |
| 163 device::BluetoothDevice* bluetooth_device) { |
| 164 DCHECK_EQ(adapter_.get(), adapter); |
| 165 HandleDeviceUpdated(bluetooth_device); |
| 166 } |
| 167 |
| 168 void BleScanner::DeviceChanged(device::BluetoothAdapter* adapter, |
| 169 device::BluetoothDevice* bluetooth_device) { |
| 170 DCHECK_EQ(adapter_.get(), adapter); |
| 171 HandleDeviceUpdated(bluetooth_device); |
| 172 } |
| 173 |
| 174 void BleScanner::UpdateDiscoveryStatus() { |
| 175 if (registered_remote_devices_.empty()) { |
| 176 StopDiscoverySession(); |
| 177 return; |
| 178 } |
| 179 |
| 180 if (is_initializing_adapter_) { |
| 181 return; |
| 182 } else if (!adapter_) { |
| 183 InitializeBluetoothAdapter(); |
| 184 return; |
| 185 } |
| 186 |
| 187 if (!adapter_->IsPowered()) { |
| 188 // If the adapter has powered off, no devices can be discovered. |
| 189 StopDiscoverySession(); |
| 190 return; |
| 191 } |
| 192 |
| 193 if (is_initializing_discovery_session_) { |
| 194 return; |
| 195 } else if (!discovery_session_ || |
| 196 (discovery_session_ && !discovery_session_->IsActive())) { |
| 197 StartDiscoverySession(); |
| 198 } |
| 199 } |
| 200 |
| 201 void BleScanner::InitializeBluetoothAdapter() { |
| 202 PA_LOG(INFO) << "Initializing Bluetooth adapter."; |
| 203 is_initializing_adapter_ = true; |
| 204 delegate_->GetAdapter(base::Bind(&BleScanner::OnAdapterInitialized, |
| 205 weak_ptr_factory_.GetWeakPtr())); |
| 206 } |
| 207 |
| 208 void BleScanner::OnAdapterInitialized( |
| 209 scoped_refptr<device::BluetoothAdapter> adapter) { |
| 210 DCHECK(is_initializing_adapter_ && !discovery_session_ && |
| 211 !is_initializing_discovery_session_); |
| 212 PA_LOG(INFO) << "Bluetooth adapter initialized."; |
| 213 is_initializing_adapter_ = false; |
| 214 |
| 215 adapter_ = adapter; |
| 216 adapter_->AddObserver(this); |
| 217 |
| 218 UpdateDiscoveryStatus(); |
| 219 } |
| 220 |
| 221 void BleScanner::StartDiscoverySession() { |
| 222 DCHECK(adapter_); |
| 223 PA_LOG(INFO) << "Starting discovery session."; |
| 224 is_initializing_discovery_session_ = true; |
| 225 |
| 226 // Discover only low energy (LE) devices with strong enough signal. |
| 227 std::unique_ptr<device::BluetoothDiscoveryFilter> filter = |
| 228 base::MakeUnique<device::BluetoothDiscoveryFilter>( |
| 229 device::BLUETOOTH_TRANSPORT_LE); |
| 230 filter->SetRSSI(kMinDiscoveryRSSI); |
| 231 |
| 232 adapter_->StartDiscoverySessionWithFilter( |
| 233 std::move(filter), base::Bind(&BleScanner::OnDiscoverySessionStarted, |
| 234 weak_ptr_factory_.GetWeakPtr()), |
| 235 base::Bind(&BleScanner::OnStartDiscoverySessionError, |
| 236 weak_ptr_factory_.GetWeakPtr())); |
| 237 } |
| 238 |
| 239 void BleScanner::OnDiscoverySessionStarted( |
| 240 std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) { |
| 241 PA_LOG(INFO) << "Discovery session started. Scanning fully initialized."; |
| 242 is_initializing_discovery_session_ = false; |
| 243 discovery_session_ = std::move(discovery_session); |
| 244 } |
| 245 |
| 246 void BleScanner::OnStartDiscoverySessionError() { |
| 247 PA_LOG(WARNING) << "Error starting discovery session. Initialization failed."; |
| 248 is_initializing_discovery_session_ = false; |
| 249 } |
| 250 |
| 251 void BleScanner::StopDiscoverySession() { |
| 252 if (!discovery_session_) { |
| 253 // If there is no discovery session to stop, return early. |
| 254 return; |
| 255 } |
| 256 |
| 257 PA_LOG(WARNING) << "Stopping discovery session."; |
| 258 discovery_session_.reset(); |
| 259 } |
| 260 |
| 261 void BleScanner::HandleDeviceUpdated( |
| 262 device::BluetoothDevice* bluetooth_device) { |
| 263 DCHECK(bluetooth_device); |
| 264 |
| 265 const std::vector<uint8_t>* service_data = delegate_->GetServiceDataForUUID( |
| 266 device::BluetoothUUID(kAdvertisingServiceUuid), bluetooth_device); |
| 267 if (!service_data || service_data->size() < kMinNumBytesInServiceData) { |
| 268 // If there is no service data or the service data is of insufficient |
| 269 // length, there is not enough information to create a connection. |
| 270 return; |
| 271 } |
| 272 |
| 273 // Convert the service data from a std::vector<uint8_t> to a std::string. |
| 274 std::string service_data_str; |
| 275 char* string_contents_ptr = |
| 276 base::WriteInto(&service_data_str, service_data->size() + 1); |
| 277 memcpy(string_contents_ptr, service_data->data(), service_data->size() + 1); |
| 278 |
| 279 CheckForMatchingScanFilters(bluetooth_device, service_data_str); |
| 280 } |
| 281 |
| 282 void BleScanner::CheckForMatchingScanFilters( |
| 283 device::BluetoothDevice* bluetooth_device, |
| 284 std::string& service_data) { |
| 285 std::vector<cryptauth::BeaconSeed> beacon_seeds; |
| 286 if (!local_device_data_provider_->GetLocalDeviceData(nullptr, |
| 287 &beacon_seeds)) { |
| 288 // If no beacon seeds are available, the scan cannot be checked for a match. |
| 289 return; |
| 290 } |
| 291 |
| 292 const cryptauth::RemoteDevice* identified_device = |
| 293 eid_generator_->IdentifyRemoteDeviceByAdvertisement( |
| 294 service_data, registered_remote_devices_, beacon_seeds); |
| 295 |
| 296 if (identified_device) { |
| 297 PA_LOG(INFO) << "Received advertisement from remote device with ID " |
| 298 << identified_device->GetTruncatedDeviceIdForLogs() << "."; |
| 299 for (auto& observer : observer_list_) { |
| 300 observer.OnReceivedAdvertisementFromDevice(bluetooth_device, |
| 301 *identified_device); |
| 302 } |
| 303 } else { |
| 304 PA_LOG(INFO) << "Received advertisement remote device, but could not " |
| 305 << "identify the device. Service data: " |
| 306 << StringToHexOfContents(service_data) << "."; |
| 307 } |
| 308 } |
| 309 |
| 310 } // namespace tether |
| 311 |
| 312 } // namespace chromeos |
OLD | NEW |