1// 2// Copyright 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_advertiser.h" 18 19#include <base/bind.h> 20#include <base/logging.h> 21 22#include "service/adapter.h" 23#include "service/logging_helpers.h" 24#include "stack/include/bt_types.h" 25#include "stack/include/hcidefs.h" 26 27using std::lock_guard; 28using std::mutex; 29 30namespace bluetooth { 31 32namespace { 33 34BLEStatus GetBLEStatus(int status) { 35 if (status == BT_STATUS_FAIL) return BLE_STATUS_FAILURE; 36 37 return static_cast<BLEStatus>(status); 38} 39 40// The Bluetooth Core Specification defines time interval (e.g. Page Scan 41// Interval, Advertising Interval, etc) units as 0.625 milliseconds (or 1 42// Baseband slot). The HAL advertising functions expect the interval in this 43// unit. This function maps an AdvertiseSettings::Mode value to the 44// corresponding time unit. 45int GetAdvertisingIntervalUnit(AdvertiseSettings::Mode mode) { 46 int ms; 47 48 switch (mode) { 49 case AdvertiseSettings::MODE_BALANCED: 50 ms = kAdvertisingIntervalMediumMs; 51 break; 52 case AdvertiseSettings::MODE_LOW_LATENCY: 53 ms = kAdvertisingIntervalLowMs; 54 break; 55 case AdvertiseSettings::MODE_LOW_POWER: 56 // Fall through 57 default: 58 ms = kAdvertisingIntervalHighMs; 59 break; 60 } 61 62 // Convert milliseconds to Bluetooth units. 63 return (ms * 1000) / 625; 64} 65 66int8_t GetAdvertisingTxPower(AdvertiseSettings::TxPowerLevel tx_power) { 67 int8_t power; 68 69 switch (tx_power) { 70 case AdvertiseSettings::TX_POWER_LEVEL_ULTRA_LOW: 71 power = -21; 72 break; 73 case AdvertiseSettings::TX_POWER_LEVEL_LOW: 74 power = -15; 75 break; 76 case AdvertiseSettings::TX_POWER_LEVEL_MEDIUM: 77 power = -7; 78 break; 79 case AdvertiseSettings::TX_POWER_LEVEL_HIGH: 80 // Fall through 81 default: 82 power = 1; 83 break; 84 } 85 86 return power; 87} 88 89void GetAdvertiseParams(const AdvertiseSettings& settings, bool has_scan_rsp, 90 AdvertiseParameters* out_params) { 91 CHECK(out_params); 92 93 out_params->min_interval = GetAdvertisingIntervalUnit(settings.mode()); 94 out_params->max_interval = 95 out_params->min_interval + kAdvertisingIntervalDeltaUnit; 96 97 if (settings.connectable()) 98 out_params->advertising_event_properties = 99 kAdvertisingEventTypeLegacyConnectable; 100 else if (has_scan_rsp) 101 out_params->advertising_event_properties = 102 kAdvertisingEventTypeLegacyScannable; 103 else 104 out_params->advertising_event_properties = 105 kAdvertisingEventTypeLegacyNonConnectable; 106 107 out_params->channel_map = kAdvertisingChannelAll; 108 out_params->tx_power = GetAdvertisingTxPower(settings.tx_power_level()); 109 110 // TODO: expose those new setting through AdvertiseSettings 111 out_params->primary_advertising_phy = 0x01; 112 out_params->secondary_advertising_phy = 0x01; 113 out_params->scan_request_notification_enable = 0; 114} 115 116void DoNothing(uint8_t status) {} 117 118} // namespace 119 120// LowEnergyAdvertiser implementation 121// ======================================================== 122 123LowEnergyAdvertiser::LowEnergyAdvertiser(const Uuid& uuid, int advertiser_id) 124 : app_identifier_(uuid), 125 advertiser_id_(advertiser_id), 126 adv_started_(false), 127 adv_start_callback_(nullptr), 128 adv_stop_callback_(nullptr) {} 129 130LowEnergyAdvertiser::~LowEnergyAdvertiser() { 131 // Automatically unregister the advertiser. 132 VLOG(1) << "LowEnergyAdvertiser unregistering advertiser: " << advertiser_id_; 133 134 // Stop advertising and ignore the result. 135 hal::BluetoothGattInterface::Get()->GetAdvertiserHALInterface()->Enable( 136 advertiser_id_, false, base::Bind(&DoNothing), 0, 0, 137 base::Bind(&DoNothing)); 138 hal::BluetoothGattInterface::Get()->GetAdvertiserHALInterface()->Unregister( 139 advertiser_id_); 140} 141 142bool LowEnergyAdvertiser::StartAdvertising(const AdvertiseSettings& settings, 143 const AdvertiseData& advertise_data, 144 const AdvertiseData& scan_response, 145 const StatusCallback& callback) { 146 VLOG(2) << __func__; 147 lock_guard<mutex> lock(adv_fields_lock_); 148 149 if (IsAdvertisingStarted()) { 150 LOG(WARNING) << "Already advertising"; 151 return false; 152 } 153 154 if (IsStartingAdvertising()) { 155 LOG(WARNING) << "StartAdvertising already pending"; 156 return false; 157 } 158 159 if (!advertise_data.IsValid()) { 160 LOG(ERROR) << "Invalid advertising data"; 161 return false; 162 } 163 164 if (!scan_response.IsValid()) { 165 LOG(ERROR) << "Invalid scan response data"; 166 return false; 167 } 168 169 advertise_settings_ = settings; 170 171 AdvertiseParameters params; 172 GetAdvertiseParams(settings, !scan_response.data().empty(), ¶ms); 173 174 hal::BluetoothGattInterface::Get() 175 ->GetAdvertiserHALInterface() 176 ->StartAdvertising( 177 advertiser_id_, 178 base::Bind(&LowEnergyAdvertiser::EnableCallback, 179 base::Unretained(this), true, advertiser_id_), 180 params, advertise_data.data(), scan_response.data(), 181 settings.timeout().InSeconds(), 182 base::Bind(&LowEnergyAdvertiser::EnableCallback, 183 base::Unretained(this), false, advertiser_id_)); 184 ; 185 186 adv_start_callback_.reset(new StatusCallback(callback)); 187 return true; 188} 189 190bool LowEnergyAdvertiser::StopAdvertising(const StatusCallback& callback) { 191 VLOG(2) << __func__; 192 lock_guard<mutex> lock(adv_fields_lock_); 193 194 if (!IsAdvertisingStarted()) { 195 LOG(ERROR) << "Not advertising"; 196 return false; 197 } 198 199 if (IsStoppingAdvertising()) { 200 LOG(ERROR) << "StopAdvertising already pending"; 201 return false; 202 } 203 204 hal::BluetoothGattInterface::Get()->GetAdvertiserHALInterface()->Enable( 205 advertiser_id_, false, 206 base::Bind(&LowEnergyAdvertiser::EnableCallback, base::Unretained(this), 207 false, advertiser_id_), 208 0, 0, base::Bind(&LowEnergyAdvertiser::EnableCallback, 209 base::Unretained(this), false, advertiser_id_)); 210 211 // OK to set this at the end since we're still holding |adv_fields_lock_|. 212 adv_stop_callback_.reset(new StatusCallback(callback)); 213 214 return true; 215} 216 217bool LowEnergyAdvertiser::IsAdvertisingStarted() const { 218 return adv_started_.load(); 219} 220 221bool LowEnergyAdvertiser::IsStartingAdvertising() const { 222 return !IsAdvertisingStarted() && adv_start_callback_; 223} 224 225bool LowEnergyAdvertiser::IsStoppingAdvertising() const { 226 return IsAdvertisingStarted() && adv_stop_callback_; 227} 228 229const Uuid& LowEnergyAdvertiser::GetAppIdentifier() const { 230 return app_identifier_; 231} 232 233int LowEnergyAdvertiser::GetInstanceId() const { return advertiser_id_; } 234 235void LowEnergyAdvertiser::EnableCallback(bool enable, uint8_t advertiser_id, 236 uint8_t status) { 237 if (advertiser_id != advertiser_id_) return; 238 239 lock_guard<mutex> lock(adv_fields_lock_); 240 241 VLOG(1) << __func__ << "advertiser_id: " << advertiser_id 242 << " status: " << status << " enable: " << enable; 243 244 if (enable) { 245 CHECK(adv_start_callback_); 246 CHECK(!adv_stop_callback_); 247 248 // Terminate operation in case of error. 249 if (status != BT_STATUS_SUCCESS) { 250 LOG(ERROR) << "Failed to enable multi-advertising"; 251 InvokeAndClearStartCallback(GetBLEStatus(status)); 252 return; 253 } 254 255 // All pending tasks are complete. Report success. 256 adv_started_ = true; 257 InvokeAndClearStartCallback(BLE_STATUS_SUCCESS); 258 259 } else { 260 CHECK(!adv_start_callback_); 261 CHECK(adv_stop_callback_); 262 263 if (status == BT_STATUS_SUCCESS) { 264 VLOG(1) << "Multi-advertising stopped for advertiser_id: " 265 << advertiser_id; 266 adv_started_ = false; 267 } else { 268 LOG(ERROR) << "Failed to stop multi-advertising"; 269 } 270 271 InvokeAndClearStopCallback(GetBLEStatus(status)); 272 } 273} 274 275void LowEnergyAdvertiser::InvokeAndClearStartCallback(BLEStatus status) { 276 // We allow NULL callbacks. 277 if (*adv_start_callback_) (*adv_start_callback_)(status); 278 279 adv_start_callback_ = nullptr; 280} 281 282void LowEnergyAdvertiser::InvokeAndClearStopCallback(BLEStatus status) { 283 // We allow NULL callbacks. 284 if (*adv_stop_callback_) (*adv_stop_callback_)(status); 285 286 adv_stop_callback_ = nullptr; 287} 288 289// LowEnergyAdvertiserFactory implementation 290// ======================================================== 291 292LowEnergyAdvertiserFactory::LowEnergyAdvertiserFactory() {} 293 294LowEnergyAdvertiserFactory::~LowEnergyAdvertiserFactory() {} 295 296bool LowEnergyAdvertiserFactory::RegisterInstance( 297 const Uuid& app_uuid, const RegisterCallback& callback) { 298 VLOG(1) << __func__; 299 lock_guard<mutex> lock(pending_calls_lock_); 300 301 if (pending_calls_.find(app_uuid) != pending_calls_.end()) { 302 LOG(ERROR) << "Low-Energy advertiser with given Uuid already registered - " 303 << "Uuid: " << app_uuid.ToString(); 304 return false; 305 } 306 307 BleAdvertiserInterface* hal_iface = 308 hal::BluetoothGattInterface::Get()->GetAdvertiserHALInterface(); 309 310 VLOG(1) << __func__ << " calling register!"; 311 hal_iface->RegisterAdvertiser( 312 base::Bind(&LowEnergyAdvertiserFactory::RegisterAdvertiserCallback, 313 base::Unretained(this), callback, app_uuid)); 314 VLOG(1) << __func__ << " call finished!"; 315 316 pending_calls_.insert(app_uuid); 317 318 return true; 319} 320 321void LowEnergyAdvertiserFactory::RegisterAdvertiserCallback( 322 const RegisterCallback& callback, const Uuid& app_uuid, 323 uint8_t advertiser_id, uint8_t status) { 324 VLOG(1) << __func__; 325 lock_guard<mutex> lock(pending_calls_lock_); 326 327 auto iter = pending_calls_.find(app_uuid); 328 if (iter == pending_calls_.end()) { 329 VLOG(1) << "Ignoring callback for unknown app_id: " << app_uuid.ToString(); 330 return; 331 } 332 333 // No need to construct a advertiser if the call wasn't successful. 334 std::unique_ptr<LowEnergyAdvertiser> advertiser; 335 BLEStatus result = BLE_STATUS_FAILURE; 336 if (status == BT_STATUS_SUCCESS) { 337 advertiser.reset(new LowEnergyAdvertiser(app_uuid, advertiser_id)); 338 339 result = BLE_STATUS_SUCCESS; 340 } 341 342 // Notify the result via the result callback. 343 callback(result, app_uuid, std::move(advertiser)); 344 345 pending_calls_.erase(iter); 346} 347 348} // namespace bluetooth 349