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

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

Issue 2604063003: [CrOS Tether] Create BleScanner, a class which scan BLE advertisements and identifies nearby device… (Closed)
Patch Set: Add missing dependency. Created 3 years, 11 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 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
OLDNEW
« no previous file with comments | « chromeos/components/tether/ble_scanner.h ('k') | chromeos/components/tether/ble_scanner_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698