| 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 "base/bind.h" |
| 6 #include "base/threading/thread_task_runner_handle.h" |
| 7 #include "base/time/time.h" |
| 8 #include "components/proximity_auth/ble/bluetooth_low_energy_weave_server.h" |
| 9 #include "device/bluetooth/bluetooth_adapter_factory.h" |
| 10 #include "device/bluetooth/bluetooth_uuid.h" |
| 11 |
| 12 namespace proximity_auth { |
| 13 namespace weave { |
| 14 namespace { |
| 15 |
| 16 typedef BluetoothLocalGattCharacteristic::Property Property; |
| 17 typedef device::BluetoothUUID BluetoothUUID; |
| 18 |
| 19 const uint32_t kAdvertisementRotationPeriodInSecs = 5; |
| 20 const uint32_t kEidRotationPeriodInHours = 8; |
| 21 const uint32_t kEidSeedRotationPeriodInDays = 14; |
| 22 // TODO(jingxuy): find out the right UUID |
| 23 // The UUID of the BLE service that host the RX and TX characteristics. |
| 24 const char kWeaveServiceUUID[] = "00000100-0004-1000-8000-001A11000100"; |
| 25 const char kRXCharacteristicUUID[] = "00000100-0004-1000-8000-001A11000101"; |
| 26 const char kTXCharacteristicUUID[] = "00000100-0004-1000-8000-001A11000102"; |
| 27 |
| 28 // TODO(jingxuy): verify these properties, currently just a draft version. |
| 29 const uint32_t kRXCharacteristicProperties = |
| 30 Property::PROPERTY_NOTIFY | Property::PROPERTY_READ; |
| 31 |
| 32 const uint32_t kTXCharacteristicProperties = Property::PROPERTY_WRITE; |
| 33 |
| 34 const uint32_t kCharacteristicPermissions = |
| 35 BluetoothLocalGattCharacteristic::Permission::PERMISSION_NONE; |
| 36 |
| 37 } // namespace |
| 38 |
| 39 // static. |
| 40 BluetoothLowEnergyWeaveServer::Factory* |
| 41 BluetoothLowEnergyWeaveServer::Factory::factory_instance_ = nullptr; |
| 42 |
| 43 // static. |
| 44 std::unique_ptr<BluetoothLowEnergyWeaveServer> |
| 45 BluetoothLowEnergyWeaveServer::Factory::NewInstance() { |
| 46 if (factory_instance_ == nullptr) { |
| 47 factory_instance_ = new Factory(); |
| 48 } |
| 49 return factory_instance_->BuildInstance(); |
| 50 } |
| 51 |
| 52 // static. |
| 53 void BluetoothLowEnergyWeaveServer::Factory::SetInstanceForTesting( |
| 54 Factory* factory) { |
| 55 factory_instance_ = factory; |
| 56 } |
| 57 |
| 58 std::unique_ptr<BluetoothLowEnergyWeaveServer> |
| 59 BluetoothLowEnergyWeaveServer::Factory::BuildInstance() { |
| 60 return std::unique_ptr<BluetoothLowEnergyWeaveServer>( |
| 61 new BluetoothLowEnergyWeaveServer()); |
| 62 } |
| 63 |
| 64 BluetoothLowEnergyWeaveServer::BluetoothLowEnergyWeaveServer() |
| 65 : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 66 ad_rotator_(BluetoothLowEnergyAdvertisementRotator::Factory::NewInstance( |
| 67 kWeaveServiceUUID)), |
| 68 weak_ptr_factory_(this) { |
| 69 adapter_ = |
| 70 BluetoothAdapter::CreateAdapter( |
| 71 base::Bind(&BluetoothLowEnergyWeaveServer::CreateAdapterCallback, |
| 72 weak_ptr_factory_.GetWeakPtr())) |
| 73 .get(); |
| 74 service_ = BluetoothLocalGattService::Create( |
| 75 adapter_.get(), BluetoothUUID(kWeaveServiceUUID), true, nullptr, this); |
| 76 rx_characteristic_ = BluetoothLocalGattCharacteristic::Create( |
| 77 BluetoothUUID(kRXCharacteristicUUID), kRXCharacteristicProperties, |
| 78 kCharacteristicPermissions, service_.get()); |
| 79 tx_characteristic_ = BluetoothLocalGattCharacteristic::Create( |
| 80 BluetoothUUID(kTXCharacteristicUUID), kTXCharacteristicProperties, |
| 81 kCharacteristicPermissions, service_.get()); |
| 82 } |
| 83 |
| 84 void BluetoothLowEnergyWeaveServer::CreateAdapterCallback() {} |
| 85 |
| 86 void BluetoothLowEnergyWeaveServer::OnCharacteristicReadRequest( |
| 87 const BluetoothDevice* bluetooth_device, |
| 88 const BluetoothLocalGattCharacteristic* characteristic, |
| 89 int offset, |
| 90 const ValueCallback& callback, |
| 91 const ErrorCallback& error_callback) { |
| 92 // Only care about the read on the rx_characteristic from registered devices. |
| 93 if (characteristic != rx_characteristic_.get() || |
| 94 !HasDevice(bluetooth_device->GetAddress())) { |
| 95 return; |
| 96 } |
| 97 |
| 98 // TODO(jingxuy): Do we even need to support this function? |
| 99 // I thought the client only gets informed of rx_characteristics changes and |
| 100 // wont't starting actively reading the value. |
| 101 } |
| 102 |
| 103 void BluetoothLowEnergyWeaveServer::OnCharacteristicWriteRequest( |
| 104 const BluetoothDevice* bluetooth_device, |
| 105 const BluetoothLocalGattCharacteristic* characteristic, |
| 106 const Packet& value, |
| 107 int offset, |
| 108 const base::Closure& callback, |
| 109 const ErrorCallback& error_callback) { |
| 110 // Only care about the writes on the tx_characteristic from registered |
| 111 // devices. |
| 112 if (characteristic != tx_characteristic_.get() || |
| 113 !HasDevice(bluetooth_device->GetAddress())) { |
| 114 return; |
| 115 } |
| 116 |
| 117 auto it = map_to_server_connection_.find(bluetooth_device->GetAddress()); |
| 118 |
| 119 // Device is registered but it's not connected. |
| 120 if (it == map_to_server_connection_.end()) |
| 121 return; |
| 122 |
| 123 // TODO(jingxuy): do I need to care about offset? |
| 124 if (it->second->ReceivePacket(value)) { |
| 125 callback.Run(); |
| 126 } else { |
| 127 error_callback.Run(); |
| 128 } |
| 129 } |
| 130 |
| 131 void BluetoothLowEnergyWeaveServer::OnNotificationsStart( |
| 132 const BluetoothDevice* bluetooth_device, |
| 133 const BluetoothLocalGattCharacteristic* characteristic) { |
| 134 if (characteristic != rx_characteristic_.get() || |
| 135 !HasDevice(bluetooth_device->GetAddress())) { |
| 136 return; |
| 137 } |
| 138 |
| 139 auto it = map_to_server_connection_.find(bluetooth_device->GetAddress()); |
| 140 |
| 141 if (it == map_to_server_connection_.end()) { |
| 142 std::unique_ptr<BluetoothLowEnergyWeaveServerConnection> connection( |
| 143 new BluetoothLowEnergyWeaveServerConnection( |
| 144 bluetooth_device, weak_ptr_factory_.GetWeakPtr(), |
| 145 rx_characteristic_)); |
| 146 |
| 147 map_to_server_connection_.insert( |
| 148 std::make_pair(bluetooth_device->GetAddress(), std::move(connection))); |
| 149 |
| 150 } else if (it->second->status() == ConnectionStatus::DISCONNECTED) { |
| 151 it->second->Connect(); |
| 152 } |
| 153 } |
| 154 |
| 155 void BluetoothLowEnergyWeaveServer::OnNotificationsStop( |
| 156 const BluetoothDevice* bluetooth_device, |
| 157 const BluetoothLocalGattCharacteristic* characteristic) { |
| 158 if (characteristic != rx_characteristic_.get() || |
| 159 !HasDevice(bluetooth_device->GetAddress())) { |
| 160 return; |
| 161 } |
| 162 |
| 163 auto it = map_to_server_connection_.find(bluetooth_device->GetAddress()); |
| 164 |
| 165 // Getting notification stop when it never started! |
| 166 if (it == map_to_server_connection_.end()) { |
| 167 return; |
| 168 } |
| 169 |
| 170 if (it->second->status() == ConnectionStatus::CONNECTED) { |
| 171 it->second->Connect(); |
| 172 } |
| 173 } |
| 174 |
| 175 void BluetoothLowEnergyWeaveServer::RegisterDevice( |
| 176 const RemoteDevice& remote_device) { |
| 177 map_to_remote_device_[remote_device.bluetooth_address] = remote_device; |
| 178 ad_rotator_->AddDevice(remote_device); |
| 179 } |
| 180 |
| 181 bool BluetoothLowEnergyWeaveServer::HasDevice(std::string device_address) { |
| 182 return map_to_remote_device_.find(device_address) != |
| 183 map_to_remote_device_.end(); |
| 184 } |
| 185 |
| 186 const RemoteDevice& BluetoothLowEnergyWeaveServer::GetRemoteDevice( |
| 187 const BluetoothDevice* bluetooth_device) { |
| 188 DCHECK(HasDevice(bluetooth_device->GetAddress())); |
| 189 return map_to_remote_device_[bluetooth_device->GetAddress()]; |
| 190 } |
| 191 |
| 192 void BluetoothLowEnergyWeaveServer::StartAdvertising( |
| 193 const RemoteDevice& remote_device) { |
| 194 DCHECK(HasDevice(remote_device.bluetooth_address) || |
| 195 !ad_rotator_->HasDeviceWithAddress(remote_device.bluetooth_address)); |
| 196 |
| 197 ad_rotator_->AddDevice(remote_device); |
| 198 } |
| 199 |
| 200 void BluetoothLowEnergyWeaveServer::StopAdvertising( |
| 201 const RemoteDevice& remote_device) { |
| 202 DCHECK(HasDevice(remote_device.bluetooth_address) || |
| 203 ad_rotator_->HasDeviceWithAddress(remote_device.bluetooth_address)); |
| 204 |
| 205 ad_rotator_->RemoveDevice(remote_device); |
| 206 |
| 207 if (map_to_advertisement_.find(remote_device.bluetooth_address) != |
| 208 map_to_advertisement_.end()) { |
| 209 UnregisterAdvertisement(remote_device.bluetooth_address); |
| 210 } |
| 211 } |
| 212 |
| 213 void BluetoothLowEnergyWeaveServer::SetTaskRunnerForTesting( |
| 214 scoped_refptr<base::TaskRunner> task_runner) { |
| 215 task_runner_ = task_runner; |
| 216 } |
| 217 |
| 218 void BluetoothLowEnergyWeaveServer::UpdateEidSeeds() { |
| 219 RefreshAdvertisement(); |
| 220 // TODO (jingxuy): make this follow 14 day boundaries. |
| 221 base::TimeDelta wait_time = |
| 222 base::TimeDelta::FromDays(kEidSeedRotationPeriodInDays); |
| 223 task_runner_->PostDelayedTask( |
| 224 FROM_HERE, base::Bind(&BluetoothLowEnergyWeaveServer::UpdateEidSeeds, |
| 225 weak_ptr_factory_.GetWeakPtr()), |
| 226 wait_time); |
| 227 } |
| 228 |
| 229 void BluetoothLowEnergyWeaveServer::UpdateEids() { |
| 230 RefreshAdvertisement(); |
| 231 // TODO(jingxuy): make this follow 8 hour boundaries. |
| 232 base::TimeDelta wait_time = |
| 233 base::TimeDelta::FromHours(kEidRotationPeriodInHours); |
| 234 task_runner_->PostDelayedTask( |
| 235 FROM_HERE, base::Bind(&BluetoothLowEnergyWeaveServer::UpdateEids, |
| 236 weak_ptr_factory_.GetWeakPtr()), |
| 237 wait_time); |
| 238 } |
| 239 |
| 240 void BluetoothLowEnergyWeaveServer::RefreshAdvertisement() { |
| 241 // Can't change content of current advertisement. |
| 242 // Hence unregister all current advertisement and put them on the top of the |
| 243 // advertising queue and retry advertising them again. |
| 244 // Unless some other application come in between the process and steal the |
| 245 // advertisement quota, we can still advertise everything that's unregistered. |
| 246 for (auto const& it : map_to_advertisement_) { |
| 247 UnregisterAdvertisement(it.first); |
| 248 ad_rotator_->CutAdvertisementLine(it.first); |
| 249 } |
| 250 Advertise(); |
| 251 } |
| 252 |
| 253 void BluetoothLowEnergyWeaveServer::RotateAdvertisement() { |
| 254 for (auto const& it : map_to_advertisement_) { |
| 255 UnregisterAdvertisement(it.first); |
| 256 } |
| 257 |
| 258 Advertise(); |
| 259 base::TimeDelta wait_time = |
| 260 base::TimeDelta::FromSeconds(kAdvertisementRotationPeriodInSecs); |
| 261 |
| 262 task_runner_->PostDelayedTask( |
| 263 FROM_HERE, base::Bind(&BluetoothLowEnergyWeaveServer::RotateAdvertisement, |
| 264 weak_ptr_factory_.GetWeakPtr()), |
| 265 wait_time); |
| 266 } |
| 267 |
| 268 void BluetoothLowEnergyWeaveServer::Advertise() { |
| 269 std::pair<std::string, std::unique_ptr<Advertisement>> ad = |
| 270 ad_rotator_->GetNextAdvertisement(); |
| 271 adapter_->RegisterAdvertisement( |
| 272 std::move(ad.second), |
| 273 base::Bind(&BluetoothLowEnergyWeaveServer::RegisterAdvertisementSuccess, |
| 274 weak_ptr_factory_.GetWeakPtr(), ad.first), |
| 275 base::Bind(&BluetoothLowEnergyWeaveServer::RegisterAdvertisementError, |
| 276 weak_ptr_factory_.GetWeakPtr())); |
| 277 } |
| 278 |
| 279 void BluetoothLowEnergyWeaveServer::UnregisterAdvertisement( |
| 280 std::string bluetooth_address) { |
| 281 DCHECK(map_to_advertisement_.find(bluetooth_address) != |
| 282 map_to_advertisement_.end()); |
| 283 map_to_advertisement_[bluetooth_address]->Unregister( |
| 284 base::Bind(&BluetoothLowEnergyWeaveServer::UnregisterAdvertisementSuccess, |
| 285 weak_ptr_factory_.GetWeakPtr(), bluetooth_address), |
| 286 base::Bind(&BluetoothLowEnergyWeaveServer::UnregisterAdvertisementError, |
| 287 weak_ptr_factory_.GetWeakPtr(), bluetooth_address)); |
| 288 } |
| 289 |
| 290 void BluetoothLowEnergyWeaveServer::UnregisterAdvertisementSuccess( |
| 291 std::string bluetooth_address) { |
| 292 map_to_advertisement_.erase(bluetooth_address); |
| 293 } |
| 294 |
| 295 void BluetoothLowEnergyWeaveServer::UnregisterAdvertisementError( |
| 296 std::string bluetooth_address, |
| 297 BluetoothAdvertisement::ErrorCode error) { |
| 298 DCHECK(error == |
| 299 BluetoothAdvertisement::ErrorCode::ERROR_ADVERTISEMENT_DOES_NOT_EXIST); |
| 300 // The only way the unregister could fail is if the advertisement is not |
| 301 // registered to begin with. Which means in our use case it should never fail. |
| 302 // Hence remove the advertisement from the map. |
| 303 map_to_advertisement_.erase(bluetooth_address); |
| 304 } |
| 305 |
| 306 void BluetoothLowEnergyWeaveServer::RegisterAdvertisementSuccess( |
| 307 std::string bluetooth_address, |
| 308 scoped_refptr<BluetoothAdvertisement> advertisement) { |
| 309 map_to_advertisement_[bluetooth_address] = advertisement; |
| 310 ad_rotator_->RotateAdvertisement(); |
| 311 // Keep trying to advertise until failures. |
| 312 Advertise(); |
| 313 } |
| 314 |
| 315 void BluetoothLowEnergyWeaveServer::RegisterAdvertisementError( |
| 316 BluetoothAdvertisement::ErrorCode error) {} |
| 317 |
| 318 } // namespace weave |
| 319 |
| 320 } // namespace |
| OLD | NEW |