mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
266 lines
7.3 KiB
C++
266 lines
7.3 KiB
C++
// Copyright 2024 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/linux/ble_v2_socket.h"
|
|
|
|
#include <cstdint>
|
|
|
|
#include "absl/synchronization/mutex.h"
|
|
#include "internal/platform/byte_array.h"
|
|
#include "internal/platform/exception.h"
|
|
#include "internal/platform/logging.h"
|
|
|
|
namespace nearby {
|
|
namespace linux {
|
|
|
|
InputStream& BleV2Socket::GetInputStream() { return input_stream_; }
|
|
|
|
OutputStream& BleV2Socket::GetOutputStream() { return output_stream_; }
|
|
|
|
Exception BleV2Socket::Close() {
|
|
absl::MutexLock lock(&mutex_);
|
|
if (closed_) {
|
|
return {Exception::kSuccess};
|
|
}
|
|
closed_ = true;
|
|
|
|
// Close streams
|
|
input_stream_.NotifyClose();
|
|
output_stream_.Close();
|
|
|
|
// Cleanup GATT resources
|
|
if (gatt_client_) {
|
|
LOG(INFO) << "Disconnecting GATT client for peripheral "
|
|
<< peripheral_id_;
|
|
gatt_client_->Disconnect();
|
|
gatt_client_.reset();
|
|
}
|
|
|
|
if (gatt_server_) {
|
|
LOG(INFO) << "Stopping GATT server for peripheral "
|
|
<< peripheral_id_;
|
|
// Server cleanup is handled by the server itself
|
|
gatt_server_.reset();
|
|
}
|
|
|
|
return {Exception::kSuccess};
|
|
}
|
|
|
|
bool BleV2Socket::IsClosed() const {
|
|
absl::MutexLock lock(&mutex_);
|
|
return closed_;
|
|
}
|
|
|
|
// BleInputStream implementation
|
|
ExceptionOr<ByteArray> BleV2Socket::BleInputStream::Read(std::int64_t size) {
|
|
absl::MutexLock lock(&mutex_);
|
|
|
|
while (buffer_.Empty() && !closed_) {
|
|
cond_.Wait(&mutex_);
|
|
}
|
|
|
|
if (closed_ && buffer_.Empty()) {
|
|
return ExceptionOr<ByteArray>(Exception::kIo);
|
|
}
|
|
|
|
if (size < 0 || static_cast<size_t>(size) >= buffer_.size()) {
|
|
ByteArray result = buffer_;
|
|
buffer_ = ByteArray();
|
|
return ExceptionOr<ByteArray>(result);
|
|
}
|
|
|
|
ByteArray result(buffer_.data(), size);
|
|
buffer_ = ByteArray(buffer_.data() + size, buffer_.size() - size);
|
|
return ExceptionOr<ByteArray>(result);
|
|
}
|
|
|
|
Exception BleV2Socket::BleInputStream::Close() {
|
|
absl::MutexLock lock(&mutex_);
|
|
if (closed_) {
|
|
return {Exception::kSuccess};
|
|
}
|
|
closed_ = true;
|
|
cond_.SignalAll();
|
|
return {Exception::kSuccess};
|
|
}
|
|
|
|
void BleV2Socket::BleInputStream::ReceiveData(const ByteArray& data) {
|
|
absl::MutexLock lock(&mutex_);
|
|
if (closed_) {
|
|
return;
|
|
}
|
|
|
|
if (buffer_.Empty()) {
|
|
buffer_ = data;
|
|
} else {
|
|
ByteArray combined(buffer_.size() + data.size());
|
|
std::memcpy(combined.data(), buffer_.data(), buffer_.size());
|
|
std::memcpy(combined.data() + buffer_.size(), data.data(), data.size());
|
|
buffer_ = std::move(combined);
|
|
}
|
|
cond_.SignalAll();
|
|
}
|
|
|
|
void BleV2Socket::BleInputStream::NotifyClose() {
|
|
absl::MutexLock lock(&mutex_);
|
|
closed_ = true;
|
|
cond_.SignalAll();
|
|
}
|
|
|
|
// BleOutputStream implementation
|
|
Exception BleV2Socket::BleOutputStream::Write(absl::string_view data) {
|
|
absl::MutexLock lock(&mutex_);
|
|
if (closed_) {
|
|
return {Exception::kIo};
|
|
}
|
|
|
|
if (!write_callback_) {
|
|
LOG(WARNING) << "BleOutputStream: No write callback set";
|
|
return {Exception::kIo};
|
|
}
|
|
|
|
bool success = write_callback_(data);
|
|
return {success ? Exception::kSuccess : Exception::kIo};
|
|
}
|
|
|
|
Exception BleV2Socket::BleOutputStream::Flush() {
|
|
return {Exception::kSuccess};
|
|
}
|
|
|
|
Exception BleV2Socket::BleOutputStream::Close() {
|
|
absl::MutexLock lock(&mutex_);
|
|
if (closed_) {
|
|
return {Exception::kSuccess};
|
|
}
|
|
closed_ = true;
|
|
write_callback_ = nullptr;
|
|
return {Exception::kSuccess};
|
|
}
|
|
|
|
void BleV2Socket::BleOutputStream::SetWriteCallback(WriteCallback callback) {
|
|
absl::MutexLock lock(&mutex_);
|
|
write_callback_ = std::move(callback);
|
|
}
|
|
|
|
void BleV2Socket::SetGattServer(
|
|
std::unique_ptr<api::ble::GattServer> gatt_server,
|
|
const api::ble::GattCharacteristic& rx_char,
|
|
const api::ble::GattCharacteristic& tx_char) {
|
|
absl::MutexLock lock(&mutex_);
|
|
|
|
if (closed_) {
|
|
LOG(WARNING) << "Cannot set GATT server on closed socket";
|
|
return;
|
|
}
|
|
|
|
gatt_server_ = std::move(gatt_server);
|
|
rx_char_ = rx_char;
|
|
tx_char_ = tx_char;
|
|
|
|
// Set up write callback to use GATT server notifications
|
|
output_stream_.SetWriteCallback(
|
|
[this](absl::string_view data) -> bool {
|
|
absl::MutexLock lock(&mutex_);
|
|
if (!gatt_server_) {
|
|
LOG(ERROR) << "GATT server not available for write";
|
|
return false;
|
|
}
|
|
|
|
if (closed_) {
|
|
LOG(WARNING) << "Socket is closed, cannot write";
|
|
return false;
|
|
}
|
|
|
|
// Notify remote device via TX characteristic
|
|
absl::Status status = gatt_server_->NotifyCharacteristicChanged(
|
|
tx_char_, /*confirm=*/false, ByteArray::FromStringView(data));
|
|
|
|
if (!status.ok()) {
|
|
LOG(WARNING) << "Failed to notify TX characteristic: "
|
|
<< status.message();
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
LOG(INFO) << "BLE socket configured with GATT server, RX: "
|
|
<< std::string(rx_char.uuid)
|
|
<< ", TX: " << std::string(tx_char.uuid);
|
|
}
|
|
|
|
void BleV2Socket::SetGattClient(
|
|
std::unique_ptr<api::ble::GattClient> gatt_client,
|
|
const api::ble::GattCharacteristic& rx_char,
|
|
const api::ble::GattCharacteristic& tx_char) {
|
|
absl::MutexLock lock(&mutex_);
|
|
|
|
if (closed_) {
|
|
LOG(WARNING) << "Cannot set GATT client on closed socket";
|
|
return;
|
|
}
|
|
|
|
gatt_client_ = std::move(gatt_client);
|
|
rx_char_ = rx_char;
|
|
tx_char_ = tx_char;
|
|
|
|
// Set up write callback to use GATT client writes
|
|
output_stream_.SetWriteCallback(
|
|
[this](absl::string_view data) -> bool {
|
|
absl::MutexLock lock(&mutex_);
|
|
if (!gatt_client_) {
|
|
LOG(ERROR) << "GATT client not available for write";
|
|
return false;
|
|
}
|
|
|
|
if (closed_) {
|
|
LOG(WARNING) << "Socket is closed, cannot write";
|
|
return false;
|
|
}
|
|
|
|
// Write to TX characteristic on remote device
|
|
std::string data_str(data.data(), data.size());
|
|
bool success = gatt_client_->WriteCharacteristic(
|
|
tx_char_, data_str,
|
|
api::ble::GattClient::WriteType::kWithoutResponse);
|
|
|
|
if (!success) {
|
|
LOG(WARNING) << "Failed to write to TX characteristic";
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
// Subscribe to RX characteristic to receive data
|
|
|
|
//bool subscribed = gatt_client_->SetCharacteristicSubscription(
|
|
// rx_char_, /*enable=*/true,
|
|
// [this](absl::string_view value) {
|
|
// if (!IsClosed()) {
|
|
// ByteArray data(value.data(), value.size());
|
|
// input_stream_.ReceiveData(data);
|
|
// }
|
|
// });
|
|
|
|
//if (!subscribed) {
|
|
// LOG(ERROR) << "Failed to subscribe to RX characteristic";
|
|
//}
|
|
|
|
LOG(INFO) << "BLE socket configured with GATT client, RX: "
|
|
<< std::string(rx_char.uuid)
|
|
<< ", TX: " << std::string(tx_char.uuid);
|
|
}
|
|
|
|
} // namespace linux
|
|
} // namespace nearby
|