blob: a6653b78c432593579badb37a5cdb0c3114e11e0 [file] [log] [blame]
Arman Ugurayc2fc0f22015-09-03 15:09:41 -07001//
2// Copyright (C) 2015 Google, Inc.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "service/low_energy_client.h"
18
19#include <base/logging.h>
20
Jakub Pawlowski60b0e8f2016-01-12 13:51:35 -080021#include "service/adapter.h"
Jakub Pawlowskia6372e92016-01-19 16:42:37 -080022#include "service/common/bluetooth/util/address_helper.h"
Arman Uguray82ea72f2015-12-02 17:29:27 -080023#include "service/logging_helpers.h"
Arman Uguray12338402015-09-16 18:00:05 -070024#include "stack/include/bt_types.h"
25#include "stack/include/hcidefs.h"
26
Arman Ugurayc2fc0f22015-09-03 15:09:41 -070027using std::lock_guard;
28using std::mutex;
29
30namespace bluetooth {
31
Arman Uguray12338402015-09-16 18:00:05 -070032namespace {
33
Arman Uguray82ea72f2015-12-02 17:29:27 -080034// 31 + 31 for advertising data and scan response. This is the maximum length
35// TODO(armansito): Fix the HAL to return a concatenated blob that contains the
36// true length of each field and also provide a length parameter so that we
37// can support advertising length extensions in the future.
38const size_t kScanRecordLength = 62;
39
Arman Uguray12338402015-09-16 18:00:05 -070040BLEStatus GetBLEStatus(int status) {
41 if (status == BT_STATUS_FAIL)
42 return BLE_STATUS_FAILURE;
43
44 return static_cast<BLEStatus>(status);
45}
46
Arman Uguray82ea72f2015-12-02 17:29:27 -080047// Returns the length of the given scan record array. We have to calculate this
48// based on the maximum possible data length and the TLV data. See TODO above
49// |kScanRecordLength|.
50size_t GetScanRecordLength(uint8_t* bytes) {
51 for (size_t i = 0, field_len = 0; i < kScanRecordLength;
52 i += (field_len + 1)) {
53 field_len = bytes[i];
54
55 // Assert here that the data returned from the stack is correctly formatted
56 // in TLV form and that the length of the current field won't exceed the
57 // total data length.
58 CHECK(i + field_len < kScanRecordLength);
59
60 // If the field length is zero and we haven't reached the maximum length,
61 // then we have found the length, as the stack will pad the data with zeros
62 // accordingly.
63 if (field_len == 0)
64 return i;
65 }
66
67 // We have reached the end.
68 return kScanRecordLength;
69}
70
Arman Uguray12338402015-09-16 18:00:05 -070071// TODO(armansito): BTIF currently expects each advertising field in a
72// specific format passed directly in arguments. We should fix BTIF to accept
73// the advertising data directly instead.
74struct HALAdvertiseData {
75 std::vector<uint8_t> manufacturer_data;
76 std::vector<uint8_t> service_data;
77 std::vector<uint8_t> service_uuid;
78};
79
Arman Uguray9fc7d812015-10-14 12:22:27 -070080bool ProcessUUID(const uint8_t* uuid_data, size_t uuid_len, UUID* out_uuid) {
81 // BTIF expects a single 128-bit UUID to be passed in little-endian form, so
82 // we need to convert into that from raw data.
83 // TODO(armansito): We have three repeated if bodies below only because UUID
84 // accepts std::array which requires constexpr lengths. We should just have a
85 // single UUID constructor that takes in an std::vector instead.
86 if (uuid_len == UUID::kNumBytes16) {
87 UUID::UUID16Bit uuid_bytes;
88 for (size_t i = 0; i < uuid_len; ++i)
89 uuid_bytes[uuid_len - i - 1] = uuid_data[i];
90 *out_uuid = UUID(uuid_bytes);
91 } else if (uuid_len == UUID::kNumBytes32) {
92 UUID::UUID32Bit uuid_bytes;
93 for (size_t i = 0; i < uuid_len; ++i)
94 uuid_bytes[uuid_len - i - 1] = uuid_data[i];
95 *out_uuid = UUID(uuid_bytes);
96 } else if (uuid_len == UUID::kNumBytes128) {
97 UUID::UUID128Bit uuid_bytes;
98 for (size_t i = 0; i < uuid_len; ++i)
99 uuid_bytes[uuid_len - i - 1] = uuid_data[i];
100 *out_uuid = UUID(uuid_bytes);
101 } else {
102 LOG(ERROR) << "Invalid UUID length";
103 return false;
104 }
105
106 return true;
107}
108
Ajay Panickerff651b72015-09-30 15:49:47 -0700109bool ProcessServiceData(const uint8_t* data,
Arman Uguray9fc7d812015-10-14 12:22:27 -0700110 uint8_t uuid_len,
Jakub Pawlowskia6372e92016-01-19 16:42:37 -0800111 HALAdvertiseData* out_data) {
Ajay Panickerff651b72015-09-30 15:49:47 -0700112 size_t field_len = data[0];
113
114 // Minimum packet size should be equal to the uuid length + 1 to include
115 // the byte for the type of packet
Arman Uguray9fc7d812015-10-14 12:22:27 -0700116 if (field_len < uuid_len + 1) {
Ajay Panickerff651b72015-09-30 15:49:47 -0700117 // Invalid packet size
118 return false;
119 }
120
121 if (!out_data->service_data.empty()) {
122 // More than one Service Data is not allowed due to the limitations
123 // of the HAL API. We error in order to make sure there
124 // is no ambiguity on which data to send.
125 VLOG(1) << "More than one Service Data entry not allowed";
126 return false;
127 }
128
129 const uint8_t* service_uuid = data + 2;
Arman Uguray9fc7d812015-10-14 12:22:27 -0700130 UUID uuid;
131 if (!ProcessUUID(service_uuid, uuid_len, &uuid))
132 return false;
133
134 UUID::UUID128Bit uuid_bytes = uuid.GetFullLittleEndian();
135 const std::vector<uint8_t> temp_uuid(
136 uuid_bytes.data(), uuid_bytes.data() + uuid_bytes.size());
Ajay Panickerff651b72015-09-30 15:49:47 -0700137
138 // This section is to make sure that there is no UUID conflict
139 if (out_data->service_uuid.empty()) {
140 out_data->service_uuid = temp_uuid;
141 } else if (out_data->service_uuid != temp_uuid) {
142 // Mismatch in uuid passed through service data and uuid passed
143 // through uuid field
144 VLOG(1) << "More than one UUID entry not allowed";
145 return false;
Jakub Pawlowskia6372e92016-01-19 16:42:37 -0800146 } // else do nothing as UUID is already properly assigned
Ajay Panickerff651b72015-09-30 15:49:47 -0700147
Arman Uguray9fc7d812015-10-14 12:22:27 -0700148 // Use + uuid_len + 2 here in order to skip over a
Ajay Panickerff651b72015-09-30 15:49:47 -0700149 // uuid contained in the beggining of the field
Arman Uguray9fc7d812015-10-14 12:22:27 -0700150 const uint8_t* srv_data = data + uuid_len + 2;
Ajay Panickerff651b72015-09-30 15:49:47 -0700151
152
153 out_data->service_data.insert(
154 out_data->service_data.begin(),
Arman Uguray9fc7d812015-10-14 12:22:27 -0700155 srv_data, srv_data + field_len - uuid_len - 1);
Ajay Panickerff651b72015-09-30 15:49:47 -0700156
157 return true;
158}
159
Arman Uguray12338402015-09-16 18:00:05 -0700160bool ProcessAdvertiseData(const AdvertiseData& adv,
161 HALAdvertiseData* out_data) {
162 CHECK(out_data);
163 CHECK(out_data->manufacturer_data.empty());
164 CHECK(out_data->service_data.empty());
165 CHECK(out_data->service_uuid.empty());
166
167 const auto& data = adv.data();
168 size_t len = data.size();
169 for (size_t i = 0, field_len = 0; i < len; i += (field_len + 1)) {
170 // The length byte is the first byte in the adv. "TLV" format.
171 field_len = data[i];
172
173 // The type byte is the next byte in the adv. "TLV" format.
174 uint8_t type = data[i + 1];
175 size_t uuid_len = 0;
176
177 switch (type) {
178 case HCI_EIR_MANUFACTURER_SPECIFIC_TYPE: {
179 // TODO(armansito): BTIF doesn't allow setting more than one
180 // manufacturer-specific data entry. This is something we should fix. For
181 // now, fail if more than one entry was set.
182 if (!out_data->manufacturer_data.empty()) {
Arman Uguray9fc7d812015-10-14 12:22:27 -0700183 LOG(ERROR) << "More than one Manufacturer Specific Data entry not allowed";
Arman Uguray12338402015-09-16 18:00:05 -0700184 return false;
185 }
186
187 // The value bytes start at the next byte in the "TLV" format.
188 const uint8_t* mnf_data = data.data() + i + 2;
189 out_data->manufacturer_data.insert(
190 out_data->manufacturer_data.begin(),
191 mnf_data, mnf_data + field_len - 1);
192 break;
193 }
Ajay Panickerff651b72015-09-30 15:49:47 -0700194 case HCI_EIR_MORE_16BITS_UUID_TYPE:
195 case HCI_EIR_COMPLETE_16BITS_UUID_TYPE:
196 case HCI_EIR_MORE_32BITS_UUID_TYPE:
197 case HCI_EIR_COMPLETE_32BITS_UUID_TYPE:
198 case HCI_EIR_MORE_128BITS_UUID_TYPE:
199 case HCI_EIR_COMPLETE_128BITS_UUID_TYPE: {
200 const uint8_t* uuid_data = data.data() + i + 2;
Arman Uguray9fc7d812015-10-14 12:22:27 -0700201 size_t uuid_len = field_len - 1;
202 UUID uuid;
203 if (!ProcessUUID(uuid_data, uuid_len, &uuid))
204 return false;
205
206 UUID::UUID128Bit uuid_bytes = uuid.GetFullLittleEndian();
Ajay Panickerff651b72015-09-30 15:49:47 -0700207
208 if (!out_data->service_uuid.empty() &&
Arman Uguray9fc7d812015-10-14 12:22:27 -0700209 memcmp(out_data->service_uuid.data(),
210 uuid_bytes.data(), uuid_bytes.size()) != 0) {
Ajay Panickerff651b72015-09-30 15:49:47 -0700211 // More than one UUID is not allowed due to the limitations
212 // of the HAL API. We error in order to make sure there
213 // is no ambiguity on which UUID to send. Also makes sure that
214 // UUID Hasn't been set by service data first
Arman Uguray9fc7d812015-10-14 12:22:27 -0700215 LOG(ERROR) << "More than one UUID entry not allowed";
Ajay Panickerff651b72015-09-30 15:49:47 -0700216 return false;
217 }
218
219 out_data->service_uuid.assign(
Arman Uguray9fc7d812015-10-14 12:22:27 -0700220 uuid_bytes.data(), uuid_bytes.data() + UUID::kNumBytes128);
Ajay Panickerff651b72015-09-30 15:49:47 -0700221 break;
222 }
223 case HCI_EIR_SERVICE_DATA_16BITS_UUID_TYPE: {
224 if (!ProcessServiceData(data.data() + i, 2, out_data))
225 return false;
226 break;
227 }
228 case HCI_EIR_SERVICE_DATA_32BITS_UUID_TYPE: {
229 if (!ProcessServiceData(data.data() + i, 4, out_data))
230 return false;
231 break;
232 }
233 case HCI_EIR_SERVICE_DATA_128BITS_UUID_TYPE: {
234 if (!ProcessServiceData(data.data() + i, 16, out_data))
235 return false;
236 break;
237 }
Arman Uguray12338402015-09-16 18:00:05 -0700238 // TODO(armansito): Support other fields.
239 default:
240 VLOG(1) << "Unrecognized EIR field: " << type;
241 return false;
242 }
243 }
244
245 return true;
246}
247
248// The Bluetooth Core Specification defines time interval (e.g. Page Scan
249// Interval, Advertising Interval, etc) units as 0.625 milliseconds (or 1
250// Baseband slot). The HAL advertising functions expect the interval in this
251// unit. This function maps an AdvertiseSettings::Mode value to the
252// corresponding time unit.
253int GetAdvertisingIntervalUnit(AdvertiseSettings::Mode mode) {
254 int ms;
255
256 switch (mode) {
257 case AdvertiseSettings::MODE_BALANCED:
258 ms = kAdvertisingIntervalMediumMs;
259 break;
260 case AdvertiseSettings::MODE_LOW_LATENCY:
261 ms = kAdvertisingIntervalLowMs;
262 break;
263 case AdvertiseSettings::MODE_LOW_POWER:
264 // Fall through
265 default:
266 ms = kAdvertisingIntervalHighMs;
267 break;
268 }
269
270 // Convert milliseconds Bluetooth units.
271 return (ms * 1000) / 625;
272}
273
274struct AdvertiseParams {
275 int min_interval;
276 int max_interval;
277 int event_type;
278 int tx_power_level;
279 int timeout_s;
280};
281
282void GetAdvertiseParams(const AdvertiseSettings& settings, bool has_scan_rsp,
283 AdvertiseParams* out_params) {
284 CHECK(out_params);
285
286 out_params->min_interval = GetAdvertisingIntervalUnit(settings.mode());
287 out_params->max_interval =
288 out_params->min_interval + kAdvertisingIntervalDeltaUnit;
289
290 if (settings.connectable())
291 out_params->event_type = kAdvertisingEventTypeConnectable;
292 else if (has_scan_rsp)
293 out_params->event_type = kAdvertisingEventTypeScannable;
294 else
295 out_params->event_type = kAdvertisingEventTypeNonConnectable;
296
297 out_params->tx_power_level = settings.tx_power_level();
298 out_params->timeout_s = settings.timeout().InSeconds();
299}
300
301} // namespace
302
303// LowEnergyClient implementation
304// ========================================================
305
Jakub Pawlowski60b0e8f2016-01-12 13:51:35 -0800306LowEnergyClient::LowEnergyClient(
307 Adapter& adapter, const UUID& uuid, int client_id)
308 : adapter_(adapter),
309 app_identifier_(uuid),
Arman Uguraybb18c412015-11-12 13:44:31 -0800310 client_id_(client_id),
Arman Uguray12338402015-09-16 18:00:05 -0700311 adv_data_needs_update_(false),
312 scan_rsp_needs_update_(false),
313 is_setting_adv_data_(false),
314 adv_started_(false),
315 adv_start_callback_(nullptr),
Arman Uguray480174f2015-11-30 15:36:17 -0800316 adv_stop_callback_(nullptr),
317 scan_started_(false) {
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700318}
319
320LowEnergyClient::~LowEnergyClient() {
321 // Automatically unregister the client.
Arman Uguraybb18c412015-11-12 13:44:31 -0800322 VLOG(1) << "LowEnergyClient unregistering client: " << client_id_;
Arman Uguray12338402015-09-16 18:00:05 -0700323
324 // Unregister as observer so we no longer receive any callbacks.
325 hal::BluetoothGattInterface::Get()->RemoveClientObserver(this);
326
327 // Stop advertising and ignore the result.
328 hal::BluetoothGattInterface::Get()->
Arman Uguraybb18c412015-11-12 13:44:31 -0800329 GetClientHALInterface()->multi_adv_disable(client_id_);
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700330 hal::BluetoothGattInterface::Get()->
Arman Uguraybb18c412015-11-12 13:44:31 -0800331 GetClientHALInterface()->unregister_client(client_id_);
Arman Uguray480174f2015-11-30 15:36:17 -0800332
333 // Stop any scans started by this client.
334 if (scan_started_.load())
335 StopScan();
336}
337
Jakub Pawlowskia6372e92016-01-19 16:42:37 -0800338bool LowEnergyClient::Connect(std::string address, bool is_direct) {
339 VLOG(2) << __func__ << "Address: " << address << " is_direct: " << is_direct;
340
341 bt_bdaddr_t bda;
342 util::BdAddrFromString(address, &bda);
343
344 bt_status_t status = hal::BluetoothGattInterface::Get()->
345 GetClientHALInterface()->connect(client_id_, &bda, is_direct,
346 BT_TRANSPORT_LE);
347 if (status != BT_STATUS_SUCCESS) {
348 LOG(ERROR) << "HAL call to connect failed";
349 return false;
350 }
351
352 return true;
353}
354
355bool LowEnergyClient::Disconnect(std::string address) {
356 VLOG(2) << __func__ << "Address: " << address;
357
358 bt_bdaddr_t bda;
359 util::BdAddrFromString(address, &bda);
360
361 std::map<const bt_bdaddr_t, int>::iterator conn_id;
362 {
363 lock_guard<mutex> lock(connection_fields_lock_);
364 conn_id = connection_ids_.find(bda);
365 if (conn_id == connection_ids_.end()) {
366 LOG(WARNING) << "Can't disconnect, no existing connection to " << address;
367 return false;
368 }
369 }
370
371 bt_status_t status = hal::BluetoothGattInterface::Get()->
372 GetClientHALInterface()->disconnect(client_id_, &bda, conn_id->second);
373 if (status != BT_STATUS_SUCCESS) {
374 LOG(ERROR) << "HAL call to disconnect failed";
375 return false;
376 }
377
378 return true;
379}
380
Jakub Pawlowskia6551072016-01-26 12:58:47 -0800381bool LowEnergyClient::SetMtu(std::string address, int mtu) {
382 VLOG(2) << __func__ << "Address: " << address
383 << " MTU: " << mtu;
384
385 bt_bdaddr_t bda;
386 util::BdAddrFromString(address, &bda);
387
388 std::map<const bt_bdaddr_t, int>::iterator conn_id;
389 {
390 lock_guard<mutex> lock(connection_fields_lock_);
391 conn_id = connection_ids_.find(bda);
392 if (conn_id == connection_ids_.end()) {
393 LOG(WARNING) << "Can't set MTU, no existing connection to " << address;
394 return false;
395 }
396 }
397
398 bt_status_t status = hal::BluetoothGattInterface::Get()->
399 GetClientHALInterface()->configure_mtu(conn_id->second, mtu);
400 if (status != BT_STATUS_SUCCESS) {
401 LOG(ERROR) << "HAL call to set MTU failed";
402 return false;
403 }
404
405 return true;
406}
407
Arman Uguray82ea72f2015-12-02 17:29:27 -0800408void LowEnergyClient::SetDelegate(Delegate* delegate) {
409 lock_guard<mutex> lock(delegate_mutex_);
410 delegate_ = delegate;
411}
412
Arman Uguray480174f2015-11-30 15:36:17 -0800413bool LowEnergyClient::StartScan(const ScanSettings& settings,
414 const std::vector<ScanFilter>& filters) {
415 VLOG(2) << __func__;
416
417 // Cannot start a scan if the adapter is not enabled.
418 if (!adapter_.IsEnabled()) {
419 LOG(ERROR) << "Cannot scan while Bluetooth is disabled";
420 return false;
421 }
422
423 // TODO(jpawlowski): Push settings and filtering logic below the HAL.
424 bt_status_t status = hal::BluetoothGattInterface::Get()->
425 StartScan(client_id_);
426 if (status != BT_STATUS_SUCCESS) {
427 LOG(ERROR) << "Failed to initiate scanning for client: " << client_id_;
428 return false;
429 }
430
431 scan_started_ = true;
432 return true;
433}
434
435bool LowEnergyClient::StopScan() {
436 VLOG(2) << __func__;
437
438 // TODO(armansito): We don't support batch scanning yet so call
439 // StopRegularScanForClient directly. In the future we will need to
440 // conditionally call a batch scan API here.
441 bt_status_t status = hal::BluetoothGattInterface::Get()->
442 StopScan(client_id_);
443 if (status != BT_STATUS_SUCCESS) {
444 LOG(ERROR) << "Failed to stop scan for client: " << client_id_;
445 return false;
446 }
447
448 scan_started_ = false;
449 return true;
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700450}
451
Arman Uguray12338402015-09-16 18:00:05 -0700452bool LowEnergyClient::StartAdvertising(const AdvertiseSettings& settings,
453 const AdvertiseData& advertise_data,
454 const AdvertiseData& scan_response,
455 const StatusCallback& callback) {
456 VLOG(2) << __func__;
457 lock_guard<mutex> lock(adv_fields_lock_);
458
459 if (IsAdvertisingStarted()) {
460 LOG(WARNING) << "Already advertising";
461 return false;
462 }
463
464 if (IsStartingAdvertising()) {
465 LOG(WARNING) << "StartAdvertising already pending";
466 return false;
467 }
468
469 if (!advertise_data.IsValid()) {
470 LOG(ERROR) << "Invalid advertising data";
471 return false;
472 }
473
474 if (!scan_response.IsValid()) {
475 LOG(ERROR) << "Invalid scan response data";
476 return false;
477 }
478
479 CHECK(!adv_data_needs_update_.load());
480 CHECK(!scan_rsp_needs_update_.load());
481
482 adv_data_ = advertise_data;
483 scan_response_ = scan_response;
Jakub Pawlowskid748ef22016-01-12 13:43:33 -0800484 advertise_settings_ = settings;
Arman Uguray12338402015-09-16 18:00:05 -0700485
486 AdvertiseParams params;
487 GetAdvertiseParams(settings, !scan_response_.data().empty(), &params);
488
489 bt_status_t status = hal::BluetoothGattInterface::Get()->
490 GetClientHALInterface()->multi_adv_enable(
Arman Uguraybb18c412015-11-12 13:44:31 -0800491 client_id_,
Arman Uguray12338402015-09-16 18:00:05 -0700492 params.min_interval,
493 params.max_interval,
494 params.event_type,
495 kAdvertisingChannelAll,
496 params.tx_power_level,
497 params.timeout_s);
498 if (status != BT_STATUS_SUCCESS) {
499 LOG(ERROR) << "Failed to initiate call to enable multi-advertising";
500 return false;
501 }
502
503 // Always update advertising data.
504 adv_data_needs_update_ = true;
505
506 // Update scan response only if it has data, since otherwise we just won't
507 // send ADV_SCAN_IND.
508 if (!scan_response_.data().empty())
509 scan_rsp_needs_update_ = true;
510
511 // OK to set this at the end since we're still holding |adv_fields_lock_|.
512 adv_start_callback_.reset(new StatusCallback(callback));
513
514 return true;
515}
516
517bool LowEnergyClient::StopAdvertising(const StatusCallback& callback) {
518 VLOG(2) << __func__;
519 lock_guard<mutex> lock(adv_fields_lock_);
520
521 if (!IsAdvertisingStarted()) {
522 LOG(ERROR) << "Not advertising";
523 return false;
524 }
525
526 if (IsStoppingAdvertising()) {
527 LOG(ERROR) << "StopAdvertising already pending";
528 return false;
529 }
530
531 CHECK(!adv_start_callback_);
532
533 bt_status_t status = hal::BluetoothGattInterface::Get()->
Arman Uguraybb18c412015-11-12 13:44:31 -0800534 GetClientHALInterface()->multi_adv_disable(client_id_);
Arman Uguray12338402015-09-16 18:00:05 -0700535 if (status != BT_STATUS_SUCCESS) {
536 LOG(ERROR) << "Failed to initiate call to disable multi-advertising";
537 return false;
538 }
539
540 // OK to set this at the end since we're still holding |adv_fields_lock_|.
541 adv_stop_callback_.reset(new StatusCallback(callback));
542
543 return true;
544}
545
546bool LowEnergyClient::IsAdvertisingStarted() const {
547 return adv_started_.load();
548}
549
550bool LowEnergyClient::IsStartingAdvertising() const {
551 return !IsAdvertisingStarted() && adv_start_callback_;
552}
553
554bool LowEnergyClient::IsStoppingAdvertising() const {
555 return IsAdvertisingStarted() && adv_stop_callback_;
556}
557
Arman Uguray08f80eb2015-09-21 11:17:07 -0700558const UUID& LowEnergyClient::GetAppIdentifier() const {
559 return app_identifier_;
560}
561
Arman Uguraybb18c412015-11-12 13:44:31 -0800562int LowEnergyClient::GetInstanceId() const {
563 return client_id_;
Arman Uguray08f80eb2015-09-21 11:17:07 -0700564}
565
Arman Uguray82ea72f2015-12-02 17:29:27 -0800566void LowEnergyClient::ScanResultCallback(
567 hal::BluetoothGattInterface* gatt_iface,
568 const bt_bdaddr_t& bda, int rssi, uint8_t* adv_data) {
569 // Ignore scan results if this client didn't start a scan.
570 if (!scan_started_.load())
571 return;
572
573 lock_guard<mutex> lock(delegate_mutex_);
574 if (!delegate_)
575 return;
576
577 // TODO(armansito): Apply software filters here.
578
579 size_t record_len = GetScanRecordLength(adv_data);
580 std::vector<uint8_t> scan_record(adv_data, adv_data + record_len);
581
582 ScanResult result(BtAddrString(&bda), scan_record, rssi);
583
584 delegate_->OnScanResult(this, result);
585}
586
Jakub Pawlowskia6372e92016-01-19 16:42:37 -0800587void LowEnergyClient::ConnectCallback(
588 hal::BluetoothGattInterface* gatt_iface, int conn_id, int status,
589 int client_id, const bt_bdaddr_t& bda) {
590 if (client_id != client_id_)
591 return;
592
593 VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
594
595 {
596 lock_guard<mutex> lock(connection_fields_lock_);
597 auto success = connection_ids_.emplace(bda, conn_id);
598 if (!success.second) {
599 LOG(ERROR) << __func__ << " Insertion into connection_ids_ failed!";
600 }
601 }
602
603 if (delegate_)
604 delegate_->OnConnectionState(this, status, BtAddrString(&bda).c_str(),
605 true);
606}
607
608void LowEnergyClient::DisconnectCallback(
609 hal::BluetoothGattInterface* gatt_iface, int conn_id, int status,
610 int client_id, const bt_bdaddr_t& bda) {
611 if (client_id != client_id_)
612 return;
613
614 VLOG(1) << __func__ << " client_id: " << client_id << " status: " << status;
615 {
616 lock_guard<mutex> lock(connection_fields_lock_);
617 if (!connection_ids_.erase(bda)) {
618 LOG(ERROR) << __func__ << " Erasing from connection_ids_ failed!";
619 }
620 }
621
622 if (delegate_)
623 delegate_->OnConnectionState(this, status, BtAddrString(&bda).c_str(),
624 false);
625}
626
Jakub Pawlowskia6551072016-01-26 12:58:47 -0800627void LowEnergyClient::MtuChangedCallback(
628 hal::BluetoothGattInterface* gatt_iface, int conn_id, int status,
629 int mtu) {
630 VLOG(1) << __func__ << " conn_id: " << conn_id << " status: " << status
631 << " mtu: " << mtu;
632
633 const bt_bdaddr_t *bda = nullptr;
634 {
635 lock_guard<mutex> lock(connection_fields_lock_);
636 for (auto& connection: connection_ids_) {
637 if (connection.second == conn_id) {
638 bda = &connection.first;
639 break;
640 }
641 }
642 }
643
644 if (!bda)
645 return;
646
647 const char *addr = BtAddrString(bda).c_str();
648 if (delegate_)
649 delegate_->OnMtuChanged(this, status, addr, mtu);
650}
651
Arman Uguray12338402015-09-16 18:00:05 -0700652void LowEnergyClient::MultiAdvEnableCallback(
653 hal::BluetoothGattInterface* gatt_iface,
Arman Uguraybb18c412015-11-12 13:44:31 -0800654 int client_id, int status) {
655 if (client_id != client_id_)
Arman Uguray12338402015-09-16 18:00:05 -0700656 return;
657
658 lock_guard<mutex> lock(adv_fields_lock_);
659
Arman Uguraybb18c412015-11-12 13:44:31 -0800660 VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
Arman Uguray12338402015-09-16 18:00:05 -0700661
662 CHECK(adv_start_callback_);
663 CHECK(!adv_stop_callback_);
664
665 // Terminate operation in case of error.
666 if (status != BT_STATUS_SUCCESS) {
667 LOG(ERROR) << "Failed to enable multi-advertising";
668 InvokeAndClearStartCallback(GetBLEStatus(status));
669 return;
670 }
671
672 // Now handle deferred tasks.
673 HandleDeferredAdvertiseData(gatt_iface);
674}
675
676void LowEnergyClient::MultiAdvDataCallback(
677 hal::BluetoothGattInterface* gatt_iface,
Arman Uguraybb18c412015-11-12 13:44:31 -0800678 int client_id, int status) {
679 if (client_id != client_id_)
Arman Uguray12338402015-09-16 18:00:05 -0700680 return;
681
682 lock_guard<mutex> lock(adv_fields_lock_);
683
Arman Uguraybb18c412015-11-12 13:44:31 -0800684 VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
Arman Uguray12338402015-09-16 18:00:05 -0700685
686 is_setting_adv_data_ = false;
687
688 // Terminate operation in case of error.
689 if (status != BT_STATUS_SUCCESS) {
690 LOG(ERROR) << "Failed to set advertising data";
691 InvokeAndClearStartCallback(GetBLEStatus(status));
692 return;
693 }
694
695 // Now handle deferred tasks.
696 HandleDeferredAdvertiseData(gatt_iface);
697}
698
699void LowEnergyClient::MultiAdvDisableCallback(
700 hal::BluetoothGattInterface* /* gatt_iface */,
Arman Uguraybb18c412015-11-12 13:44:31 -0800701 int client_id, int status) {
702 if (client_id != client_id_)
Arman Uguray12338402015-09-16 18:00:05 -0700703 return;
704
705 lock_guard<mutex> lock(adv_fields_lock_);
706
Arman Uguraybb18c412015-11-12 13:44:31 -0800707 VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
Arman Uguray12338402015-09-16 18:00:05 -0700708
709 CHECK(!adv_start_callback_);
710 CHECK(adv_stop_callback_);
711
712 if (status == BT_STATUS_SUCCESS) {
Arman Uguraybb18c412015-11-12 13:44:31 -0800713 VLOG(1) << "Multi-advertising stopped for client_id: " << client_id;
Arman Uguray12338402015-09-16 18:00:05 -0700714 adv_started_ = false;
715 } else {
716 LOG(ERROR) << "Failed to stop multi-advertising";
717 }
718
719 InvokeAndClearStopCallback(GetBLEStatus(status));
720}
721
722bt_status_t LowEnergyClient::SetAdvertiseData(
723 hal::BluetoothGattInterface* gatt_iface,
724 const AdvertiseData& data,
725 bool set_scan_rsp) {
726 VLOG(2) << __func__;
727
728 HALAdvertiseData hal_data;
729
730 // TODO(armansito): The stack should check that the length is valid when other
731 // fields inserted by the stack (e.g. flags, device name, tx-power) are taken
732 // into account. At the moment we are skipping this check; this means that if
733 // the given data is too long then the stack will truncate it.
734 if (!ProcessAdvertiseData(data, &hal_data)) {
Ajay Panickerff651b72015-09-30 15:49:47 -0700735 LOG(ERROR) << "Malformed advertise data given";
Arman Uguray12338402015-09-16 18:00:05 -0700736 return BT_STATUS_FAIL;
737 }
738
739 if (is_setting_adv_data_.load()) {
Ajay Panickerff651b72015-09-30 15:49:47 -0700740 LOG(ERROR) << "Setting advertising data already in progress.";
Arman Uguray12338402015-09-16 18:00:05 -0700741 return BT_STATUS_FAIL;
742 }
743
744 // TODO(armansito): The length fields in the BTIF function below are signed
745 // integers so a call to std::vector::size might get capped. This is very
746 // unlikely anyway but it's safer to stop using signed-integer types for
747 // length in APIs, so we should change that.
748 bt_status_t status = gatt_iface->GetClientHALInterface()->
749 multi_adv_set_inst_data(
Arman Uguraybb18c412015-11-12 13:44:31 -0800750 client_id_,
Arman Uguray12338402015-09-16 18:00:05 -0700751 set_scan_rsp,
752 data.include_device_name(),
753 data.include_tx_power_level(),
754 0, // This is what Bluetooth.apk current hardcodes for "appearance".
755 hal_data.manufacturer_data.size(),
756 reinterpret_cast<char*>(hal_data.manufacturer_data.data()),
Ajay Panickerff651b72015-09-30 15:49:47 -0700757 hal_data.service_data.size(),
758 reinterpret_cast<char*>(hal_data.service_data.data()),
759 hal_data.service_uuid.size(),
760 reinterpret_cast<char*>(hal_data.service_uuid.data()));
761
Arman Uguray12338402015-09-16 18:00:05 -0700762 if (status != BT_STATUS_SUCCESS) {
763 LOG(ERROR) << "Failed to set instance advertising data.";
764 return status;
765 }
766
767 if (set_scan_rsp)
768 scan_rsp_needs_update_ = false;
769 else
770 adv_data_needs_update_ = false;
771
772 is_setting_adv_data_ = true;
773
774 return status;
775}
776
777void LowEnergyClient::HandleDeferredAdvertiseData(
778 hal::BluetoothGattInterface* gatt_iface) {
779 VLOG(2) << __func__;
780
781 CHECK(!IsAdvertisingStarted());
782 CHECK(!IsStoppingAdvertising());
783 CHECK(IsStartingAdvertising());
784 CHECK(!is_setting_adv_data_.load());
785
786 if (adv_data_needs_update_.load()) {
787 bt_status_t status = SetAdvertiseData(gatt_iface, adv_data_, false);
788 if (status != BT_STATUS_SUCCESS) {
789 LOG(ERROR) << "Failed setting advertisement data";
790 InvokeAndClearStartCallback(GetBLEStatus(status));
791 }
792 return;
793 }
794
795 if (scan_rsp_needs_update_.load()) {
796 bt_status_t status = SetAdvertiseData(gatt_iface, scan_response_, true);
797 if (status != BT_STATUS_SUCCESS) {
798 LOG(ERROR) << "Failed setting scan response data";
799 InvokeAndClearStartCallback(GetBLEStatus(status));
800 }
801 return;
802 }
803
804 // All pending tasks are complete. Report success.
805 adv_started_ = true;
806 InvokeAndClearStartCallback(BLE_STATUS_SUCCESS);
807}
808
809void LowEnergyClient::InvokeAndClearStartCallback(BLEStatus status) {
810 adv_data_needs_update_ = false;
811 scan_rsp_needs_update_ = false;
812
813 // We allow NULL callbacks.
814 if (*adv_start_callback_)
815 (*adv_start_callback_)(status);
816
817 adv_start_callback_ = nullptr;
818}
819
820void LowEnergyClient::InvokeAndClearStopCallback(BLEStatus status) {
821 // We allow NULL callbacks.
822 if (*adv_stop_callback_)
823 (*adv_stop_callback_)(status);
824
825 adv_stop_callback_ = nullptr;
826}
827
828// LowEnergyClientFactory implementation
829// ========================================================
830
Jakub Pawlowski60b0e8f2016-01-12 13:51:35 -0800831LowEnergyClientFactory::LowEnergyClientFactory(Adapter& adapter)
832 : adapter_(adapter) {
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700833 hal::BluetoothGattInterface::Get()->AddClientObserver(this);
834}
835
836LowEnergyClientFactory::~LowEnergyClientFactory() {
837 hal::BluetoothGattInterface::Get()->RemoveClientObserver(this);
838}
839
Arman Uguraybb18c412015-11-12 13:44:31 -0800840bool LowEnergyClientFactory::RegisterInstance(
841 const UUID& uuid,
842 const RegisterCallback& callback) {
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700843 VLOG(1) << __func__ << " - UUID: " << uuid.ToString();
844 lock_guard<mutex> lock(pending_calls_lock_);
845
846 if (pending_calls_.find(uuid) != pending_calls_.end()) {
847 LOG(ERROR) << "Low-Energy client with given UUID already registered - "
848 << "UUID: " << uuid.ToString();
849 return false;
850 }
851
852 const btgatt_client_interface_t* hal_iface =
853 hal::BluetoothGattInterface::Get()->GetClientHALInterface();
854 bt_uuid_t app_uuid = uuid.GetBlueDroid();
855
856 if (hal_iface->register_client(&app_uuid) != BT_STATUS_SUCCESS)
857 return false;
858
859 pending_calls_[uuid] = callback;
860
861 return true;
862}
863
864void LowEnergyClientFactory::RegisterClientCallback(
Arman Uguray12338402015-09-16 18:00:05 -0700865 hal::BluetoothGattInterface* gatt_iface,
Arman Uguraybb18c412015-11-12 13:44:31 -0800866 int status, int client_id,
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700867 const bt_uuid_t& app_uuid) {
868 UUID uuid(app_uuid);
869
870 VLOG(1) << __func__ << " - UUID: " << uuid.ToString();
871 lock_guard<mutex> lock(pending_calls_lock_);
872
873 auto iter = pending_calls_.find(uuid);
874 if (iter == pending_calls_.end()) {
875 VLOG(1) << "Ignoring callback for unknown app_id: " << uuid.ToString();
876 return;
877 }
878
879 // No need to construct a client if the call wasn't successful.
880 std::unique_ptr<LowEnergyClient> client;
881 BLEStatus result = BLE_STATUS_FAILURE;
882 if (status == BT_STATUS_SUCCESS) {
Jakub Pawlowski60b0e8f2016-01-12 13:51:35 -0800883 client.reset(new LowEnergyClient(adapter_, uuid, client_id));
Arman Uguray12338402015-09-16 18:00:05 -0700884
Jakub Pawlowski25689c12016-01-20 16:24:03 -0800885 gatt_iface->AddClientObserver(client.get());
Arman Uguray12338402015-09-16 18:00:05 -0700886
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700887 result = BLE_STATUS_SUCCESS;
888 }
889
Arman Uguray08f80eb2015-09-21 11:17:07 -0700890 // Notify the result via the result callback.
Arman Ugurayc2fc0f22015-09-03 15:09:41 -0700891 iter->second(result, uuid, std::move(client));
892
893 pending_calls_.erase(iter);
894}
895
896} // namespace bluetooth