| Index: components/proximity_auth/ble/bluetooth_low_energy_weave_server.cc
|
| diff --git a/components/proximity_auth/ble/bluetooth_low_energy_weave_server.cc b/components/proximity_auth/ble/bluetooth_low_energy_weave_server.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0fa52485fa79ebf011356cccbeba630a16c1393a
|
| --- /dev/null
|
| +++ b/components/proximity_auth/ble/bluetooth_low_energy_weave_server.cc
|
| @@ -0,0 +1,320 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "base/time/time.h"
|
| +#include "components/proximity_auth/ble/bluetooth_low_energy_weave_server.h"
|
| +#include "device/bluetooth/bluetooth_adapter_factory.h"
|
| +#include "device/bluetooth/bluetooth_uuid.h"
|
| +
|
| +namespace proximity_auth {
|
| +namespace weave {
|
| +namespace {
|
| +
|
| +typedef BluetoothLocalGattCharacteristic::Property Property;
|
| +typedef device::BluetoothUUID BluetoothUUID;
|
| +
|
| +const uint32_t kAdvertisementRotationPeriodInSecs = 5;
|
| +const uint32_t kEidRotationPeriodInHours = 8;
|
| +const uint32_t kEidSeedRotationPeriodInDays = 14;
|
| +// TODO(jingxuy): find out the right UUID
|
| +// The UUID of the BLE service that host the RX and TX characteristics.
|
| +const char kWeaveServiceUUID[] = "00000100-0004-1000-8000-001A11000100";
|
| +const char kRXCharacteristicUUID[] = "00000100-0004-1000-8000-001A11000101";
|
| +const char kTXCharacteristicUUID[] = "00000100-0004-1000-8000-001A11000102";
|
| +
|
| +// TODO(jingxuy): verify these properties, currently just a draft version.
|
| +const uint32_t kRXCharacteristicProperties =
|
| + Property::PROPERTY_NOTIFY | Property::PROPERTY_READ;
|
| +
|
| +const uint32_t kTXCharacteristicProperties = Property::PROPERTY_WRITE;
|
| +
|
| +const uint32_t kCharacteristicPermissions =
|
| + BluetoothLocalGattCharacteristic::Permission::PERMISSION_NONE;
|
| +
|
| +} // namespace
|
| +
|
| +// static.
|
| +BluetoothLowEnergyWeaveServer::Factory*
|
| + BluetoothLowEnergyWeaveServer::Factory::factory_instance_ = nullptr;
|
| +
|
| +// static.
|
| +std::unique_ptr<BluetoothLowEnergyWeaveServer>
|
| +BluetoothLowEnergyWeaveServer::Factory::NewInstance() {
|
| + if (factory_instance_ == nullptr) {
|
| + factory_instance_ = new Factory();
|
| + }
|
| + return factory_instance_->BuildInstance();
|
| +}
|
| +
|
| +// static.
|
| +void BluetoothLowEnergyWeaveServer::Factory::SetInstanceForTesting(
|
| + Factory* factory) {
|
| + factory_instance_ = factory;
|
| +}
|
| +
|
| +std::unique_ptr<BluetoothLowEnergyWeaveServer>
|
| +BluetoothLowEnergyWeaveServer::Factory::BuildInstance() {
|
| + return std::unique_ptr<BluetoothLowEnergyWeaveServer>(
|
| + new BluetoothLowEnergyWeaveServer());
|
| +}
|
| +
|
| +BluetoothLowEnergyWeaveServer::BluetoothLowEnergyWeaveServer()
|
| + : task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| + ad_rotator_(BluetoothLowEnergyAdvertisementRotator::Factory::NewInstance(
|
| + kWeaveServiceUUID)),
|
| + weak_ptr_factory_(this) {
|
| + adapter_ =
|
| + BluetoothAdapter::CreateAdapter(
|
| + base::Bind(&BluetoothLowEnergyWeaveServer::CreateAdapterCallback,
|
| + weak_ptr_factory_.GetWeakPtr()))
|
| + .get();
|
| + service_ = BluetoothLocalGattService::Create(
|
| + adapter_.get(), BluetoothUUID(kWeaveServiceUUID), true, nullptr, this);
|
| + rx_characteristic_ = BluetoothLocalGattCharacteristic::Create(
|
| + BluetoothUUID(kRXCharacteristicUUID), kRXCharacteristicProperties,
|
| + kCharacteristicPermissions, service_.get());
|
| + tx_characteristic_ = BluetoothLocalGattCharacteristic::Create(
|
| + BluetoothUUID(kTXCharacteristicUUID), kTXCharacteristicProperties,
|
| + kCharacteristicPermissions, service_.get());
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::CreateAdapterCallback() {}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::OnCharacteristicReadRequest(
|
| + const BluetoothDevice* bluetooth_device,
|
| + const BluetoothLocalGattCharacteristic* characteristic,
|
| + int offset,
|
| + const ValueCallback& callback,
|
| + const ErrorCallback& error_callback) {
|
| + // Only care about the read on the rx_characteristic from registered devices.
|
| + if (characteristic != rx_characteristic_.get() ||
|
| + !HasDevice(bluetooth_device->GetAddress())) {
|
| + return;
|
| + }
|
| +
|
| + // TODO(jingxuy): Do we even need to support this function?
|
| + // I thought the client only gets informed of rx_characteristics changes and
|
| + // wont't starting actively reading the value.
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::OnCharacteristicWriteRequest(
|
| + const BluetoothDevice* bluetooth_device,
|
| + const BluetoothLocalGattCharacteristic* characteristic,
|
| + const Packet& value,
|
| + int offset,
|
| + const base::Closure& callback,
|
| + const ErrorCallback& error_callback) {
|
| + // Only care about the writes on the tx_characteristic from registered
|
| + // devices.
|
| + if (characteristic != tx_characteristic_.get() ||
|
| + !HasDevice(bluetooth_device->GetAddress())) {
|
| + return;
|
| + }
|
| +
|
| + auto it = map_to_server_connection_.find(bluetooth_device->GetAddress());
|
| +
|
| + // Device is registered but it's not connected.
|
| + if (it == map_to_server_connection_.end())
|
| + return;
|
| +
|
| + // TODO(jingxuy): do I need to care about offset?
|
| + if (it->second->ReceivePacket(value)) {
|
| + callback.Run();
|
| + } else {
|
| + error_callback.Run();
|
| + }
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::OnNotificationsStart(
|
| + const BluetoothDevice* bluetooth_device,
|
| + const BluetoothLocalGattCharacteristic* characteristic) {
|
| + if (characteristic != rx_characteristic_.get() ||
|
| + !HasDevice(bluetooth_device->GetAddress())) {
|
| + return;
|
| + }
|
| +
|
| + auto it = map_to_server_connection_.find(bluetooth_device->GetAddress());
|
| +
|
| + if (it == map_to_server_connection_.end()) {
|
| + std::unique_ptr<BluetoothLowEnergyWeaveServerConnection> connection(
|
| + new BluetoothLowEnergyWeaveServerConnection(
|
| + bluetooth_device, weak_ptr_factory_.GetWeakPtr(),
|
| + rx_characteristic_));
|
| +
|
| + map_to_server_connection_.insert(
|
| + std::make_pair(bluetooth_device->GetAddress(), std::move(connection)));
|
| +
|
| + } else if (it->second->status() == ConnectionStatus::DISCONNECTED) {
|
| + it->second->Connect();
|
| + }
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::OnNotificationsStop(
|
| + const BluetoothDevice* bluetooth_device,
|
| + const BluetoothLocalGattCharacteristic* characteristic) {
|
| + if (characteristic != rx_characteristic_.get() ||
|
| + !HasDevice(bluetooth_device->GetAddress())) {
|
| + return;
|
| + }
|
| +
|
| + auto it = map_to_server_connection_.find(bluetooth_device->GetAddress());
|
| +
|
| + // Getting notification stop when it never started!
|
| + if (it == map_to_server_connection_.end()) {
|
| + return;
|
| + }
|
| +
|
| + if (it->second->status() == ConnectionStatus::CONNECTED) {
|
| + it->second->Connect();
|
| + }
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::RegisterDevice(
|
| + const RemoteDevice& remote_device) {
|
| + map_to_remote_device_[remote_device.bluetooth_address] = remote_device;
|
| + ad_rotator_->AddDevice(remote_device);
|
| +}
|
| +
|
| +bool BluetoothLowEnergyWeaveServer::HasDevice(std::string device_address) {
|
| + return map_to_remote_device_.find(device_address) !=
|
| + map_to_remote_device_.end();
|
| +}
|
| +
|
| +const RemoteDevice& BluetoothLowEnergyWeaveServer::GetRemoteDevice(
|
| + const BluetoothDevice* bluetooth_device) {
|
| + DCHECK(HasDevice(bluetooth_device->GetAddress()));
|
| + return map_to_remote_device_[bluetooth_device->GetAddress()];
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::StartAdvertising(
|
| + const RemoteDevice& remote_device) {
|
| + DCHECK(HasDevice(remote_device.bluetooth_address) ||
|
| + !ad_rotator_->HasDeviceWithAddress(remote_device.bluetooth_address));
|
| +
|
| + ad_rotator_->AddDevice(remote_device);
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::StopAdvertising(
|
| + const RemoteDevice& remote_device) {
|
| + DCHECK(HasDevice(remote_device.bluetooth_address) ||
|
| + ad_rotator_->HasDeviceWithAddress(remote_device.bluetooth_address));
|
| +
|
| + ad_rotator_->RemoveDevice(remote_device);
|
| +
|
| + if (map_to_advertisement_.find(remote_device.bluetooth_address) !=
|
| + map_to_advertisement_.end()) {
|
| + UnregisterAdvertisement(remote_device.bluetooth_address);
|
| + }
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::SetTaskRunnerForTesting(
|
| + scoped_refptr<base::TaskRunner> task_runner) {
|
| + task_runner_ = task_runner;
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::UpdateEidSeeds() {
|
| + RefreshAdvertisement();
|
| + // TODO (jingxuy): make this follow 14 day boundaries.
|
| + base::TimeDelta wait_time =
|
| + base::TimeDelta::FromDays(kEidSeedRotationPeriodInDays);
|
| + task_runner_->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&BluetoothLowEnergyWeaveServer::UpdateEidSeeds,
|
| + weak_ptr_factory_.GetWeakPtr()),
|
| + wait_time);
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::UpdateEids() {
|
| + RefreshAdvertisement();
|
| + // TODO(jingxuy): make this follow 8 hour boundaries.
|
| + base::TimeDelta wait_time =
|
| + base::TimeDelta::FromHours(kEidRotationPeriodInHours);
|
| + task_runner_->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&BluetoothLowEnergyWeaveServer::UpdateEids,
|
| + weak_ptr_factory_.GetWeakPtr()),
|
| + wait_time);
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::RefreshAdvertisement() {
|
| + // Can't change content of current advertisement.
|
| + // Hence unregister all current advertisement and put them on the top of the
|
| + // advertising queue and retry advertising them again.
|
| + // Unless some other application come in between the process and steal the
|
| + // advertisement quota, we can still advertise everything that's unregistered.
|
| + for (auto const& it : map_to_advertisement_) {
|
| + UnregisterAdvertisement(it.first);
|
| + ad_rotator_->CutAdvertisementLine(it.first);
|
| + }
|
| + Advertise();
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::RotateAdvertisement() {
|
| + for (auto const& it : map_to_advertisement_) {
|
| + UnregisterAdvertisement(it.first);
|
| + }
|
| +
|
| + Advertise();
|
| + base::TimeDelta wait_time =
|
| + base::TimeDelta::FromSeconds(kAdvertisementRotationPeriodInSecs);
|
| +
|
| + task_runner_->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&BluetoothLowEnergyWeaveServer::RotateAdvertisement,
|
| + weak_ptr_factory_.GetWeakPtr()),
|
| + wait_time);
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::Advertise() {
|
| + std::pair<std::string, std::unique_ptr<Advertisement>> ad =
|
| + ad_rotator_->GetNextAdvertisement();
|
| + adapter_->RegisterAdvertisement(
|
| + std::move(ad.second),
|
| + base::Bind(&BluetoothLowEnergyWeaveServer::RegisterAdvertisementSuccess,
|
| + weak_ptr_factory_.GetWeakPtr(), ad.first),
|
| + base::Bind(&BluetoothLowEnergyWeaveServer::RegisterAdvertisementError,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::UnregisterAdvertisement(
|
| + std::string bluetooth_address) {
|
| + DCHECK(map_to_advertisement_.find(bluetooth_address) !=
|
| + map_to_advertisement_.end());
|
| + map_to_advertisement_[bluetooth_address]->Unregister(
|
| + base::Bind(&BluetoothLowEnergyWeaveServer::UnregisterAdvertisementSuccess,
|
| + weak_ptr_factory_.GetWeakPtr(), bluetooth_address),
|
| + base::Bind(&BluetoothLowEnergyWeaveServer::UnregisterAdvertisementError,
|
| + weak_ptr_factory_.GetWeakPtr(), bluetooth_address));
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::UnregisterAdvertisementSuccess(
|
| + std::string bluetooth_address) {
|
| + map_to_advertisement_.erase(bluetooth_address);
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::UnregisterAdvertisementError(
|
| + std::string bluetooth_address,
|
| + BluetoothAdvertisement::ErrorCode error) {
|
| + DCHECK(error ==
|
| + BluetoothAdvertisement::ErrorCode::ERROR_ADVERTISEMENT_DOES_NOT_EXIST);
|
| + // The only way the unregister could fail is if the advertisement is not
|
| + // registered to begin with. Which means in our use case it should never fail.
|
| + // Hence remove the advertisement from the map.
|
| + map_to_advertisement_.erase(bluetooth_address);
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::RegisterAdvertisementSuccess(
|
| + std::string bluetooth_address,
|
| + scoped_refptr<BluetoothAdvertisement> advertisement) {
|
| + map_to_advertisement_[bluetooth_address] = advertisement;
|
| + ad_rotator_->RotateAdvertisement();
|
| + // Keep trying to advertise until failures.
|
| + Advertise();
|
| +}
|
| +
|
| +void BluetoothLowEnergyWeaveServer::RegisterAdvertisementError(
|
| + BluetoothAdvertisement::ErrorCode error) {}
|
| +
|
| +} // namespace weave
|
| +
|
| +} // namespace
|
|
|