From 9d2cfc19c954bb6164f2a490c6027f0e5570703f Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Mon, 31 Jul 2023 17:18:52 -0700 Subject: [PATCH] Add Objective-C++ GATT server wrapper PiperOrigin-RevId: 552636264 --- Package.swift | 1 + internal/platform/implementation/apple/BUILD | 4 + .../implementation/apple/ble_gatt_server.h | 78 +++++++++++++ .../implementation/apple/ble_gatt_server.mm | 104 ++++++++++++++++++ .../implementation/apple/ble_peripheral.h | 1 + 5 files changed, 188 insertions(+) create mode 100644 internal/platform/implementation/apple/ble_gatt_server.h create mode 100644 internal/platform/implementation/apple/ble_gatt_server.mm diff --git a/Package.swift b/Package.swift index e4c984bd..5f66797b 100644 --- a/Package.swift +++ b/Package.swift @@ -572,6 +572,7 @@ let package = Package( "internal/platform/medium_environment.cc", // Temporarily ignore BLEv2 source files. // TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete. + "internal/platform/implementation/apple/ble_gatt_server.mm", "internal/platform/implementation/apple/ble_peripheral.mm", "internal/platform/implementation/apple/ble_server_socket.mm", "internal/platform/implementation/apple/ble_socket.mm", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 98af2cba..0bd4ef8d 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -107,6 +107,7 @@ objc_library( objc_library( name = "ble_v2", srcs = [ + "ble_gatt_server.mm", "ble_peripheral.mm", "ble_server_socket.mm", "ble_socket.mm", @@ -115,6 +116,7 @@ objc_library( "utils.mm", ], hdrs = [ + "ble_gatt_server.h", "ble_peripheral.h", "ble_server_socket.h", "ble_socket.h", @@ -126,10 +128,12 @@ objc_library( aspect_hints = ["//tools/build_defs/swift:no_module"], deps = [ "//internal/platform:base", + "//internal/platform:uuid", "//internal/platform/implementation:comm", "//internal/platform/implementation/apple/Mediums", "//third_party/apple_frameworks:CoreBluetooth", "//third_party/apple_frameworks:Foundation", + "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/strings", diff --git a/internal/platform/implementation/apple/ble_gatt_server.h b/internal/platform/implementation/apple/ble_gatt_server.h new file mode 100644 index 00000000..e42d4998 --- /dev/null +++ b/internal/platform/implementation/apple/ble_gatt_server.h @@ -0,0 +1,78 @@ +// Copyright 2023 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +#import + +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/uuid.h" + +#import "internal/platform/implementation/apple/ble_peripheral.h" + +@class GNCBLEGATTServer; + +namespace nearby { +namespace apple { + +class GattServer : public api::ble_v2::GattServer { + public: + explicit GattServer(GNCBLEGATTServer *gatt_server_); + ~GattServer() override = default; + + // Returns an empty BlePeripheral object. + // + // Use of this method should be avoided and its only purpose seems to be a check that the GATT + // server is valid. + api::ble_v2::BlePeripheral &GetBlePeripheral() override; + + // Creates a characteristic and adds it to the GATT server under the given characteristic and + // service UUIDs. + // + // Characteristics of the same service UUID will be put under one service rather than many + // services with the same UUID. + // + // Returns no value upon error. + std::optional CreateCharacteristic( + const Uuid &service_uuid, const Uuid &characteristic_uuid, + api::ble_v2::GattCharacteristic::Permission permission, + api::ble_v2::GattCharacteristic::Property property) override; + + // Updates a local characteristic with the provided value. + // + // Returns whether or not the update was successful. + bool UpdateCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, + const nearby::ByteArray &value) override; + + // Send a notification or indication that a local characteristic has been updated. + // + // Returns an absl::Status indicating success or what went wrong. + absl::Status NotifyCharacteristicChanged(const api::ble_v2::GattCharacteristic &characteristic, + bool confirm, const ByteArray &new_value) override; + + // Stops a GATT server. + void Stop() override; + + private: + GNCBLEGATTServer *gatt_server_; + BlePeripheral peripheral_; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_gatt_server.mm b/internal/platform/implementation/apple/ble_gatt_server.mm new file mode 100644 index 00000000..57682c73 --- /dev/null +++ b/internal/platform/implementation/apple/ble_gatt_server.mm @@ -0,0 +1,104 @@ +// Copyright 2023 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. + +#import "internal/platform/implementation/apple/ble_gatt_server.h" + +#import +#import + +#include "internal/platform/implementation/ble_v2.h" + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" +#import "internal/platform/implementation/apple/ble_utils.h" +#import "internal/platform/implementation/apple/utils.h" +#import "GoogleToolboxForMac/GTMLogger.h" + +namespace nearby { +namespace apple { + +GattServer::GattServer(GNCBLEGATTServer *gatt_server) : gatt_server_(gatt_server) {} + +std::optional GattServer::CreateCharacteristic( + const Uuid &service_uuid, const Uuid &characteristic_uuid, + api::ble_v2::GattCharacteristic::Permission permission, + api::ble_v2::GattCharacteristic::Property property) { + CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); + CBUUID *characteristicUUID = CBUUID128FromCPP(characteristic_uuid); + CBAttributePermissions permissions = CBAttributePermissionsFromCPP(permission); + CBCharacteristicProperties properties = CBCharacteristicPropertiesFromCPP(property); + + NSCondition *condition = [[NSCondition alloc] init]; + [condition lock]; + __block GNCBLEGATTCharacteristic *blockCharacteristic = nil; + [gatt_server_ createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID + permissions:permissions + properties:properties + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [condition lock]; + if (error != nil) { + GTMLoggerError(@"Error creating characteristic: %@", error); + } + blockCharacteristic = characteristic; + [condition signal]; + [condition unlock]; + }]; + [condition wait]; + [condition unlock]; + if (blockCharacteristic == nil) { + return std::nullopt; + } + return CPPGATTCharacteristicFromObjC(blockCharacteristic); +} + +bool GattServer::UpdateCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, + const nearby::ByteArray &value) { + NSCondition *condition = [[NSCondition alloc] init]; + [condition lock]; + __block NSError *blockError = nil; + [gatt_server_ updateCharacteristic:ObjCGATTCharacteristicFromCPP(characteristic) + value:NSDataFromByteArray(value) + completionHandler:^(NSError *error) { + [condition lock]; + if (error != nil) { + GTMLoggerError(@"Error updating characteristic: %@", error); + } + blockError = error; + [condition signal]; + [condition unlock]; + }]; + [condition wait]; + [condition unlock]; + return blockError == nil; +} + +// TODO(b/290385712): Implement. +absl::Status GattServer::NotifyCharacteristicChanged( + const api::ble_v2::GattCharacteristic &characteristic, bool confirm, + const ByteArray &new_value) { + return absl::UnimplementedError(""); +} + +void GattServer::Stop() { + [gatt_server_ stop]; +} + +// TODO(b/290385712): Implement. +api::ble_v2::BlePeripheral &GattServer::GetBlePeripheral() { + return peripheral_; +} + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_peripheral.h b/internal/platform/implementation/apple/ble_peripheral.h index b31b918e..c17c5a4d 100644 --- a/internal/platform/implementation/apple/ble_peripheral.h +++ b/internal/platform/implementation/apple/ble_peripheral.h @@ -33,6 +33,7 @@ namespace apple { // identify a peripheral and connect to its GATT server. class BlePeripheral : public api::ble_v2::BlePeripheral { public: + BlePeripheral() = default; explicit BlePeripheral(CBPeripheral *peripheral); ~BlePeripheral() override = default;