Files
nearby/internal/platform/implementation/g3/ble_v2.cc
T
2022-06-10 20:20:40 -07:00

470 lines
15 KiB
C++

// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/g3/ble_v2.h"
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "absl/strings/escaping.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/logging.h"
#include "internal/platform/medium_environment.h"
namespace location {
namespace nearby {
namespace g3 {
namespace {
using ::location::nearby::api::ble_v2::BleAdvertisementData;
using ::location::nearby::api::ble_v2::TxPowerLevel;
std::string TxPowerLevelToName(TxPowerLevel power_mode) {
switch (power_mode) {
case TxPowerLevel::kUltraLow:
return "UltraLow";
case TxPowerLevel::kLow:
return "Low";
case TxPowerLevel::kMedium:
return "Medium";
case TxPowerLevel::kHigh:
return "High";
case TxPowerLevel::kUnknown:
return "Unknown";
}
}
} // namespace
BleV2Socket::~BleV2Socket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
void BleV2Socket::Connect(BleV2Socket& other) {
absl::MutexLock lock(&mutex_);
remote_socket_ = &other;
input_ = other.output_;
}
InputStream& BleV2Socket::GetInputStream() {
auto* remote_socket = GetRemoteSocket();
CHECK(remote_socket != nullptr);
return remote_socket->GetLocalInputStream();
}
OutputStream& BleV2Socket::GetOutputStream() { return GetLocalOutputStream(); }
BleV2Socket* BleV2Socket::GetRemoteSocket() {
absl::MutexLock lock(&mutex_);
return remote_socket_;
}
bool BleV2Socket::IsConnected() const {
absl::MutexLock lock(&mutex_);
return IsConnectedLocked();
}
bool BleV2Socket::IsClosed() const {
absl::MutexLock lock(&mutex_);
return closed_;
}
Exception BleV2Socket::Close() {
absl::MutexLock lock(&mutex_);
DoClose();
return {Exception::kSuccess};
}
BleV2Peripheral* BleV2Socket::GetRemotePeripheral() {
BluetoothAdapter* remote_adapter = nullptr;
{
absl::MutexLock lock(&mutex_);
if (remote_socket_ == nullptr || remote_socket_->adapter_ == nullptr) {
return nullptr;
}
remote_adapter = remote_socket_->adapter_;
}
return remote_adapter ? &remote_adapter->GetPeripheralV2() : nullptr;
}
void BleV2Socket::DoClose() {
if (!closed_) {
remote_socket_ = nullptr;
output_->GetOutputStream().Close();
output_->GetInputStream().Close();
input_->GetOutputStream().Close();
input_->GetInputStream().Close();
closed_ = true;
}
}
bool BleV2Socket::IsConnectedLocked() const { return input_ != nullptr; }
InputStream& BleV2Socket::GetLocalInputStream() {
absl::MutexLock lock(&mutex_);
return output_->GetInputStream();
}
OutputStream& BleV2Socket::GetLocalOutputStream() {
absl::MutexLock lock(&mutex_);
return output_->GetOutputStream();
}
std::unique_ptr<api::ble_v2::BleSocket> BleV2ServerSocket::Accept() {
absl::MutexLock lock(&mutex_);
while (!closed_ && pending_sockets_.empty()) {
cond_.Wait(&mutex_);
}
// whether or not we were running in the wait loop, return early if closed.
if (closed_) return {};
auto* remote_socket =
pending_sockets_.extract(pending_sockets_.begin()).value();
CHECK(remote_socket);
auto local_socket = std::make_unique<BleV2Socket>(adapter_);
local_socket->Connect(*remote_socket);
remote_socket->Connect(*local_socket);
cond_.SignalAll();
return local_socket;
}
bool BleV2ServerSocket::Connect(BleV2Socket& socket) {
absl::MutexLock lock(&mutex_);
if (closed_) return false;
if (socket.IsConnected()) {
NEARBY_LOGS(WARNING)
<< "Failed to connect to Ble server socket: already connected";
return true; // already connected.
}
// add client socket to the pending list
pending_sockets_.insert(&socket);
cond_.SignalAll();
while (!socket.IsConnected()) {
cond_.Wait(&mutex_);
if (closed_) return false;
}
return true;
}
void BleV2ServerSocket::SetCloseNotifier(std::function<void()> notifier) {
absl::MutexLock lock(&mutex_);
close_notifier_ = std::move(notifier);
}
BleV2ServerSocket::~BleV2ServerSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
Exception BleV2ServerSocket::Close() {
absl::MutexLock lock(&mutex_);
return DoClose();
}
Exception BleV2ServerSocket::DoClose() {
bool should_notify = !closed_;
closed_ = true;
if (should_notify) {
cond_.SignalAll();
if (close_notifier_) {
auto notifier = std::move(close_notifier_);
mutex_.Unlock();
// Notifier may contain calls to public API, and may cause deadlock, if
// mutex_ is held during the call.
notifier();
mutex_.Lock();
}
}
return {Exception::kSuccess};
}
BleV2Medium::BleV2Medium(api::BluetoothAdapter& adapter)
: adapter_(static_cast<BluetoothAdapter*>(&adapter)) {
adapter_->SetBleV2Medium(this);
MediumEnvironment::Instance().RegisterBleV2Medium(*this);
}
BleV2Medium::~BleV2Medium() {
adapter_->SetBleV2Medium(nullptr);
MediumEnvironment::Instance().UnregisterBleV2Medium(*this);
}
bool BleV2Medium::StartAdvertising(
const BleAdvertisementData& advertising_data,
api::ble_v2::AdvertiseParameters advertise_parameters) {
NEARBY_LOGS(INFO)
<< "G3 Ble StartAdvertising: advertising_data.is_extended_advertisement="
<< advertising_data.is_extended_advertisement
<< ", advertising_data.service_data size="
<< advertising_data.service_data.size() << ", tx_power_level="
<< TxPowerLevelToName(advertise_parameters.tx_power_level)
<< ", is_connectable=" << advertise_parameters.is_connectable;
if (advertising_data.is_extended_advertisement &&
!is_support_extended_advertisement_) {
NEARBY_LOGS(INFO)
<< "G3 Ble StartAdvertising does not support extended advertisement";
return false;
}
absl::MutexLock lock(&mutex_);
MediumEnvironment::Instance().UpdateBleV2MediumForAdvertising(
/*enabled=*/true, *this, adapter_->GetPeripheralV2(), advertising_data);
return true;
}
bool BleV2Medium::StopAdvertising() {
NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising";
absl::MutexLock lock(&mutex_);
BleAdvertisementData empty_advertisement_data = {};
MediumEnvironment::Instance().UpdateBleV2MediumForAdvertising(
/*enabled=*/false, *this, /*mutable=*/adapter_->GetPeripheralV2(),
empty_advertisement_data);
return true;
}
bool BleV2Medium::StartScanning(const Uuid& service_uuid,
TxPowerLevel tx_power_level,
ScanCallback callback) {
NEARBY_LOGS(INFO) << "G3 Ble StartScanning";
absl::MutexLock lock(&mutex_);
MediumEnvironment::Instance().UpdateBleV2MediumForScanning(
/*enabled=*/true, service_uuid, std::move(callback), *this);
return true;
}
bool BleV2Medium::StopScanning() {
NEARBY_LOGS(INFO) << "G3 Ble StopScanning";
absl::MutexLock lock(&mutex_);
MediumEnvironment::Instance().UpdateBleV2MediumForScanning(
/*enabled=*/false,
/*service_uuid=*/{}, /*callback=*/{}, *this);
return true;
}
std::unique_ptr<api::ble_v2::GattServer> BleV2Medium::StartGattServer(
api::ble_v2::ServerGattConnectionCallback callback) {
return std::make_unique<GattServer>();
}
std::unique_ptr<api::ble_v2::GattClient> BleV2Medium::ConnectToGattServer(
api::ble_v2::BlePeripheral& peripheral, TxPowerLevel tx_power_level,
api::ble_v2::ClientGattConnectionCallback callback) {
return std::make_unique<GattClient>();
}
bool BleV2Medium::IsExtendedAdvertisementsAvailable() {
return is_support_extended_advertisement_;
}
std::optional<api::ble_v2::GattCharacteristic>
BleV2Medium::GattServer::CreateCharacteristic(
const Uuid& service_uuid, const Uuid& characteristic_uuid,
const std::vector<api::ble_v2::GattCharacteristic::Permission>& permissions,
const std::vector<api::ble_v2::GattCharacteristic::Property>& properties) {
api::ble_v2::GattCharacteristic characteristic = {
.uuid = characteristic_uuid, .service_uuid = service_uuid};
return characteristic;
}
bool BleV2Medium::GattServer::UpdateCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const location::nearby::ByteArray& value) {
NEARBY_LOGS(INFO)
<< "G3 Ble GattServer UpdateCharacteristic, characteristic=("
<< characteristic.service_uuid.Get16BitAsString() << ","
<< std::string(characteristic.uuid)
<< "), value = " << absl::BytesToHexString(value.data());
MediumEnvironment::Instance().InsertBleV2MediumGattCharacteristics(
characteristic, value);
return true;
}
void BleV2Medium::GattServer::Stop() {
NEARBY_LOGS(INFO) << "G3 Ble GattServer Stop";
MediumEnvironment::Instance().ClearBleV2MediumGattCharacteristics();
}
bool BleV2Medium::GattClient::DiscoverServiceAndCharacteristics(
const Uuid& service_uuid, const std::vector<Uuid>& characteristic_uuids) {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO)
<< "G3 Ble GattClient DiscoverServiceAndCharacteristics, service_uuid="
<< service_uuid.Get16BitAsString();
if (!is_connection_alive_) {
return false;
}
return MediumEnvironment::Instance().DiscoverBleV2MediumGattCharacteristics(
service_uuid, characteristic_uuids);
}
std::optional<api::ble_v2::GattCharacteristic>
BleV2Medium::GattClient::GetCharacteristic(const Uuid& service_uuid,
const Uuid& characteristic_uuid) {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 Ble GattClient GetCharacteristic, service_uuid="
<< service_uuid.Get16BitAsString()
<< ", characteristic_uuid="
<< std::string(characteristic_uuid);
if (!is_connection_alive_) {
return std::nullopt;
}
// clang-format off
api::ble_v2::GattCharacteristic characteristic = {
.uuid = characteristic_uuid,
.service_uuid = service_uuid};
// clang-format on
ByteArray value =
MediumEnvironment::Instance().ReadBleV2MediumGattCharacteristics(
characteristic);
if (value.Empty()) {
NEARBY_LOGS(WARNING)
<< "G3 Ble GattClient GetCharacteristic, can't find characteristic=("
<< characteristic.service_uuid.Get16BitAsString() << ","
<< std::string(characteristic.uuid) << ")";
return std::nullopt;
}
NEARBY_LOGS(INFO)
<< "G3 Ble GattClient GetCharacteristic, found characteristic=("
<< characteristic.service_uuid.Get16BitAsString() << ","
<< std::string(characteristic.uuid) << ")";
return characteristic;
}
std::optional<ByteArray> BleV2Medium::GattClient::ReadCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic) {
absl::MutexLock lock(&mutex_);
if (!is_connection_alive_) {
return std::nullopt;
}
ByteArray value =
MediumEnvironment::Instance().ReadBleV2MediumGattCharacteristics(
characteristic);
NEARBY_LOGS(INFO) << "G3 Ble ReadCharacteristic, characteristic=("
<< characteristic.service_uuid.Get16BitAsString() << ","
<< std::string(characteristic.uuid)
<< "), value = " << absl::BytesToHexString(value.data());
return std::move(value);
}
bool BleV2Medium::GattClient::WriteCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const ByteArray& value) {
// No op.
return false;
}
void BleV2Medium::GattClient::Disconnect() {
absl::MutexLock lock(&mutex_);
NEARBY_LOGS(INFO) << "G3 Ble GattClient Disconnect";
is_connection_alive_ = false;
MediumEnvironment::Instance()
.ClearBleV2MediumGattCharacteristicsForDiscovery();
}
std::unique_ptr<api::ble_v2::BleServerSocket> BleV2Medium::OpenServerSocket(
const std::string& service_id) {
auto server_socket = std::make_unique<BleV2ServerSocket>(&GetAdapter());
server_socket->SetCloseNotifier([this, service_id]() {
absl::MutexLock lock(&mutex_);
server_sockets_.erase(service_id);
});
NEARBY_LOGS(INFO) << "G3 Ble Adding server socket: medium=" << this
<< ", service_id=" << service_id;
absl::MutexLock lock(&mutex_);
server_sockets_.insert({service_id, server_socket.get()});
return server_socket;
}
std::unique_ptr<api::ble_v2::BleSocket> BleV2Medium::Connect(
const std::string& service_id, TxPowerLevel tx_power_level,
api::ble_v2::BlePeripheral& remote_peripheral,
CancellationFlag* cancellation_flag) {
NEARBY_LOGS(INFO) << "G3 Ble Connect [self]: medium=" << this
<< ", adapter=" << &GetAdapter()
<< ", peripheral=" << &GetAdapter().GetPeripheralV2()
<< ", service_id=" << service_id;
// First, find an instance of remote medium, that exposed this peripheral.
auto& remote_adapter =
static_cast<BleV2Peripheral&>(remote_peripheral).GetAdapter();
auto* remote_medium =
static_cast<BleV2Medium*>(remote_adapter.GetBleV2Medium());
if (!remote_medium) {
return nullptr;
}
BleV2ServerSocket* remote_server_socket = nullptr;
NEARBY_LOGS(INFO) << "G3 Ble Connect [peer]: medium=" << remote_medium
<< ", adapter=" << &remote_adapter
<< ", peripheral=" << &remote_peripheral
<< ", service_id=" << service_id;
// Then, find our server socket context in this medium.
{
absl::MutexLock medium_lock(&remote_medium->mutex_);
auto item = remote_medium->server_sockets_.find(service_id);
remote_server_socket =
item != server_sockets_.end() ? item->second : nullptr;
if (remote_server_socket == nullptr) {
NEARBY_LOGS(ERROR)
<< "G3 Ble Failed to find Ble Server socket: service_id="
<< service_id;
return nullptr;
}
}
if (cancellation_flag->Cancelled()) {
NEARBY_LOGS(ERROR) << "G3 BLE Connect: Has been cancelled: "
"service_id="
<< service_id;
return nullptr;
}
CancellationFlagListener listener(
cancellation_flag, [&remote_server_socket]() {
NEARBY_LOGS(INFO) << "G3 Ble Cancel Connect.";
if (remote_server_socket != nullptr) {
remote_server_socket->Close();
}
});
auto socket = std::make_unique<BleV2Socket>(&GetAdapter());
// Finally, Request to connect to this socket.
if (!remote_server_socket->Connect(*socket)) {
NEARBY_LOGS(ERROR) << "G3 Ble Failed to connect to existing Ble "
"Server socket: service_id="
<< service_id;
return nullptr;
}
NEARBY_LOGS(INFO) << "G3 Ble Connect to socket=" << socket.get();
return socket;
}
} // namespace g3
} // namespace nearby
} // namespace location