Add BLE conversion utils

PiperOrigin-RevId: 547958170
This commit is contained in:
Nick Bourdakos
2023-07-13 16:20:04 -07:00
committed by Copybara-Service
parent 47de9bdfaa
commit 96348b6ba7
5 changed files with 442 additions and 0 deletions
@@ -24,6 +24,7 @@ objc_library(
name = "apple",
srcs = [
"ble.mm",
"ble_utils.mm",
"bluetooth_adapter.mm",
"crypto.mm",
"device_info.mm",
@@ -38,6 +39,7 @@ objc_library(
],
hdrs = [
"ble.h",
"ble_utils.h",
"bluetooth_adapter.h",
"device_info.h",
"log_message.h",
@@ -24,6 +24,7 @@ objc_library(
testonly = True,
srcs = [
"GNCBLEGATTCharacteristicTest.mm",
"GNCBLEUtilsTest.mm",
"GNCBleTest.mm",
"GNCBluetoothAdapterTest.mm",
"GNCCryptoTest.mm",
@@ -0,0 +1,198 @@
// 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 <CoreBluetooth/CoreBluetooth.h>
#import <XCTest/XCTest.h>
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h"
#import "internal/platform/implementation/apple/ble_utils.h"
#include "internal/platform/implementation/ble_v2.h"
using GattCharacteristic = ::nearby::api::ble_v2::GattCharacteristic;
using Property = ::nearby::api::ble_v2::GattCharacteristic::Property;
using Permission = ::nearby::api::ble_v2::GattCharacteristic::Permission;
using WriteType = ::nearby::api::ble_v2::GattClient::WriteType;
using Uuid = ::nearby::Uuid;
@interface GNCBLEUtilsTest : XCTestCase
@end
@implementation GNCBLEUtilsTest
#pragma mark - C++ to Objective-C
- (void)testCBUUID16FromCPP {
CBUUID *expected = [CBUUID UUIDWithString:@"FEF3"];
CBUUID *actual = ::nearby::apple::CBUUID16FromCPP(Uuid(0x0000FEF300001000, 0x800000805F9B34FB));
XCTAssertEqualObjects(actual.UUIDString, expected.UUIDString);
}
- (void)testCBUUID128FromCPP {
CBUUID *expected = [CBUUID UUIDWithString:@"0000FEF3-0000-1000-8000-00805F9B34FB"];
CBUUID *actual = ::nearby::apple::CBUUID128FromCPP(Uuid(0x0000FEF300001000, 0x800000805F9B34FB));
XCTAssertEqualObjects(actual.UUIDString, expected.UUIDString);
}
- (void)testCBCharacteristicWriteTypeFromCPPWithResponse {
CBCharacteristicWriteType expected = CBCharacteristicWriteWithResponse;
CBCharacteristicWriteType actual =
::nearby::apple::CBCharacteristicWriteTypeFromCPP(WriteType::kWithResponse);
XCTAssertEqual(actual, expected);
}
- (void)testCBCharacteristicWriteTypeFromCPPWithoutResponse {
CBCharacteristicWriteType expected = CBCharacteristicWriteWithoutResponse;
CBCharacteristicWriteType actual =
::nearby::apple::CBCharacteristicWriteTypeFromCPP(WriteType::kWithoutResponse);
XCTAssertEqual(actual, expected);
}
- (void)testCBCharacteristicPropertiesFromCPPEmptyProperty {
CBCharacteristicProperties expected = 0;
CBCharacteristicProperties actual =
::nearby::apple::CBCharacteristicPropertiesFromCPP(Property::kNone);
XCTAssertEqual(actual, expected);
}
- (void)testCBCharacteristicPropertiesFromCPPSingleProperty {
CBCharacteristicProperties expected = CBCharacteristicPropertyRead;
CBCharacteristicProperties actual =
::nearby::apple::CBCharacteristicPropertiesFromCPP(Property::kRead);
XCTAssertEqual(actual, expected);
}
- (void)testCBCharacteristicPropertiesFromCPPMultipleProperties {
CBCharacteristicProperties expected = CBCharacteristicPropertyWrite |
CBCharacteristicPropertyNotify |
CBCharacteristicPropertyIndicate;
CBCharacteristicProperties actual = ::nearby::apple::CBCharacteristicPropertiesFromCPP(
Property::kWrite | Property::kNotify | Property::kIndicate);
XCTAssertEqual(actual, expected);
}
- (void)testCBAttributePermissionsFromCPPEmptyPermission {
CBAttributePermissions expected = 0;
CBAttributePermissions actual = ::nearby::apple::CBAttributePermissionsFromCPP(Permission::kNone);
XCTAssertEqual(actual, expected);
}
- (void)testCBAttributePermissionsFromCPPSinglePermission {
CBAttributePermissions expected = CBAttributePermissionsReadable;
CBAttributePermissions actual =
::nearby::apple::CBAttributePermissionsFromCPP(Permission::kRead);
XCTAssertEqual(actual, expected);
}
- (void)testCBAttributePermissionsFromCPPMultiplePermissions {
CBAttributePermissions expected =
CBAttributePermissionsReadable | CBAttributePermissionsWriteable;
CBAttributePermissions actual =
::nearby::apple::CBAttributePermissionsFromCPP(Permission::kRead | Permission::kWrite);
XCTAssertEqual(actual, expected);
}
- (void)testObjCGATTCharacteristicFromCPP {
CBUUID *characteristicUUID = [CBUUID UUIDWithString:@"00000000-0000-3000-8000-000000000000"];
CBUUID *serviceUUID = [CBUUID UUIDWithString:@"0000FEF3-0000-1000-8000-00805F9B34FB"];
CBAttributePermissions permissions = CBAttributePermissionsReadable;
CBCharacteristicProperties properties = CBCharacteristicPropertyRead;
GattCharacteristic characteristic = {Uuid(0x0000000000003000, 0x8000000000000000),
Uuid(0x0000FEF300001000, 0x800000805F9B34FB),
Permission::kRead, Property::kRead};
GNCBLEGATTCharacteristic *actual = ::nearby::apple::ObjCGATTCharacteristicFromCPP(characteristic);
XCTAssertEqualObjects(actual.characteristicUUID, characteristicUUID);
XCTAssertEqualObjects(actual.serviceUUID, serviceUUID);
XCTAssertEqual(actual.permissions, permissions);
XCTAssertEqual(actual.properties, properties);
}
#pragma mark - Objective-C to C++
- (void)testCPPUUIDFromObjC16Bit {
Uuid expected = Uuid(0x0000FEF300001000, 0x800000805F9B34FB);
Uuid actual = ::nearby::apple::CPPUUIDFromObjC([CBUUID UUIDWithString:@"FEF3"]);
XCTAssertEqualObjects(@(std::string(actual).c_str()), @(std::string(expected).c_str()));
}
- (void)testCPPUUIDFromObjC128Bit {
Uuid expected = Uuid(0x0000FEF300001000, 0x800000805F9B34FB);
Uuid actual = ::nearby::apple::CPPUUIDFromObjC(
[CBUUID UUIDWithString:@"0000FEF3-0000-1000-8000-00805F9B34FB"]);
XCTAssertEqualObjects(@(std::string(actual).c_str()), @(std::string(expected).c_str()));
}
- (void)testCPPGATTCharacteristicPropertyFromObjCEmptyProperty {
Property expected = Property::kNone;
Property actual = ::nearby::apple::CPPGATTCharacteristicPropertyFromObjC(0);
XCTAssertEqual(actual, expected);
}
- (void)testCPPGATTCharacteristicPropertyFromObjCSingleProperty {
Property expected = Property::kRead;
Property actual =
::nearby::apple::CPPGATTCharacteristicPropertyFromObjC(CBCharacteristicPropertyRead);
XCTAssertEqual(actual, expected);
}
- (void)testCPPGATTCharacteristicPropertyFromObjCMultipleProperties {
Property expected = Property::kWrite | Property::kNotify | Property::kIndicate;
Property actual = ::nearby::apple::CPPGATTCharacteristicPropertyFromObjC(
CBCharacteristicPropertyWrite | CBCharacteristicPropertyNotify |
CBCharacteristicPropertyIndicate);
XCTAssertEqual(actual, expected);
}
- (void)testCPPGATTCharacteristicPermissionFromObjCEmptyPermission {
Permission expected = Permission::kNone;
Permission actual = ::nearby::apple::CPPGATTCharacteristicPermissionFromObjC(0);
XCTAssertEqual(actual, expected);
}
- (void)testCPPGATTCharacteristicPermissionFromObjCSinglePermission {
Permission expected = Permission::kRead;
Permission actual =
::nearby::apple::CPPGATTCharacteristicPermissionFromObjC(CBAttributePermissionsReadable);
XCTAssertEqual(actual, expected);
}
- (void)testCPPGATTCharacteristicPermissionFromObjCMultiplePermissions {
Permission expected = Permission::kRead | Permission::kWrite;
Permission actual = ::nearby::apple::CPPGATTCharacteristicPermissionFromObjC(
CBAttributePermissionsReadable | CBAttributePermissionsWriteable);
XCTAssertEqual(actual, expected);
}
- (void)testCPPGATTCharacteristicFromObjC {
CBUUID *characteristicUUID = [CBUUID UUIDWithString:@"00000000-0000-3000-8000-000000000000"];
CBUUID *serviceUUID = [CBUUID UUIDWithString:@"0000FEF3-0000-1000-8000-00805F9B34FB"];
CBAttributePermissions permissions = CBAttributePermissionsReadable;
CBCharacteristicProperties properties = CBCharacteristicPropertyRead;
GNCBLEGATTCharacteristic *characteristic =
[[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID
serviceUUID:serviceUUID
permissions:permissions
properties:properties];
GattCharacteristic actual = ::nearby::apple::CPPGATTCharacteristicFromObjC(characteristic);
XCTAssertEqual(actual.uuid, Uuid(0x0000000000003000, 0x8000000000000000));
XCTAssertEqual(actual.service_uuid, Uuid(0x0000FEF300001000, 0x800000805F9B34FB));
XCTAssertEqual(actual.permission, Permission::kRead);
XCTAssertEqual(actual.property, Property::kRead);
}
@end
@@ -0,0 +1,82 @@
// 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 <Foundation/Foundation.h>" 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 <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#include "internal/platform/implementation/ble_v2.h"
@class GNCBLEGATTCharacteristic;
namespace nearby {
namespace apple {
/** Converts a C++ UUID to a 16-bit CoreBluetooth UUID. */
CBUUID *CBUUID16FromCPP(const Uuid &uuid);
/** Converts a C++ UUID to a 128-bit CoreBluetooth UUID. */
CBUUID *CBUUID128FromCPP(const Uuid &uuid);
/** Converts a C++ write type enum to a CoreBluetooth write type. */
CBCharacteristicWriteType CBCharacteristicWriteTypeFromCPP(api::ble_v2::GattClient::WriteType wt);
/**
* Converts a C++ permission enum to CoreBluetooth permissions.
*
* This conversion keeps multiple permission values intact.
*/
CBAttributePermissions CBAttributePermissionsFromCPP(api::ble_v2::GattCharacteristic::Permission p);
/**
* Converts a C++ property enum to CoreBluetooth properties.
*
* This conversion keeps multiple property values intact.
*/
CBCharacteristicProperties CBCharacteristicPropertiesFromCPP(
api::ble_v2::GattCharacteristic::Property p);
/** Converts a C++ characteristic to an Objective-C characteristic. */
GNCBLEGATTCharacteristic *ObjCGATTCharacteristicFromCPP(const api::ble_v2::GattCharacteristic &c);
/** Converts a 16 or 128 bit CoreBluetooth UUID to a C++ UUID. */
Uuid CPPUUIDFromObjC(CBUUID *uuid);
/**
* Converts CoreBluetooth permissions to a C++ permission enum.
*
* This conversion keeps multiple permission values intact.
*/
api::ble_v2::GattCharacteristic::Permission CPPGATTCharacteristicPermissionFromObjC(
CBAttributePermissions p);
/**
* Converts CoreBluetooth properties to a C++ property enum.
*
* This conversion keeps multiple property values intact.
*/
api::ble_v2::GattCharacteristic::Property CPPGATTCharacteristicPropertyFromObjC(
CBCharacteristicProperties p);
/** Converts an Objective-C characteristic to a C++ characteristic. */
api::ble_v2::GattCharacteristic CPPGATTCharacteristicFromObjC(GNCBLEGATTCharacteristic *c);
} // namespace apple
} // namespace nearby
@@ -0,0 +1,159 @@
// 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_utils.h"
#import <CoreBluetooth/CoreBluetooth.h>
#import <Foundation/Foundation.h>
#include <string>
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h"
namespace nearby {
namespace apple {
using GattCharacteristic = api::ble_v2::GattCharacteristic;
using Permission = api::ble_v2::GattCharacteristic::Permission;
using Property = api::ble_v2::GattCharacteristic::Property;
using WriteType = api::ble_v2::GattClient::WriteType;
/**
* Checks if enum `a` contains enum `b`
*
* For example, `Contains(Permission::kRead | Permission::kWrite, Permission::kRead)` should return
* `YES`.
*/
template <typename T>
BOOL Contains(T a, T b) {
return (a & b) == b;
}
BOOL Contains(unsigned long a, unsigned long b) { return Contains<unsigned long>(a, b); }
CBUUID *CBUUID16FromCPP(const Uuid &uuid) {
return [CBUUID UUIDWithString:@(uuid.Get16BitAsString().c_str())];
}
CBUUID *CBUUID128FromCPP(const Uuid &uuid) {
return [CBUUID UUIDWithString:@(std::string(uuid).c_str())];
}
CBCharacteristicWriteType CBCharacteristicWriteTypeFromCPP(WriteType wt) {
switch (wt) {
case WriteType::kWithResponse:
return CBCharacteristicWriteWithResponse;
case WriteType::kWithoutResponse:
return CBCharacteristicWriteWithoutResponse;
}
}
CBAttributePermissions CBAttributePermissionsFromCPP(Permission p) {
CBAttributePermissions permissions = 0;
if (Contains(p, Permission::kRead)) {
permissions |= CBAttributePermissionsReadable;
}
if (Contains(p, Permission::kWrite)) {
permissions |= CBAttributePermissionsWriteable;
}
return permissions;
}
CBCharacteristicProperties CBCharacteristicPropertiesFromCPP(Property p) {
CBCharacteristicProperties properties = 0;
if (Contains(p, Property::kRead)) {
properties |= CBCharacteristicPropertyRead;
}
if (Contains(p, Property::kWrite)) {
properties |= CBCharacteristicPropertyWrite;
}
if (Contains(p, Property::kIndicate)) {
properties |= CBCharacteristicPropertyIndicate;
}
if (Contains(p, Property::kNotify)) {
properties |= CBCharacteristicPropertyNotify;
}
return properties;
}
GNCBLEGATTCharacteristic *ObjCGATTCharacteristicFromCPP(const GattCharacteristic &c) {
CBUUID *characteristicUUID = CBUUID128FromCPP(c.uuid);
CBUUID *serviceUUID = CBUUID128FromCPP(c.service_uuid);
CBAttributePermissions permissions = CBAttributePermissionsFromCPP(c.permission);
CBCharacteristicProperties properties = CBCharacteristicPropertiesFromCPP(c.property);
return [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID
serviceUUID:serviceUUID
permissions:permissions
properties:properties];
}
Uuid CPPUUIDFromObjC(CBUUID *uuid) {
NSString *uuidString = uuid.UUIDString;
if (uuidString.length == 4) {
uuidString = [NSString stringWithFormat:@"0000%@-0000-1000-8000-00805F9B34FB", uuidString];
}
NSArray<NSString *> *components = [uuidString componentsSeparatedByString:@"-"];
NSCAssert(components.count == 5, @"Invalid UUID string: %@", uuidString);
uint64_t mostSignificantBits = strtoull(components[0].UTF8String, nil, 16);
mostSignificantBits <<= 16;
mostSignificantBits |= strtoull(components[1].UTF8String, nil, 16);
mostSignificantBits <<= 16;
mostSignificantBits |= strtoull(components[2].UTF8String, nil, 16);
uint64_t leastSignificantBits = strtoull(components[3].UTF8String, nil, 16);
leastSignificantBits <<= 48;
leastSignificantBits |= strtoull(components[4].UTF8String, nil, 16);
return Uuid(mostSignificantBits, leastSignificantBits);
}
Permission CPPGATTCharacteristicPermissionFromObjC(CBAttributePermissions p) {
Permission permissions = Permission::kNone;
if (Contains(p, CBAttributePermissionsReadable)) {
permissions |= Permission::kRead;
}
if (Contains(p, CBAttributePermissionsWriteable)) {
permissions |= Permission::kWrite;
}
return permissions;
}
Property CPPGATTCharacteristicPropertyFromObjC(CBCharacteristicProperties p) {
Property property = Property::kNone;
if (Contains(p, CBCharacteristicPropertyRead)) {
property |= Property::kRead;
}
if (Contains(p, CBCharacteristicPropertyWrite)) {
property |= Property::kWrite;
}
if (Contains(p, CBCharacteristicPropertyIndicate)) {
property |= Property::kIndicate;
}
if (Contains(p, CBCharacteristicPropertyNotify)) {
property |= Property::kNotify;
}
return property;
}
GattCharacteristic CPPGATTCharacteristicFromObjC(GNCBLEGATTCharacteristic *c) {
GattCharacteristic characteristic;
characteristic.uuid = CPPUUIDFromObjC(c.characteristicUUID);
characteristic.service_uuid = CPPUUIDFromObjC(c.serviceUUID);
characteristic.permission = CPPGATTCharacteristicPermissionFromObjC(c.permissions);
characteristic.property = CPPGATTCharacteristicPropertyFromObjC(c.properties);
return characteristic;
}
} // namespace apple
} // namespace nearby