OSDN Git Service

service: Rename variable holding advertisement settings
[android-x86/system-bt.git] / service / low_energy_client.cpp
1 //
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
21 #include "stack/include/bt_types.h"
22 #include "stack/include/hcidefs.h"
23
24 using std::lock_guard;
25 using std::mutex;
26
27 namespace bluetooth {
28
29 namespace {
30
31 BLEStatus GetBLEStatus(int status) {
32   if (status == BT_STATUS_FAIL)
33     return BLE_STATUS_FAILURE;
34
35   return static_cast<BLEStatus>(status);
36 }
37
38 // TODO(armansito): BTIF currently expects each advertising field in a
39 // specific format passed directly in arguments. We should fix BTIF to accept
40 // the advertising data directly instead.
41 struct HALAdvertiseData {
42   std::vector<uint8_t> manufacturer_data;
43   std::vector<uint8_t> service_data;
44   std::vector<uint8_t> service_uuid;
45 };
46
47 bool ProcessUUID(const uint8_t* uuid_data, size_t uuid_len, UUID* out_uuid) {
48   // BTIF expects a single 128-bit UUID to be passed in little-endian form, so
49   // we need to convert into that from raw data.
50   // TODO(armansito): We have three repeated if bodies below only because UUID
51   // accepts std::array which requires constexpr lengths. We should just have a
52   // single UUID constructor that takes in an std::vector instead.
53   if (uuid_len == UUID::kNumBytes16) {
54     UUID::UUID16Bit uuid_bytes;
55     for (size_t i = 0; i < uuid_len; ++i)
56       uuid_bytes[uuid_len - i - 1] = uuid_data[i];
57     *out_uuid = UUID(uuid_bytes);
58   } else if (uuid_len == UUID::kNumBytes32) {
59     UUID::UUID32Bit uuid_bytes;
60     for (size_t i = 0; i < uuid_len; ++i)
61       uuid_bytes[uuid_len - i - 1] = uuid_data[i];
62     *out_uuid = UUID(uuid_bytes);
63   } else if (uuid_len == UUID::kNumBytes128) {
64     UUID::UUID128Bit uuid_bytes;
65     for (size_t i = 0; i < uuid_len; ++i)
66       uuid_bytes[uuid_len - i - 1] = uuid_data[i];
67     *out_uuid = UUID(uuid_bytes);
68   } else {
69     LOG(ERROR) << "Invalid UUID length";
70     return false;
71   }
72
73   return true;
74 }
75
76 bool ProcessServiceData(const uint8_t* data,
77         uint8_t uuid_len,
78         HALAdvertiseData* out_data){
79   size_t field_len = data[0];
80
81   // Minimum packet size should be equal to the uuid length + 1 to include
82   // the byte for the type of packet
83   if (field_len < uuid_len + 1) {
84     // Invalid packet size
85     return false;
86   }
87
88   if (!out_data->service_data.empty()) {
89     // More than one Service Data is not allowed due to the limitations
90     // of the HAL API. We error in order to make sure there
91     // is no ambiguity on which data to send.
92     VLOG(1) << "More than one Service Data entry not allowed";
93     return false;
94   }
95
96   const uint8_t* service_uuid = data + 2;
97   UUID uuid;
98   if (!ProcessUUID(service_uuid, uuid_len, &uuid))
99     return false;
100
101   UUID::UUID128Bit uuid_bytes = uuid.GetFullLittleEndian();
102   const std::vector<uint8_t> temp_uuid(
103       uuid_bytes.data(), uuid_bytes.data() + uuid_bytes.size());
104
105   // This section is to make sure that there is no UUID conflict
106   if (out_data->service_uuid.empty()) {
107     out_data->service_uuid = temp_uuid;
108   } else if (out_data->service_uuid != temp_uuid) {
109     // Mismatch in uuid passed through service data and uuid passed
110     // through uuid field
111     VLOG(1) << "More than one UUID entry not allowed";
112     return false;
113   } // else do nothing as UUID is already properly assigned
114
115   // Use + uuid_len + 2 here in order to skip over a
116   // uuid contained in the beggining of the field
117   const uint8_t* srv_data = data + uuid_len + 2;
118
119
120   out_data->service_data.insert(
121       out_data->service_data.begin(),
122       srv_data, srv_data + field_len - uuid_len - 1);
123
124   return true;
125 }
126
127 bool ProcessAdvertiseData(const AdvertiseData& adv,
128                           HALAdvertiseData* out_data) {
129   CHECK(out_data);
130   CHECK(out_data->manufacturer_data.empty());
131   CHECK(out_data->service_data.empty());
132   CHECK(out_data->service_uuid.empty());
133
134   const auto& data = adv.data();
135   size_t len = data.size();
136   for (size_t i = 0, field_len = 0; i < len; i += (field_len + 1)) {
137     // The length byte is the first byte in the adv. "TLV" format.
138     field_len = data[i];
139
140     // The type byte is the next byte in the adv. "TLV" format.
141     uint8_t type = data[i + 1];
142     size_t uuid_len = 0;
143
144     switch (type) {
145     case HCI_EIR_MANUFACTURER_SPECIFIC_TYPE: {
146       // TODO(armansito): BTIF doesn't allow setting more than one
147       // manufacturer-specific data entry. This is something we should fix. For
148       // now, fail if more than one entry was set.
149       if (!out_data->manufacturer_data.empty()) {
150         LOG(ERROR) << "More than one Manufacturer Specific Data entry not allowed";
151         return false;
152       }
153
154       // The value bytes start at the next byte in the "TLV" format.
155       const uint8_t* mnf_data = data.data() + i + 2;
156       out_data->manufacturer_data.insert(
157           out_data->manufacturer_data.begin(),
158           mnf_data, mnf_data + field_len - 1);
159       break;
160     }
161     case HCI_EIR_MORE_16BITS_UUID_TYPE:
162     case HCI_EIR_COMPLETE_16BITS_UUID_TYPE:
163     case HCI_EIR_MORE_32BITS_UUID_TYPE:
164     case HCI_EIR_COMPLETE_32BITS_UUID_TYPE:
165     case HCI_EIR_MORE_128BITS_UUID_TYPE:
166     case HCI_EIR_COMPLETE_128BITS_UUID_TYPE: {
167       const uint8_t* uuid_data = data.data() + i + 2;
168       size_t uuid_len = field_len - 1;
169       UUID uuid;
170       if (!ProcessUUID(uuid_data, uuid_len, &uuid))
171         return false;
172
173       UUID::UUID128Bit uuid_bytes = uuid.GetFullLittleEndian();
174
175       if (!out_data->service_uuid.empty() &&
176           memcmp(out_data->service_uuid.data(),
177                  uuid_bytes.data(), uuid_bytes.size()) != 0) {
178         // More than one UUID is not allowed due to the limitations
179         // of the HAL API. We error in order to make sure there
180         // is no ambiguity on which UUID to send. Also makes sure that
181         // UUID Hasn't been set by service data first
182         LOG(ERROR) << "More than one UUID entry not allowed";
183         return false;
184       }
185
186       out_data->service_uuid.assign(
187           uuid_bytes.data(), uuid_bytes.data() + UUID::kNumBytes128);
188       break;
189     }
190     case HCI_EIR_SERVICE_DATA_16BITS_UUID_TYPE: {
191       if (!ProcessServiceData(data.data() + i, 2, out_data))
192         return false;
193       break;
194     }
195     case HCI_EIR_SERVICE_DATA_32BITS_UUID_TYPE: {
196       if (!ProcessServiceData(data.data() + i, 4, out_data))
197         return false;
198       break;
199     }
200     case HCI_EIR_SERVICE_DATA_128BITS_UUID_TYPE: {
201       if (!ProcessServiceData(data.data() + i, 16, out_data))
202         return false;
203       break;
204     }
205     // TODO(armansito): Support other fields.
206     default:
207       VLOG(1) << "Unrecognized EIR field: " << type;
208       return false;
209     }
210   }
211
212   return true;
213 }
214
215 // The Bluetooth Core Specification defines time interval (e.g. Page Scan
216 // Interval, Advertising Interval, etc) units as 0.625 milliseconds (or 1
217 // Baseband slot). The HAL advertising functions expect the interval in this
218 // unit. This function maps an AdvertiseSettings::Mode value to the
219 // corresponding time unit.
220 int GetAdvertisingIntervalUnit(AdvertiseSettings::Mode mode) {
221   int ms;
222
223   switch (mode) {
224   case AdvertiseSettings::MODE_BALANCED:
225     ms = kAdvertisingIntervalMediumMs;
226     break;
227   case AdvertiseSettings::MODE_LOW_LATENCY:
228     ms = kAdvertisingIntervalLowMs;
229     break;
230   case AdvertiseSettings::MODE_LOW_POWER:
231     // Fall through
232   default:
233     ms = kAdvertisingIntervalHighMs;
234     break;
235   }
236
237   // Convert milliseconds Bluetooth units.
238   return (ms * 1000) / 625;
239 }
240
241 struct AdvertiseParams {
242   int min_interval;
243   int max_interval;
244   int event_type;
245   int tx_power_level;
246   int timeout_s;
247 };
248
249 void GetAdvertiseParams(const AdvertiseSettings& settings, bool has_scan_rsp,
250                         AdvertiseParams* out_params) {
251   CHECK(out_params);
252
253   out_params->min_interval = GetAdvertisingIntervalUnit(settings.mode());
254   out_params->max_interval =
255       out_params->min_interval + kAdvertisingIntervalDeltaUnit;
256
257   if (settings.connectable())
258     out_params->event_type = kAdvertisingEventTypeConnectable;
259   else if (has_scan_rsp)
260     out_params->event_type = kAdvertisingEventTypeScannable;
261   else
262     out_params->event_type = kAdvertisingEventTypeNonConnectable;
263
264   out_params->tx_power_level = settings.tx_power_level();
265   out_params->timeout_s = settings.timeout().InSeconds();
266 }
267
268 }  // namespace
269
270 // LowEnergyClient implementation
271 // ========================================================
272
273 LowEnergyClient::LowEnergyClient(const UUID& uuid, int client_id)
274     : app_identifier_(uuid),
275       client_id_(client_id),
276       adv_data_needs_update_(false),
277       scan_rsp_needs_update_(false),
278       is_setting_adv_data_(false),
279       adv_started_(false),
280       adv_start_callback_(nullptr),
281       adv_stop_callback_(nullptr) {
282 }
283
284 LowEnergyClient::~LowEnergyClient() {
285   // Automatically unregister the client.
286   VLOG(1) << "LowEnergyClient unregistering client: " << client_id_;
287
288   // Unregister as observer so we no longer receive any callbacks.
289   hal::BluetoothGattInterface::Get()->RemoveClientObserver(this);
290
291   // Stop advertising and ignore the result.
292   hal::BluetoothGattInterface::Get()->
293       GetClientHALInterface()->multi_adv_disable(client_id_);
294   hal::BluetoothGattInterface::Get()->
295       GetClientHALInterface()->unregister_client(client_id_);
296 }
297
298 bool LowEnergyClient::StartAdvertising(const AdvertiseSettings& settings,
299                                        const AdvertiseData& advertise_data,
300                                        const AdvertiseData& scan_response,
301                                        const StatusCallback& callback) {
302   VLOG(2) << __func__;
303   lock_guard<mutex> lock(adv_fields_lock_);
304
305   if (IsAdvertisingStarted()) {
306     LOG(WARNING) << "Already advertising";
307     return false;
308   }
309
310   if (IsStartingAdvertising()) {
311     LOG(WARNING) << "StartAdvertising already pending";
312     return false;
313   }
314
315   if (!advertise_data.IsValid()) {
316     LOG(ERROR) << "Invalid advertising data";
317     return false;
318   }
319
320   if (!scan_response.IsValid()) {
321     LOG(ERROR) << "Invalid scan response data";
322     return false;
323   }
324
325   CHECK(!adv_data_needs_update_.load());
326   CHECK(!scan_rsp_needs_update_.load());
327
328   adv_data_ = advertise_data;
329   scan_response_ = scan_response;
330   advertise_settings_ = settings;
331
332   AdvertiseParams params;
333   GetAdvertiseParams(settings, !scan_response_.data().empty(), &params);
334
335   bt_status_t status = hal::BluetoothGattInterface::Get()->
336       GetClientHALInterface()->multi_adv_enable(
337           client_id_,
338           params.min_interval,
339           params.max_interval,
340           params.event_type,
341           kAdvertisingChannelAll,
342           params.tx_power_level,
343           params.timeout_s);
344   if (status != BT_STATUS_SUCCESS) {
345     LOG(ERROR) << "Failed to initiate call to enable multi-advertising";
346     return false;
347   }
348
349   // Always update advertising data.
350   adv_data_needs_update_ = true;
351
352   // Update scan response only if it has data, since otherwise we just won't
353   // send ADV_SCAN_IND.
354   if (!scan_response_.data().empty())
355     scan_rsp_needs_update_ = true;
356
357   // OK to set this at the end since we're still holding |adv_fields_lock_|.
358   adv_start_callback_.reset(new StatusCallback(callback));
359
360   return true;
361 }
362
363 bool LowEnergyClient::StopAdvertising(const StatusCallback& callback) {
364   VLOG(2) << __func__;
365   lock_guard<mutex> lock(adv_fields_lock_);
366
367   if (!IsAdvertisingStarted()) {
368     LOG(ERROR) << "Not advertising";
369     return false;
370   }
371
372   if (IsStoppingAdvertising()) {
373     LOG(ERROR) << "StopAdvertising already pending";
374     return false;
375   }
376
377   CHECK(!adv_start_callback_);
378
379   bt_status_t status = hal::BluetoothGattInterface::Get()->
380       GetClientHALInterface()->multi_adv_disable(client_id_);
381   if (status != BT_STATUS_SUCCESS) {
382     LOG(ERROR) << "Failed to initiate call to disable multi-advertising";
383     return false;
384   }
385
386   // OK to set this at the end since we're still holding |adv_fields_lock_|.
387   adv_stop_callback_.reset(new StatusCallback(callback));
388
389   return true;
390 }
391
392 bool LowEnergyClient::IsAdvertisingStarted() const {
393   return adv_started_.load();
394 }
395
396 bool LowEnergyClient::IsStartingAdvertising() const {
397   return !IsAdvertisingStarted() && adv_start_callback_;
398 }
399
400 bool LowEnergyClient::IsStoppingAdvertising() const {
401   return IsAdvertisingStarted() && adv_stop_callback_;
402 }
403
404 const UUID& LowEnergyClient::GetAppIdentifier() const {
405   return app_identifier_;
406 }
407
408 int LowEnergyClient::GetInstanceId() const {
409   return client_id_;
410 }
411
412 void LowEnergyClient::MultiAdvEnableCallback(
413     hal::BluetoothGattInterface* gatt_iface,
414     int client_id, int status) {
415   if (client_id != client_id_)
416     return;
417
418   lock_guard<mutex> lock(adv_fields_lock_);
419
420   VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
421
422   CHECK(adv_start_callback_);
423   CHECK(!adv_stop_callback_);
424
425   // Terminate operation in case of error.
426   if (status != BT_STATUS_SUCCESS) {
427     LOG(ERROR) << "Failed to enable multi-advertising";
428     InvokeAndClearStartCallback(GetBLEStatus(status));
429     return;
430   }
431
432   // Now handle deferred tasks.
433   HandleDeferredAdvertiseData(gatt_iface);
434 }
435
436 void LowEnergyClient::MultiAdvDataCallback(
437     hal::BluetoothGattInterface* gatt_iface,
438     int client_id, int status) {
439   if (client_id != client_id_)
440     return;
441
442   lock_guard<mutex> lock(adv_fields_lock_);
443
444   VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
445
446   is_setting_adv_data_ = false;
447
448   // Terminate operation in case of error.
449   if (status != BT_STATUS_SUCCESS) {
450     LOG(ERROR) << "Failed to set advertising data";
451     InvokeAndClearStartCallback(GetBLEStatus(status));
452     return;
453   }
454
455   // Now handle deferred tasks.
456   HandleDeferredAdvertiseData(gatt_iface);
457 }
458
459 void LowEnergyClient::MultiAdvDisableCallback(
460     hal::BluetoothGattInterface* /* gatt_iface */,
461     int client_id, int status) {
462   if (client_id != client_id_)
463     return;
464
465   lock_guard<mutex> lock(adv_fields_lock_);
466
467   VLOG(1) << __func__ << "client_id: " << client_id << " status: " << status;
468
469   CHECK(!adv_start_callback_);
470   CHECK(adv_stop_callback_);
471
472   if (status == BT_STATUS_SUCCESS) {
473     VLOG(1) << "Multi-advertising stopped for client_id: " << client_id;
474     adv_started_ = false;
475   } else {
476     LOG(ERROR) << "Failed to stop multi-advertising";
477   }
478
479   InvokeAndClearStopCallback(GetBLEStatus(status));
480 }
481
482 bt_status_t LowEnergyClient::SetAdvertiseData(
483     hal::BluetoothGattInterface* gatt_iface,
484     const AdvertiseData& data,
485     bool set_scan_rsp) {
486   VLOG(2) << __func__;
487
488   HALAdvertiseData hal_data;
489
490   // TODO(armansito): The stack should check that the length is valid when other
491   // fields inserted by the stack (e.g. flags, device name, tx-power) are taken
492   // into account. At the moment we are skipping this check; this means that if
493   // the given data is too long then the stack will truncate it.
494   if (!ProcessAdvertiseData(data, &hal_data)) {
495     LOG(ERROR) << "Malformed advertise data given";
496     return BT_STATUS_FAIL;
497   }
498
499   if (is_setting_adv_data_.load()) {
500     LOG(ERROR) << "Setting advertising data already in progress.";
501     return BT_STATUS_FAIL;
502   }
503
504   // TODO(armansito): The length fields in the BTIF function below are signed
505   // integers so a call to std::vector::size might get capped. This is very
506   // unlikely anyway but it's safer to stop using signed-integer types for
507   // length in APIs, so we should change that.
508   bt_status_t status = gatt_iface->GetClientHALInterface()->
509       multi_adv_set_inst_data(
510           client_id_,
511           set_scan_rsp,
512           data.include_device_name(),
513           data.include_tx_power_level(),
514           0,  // This is what Bluetooth.apk current hardcodes for "appearance".
515           hal_data.manufacturer_data.size(),
516           reinterpret_cast<char*>(hal_data.manufacturer_data.data()),
517           hal_data.service_data.size(),
518           reinterpret_cast<char*>(hal_data.service_data.data()),
519           hal_data.service_uuid.size(),
520           reinterpret_cast<char*>(hal_data.service_uuid.data()));
521
522   if (status != BT_STATUS_SUCCESS) {
523     LOG(ERROR) << "Failed to set instance advertising data.";
524     return status;
525   }
526
527   if (set_scan_rsp)
528     scan_rsp_needs_update_ = false;
529   else
530     adv_data_needs_update_ = false;
531
532   is_setting_adv_data_ = true;
533
534   return status;
535 }
536
537 void LowEnergyClient::HandleDeferredAdvertiseData(
538     hal::BluetoothGattInterface* gatt_iface) {
539   VLOG(2) << __func__;
540
541   CHECK(!IsAdvertisingStarted());
542   CHECK(!IsStoppingAdvertising());
543   CHECK(IsStartingAdvertising());
544   CHECK(!is_setting_adv_data_.load());
545
546   if (adv_data_needs_update_.load()) {
547     bt_status_t status = SetAdvertiseData(gatt_iface, adv_data_, false);
548     if (status != BT_STATUS_SUCCESS) {
549       LOG(ERROR) << "Failed setting advertisement data";
550       InvokeAndClearStartCallback(GetBLEStatus(status));
551     }
552     return;
553   }
554
555   if (scan_rsp_needs_update_.load()) {
556     bt_status_t status = SetAdvertiseData(gatt_iface, scan_response_, true);
557     if (status != BT_STATUS_SUCCESS) {
558       LOG(ERROR) << "Failed setting scan response data";
559       InvokeAndClearStartCallback(GetBLEStatus(status));
560     }
561     return;
562   }
563
564   // All pending tasks are complete. Report success.
565   adv_started_ = true;
566   InvokeAndClearStartCallback(BLE_STATUS_SUCCESS);
567 }
568
569 void LowEnergyClient::InvokeAndClearStartCallback(BLEStatus status) {
570   adv_data_needs_update_ = false;
571   scan_rsp_needs_update_ = false;
572
573   // We allow NULL callbacks.
574   if (*adv_start_callback_)
575     (*adv_start_callback_)(status);
576
577   adv_start_callback_ = nullptr;
578 }
579
580 void LowEnergyClient::InvokeAndClearStopCallback(BLEStatus status) {
581   // We allow NULL callbacks.
582   if (*adv_stop_callback_)
583     (*adv_stop_callback_)(status);
584
585   adv_stop_callback_ = nullptr;
586 }
587
588 // LowEnergyClientFactory implementation
589 // ========================================================
590
591 LowEnergyClientFactory::LowEnergyClientFactory() {
592   hal::BluetoothGattInterface::Get()->AddClientObserver(this);
593 }
594
595 LowEnergyClientFactory::~LowEnergyClientFactory() {
596   hal::BluetoothGattInterface::Get()->RemoveClientObserver(this);
597 }
598
599 bool LowEnergyClientFactory::RegisterInstance(
600     const UUID& uuid,
601     const RegisterCallback& callback) {
602   VLOG(1) << __func__ << " - UUID: " << uuid.ToString();
603   lock_guard<mutex> lock(pending_calls_lock_);
604
605   if (pending_calls_.find(uuid) != pending_calls_.end()) {
606     LOG(ERROR) << "Low-Energy client with given UUID already registered - "
607                << "UUID: " << uuid.ToString();
608     return false;
609   }
610
611   const btgatt_client_interface_t* hal_iface =
612       hal::BluetoothGattInterface::Get()->GetClientHALInterface();
613   bt_uuid_t app_uuid = uuid.GetBlueDroid();
614
615   if (hal_iface->register_client(&app_uuid) != BT_STATUS_SUCCESS)
616     return false;
617
618   pending_calls_[uuid] = callback;
619
620   return true;
621 }
622
623 void LowEnergyClientFactory::RegisterClientCallback(
624     hal::BluetoothGattInterface* gatt_iface,
625     int status, int client_id,
626     const bt_uuid_t& app_uuid) {
627   UUID uuid(app_uuid);
628
629   VLOG(1) << __func__ << " - UUID: " << uuid.ToString();
630   lock_guard<mutex> lock(pending_calls_lock_);
631
632   auto iter = pending_calls_.find(uuid);
633   if (iter == pending_calls_.end()) {
634     VLOG(1) << "Ignoring callback for unknown app_id: " << uuid.ToString();
635     return;
636   }
637
638   // No need to construct a client if the call wasn't successful.
639   std::unique_ptr<LowEnergyClient> client;
640   BLEStatus result = BLE_STATUS_FAILURE;
641   if (status == BT_STATUS_SUCCESS) {
642     client.reset(new LowEnergyClient(uuid, client_id));
643
644     // Use the unsafe variant to register this as an observer, since
645     // LowEnergyClient instances only get created by LowEnergyClientCallback
646     // from inside this GATT client observer event, which would otherwise cause
647     // a deadlock.
648     gatt_iface->AddClientObserverUnsafe(client.get());
649
650     result = BLE_STATUS_SUCCESS;
651   }
652
653   // Notify the result via the result callback.
654   iter->second(result, uuid, std::move(client));
655
656   pending_calls_.erase(iter);
657 }
658
659 }  // namespace bluetooth