Merge pull request #600 from google/embedded

Add embedded SDK
This commit is contained in:
jgsobczak
2022-04-14 13:14:48 -07:00
committed by GitHub
40 changed files with 7443 additions and 0 deletions
+181
View File
@@ -0,0 +1,181 @@
# Makefile arguments
#
#
# Delete the default suffix rules.
.SUFFIXES:
.SECONDARY:
GTEST_DIR = ../third_party/gtest/googletest/
GMOCK_DIR = ../third_party/gtest/googlemock/
ARCH ?= host
OUT_DIR_NAME ?= $(ARCH)
OUT_DIR = out/$(OUT_DIR_NAME)
DIST_DIR = dist/$(OUT_DIR_NAME)
CFLAGS := -Wall
COMMON_INCLUDE_DIRS :=
CLIENT_INCLUDES :=
# For ARCH variants like gLinux_hw set to gLinux.
ARCH_COMMON_NAME := $(ARCH)
ARCH_COMMON_DIR = common/source/$(ARCH_COMMON_NAME)
CLIENT_DIR = client/source
CLIENT_SRCS :=
NEARBY_LIB_NAME = libnearby.a
NAME = $(OUT_DIR)/$(NEARBY_LIB_NAME)
all : $(NAME)
COMMON_DIR = common/source
COMMON_C_FILES :=
# All output objects for the build system should be added to ALL_OBJS.
ALL_OBJS :=
# Extra dependencies of the dist target.
DIST_LIST :=
# Name of the libnearby static library that we distribute.
DIST_NEARBY_LIB_NAME ?= $(DIST_DIR)/$(NEARBY_LIB_NAME)
# Kokoro on Windows is wonky. We can't shell out to git because we are in
# cygwin, so this is pre-computed by continuous.bat
ifneq ($(FIRMWARE_VERSION_FILENAME),)
FIRMWARE_VERSION := $(shell cat $(FIRMWARE_VERSION_FILENAME))
else
FIRMWARE_VERSION_FILENAME = $(OUT_DIR)/FIRMWARE_VERSION
PHONY_VERSION_FILENAME = $(OUT_DIR)/PHONY_FIRMWARE_VERSION
FIRMWARE_VERSION := $(shell git -c core.fileMode=false describe --always --dirty --exclude="*")
FIRMWARE_VERSION_FILENAME: $(PHONY_VERSION_FILENAME)
ifneq ($(KOKORO_RELEASE_BUILD),1)
FIRMWARE_VERSION := $(FIRMWARE_VERSION)ENG
endif
# Update the phony version on every build.
.PHONY: $(PHONY_VERSION_FILENAME)
$(PHONY_VERSION_FILENAME):
@mkdir -p $(dir $@) ;\
echo $(FIRMWARE_VERSION) > $@ ;\
# Only update this version if necessary (if it's different from the phony). This
# tricks make into not updating dependencies of FIRMWARE_VERSION_FILENAME unless
# the file is updated. .PHONY is not transitive.
$(FIRMWARE_VERSION_FILENAME): $(PHONY_VERSION_FILENAME)
@cmp $@ $< 2>> /dev/null || cp -v $< $@
endif
# Note $(notdir) below converts a path to just the base file name
define compile_c
mkdir -p $(dir $@)
$(CC) -c $(1) -o $@ -D__NEARBY_SHORT_FILE__='"$(notdir $<)"' $<
endef
# Include all of the target specific variables and rules.
# The $(ARCH).mk files should only depend on the variables listed above.
include target/$(ARCH)/config.mk
# Set trace verbosity from target specific variable
CFLAGS += -DNEARBY_TRACE_LEVEL=NEARBY_TRACE_LEVEL_$(NEARBY_TRACE_LEVEL)
# if directory doesn't exist, raise an error so we don't fail later with more cryptic warnings
ifeq ($(wildcard common/target/$(ARCH_COMMON_NAME)),)
$(error need to create directory common/target/$(ARCH_COMMON_NAME) )
endif
COMMON_INCLUDE_DIRS += \
-I. \
-I$(ARCH_COMMON_DIR) \
-Icommon/target \
-Icommon/target/$(ARCH_COMMON_NAME) \
-I$(COMMON_DIR)
CLIENT_INCLUDES += \
-I$(CLIENT_DIR) \
-Iclient/target
COMMON_C_FILES += \
$(wildcard $(COMMON_DIR)/*.c)
CLIENT_SRCS += \
$(wildcard $(CLIENT_DIR)/*.c)
CFLAGS += $(COMMON_INCLUDE_DIRS)
CROSS_COMPILE ?= arm-none-eabi-
CC ?= $(CROSS_COMPILE)gcc
AR ?= $(CROSS_COMPILE)ar
ALL_C_FILES = $(COMMON_C_FILES)
COMMON_OBJS = $(patsubst %.c,$(OUT_DIR)/%.o,$(ALL_C_FILES))
CLIENT_OBJS = $(patsubst %.c,$(OUT_DIR)/%.o,$(CLIENT_SRCS))
NEARBY_LIB_OBJS = $(COMMON_OBJS) $(CLIENT_OBJS)
NEARBY_HEADERS += $(patsubst common/target/%.h,$(DIST_DIR)/%.h,$(wildcard common/target/*.h))
NEARBY_HEADERS += $(patsubst common/target/$(ARCH_COMMON_NAME)/%.h,$(DIST_DIR)/%.h,$(wildcard common/target/$(ARCH_COMMON_NAME)/*.h))
NEARBY_HEADERS += $(patsubst client/target/%,$(DIST_DIR)/%,$(wildcard client/target/*.h))
NEARBY_DIST_HEADERS := $(NEARBY_HEADERS)
DIST_LIST += $(NEARBY_DIST_HEADERS)
DIST_LIST += $(DIST_NEARBY_LIB_NAME)
$(DIST_DIR)/nearby.h: $(FIRMWARE_VERSION_FILENAME)
$(DIST_DIR)/nearby.h: common/target/nearby.h
@mkdir -p $(dir $@)
$(call copy_dist_header)
sed -i -e "s/\(define\s*NEARBY_BUILD_ID\).*/\1 \"$(FIRMWARE_VERSION)\"/" $@
define copy_dist_header
@mkdir -p $(dir $@)
@test "$$(head -n 2 $< | grep 'Copyright [0-9]\{4\} Google LLC.')" != "" || \
(echo ; echo "=====ERROR=====" ; echo "Missing copyright in $<" ; exit 1)
unifdef -t -b -x2 $(UNIFDEF_ARG) -o - $< | cat - >$@
@# (mkartoz) Should be able to specify $@ as the output directly from
@# unifdef but, if you do that, the file never shows up on kokoro. Piping to
@# cat is a workaround.
endef
# Do not bypass the whole build process if the dist directory exists
.PHONY: dist
dist : all \
$(DIST_LIST)
$(DIST_DIR)/$(NEARBY_LIB_NAME) : $(NAME)
mkdir -p $(dir $@)
cp -uv $< $@
$(DIST_DIR)/%.h : common/target/%.h
$(call copy_dist_header)
$(DIST_DIR)/%.h : common/target/$(ARCH_COMMON_NAME)/%.h
$(call copy_dist_header)
$(NAME): $(NEARBY_LIB_OBJS)
mkdir -p $(dir $@)
$(AR) rcs $@ $(NEARBY_LIB_OBJS)
# If the hash has changed then we need to rebuild nearby.o and nearby_client.o
$(filter %/nearby_client.o, $(NEARBY_LIB_OBJS)): $(FIRMWARE_VERSION_FILENAME)
$(filter %/nearby.o, $(NEARBY_LIB_OBJS)): $(FIRMWARE_VERSION_FILENAME)
$(CLIENT_OBJS) : $(OUT_DIR)/%.o : %.c
$(call compile_c, $(CFLAGS) -Icommon/target $(CLIENT_INCLUDES))
$(COMMON_OBJS) : $(OUT_DIR)/%.o: %.c
$(call compile_c,$(CFLAGS))
ALL_OBJS += $(NEARBY_LIB_OBJS) $(CLIENT_OBJS)
DEPFILES = $(ALL_OBJS:.o=.d)
-include $(DEPFILES)
.PHONY: clean
clean:
rm -f $(NAME)
rm -f $(ALL_OBJS) $(DEPFILES)
rm -f $(FIRMWARE_VERSION_FILENAME) $(PHONY_VERSION_FILENAME)
rm -f $(DIST_LIST) $(TEST_BINARIES)
-include target/$(ARCH)/rules.mk
+31
View File
@@ -0,0 +1,31 @@
# Nearby embedded SDK library
This repository contains Nearby SDK library for embedded systems. Nearby SDK
implements the Fast Pair protocol and its future versions per
https://developers.google.com/nearby/fast-pair/spec
## Threading model
Nearby SDK assumes a single-threading model. All calls to the SDK must be on the same thread, or protected with a mutex. That includes:
- client API, for example `nearby_fp_client_SetAdvertisement()`
- ble and other event callbacks
- timers
## Porting guidelines
1. Follow the steps at [Fast Pair Help](https://developers.google.com/nearby/fast-pair/help) to
register a Model Id for your device.
1. Review `nearby_config.h` and disable features, if any, that you don't
wish to support.
1. Implement the HAL defined in `nearby_platform_*.h` headers.
1. Set platform specific compile flags in `config.mk` - see
`target/gLinux/config.mk` for inspiration, and add your platform in
`build.sh`, or use your own build system.
1. Compile with `./build.sh <your platform>`
1. Start advertising in your application. For example
```
nearby_fp_client_Init(NULL);
nearby_fp_client_SetAdvertisement(NEARBY_FP_ADVERTISEMENT_DISCOVERABLE);
```
1. Use [Fast Pair Validator](https://play.google.com/store/apps/details?id=com.google.location.nearby.apps.fastpair.validator) to verify that your device is behaving correctly.
+83
View File
@@ -0,0 +1,83 @@
#!/bin/sh
#
# Parameters:
# ${1} architecture (default: gLinux)
# ${2} make target (e.g. "dist")
# (remaining params are also passed to make)
#
# Checks for environment variables:
# NEARBY_MAKEFILE_DEBUG: if set, passes -d argument to make
# many others, not fully documented here yet
#
# e.g.
# ./build.sh gLinux dist DEBUG=1
if [ "${1}" == "help" ] ; then
echo "Usage:"
echo " ./build.sh <target> [dist] [args]"
echo ""
echo "Optional arguments:"
echo " DEBUG=1"
echo " -j72 # parallelizes build using 72 cores"
exit -1;
fi
if [ "${1}" != "" ] ; then
ARCH="${1}"
shift 1
else
ARCH=gLinux
fi
CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar
UNAME_OUT="$(uname -s)"
case "${UNAME_OUT}" in
Linux*) MACHINE=linux;;
Darwin*) MACHINE=mac;;
CYGWIN*) MACHINE=cygwin;;
MINGW*) MACHINE=mingw;;
*) MACHINE=windows;;
esac
if [ "${ARCH}" = "gLinux" ] ; then
if [ -x /usr/bin/clang ] ; then
CC=clang
else
CC=clang-3.8
fi
AR=ar
else
echo "Invalid target specified on command line ${ARCH}"
exit 1
fi
export NEARBY=$(cd `dirname ${0}` && pwd)
PLATFORM=$(uname -s | grep -i '\(mingw\|cygwin\)')
echo $PLATFORM
make_args=
if [ ! -z "$NEARBY_MAKEFILE_DEBUG" ]; then
echo "NEARBY_MAKEFILE_DEBUG is set, passing -d to make"
make_args+=" -d"
fi
make \
$make_args \
-C "${NEARBY}" \
CC="${CC}" \
AR="${AR}" \
ARCH="${ARCH}" \
$@
make_rc=$?
# NOTE: if make fails, it is important that the bad return code is bubbled up
# to the caller of this script.
# We don't want automated processes to keep going if there was a build error.
if [ $make_rc -ne 0 ]; then
exit $make_rc
fi
File diff suppressed because it is too large Load Diff
+109
View File
@@ -0,0 +1,109 @@
// 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.
#ifndef NEARBY_FP_CLIENT_H
#define NEARBY_FP_CLIENT_H
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby.h"
#include "nearby_event.h"
#include "nearby_message_stream.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
// Optional event callback
void (*on_event)(nearby_event_Event* event);
} nearby_fp_client_Callbacks;
// Seeker information structure
// Returns current information on a given seeker.
typedef struct {
uint64_t peer_address; // address of peer/seeker
uint8_t capabilities; // Capability bits
// Bit 0: Companion app is installed/not installed.
// Bit 1: Silence mode is supported/not supported.
uint8_t platform_type; // platform type code (Android = 0x01)
uint8_t platform_build; // Platform build number (Android Pie=0x1c)
} nearby_fp_client_SeekerInfo;
// No advertisement, default state
#define NEARBY_FP_ADVERTISEMENT_NONE 0x00
// The device is discoverable and can be paired with
#define NEARBY_FP_ADVERTISEMENT_DISCOVERABLE 0x01
// The device is not discoverable
#define NEARBY_FP_ADVERTISEMENT_NON_DISCOVERABLE 0x02
// Ask the Seeker to show pairing UI indication. This flag can be combined with
// NEARBY_FP_ADVERTISEMENT_NON_DISCOVERABLE
#define NEARBY_FP_ADVERTISEMENT_PAIRING_UI_INDICATOR 0x04
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
// Include battery and charging info in the advertisement. This flag can be
// combined with NEARBY_FP_ADVERTISEMENT_NON_DISCOVERABLE
#define NEARBY_FP_ADVERTISEMENT_INCLUDE_BATTERY_INFO 0x08
// Ask the Seeker to show Battery UI indication. This flag can be combined with
// NEARBY_FP_ADVERTISEMENT_INCLUDE_BATTERY_INFO
#define NEARBY_FP_ADVERTISEMENT_BATTERY_UI_INDICATOR 0x10
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
// Sets Fast Pair advertisement type
nearby_platform_status nearby_fp_client_SetAdvertisement(int mode);
// Initalizes Fast Pair provider. The |callbacks| are optional - can be NULL.
nearby_platform_status nearby_fp_client_Init(
const nearby_fp_client_Callbacks* callbacks);
#ifdef NEARBY_FP_MESSAGE_STREAM
// Serializes and sends |message| over Message Stream
nearby_platform_status nearby_fp_client_SendMessage(
uint64_t peer_address, const nearby_message_stream_Message* message);
// Sends out an ACK message for a received |message|. Note that not all messages
// require an ACK
nearby_platform_status nearby_fp_client_SendAck(
const nearby_event_MessageStreamReceived* message);
// Sends out a NACK message for a received |message| with |fail_reason| reason.
nearby_platform_status nearby_fp_client_SendNack(
const nearby_event_MessageStreamReceived* message, uint8_t fail_reason);
// Sends out a enable or disable silence mode, which stops audio from being
// routed to the provider.
nearby_platform_status nearby_fp_client_SetSilenceMode(uint64_t peer_address,
bool enable);
// Sends log buffer full message to seeker, which passes it on to the companion
// app to manage log collection.
nearby_platform_status nearby_fp_client_SignalLogBufferFull(
uint64_t peer_address);
// Gets information on currently connected seekers.
// An array of `SeekerInfo` structures is passed in, along with the length.
// On output, `seeker_info_length` is the actual number of structures read.
// Returns an error if input array is too small to hold information about
// all connected seekers.
nearby_platform_status nearby_fp_client_GetSeekerInfo(
nearby_fp_client_SeekerInfo* seeker_info, size_t* seeker_info_length);
#endif /* NEARBY_FP_MESSAGE_STREAM */
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_FP_CLIENT_H */
+22
View File
@@ -0,0 +1,22 @@
// 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 "fakes.h"
#include "nearby.h"
#include "nearby_fp_client.h"
#include "nearby_platform_audio.h"
bool nearby_platform_GetEarbudRightStatus() { return false; }
bool nearby_platform_GetEarbudLeftStatus() { return false; }
+62
View File
@@ -0,0 +1,62 @@
// 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 "fakes.h"
#include "nearby.h"
#include "nearby_platform_battery.h"
static nearby_platform_BatteryInfo test_battery_info = {
.is_charging = true,
.right_bud_battery_level = 80,
.left_bud_battery_level = 85,
.charging_case_battery_level = 90,
.remaining_time_minutes = 100};
static nearby_platform_status get_battery_info_result = kNearbyStatusOK;
static const nearby_platform_BatteryInterface* battery_interface;
nearby_platform_status nearby_platform_GetBatteryInfo(
nearby_platform_BatteryInfo* battery_info) {
(*battery_info) = test_battery_info;
return get_battery_info_result;
}
void nearby_test_fakes_SetIsCharging(bool charging) {
test_battery_info.is_charging = charging;
}
void nearby_test_fakes_SetRightBudBatteryLevel(unsigned battery_level) {
test_battery_info.right_bud_battery_level = battery_level;
}
void nearby_test_fakes_SetLeftBudBatteryLevel(unsigned battery_level) {
test_battery_info.left_bud_battery_level = battery_level;
}
void nearby_test_fakes_SetChargingCaseBatteryLevel(unsigned battery_level) {
test_battery_info.charging_case_battery_level = battery_level;
}
void nearby_test_fakes_BatteryTime(uint16_t battery_time) {
test_battery_info.remaining_time_minutes = battery_time;
}
void nearby_test_fakes_SetGetBatteryInfoResult(nearby_platform_status status) {
get_battery_info_result = status;
}
nearby_platform_status nearby_platform_BatteryInit(
nearby_platform_BatteryInterface* callbacks) {
battery_interface = callbacks;
return kNearbyStatusOK;
}
+127
View File
@@ -0,0 +1,127 @@
// 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 <map>
#include <vector>
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "fakes.h"
#include "nearby_platform_ble.h"
#include "nearby_platform_se.h"
constexpr uint64_t kDefaultBleAddress = 0xbabababa;
static uint64_t ble_address = kDefaultBleAddress;
static uint64_t peer_address = 0x345678ab;
static const nearby_platform_BleInterface* ble_interface;
static std::map<nearby_fp_Characteristic, std::vector<uint8_t>> notifications;
static std::vector<uint8_t> advertisement;
static nearby_fp_AvertisementInterval interval;
std::map<nearby_fp_Characteristic, std::vector<uint8_t>>&
nearby_test_fakes_GetGattNotifications() {
return notifications;
}
// Gets BLE address.
uint64_t nearby_platform_GetBleAddress() { return ble_address; }
// Sets BLE address. Returns address after change, which may be different than
// requested address.
uint64_t nearby_platform_SetBleAddress(uint64_t address) {
ble_address = address;
return ble_address;
}
#ifdef NEARBY_FP_HAVE_BLE_ADDRESS_ROTATION
// Rotates BLE address to a random resolvable private address (RPA). Returns
// address after change.
uint64_t nearby_platform_RotateBleAddress() {
unsigned i;
uint64_t address = 0;
for (i = 0; i < sizeof(address); i++) {
address = (address << 8) ^ nearby_platform_Rand();
}
address |= (uint64_t)1 << 46;
address &= ~((uint64_t)1 << 47);
nearby_platform_SetBleAddress(address);
return address;
}
#endif /* NEARBY_FP_HAVE_BLE_ADDRESS_ROTATION */
// Sends a notification to the connected GATT client.
nearby_platform_status nearby_platform_GattNotify(
uint64_t peer_address, nearby_fp_Characteristic characteristic,
const uint8_t* message, size_t length) {
notifications.emplace(characteristic,
std::vector<uint8_t>(message, message + length));
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_SetAdvertisement(
const uint8_t* payload, size_t length,
nearby_fp_AvertisementInterval interval) {
if (payload == NULL || length == 0) {
advertisement.clear();
} else {
advertisement.assign(payload, payload + length);
}
::interval = interval;
return kNearbyStatusOK;
}
// Initializes BLE
nearby_platform_status nearby_platform_BleInit(
const nearby_platform_BleInterface* callbacks) {
ble_interface = callbacks;
notifications.clear();
advertisement.clear();
interval = kDisabled;
ble_address = kDefaultBleAddress;
return kNearbyStatusOK;
}
std::vector<uint8_t>& nearby_test_fakes_GetAdvertisement() {
return advertisement;
}
nearby_platform_status nearby_test_fakes_GattReadModelId(uint8_t* output,
size_t* length) {
return ble_interface->on_gatt_read(peer_address, kModelId, output, length);
}
nearby_platform_status nearby_fp_fakes_ReceiveKeyBasedPairingRequest(
const uint8_t* request, size_t length) {
return ble_interface->on_gatt_write(peer_address, kKeyBasedPairing, request,
length);
}
nearby_platform_status nearby_fp_fakes_ReceivePasskey(const uint8_t* request,
size_t length) {
return ble_interface->on_gatt_write(peer_address, kPasskey, request, length);
}
nearby_platform_status nearby_fp_fakes_ReceiveAccountKeyWrite(
const uint8_t* request, size_t length) {
return ble_interface->on_gatt_write(peer_address, kAccountKey, request,
length);
}
nearby_platform_status nearby_fp_fakes_ReceiveAdditionalData(
const uint8_t* request, size_t length) {
return ble_interface->on_gatt_write(peer_address, kAdditionalData, request,
length);
}
+145
View File
@@ -0,0 +1,145 @@
// 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 <cstring>
#include <vector>
#include "nearby_platform_bt.h"
static const uint32_t kFastPairId = 0x101112;
static const int8_t kTxLevel = 33;
static const uint64_t kPublicAddress = 0xA0A1A2A3A4A5;
static const uint32_t kLocalPasskey = 123456;
static uint32_t remote_passkey;
static uint64_t remote_address;
static uint64_t paired_peer_address;
static std::vector<uint8_t> rfcomm_output;
static std::vector<char> device_name;
static bool pairing_mode = false;
static const nearby_platform_BtInterface* bt_interface;
// Returns Fast Pair Model Id.
uint32_t nearby_platform_GetModelId() { return kFastPairId; }
int8_t nearby_platform_GetTxLevel() { return kTxLevel; }
// Returns public BR/EDR address
uint64_t nearby_platform_GetPublicAddress() { return kPublicAddress; }
// Returns passkey used during pairing
uint32_t nearby_platfrom_GetPairingPassKey() { return kLocalPasskey; }
void nearby_platform_SetRemotePasskey(uint32_t passkey) {
remote_passkey = passkey;
if (remote_passkey == kLocalPasskey &&
remote_address != 0 & bt_interface != NULL) {
paired_peer_address = remote_address;
bt_interface->on_paired(remote_address);
}
}
nearby_platform_status nearby_platform_SendPairingRequest(
uint64_t remote_party_br_edr_address) {
remote_address = remote_party_br_edr_address;
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_SetFastPairCapabilities() {
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_SetDefaultCapabilities() {
remote_address = 0;
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_BtInit(
const nearby_platform_BtInterface* callbacks) {
bt_interface = callbacks;
remote_address = 0;
remote_passkey = 0;
paired_peer_address = 0;
pairing_mode = false;
return kNearbyStatusOK;
}
uint64_t nearby_test_fakes_GetPairingRequestAddress() { return remote_address; }
uint32_t nearby_test_fakes_GetRemotePasskey() { return remote_passkey; }
void nearby_test_fakes_SimulatePairing(uint64_t peer_address) {
remote_address = peer_address;
if (bt_interface != NULL) {
bt_interface->on_pairing_request(peer_address);
}
}
uint64_t nearby_test_fakes_GetPairedDevice() { return paired_peer_address; }
void nearby_test_fakes_DevicePaired(uint64_t peer_address) {
bt_interface->on_paired(peer_address);
}
nearby_platform_status nearby_platform_SendMessageStream(uint64_t peer_address,
const uint8_t* message,
size_t length) {
for (int i = 0; i < length; i++) {
rfcomm_output.push_back(message[i]);
}
return kNearbyStatusOK;
}
std::vector<uint8_t>& nearby_test_fakes_GetRfcommOutput() {
return rfcomm_output;
}
nearby_platform_status nearby_platform_SetDeviceName(const char* name) {
// + 1 for null terminator
device_name = std::vector<char>(name, name + std::strlen(name) + 1);
return kNearbyStatusOK;
}
// Gets null-terminated device name string in UTF-8 encoding
// pass buffer size in char, and get string length in char.
nearby_platform_status nearby_platform_GetDeviceName(char* name,
size_t* length) {
if (*length < device_name.size()) {
return kNearbyStatusResourceExhausted;
}
*length = device_name.size();
std::memcpy(name, device_name.data(), device_name.size());
return kNearbyStatusOK;
}
bool nearby_platform_IsInPairingMode() { return pairing_mode; }
void nearby_test_fakes_SetInPairingMode(bool in_pairing_mode) {
pairing_mode = in_pairing_mode;
}
#ifdef NEARBY_FP_MESSAGE_STREAM
void nearby_test_fakes_MessageStreamConnected(uint64_t peer_address) {
bt_interface->on_message_stream_connected(peer_address);
}
void nearby_test_fakes_MessageStreamDisconnected(uint64_t peer_address) {
bt_interface->on_message_stream_disconnected(peer_address);
}
void nearby_test_fakes_MessageStreamReceived(uint64_t peer_address,
const uint8_t* message,
size_t length) {
bt_interface->on_message_stream_received(peer_address, message, length);
}
#endif /* NEARBY_FP_MESSAGE_STREAM */
+134
View File
@@ -0,0 +1,134 @@
// 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.
// Copyright 2021 Google LLC.
#ifndef NEARBY_TEST_FAKES_H
#define NEARBY_TEST_FAKES_H
#include <map>
#include <queue>
#include <vector>
#include "nearby.h"
#include "nearby_platform_ble.h"
void nearby_test_fakes_SetRandomNumber(unsigned int value);
void nearby_test_fakes_SetRandomNumberSequence(std::vector<uint8_t>& value);
void nearby_test_fakes_SetAccountKeys(const uint8_t* input, size_t length);
std::vector<uint8_t> nearby_test_fakes_GetRawAccountKeys();
nearby_platform_status nearby_test_fakes_GattReadModelId(uint8_t* output,
size_t* length);
nearby_platform_status nearby_test_fakes_SetAntiSpoofingKey(
const uint8_t private_key[32], const uint8_t public_key[64]);
nearby_platform_status nearby_test_fakes_GenSec256r1Secret(
const uint8_t remote_party_public_key[64], uint8_t secret[32]);
nearby_platform_status nearby_test_fakes_Aes128Decrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]);
nearby_platform_status nearby_test_fakes_Aes128Encrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]);
std::vector<uint8_t>& nearby_test_fakes_GetAdvertisement();
std::map<nearby_fp_Characteristic, std::vector<uint8_t>>&
nearby_test_fakes_GetGattNotifications();
void nearby_test_fakes_SimulatePairing(uint64_t peer_address);
nearby_platform_status nearby_fp_fakes_ReceiveKeyBasedPairingRequest(
const uint8_t* request, size_t length);
nearby_platform_status nearby_fp_fakes_ReceivePasskey(const uint8_t* request,
size_t length);
nearby_platform_status nearby_fp_fakes_ReceiveAccountKeyWrite(
const uint8_t* request, size_t length);
nearby_platform_status nearby_fp_fakes_ReceiveAdditionalData(
const uint8_t* request, size_t length);
uint64_t nearby_test_fakes_GetPairingRequestAddress();
uint32_t nearby_test_fakes_GetRemotePasskey();
uint64_t nearby_test_fakes_GetPairedDevice();
void nearby_test_fakes_DevicePaired(uint64_t peer_address);
class AccountKeyList {
public:
explicit AccountKeyList(const std::vector<uint8_t>& raw_values) {
if (raw_values.size() == 0) return;
int key_count = raw_values[0];
for (int i = 0; i < key_count; i++) {
const uint8_t* p = raw_values.data() + 1 + (i * ACCOUNT_KEY_SIZE_BYTES);
keys_.emplace_back(std::vector<uint8_t>(p, p + ACCOUNT_KEY_SIZE_BYTES));
}
}
size_t size() { return keys_.size(); }
std::vector<std::vector<uint8_t>>& GetKeys() { return keys_; }
std::vector<uint8_t> GetRawFormat() {
std::vector<uint8_t> result;
result.push_back(size());
for (auto& key : keys_) {
for (auto& v : key) {
result.push_back(v);
}
}
return result;
}
private:
std::vector<std::vector<uint8_t>> keys_;
};
void nearby_test_fakes_SetAccountKeys(AccountKeyList& keys);
AccountKeyList nearby_test_fakes_GetAccountKeys();
void nearby_test_fakes_SetIsCharging(bool charging);
void nearby_test_fakes_SetRightBudBatteryLevel(unsigned battery_level);
void nearby_test_fakes_SetLeftBudBatteryLevel(unsigned battery_level);
void nearby_test_fakes_SetChargingCaseBatteryLevel(unsigned battery_level);
void nearby_test_fakes_BatteryTime(uint16_t battery_time);
void nearby_test_fakes_SetGetBatteryInfoResult(nearby_platform_status status);
std::vector<uint8_t>& nearby_test_fakes_GetRfcommOutput();
void nearby_test_fakes_MessageStreamConnected(uint64_t peer_address);
void nearby_test_fakes_MessageStreamDisconnected(uint64_t peer_address);
void nearby_test_fakes_MessageStreamReceived(uint64_t peer_address,
const uint8_t* message,
size_t length);
uint32_t nearby_test_fakes_GetNextTimerMs();
// Sets current time and triggers the timer callback if it expired
void nearby_test_fakes_SetCurrentTimeMs(uint32_t ms);
void nearby_test_fakes_SetInPairingMode(bool in_pairing_mode);
uint8_t nearby_test_fakes_GetRingCommand(void);
uint16_t nearby_test_fakes_GetRingTimeout(void);
#endif /* NEARBY_TEST_FAKES_H */
+82
View File
@@ -0,0 +1,82 @@
// 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 <cstddef>
#include "nearby_assert.h"
#include "nearby_platform_os.h"
typedef void (*timer)();
static timer timer_callback;
static uint32_t timer_trigger_time;
static bool timer_has_run;
static uint32_t current_time;
static constexpr nearby_platform_RingingInfo kDefaultRingingInfo = {
.ring_state = kRingStateStoppedReasonTimeout,
.num_components = 3,
.components = 0,
.timeout = 0,
};
static nearby_platform_RingingInfo ringing_info = kDefaultRingingInfo;
// Gets current time in ms.
unsigned int nearby_platform_GetCurrentTimeMs() { return current_time; }
// Starts a timer. Returns an opaque timer handle or null on error.
void* nearby_platform_StartTimer(void (*callback)(), unsigned int delay_ms) {
timer_callback = callback;
timer_trigger_time = nearby_platform_GetCurrentTimeMs() + delay_ms;
timer_has_run = false;
return (void*)timer_callback;
}
// Cancels a timer
nearby_platform_status nearby_platform_CancelTimer(void* timer) {
NEARBY_ASSERT(timer == timer_callback);
timer_callback = NULL;
timer_trigger_time = 0;
return kNearbyStatusOK;
}
uint32_t nearby_test_fakes_GetNextTimerMs() { return timer_trigger_time; }
void nearby_test_fakes_SetCurrentTimeMs(uint32_t ms) {
current_time = ms;
if (!timer_has_run && timer_callback != NULL && timer_trigger_time <= ms) {
timer_has_run = true;
timer_callback();
}
}
nearby_platform_status nearby_platform_Ring(uint8_t command, uint16_t timeout) {
ringing_info.components = command;
ringing_info.timeout = timeout;
ringing_info.ring_state = kRingStateStarted;
return kNearbyStatusOK;
}
uint8_t nearby_test_fakes_GetRingCommand(void) {
return ringing_info.components;
}
uint16_t nearby_test_fakes_GetRingTimeout(void) { return ringing_info.timeout; }
nearby_platform_status nearby_platform_OsInit() {
current_time = 0;
timer_callback = NULL;
timer_trigger_time = 0;
timer_has_run = false;
return kNearbyStatusOK;
}
@@ -0,0 +1,69 @@
// 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 <algorithm>
#include <map>
#include <vector>
#include "fakes.h"
#include "nearby_platform_persistence.h"
static std::map<nearby_fp_StoredKey, std::vector<uint8_t>> storage;
nearby_platform_status nearby_platform_LoadValue(nearby_fp_StoredKey key,
uint8_t* output,
size_t* length) {
auto search = storage.find(key);
if (search != storage.end()) {
size_t value_length = search->second.size();
if (value_length > *length) {
*length = 0;
return kNearbyStatusResourceExhausted;
}
*length = value_length;
std::copy(search->second.begin(), search->second.end(), output);
} else {
// key not found.
*length = 0;
}
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_SaveValue(nearby_fp_StoredKey key,
const uint8_t* input,
size_t length) {
storage[key].assign(input, input + length);
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_PersistenceInit() {
storage.clear();
return kNearbyStatusOK;
}
void nearby_test_fakes_SetAccountKeys(const uint8_t* input, size_t length) {
storage[kStoredKeyAccountKeyList].assign(input, input + length);
}
std::vector<uint8_t> nearby_test_fakes_GetRawAccountKeys() {
return storage[kStoredKeyAccountKeyList];
}
void nearby_test_fakes_SetAccountKeys(AccountKeyList& keys) {
auto v = keys.GetRawFormat();
nearby_test_fakes_SetAccountKeys(v.data(), v.size());
}
AccountKeyList nearby_test_fakes_GetAccountKeys() {
return AccountKeyList(nearby_test_fakes_GetRawAccountKeys());
}
+249
View File
@@ -0,0 +1,249 @@
// 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 <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <cerrno>
#include <iomanip>
#include <iostream>
#include <memory>
#include <queue>
#include <sstream>
#include <string>
#include "fakes.h"
#include "nearby_platform_se.h"
static unsigned int random_value = 0;
static std::queue<uint8_t> random_sequence;
static std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY *)> anti_spoofing_key(
NULL, EVP_PKEY_free);
static std::string ArrayToString(const uint8_t *data, size_t length) {
std::stringstream output;
output << "0x" << std::hex << std::setfill('0') << std::setw(2);
for (int i = 0; i < length; i++) {
output << (unsigned)data[i];
}
return output.str();
}
// Generates a random number.
uint8_t nearby_platform_Rand() {
if (random_sequence.empty()) {
return random_value;
} else {
uint8_t v = random_sequence.front();
random_sequence.pop();
return v;
}
}
#ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA
static SHA256_CTX sha256_context;
nearby_platform_status nearby_platform_Sha256Start() {
SHA256_Init(&sha256_context);
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_Sha256Update(const void *data,
size_t length) {
SHA256_Update(&sha256_context, data, length);
return kNearbyStatusOK;
}
nearby_platform_status nearby_platform_Sha256Finish(uint8_t out[32]) {
SHA256_Final(out, &sha256_context);
return kNearbyStatusOK;
}
#endif /* NEARBY_FP_ENABLE_ADDITIONAL_DATA */
// Encrypts a data block with AES128 in ECB mode.
nearby_platform_status nearby_platform_Aes128Encrypt(const uint8_t input[16],
uint8_t output[16],
const uint8_t key[16]) {
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int input_length = 16;
int output_length = 16;
EVP_EncryptInit(ctx, EVP_aes_128_ecb(), key, NULL);
if (1 !=
EVP_EncryptUpdate(ctx, output, &output_length, input, input_length)) {
return kNearbyStatusError;
}
EVP_CIPHER_CTX_free(ctx);
return kNearbyStatusOK;
}
// Encrypts a data block with AES128 in ECB mode.
nearby_platform_status nearby_platform_Aes128Decrypt(const uint8_t input[16],
uint8_t output[16],
const uint8_t key[16]) {
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int input_length = 16;
int output_length = 16;
EVP_DecryptInit(ctx, EVP_aes_128_ecb(), key, NULL);
if (1 !=
EVP_DecryptUpdate(ctx, output, &output_length, input, input_length)) {
return kNearbyStatusError;
}
EVP_CIPHER_CTX_free(ctx);
return kNearbyStatusOK;
}
static EC_POINT *load_public_key(const uint8_t public_key[64]) {
BN_CTX *bn_ctx;
EC_KEY *key;
EC_POINT *point;
const EC_GROUP *group;
uint8_t oct_key[65];
oct_key[0] = 0x04;
memcpy(oct_key + 1, public_key, 64);
bn_ctx = BN_CTX_new();
key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
group = EC_KEY_get0_group(key);
point = EC_POINT_new(group);
if (1 != EC_POINT_oct2point(group, point, oct_key, sizeof(oct_key), bn_ctx))
return NULL;
BN_CTX_free(bn_ctx);
EC_KEY_free(key);
return point;
}
// Generates a shared sec256p1 secret using remote party public key and this
// device's private key.
nearby_platform_status nearby_platform_GenSec256r1Secret(
const uint8_t remote_party_public_key[64], uint8_t secret[32]) {
EVP_PKEY_CTX *ctx;
EVP_PKEY *peerkey;
EC_POINT *peer_point;
EC_KEY *ec_peer_key;
size_t secret_len;
/* Create the context for the shared secret derivation */
if (NULL == (ctx = EVP_PKEY_CTX_new(anti_spoofing_key.get(), NULL)))
return kNearbyStatusError;
/* Initialise */
if (1 != EVP_PKEY_derive_init(ctx)) return kNearbyStatusError;
if (NULL == (peer_point = load_public_key(remote_party_public_key)))
return kNearbyStatusError;
ec_peer_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (1 != EC_KEY_set_public_key(ec_peer_key, peer_point))
return kNearbyStatusError;
peerkey = EVP_PKEY_new();
if (1 != EVP_PKEY_assign_EC_KEY(peerkey, ec_peer_key))
return kNearbyStatusError;
/* Provide the peer public key */
if (1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) return kNearbyStatusError;
/* Determine buffer length for shared secret */
if (1 != EVP_PKEY_derive(ctx, NULL, &secret_len)) return kNearbyStatusError;
if (secret_len != 32) return kNearbyStatusError;
/* Derive the shared secret */
if (1 != (EVP_PKEY_derive(ctx, secret, &secret_len)))
return kNearbyStatusError;
EVP_PKEY_CTX_free(ctx);
EC_POINT_free(peer_point);
EVP_PKEY_free(peerkey);
std::cout << "Secret: " << ArrayToString(secret, 32) << std::endl;
return kNearbyStatusOK;
}
// Initializes secure element module
nearby_platform_status nearby_platform_SecureElementInit() {
random_value = 0;
random_sequence = std::queue<uint8_t>();
return kNearbyStatusOK;
}
void nearby_test_fakes_SetRandomNumber(unsigned int value) {
random_value = value;
}
void nearby_test_fakes_SetRandomNumberSequence(std::vector<uint8_t> &value) {
for (auto &v : value) random_sequence.push(v);
}
static BIGNUM *load_private_key(const uint8_t private_key[32]) {
uint8_t buffer[37];
buffer[0] = buffer[1] = buffer[2] = 0;
buffer[3] = 33;
buffer[4] = 0;
memcpy(buffer + 5, private_key, 32);
return BN_mpi2bn(buffer, sizeof(buffer), NULL);
}
nearby_platform_status nearby_test_fakes_SetAntiSpoofingKey(
const uint8_t private_key[32], const uint8_t public_key[64]) {
EC_KEY *key;
BIGNUM *prv;
EC_POINT *pub;
prv = load_private_key(private_key);
if (prv == NULL) return kNearbyStatusError;
pub = load_public_key(public_key);
if (pub == NULL) return kNearbyStatusError;
key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (1 != EC_KEY_set_private_key(key, prv)) return kNearbyStatusError;
if (1 != EC_KEY_set_public_key(key, pub)) return kNearbyStatusError;
anti_spoofing_key.reset(EVP_PKEY_new());
if (1 != EVP_PKEY_assign_EC_KEY(anti_spoofing_key.get(), key))
return kNearbyStatusError;
BN_free(prv);
EC_POINT_free(pub);
return kNearbyStatusOK;
}
nearby_platform_status nearby_test_fakes_GenSec256r1Secret(
const uint8_t remote_party_public_key[64], uint8_t secret[32]) {
return nearby_platform_GenSec256r1Secret(remote_party_public_key, secret);
}
nearby_platform_status nearby_test_fakes_Aes128Decrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]) {
return nearby_platform_Aes128Decrypt(input, output, key);
}
nearby_platform_status nearby_test_fakes_Aes128Encrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]) {
return nearby_platform_Aes128Encrypt(input, output, key);
}
+42
View File
@@ -0,0 +1,42 @@
// 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 <stdlib.h>
#include <cstdarg>
#include <cstdio>
#include <iostream>
#include <sstream>
#include "nearby_platform_trace.h"
void nearby_platform_Trace(nearby_platform_TraceLevel level,
const char *filename, int lineno, const char *fmt,
...) {
char buff[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buff, sizeof(buff), fmt, args);
std::cout << filename << ":" << lineno << " " << buff << std::endl;
va_end(args);
}
void nearby_platfrom_CrashOnAssert(const char *filename, int lineno,
const char *reason) {
fprintf(stderr, "ASSERT %s, %s:%d\n", reason, filename, lineno);
abort();
}
void nearby_platform_TraceInit(void) {}
@@ -0,0 +1,359 @@
// 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 <deque>
#include <iostream>
#include <vector>
#include "fakes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "nearby.h"
#include "nearby_message_stream.h"
#ifdef NEARBY_FP_MESSAGE_STREAM
constexpr uint64_t kPeerAddress = 0x101112;
constexpr size_t kBufferSize = 64;
constexpr size_t kMaxPayloadSize =
kBufferSize - sizeof(nearby_message_stream_Metadata);
constexpr size_t kHeaderSize = 4;
using ::testing::ElementsAreArray;
class StreamMessage {
public:
explicit StreamMessage(nearby_message_stream_Message* message)
: StreamMessage(message->message_group, message->message_code,
std::vector<uint8_t>(message->data,
message->data + message->length)) {}
StreamMessage(uint8_t group, uint8_t code) : group_(group), code_(code) {}
StreamMessage(uint8_t group, uint8_t code, std::vector<uint8_t> data)
: group_(group), code_(code), data_(data) {}
bool operator==(const StreamMessage& b) const {
return this->group_ == b.group_ && this->code_ == b.code_ &&
this->data_ == b.data_;
}
private:
unsigned int group_;
unsigned int code_;
std::vector<uint8_t> data_;
friend std::ostream& operator<<(std::ostream& os,
const StreamMessage& message);
};
static std::string VecToString(std::vector<uint8_t> data) {
std::stringstream output;
output << "0x" << std::hex;
for (int i = 0; i < data.size(); i++) {
output << std::setfill('0') << std::setw(2) << (unsigned)data[i];
}
return output.str();
}
std::ostream& operator<<(std::ostream& os, const StreamMessage& message) {
os << std::hex;
os << "group: 0x" << std::setfill('0') << std::setw(2) << message.group_;
os << " code: 0x" << std::setfill('0') << std::setw(2) << message.code_;
os << std::dec << " length: " << message.data_.size();
if (message.data_.size() > 0) {
os << " data: " << VecToString(message.data_);
}
return os;
}
uint8_t buffer[kBufferSize];
void OnMessageReceived(uint64_t peer_address,
nearby_message_stream_Message* message);
class MessageStreamTest : public ::testing::Test {
public:
void Read(const uint8_t* data, size_t length) {
nearby_message_stream_Read(&stream_state_, data, length);
}
void ReadByteByByte(const uint8_t* data, size_t length) {
for (int i = 0; i < length; i++) {
nearby_message_stream_Read(&stream_state_, data + i, 1);
}
}
void Send(const nearby_message_stream_Message* message) {
nearby_message_stream_Send(kPeerAddress, message);
}
void SendAck(const nearby_message_stream_Message* message) {
nearby_message_stream_SendAck(kPeerAddress, message);
}
void SendNack(const nearby_message_stream_Message* message,
uint8_t fail_reason) {
nearby_message_stream_SendNack(kPeerAddress, message, fail_reason);
}
protected:
void SetUp() override;
std::deque<StreamMessage> received_messages_;
private:
void AddMessage(nearby_message_stream_Message* message) {
received_messages_.emplace_back(StreamMessage(message));
}
// To allow access to |AddMessage|
friend void OnMessageReceived(uint64_t peer_address,
nearby_message_stream_Message* message);
nearby_message_stream_State stream_state_ = {
.on_message_received = OnMessageReceived,
.peer_address = kPeerAddress,
.length = sizeof(buffer),
.buffer = buffer};
} * test_fixture;
void MessageStreamTest::SetUp() {
test_fixture = this;
nearby_test_fakes_GetRfcommOutput().clear();
nearby_message_stream_Init(&stream_state_);
}
void OnMessageReceived(uint64_t peer_address,
nearby_message_stream_Message* message) {
EXPECT_EQ(kPeerAddress, peer_address);
test_fixture->AddMessage(message);
}
TEST_F(MessageStreamTest, ReadWholeMessageWithNoData) {
uint8_t group = 120;
uint8_t code = 130;
uint8_t message[] = {group, code, 0, 0};
Read(message, sizeof(message));
ASSERT_EQ(1, received_messages_.size());
ASSERT_EQ(StreamMessage(group, code), received_messages_[0]);
}
TEST_F(MessageStreamTest, ReadMessageInChunksWithNoData) {
uint8_t group = 120;
uint8_t code = 130;
uint8_t message[] = {group, code, 0, 0};
ReadByteByByte(message, sizeof(message));
ASSERT_EQ(1, received_messages_.size());
ASSERT_EQ(StreamMessage(group, code), received_messages_[0]);
}
TEST_F(MessageStreamTest, ReadWholeMessageSmallPayload) {
uint8_t group = 120;
uint8_t code = 130;
uint8_t message[] = {group, code, 0, 1, 30};
Read(message, sizeof(message));
ASSERT_EQ(1, received_messages_.size());
ASSERT_EQ(StreamMessage(group, code,
std::vector<uint8_t>(message + kHeaderSize,
message + sizeof(message))),
received_messages_[0]);
}
TEST_F(MessageStreamTest, ReadMessageInChunksSmallPayload) {
uint8_t group = 120;
uint8_t code = 130;
uint8_t message[] = {group, code, 0, 1, 30};
ReadByteByByte(message, sizeof(message));
ASSERT_EQ(1, received_messages_.size());
ASSERT_EQ(StreamMessage(group, code,
std::vector<uint8_t>(message + kHeaderSize,
message + sizeof(message))),
received_messages_[0]);
}
TEST_F(MessageStreamTest, ReadWholeMessageMaximumPayloadSize) {
uint8_t group = 120;
uint8_t code = 130;
uint8_t message[kHeaderSize + kMaxPayloadSize];
message[0] = group;
message[1] = code;
message[2] = kMaxPayloadSize >> 8;
message[3] = kMaxPayloadSize;
for (unsigned i = 0; i < kMaxPayloadSize; i++) {
message[kHeaderSize + i] = i;
}
Read(message, sizeof(message));
ASSERT_EQ(1, received_messages_.size());
ASSERT_EQ(StreamMessage(group, code,
std::vector<uint8_t>(message + kHeaderSize,
message + sizeof(message))),
received_messages_[0]);
}
TEST_F(MessageStreamTest, ReadWholeMessageTruncatedPayload) {
uint8_t group = 120;
uint8_t code = 130;
constexpr size_t kPayloadSize = 0x102;
uint8_t message[kHeaderSize + kPayloadSize];
message[0] = group;
message[1] = code;
message[2] = 0x01;
message[3] = 0x02;
for (unsigned i = 0; i < kPayloadSize; i++) {
message[kHeaderSize + i] = i;
}
Read(message, sizeof(message));
ASSERT_EQ(1, received_messages_.size());
ASSERT_EQ(StreamMessage(
group, code,
std::vector<uint8_t>(message + kHeaderSize,
message + kHeaderSize + kMaxPayloadSize)),
received_messages_[0]);
}
TEST_F(MessageStreamTest, ReadMessageInChunksTruncatedPayload) {
uint8_t group = 120;
uint8_t code = 130;
constexpr size_t kPayloadSize = 0x102;
uint8_t message[kHeaderSize + kPayloadSize];
message[0] = group;
message[1] = code;
message[2] = 0x01;
message[3] = 0x02;
for (unsigned i = 0; i < kPayloadSize; i++) {
message[kHeaderSize + i] = i;
}
ReadByteByByte(message, sizeof(message));
ASSERT_EQ(1, received_messages_.size());
ASSERT_EQ(StreamMessage(
group, code,
std::vector<uint8_t>(message + kHeaderSize,
message + kHeaderSize + kMaxPayloadSize)),
received_messages_[0]);
}
TEST_F(MessageStreamTest, ReadTwoMessages) {
uint8_t group = 120;
uint8_t code = 130;
uint8_t group2 = 121;
uint8_t code2 = 131;
uint8_t message[] = {group, code, 0, 1, 30, group2, code2, 0, 2, 31, 32};
Read(message, sizeof(message));
ASSERT_EQ(2, received_messages_.size());
ASSERT_EQ(StreamMessage(group, code,
std::vector<uint8_t>(message + 4, message + 5)),
received_messages_[0]);
ASSERT_EQ(StreamMessage(group2, code2,
std::vector<uint8_t>(message + 9, message + 11)),
received_messages_[1]);
}
TEST_F(MessageStreamTest, ReadTwoMessagesByteByByte) {
uint8_t group = 120;
uint8_t code = 130;
uint8_t group2 = 121;
uint8_t code2 = 131;
uint8_t message[] = {group, code, 0, 1, 30, group2, code2, 0, 2, 31, 32};
ReadByteByByte(message, sizeof(message));
ASSERT_EQ(2, received_messages_.size());
ASSERT_EQ(StreamMessage(group, code,
std::vector<uint8_t>(message + 4, message + 5)),
received_messages_[0]);
ASSERT_EQ(StreamMessage(group2, code2,
std::vector<uint8_t>(message + 9, message + 11)),
received_messages_[1]);
}
TEST_F(MessageStreamTest, SendMessageNoPayload) {
nearby_message_stream_Message message{
.message_group = 10,
.message_code = 20,
.length = 0,
.data = nullptr,
};
constexpr uint8_t kExpectedOutput[] = {10, 20, 0, 0};
Send(&message);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
}
TEST_F(MessageStreamTest, SendMessageWithPayload) {
uint8_t payload[] = {20, 21, 22, 23, 24};
nearby_message_stream_Message message{
.message_group = 10,
.message_code = 11,
.length = sizeof(payload),
.data = payload,
};
constexpr uint8_t kExpectedOutput[] = {10, 11, 0, sizeof(payload), 20, 21,
22, 23, 24};
Send(&message);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
}
TEST_F(MessageStreamTest, SendAck) {
nearby_message_stream_Message message{
.message_group = 10,
.message_code = 20,
.length = 0,
.data = nullptr,
};
constexpr uint8_t kExpectedOutput[] = {0xFF, 1, 0, 2, 10, 20};
SendAck(&message);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
}
TEST_F(MessageStreamTest, SendNack) {
nearby_message_stream_Message message{
.message_group = 10,
.message_code = 20,
.length = 0,
.data = nullptr,
};
constexpr uint8_t kFailReason = 0x88;
constexpr uint8_t kExpectedOutput[] = {0xFF, 2, 0, 3, kFailReason, 10, 20};
SendNack(&message, kFailReason);
ASSERT_THAT(kExpectedOutput,
ElementsAreArray(nearby_test_fakes_GetRfcommOutput()));
}
#endif /* NEARBY_FP_MESSAGE_STREAM */
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
// 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.
#ifndef NEARBY_ASSERT_H
#define NEARBY_ASSERT_H
#include "nearby.h"
#include "nearby_platform_trace.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef __NEARBY_SHORT_FILE__
#define NEARBY_ASSERT(cond) \
do { \
if (!(cond)) { \
nearby_platfrom_CrashOnAssert(__NEARBY_SHORT_FILE__, __LINE__, #cond); \
} \
} while (0)
#else
#define NEARBY_ASSERT(cond) \
do { \
if (!(cond)) { \
nearby_platfrom_CrashOnAssert(__FILE__, __LINE__, #cond); \
} \
} while (0)
#endif
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_ASSERT_H */
+46
View File
@@ -0,0 +1,46 @@
// 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.
#ifndef NEARBY_CONFIG_H
#define NEARBY_CONFIG_H
// Support FP Battery Notification extension
#define NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
// Support FP Additional Data extension
#define NEARBY_FP_ENABLE_ADDITIONAL_DATA
// Personalized name max size in bytes
#define PERSONALIZED_NAME_MAX_SIZE 64
// Support FP Message Stream extension
#define NEARBY_FP_MESSAGE_STREAM
// Does the platform have a native BLE address rotation routine?
// #define NEARBY_FP_HAVE_BLE_ADDRESS_ROTATION
// The maximum size in bytes of additional data in a message in Message Stream.
// Bigger payloads will be truncated.
#define MAX_MESSAGE_STREAM_PAYLOAD_SIZE 8
// The maximum number of concurrent RFCOMM connections
#define NEARBY_MAX_RFCOMM_CONNECTIONS 2
// Support Retroactive pairing extension
#define NEARBY_FP_RETROACTIVE_PAIRING
// The maximum number of concurrent retroactive pairing process
#define NEARBY_MAX_RETROACTIVE_PAIRING 2
#endif /* NEARBY_CONFIG_H */
+140
View File
@@ -0,0 +1,140 @@
// 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.
#ifndef NEARBY_EVENT_H
#define NEARBY_EVENT_H
#include "nearby.h"
// Types of events that can be emitted by Nearby library
typedef enum {
// A client connected over RFCOMM to the message stream endpoint defined in
// Fast Pair specification
// Payload: |nearby_event_MessageStreamConnected|
kNearbyEventMessageStreamConnected = 0,
// An RFCOMM client disconnected
// Payload: |nearby_event_MessageStreamDisonnected|
kNearbyEventMessageStreamDisconnected,
// A client has sent a message over the message stream. Some incoming messages
// are consumed by the Nearby library, in which case the event is not emitted.
// Payload: |nearby_event_MessageStreamReceived|
kNearbyEventMessageStreamReceived
} nearby_event_Type;
// An event emitted by Nearby library
typedef struct {
// The type of the event
nearby_event_Type event_type;
// Optional event payload. |payload| should be cast to one the structs defined
// below.
uint8_t *payload;
} nearby_event_Event;
// Payload for |kNearbyEventMessageStreamConnected| event.
typedef struct {
// The client BT address
uint64_t peer_address;
} nearby_event_MessageStreamConnected;
// Payload for |kNearbyEventMessageStreamDisconnected| event.
typedef struct {
// The client BT address
uint64_t peer_address;
} nearby_event_MessageStreamDisconnected;
// Message group Bluetooth
#define MESSAGE_GROUP_BLUETOOTH 1
// If the device supports silence mode, the client app should send either
// ENABLE_SILENCE_MODE or DISABLE_SILENCE_MODE message to the seeker once RFCOMM
// is connected to ensure the correct state is propagated.
// Direction: Provider -> Seeker
// Handled by: Client app
#define MESSAGE_CODE_ENABLE_SILENCE_MODE 1
// Direction: Provider -> Seeker
// Handled by: Client app
#define MESSAGE_CODE_DISABLE_SILENCE_MODE 2
// Message group Companion App Event
#define MESSAGE_GROUP_COMPANION_APP_EVENT 2
// Direction: Provider -> Seeker
// Handled by: Client app
#define MESSAGE_CODE_LOG_BUFFER_FULL 1
// Message group Device Information Event
#define MESSAGE_GROUP_DEVICE_INFORMATION_EVENT 3
// Direction: Provider -> Seeker
// Handled by: Nearby Fast Pair library
#define MESSAGE_CODE_MODEL_ID 1
// Direction: Provider -> Seeker
// Handled by: Nearby Fast Pair library
#define MESSAGE_CODE_BLE_ADDRESS_UPDATED 2
// Direction: Provider -> Seeker
// Handled by: Nearby Fast Pair library
#define MESSAGE_CODE_BATTERY_UPDATED 3
// Direction: Provider -> Seeker
// Handled by: Nearby Fast Pair library
#define MESSAGE_CODE_REMAINING_BATTERY_TIME 4
// Direction: Seeker -> Provider
// Handled by: Client app
#define MESSAGE_CODE_ACTIVE_COMPONENT_REQUEST 5
// Direction: Provider -> Seeker
// Handled by: Client app
#define MESSAGE_CODE_ACTIVE_COMPONENT_RESPONSE 6
// Direction: Provider -> Seeker
// Handled by: Client app
#define MESSAGE_CODE_CAPABILITIES 7
#define MESSAGE_CODE_CAPABILITIES_COMPANION_APP_INSTALLED 0
#define MESSAGE_CODE_CAPABILITIES_SILENCE_MODE_SUPPORTED 1
// Direction: Seeker -> Provider
// Handled by: Client app
#define MESSAGE_CODE_PLATFORM_TYPE 8
// Message group Device Action Event
#define MESSAGE_GROUP_DEVICE_ACTION_EVENT 4
// Direction: Seeker -> Provider
// Handled by: Client app
#define MESSAGE_CODE_RING 1
#define MESSAGE_CODE_RING_LEFT 0
#define MESSAGE_CODE_RING_RIGHT 1
// Payload for |kNearbyEventMessageStreamReceived| event. See
// https://developers.google.com/nearby/fast-pair/spec#MessageStream
typedef struct {
// The peer BT address
uint64_t peer_address;
// Message group
uint8_t message_group;
// Message code
uint8_t message_code;
// If |length| is 0, then there's no additional data in the message
size_t length;
// Optional. Addtional message data
uint8_t *data;
} nearby_event_MessageStreamReceived;
#endif /* NEARBY_EVENT_H */
+452
View File
@@ -0,0 +1,452 @@
// 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 "nearby_fp_library.h"
#include <string.h>
#include "nearby.h"
#include "nearby_assert.h"
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#include "nearby_platform_battery.h"
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
#include "nearby_platform_bt.h"
#include "nearby_platform_persistence.h"
#include "nearby_platform_se.h"
#include "nearby_trace.h"
#include "nearby_utils.h"
#define ACCOUNT_KEY_LIST_SIZE_BYTES 81
#define SHOW_PAIRING_INDICATION_BYTE 0
#define DONT_SHOW_PAIRING_INDICATION_BYTE 2
#define SHOW_BATTERY_INDICATION_BYTE 0x33
#define DONT_SHOW_BATTERY_INDICATION_BYTE 0x34
#define BATTERY_INFO_CHARGING (1 << 7)
#define BATTERY_INFO_NOT_CHARGING 0
// In the battery values, the highest bit indicates charging, the lower 7 are
// the battery level
#define BATTERY_LEVEL_MASK 0x7F
#define SALT_FIELD_LENGTH_AND_TYPE_BYTE 0x11
#define SALT_SIZE_BYTES 1
#define BATTERY_INFO_SIZE_BYTES 4
#define KEY_BASED_PAIRING_RESPONSE_FLAG 0x01
#define GAP_DATA_TYPE_SERVICE_DATA_UUID 0x16
#define FP_SERVICE_UUID 0xFE2C
#define GAP_DATA_TYPE_TX_POWER_LEVEL_UUID 0x0A
#define TX_POWER_DATA_SIZE 2
static uint8_t account_key_list[ACCOUNT_KEY_LIST_SIZE_BYTES];
static uint8_t sha_buffer[32];
static uint8_t key_and_salt[ACCOUNT_KEY_SIZE_BYTES + SALT_SIZE_BYTES +
BATTERY_INFO_SIZE_BYTES];
static size_t GetAccountKeyListUsedSize() {
return nearby_fp_GetAccountKeyOffset(nearby_fp_GetAccountKeyCount());
}
size_t nearby_fp_GetAccountKeyCount() { return account_key_list[0]; }
size_t nearby_fp_GetAccountKeyOffset(unsigned key_number) {
return 1 + key_number * ACCOUNT_KEY_SIZE_BYTES;
}
const uint8_t* nearby_fp_GetAccountKey(unsigned key_number) {
NEARBY_ASSERT(key_number < nearby_fp_GetAccountKeyCount());
return account_key_list + nearby_fp_GetAccountKeyOffset(key_number);
}
void nearby_fp_MarkAccountKeyAsActive(unsigned key_number) {
NEARBY_ASSERT(key_number < nearby_fp_GetAccountKeyCount());
uint8_t tmp[ACCOUNT_KEY_SIZE_BYTES];
if (key_number == 0) return;
// Move the key to the top of the list
nearby_fp_CopyAccountKey(tmp, key_number);
memmove(account_key_list + nearby_fp_GetAccountKeyOffset(1),
account_key_list + nearby_fp_GetAccountKeyOffset(0),
key_number * ACCOUNT_KEY_SIZE_BYTES);
memcpy(account_key_list + nearby_fp_GetAccountKeyOffset(0), tmp,
ACCOUNT_KEY_SIZE_BYTES);
}
void nearby_fp_CopyAccountKey(uint8_t* dest, unsigned key_number) {
size_t offset = nearby_fp_GetAccountKeyOffset(key_number);
NEARBY_ASSERT(offset + ACCOUNT_KEY_SIZE_BYTES <= ACCOUNT_KEY_LIST_SIZE_BYTES);
memcpy(dest, account_key_list + offset, ACCOUNT_KEY_SIZE_BYTES);
}
static unsigned combineNibbles(unsigned high, unsigned low) {
return ((high << 4) & 0xF0) | (low & 0x0F);
}
void nearby_fp_AddAccountKey(const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]) {
// Find if the key is already on the list
unsigned i;
size_t key_count;
size_t length;
size_t max_bytes;
key_count = nearby_fp_GetAccountKeyCount();
for (i = 0; i < key_count; i++) {
if (!memcmp(key, nearby_fp_GetAccountKey(i), ACCOUNT_KEY_SIZE_BYTES)) {
nearby_fp_MarkAccountKeyAsActive(i);
return;
}
}
// Insert `key` at the list top
length = key_count * ACCOUNT_KEY_SIZE_BYTES;
max_bytes = ACCOUNT_KEY_LIST_SIZE_BYTES - nearby_fp_GetAccountKeyOffset(1);
if (length > max_bytes) {
// Buffer is full, the last key will fall off the edge
length = max_bytes;
} else {
// We have room for one more key
account_key_list[0]++;
}
memmove(account_key_list + nearby_fp_GetAccountKeyOffset(1),
account_key_list + nearby_fp_GetAccountKeyOffset(0), length);
memcpy(account_key_list + nearby_fp_GetAccountKeyOffset(0), key,
ACCOUNT_KEY_SIZE_BYTES);
}
size_t nearby_fp_CreateDiscoverableAdvertisement(uint8_t* output,
size_t length) {
NEARBY_ASSERT(length >= DISCOVERABLE_ADV_SIZE_BYTES);
size_t i = 0;
// data size
output[i++] = 1 + FP_SERVICE_UUID_SIZE + FP_MODEL_ID_SIZE;
// data type service
output[i++] = GAP_DATA_TYPE_SERVICE_DATA_UUID;
// service data uuid
nearby_utils_CopyLittleEndian(output + i, FP_SERVICE_UUID,
FP_SERVICE_UUID_SIZE);
i += FP_SERVICE_UUID_SIZE;
// service data
uint32_t model = nearby_platform_GetModelId();
nearby_utils_CopyBigEndian(output + i, model, FP_MODEL_ID_SIZE);
i += FP_MODEL_ID_SIZE;
return i;
}
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
void SerializeBatteryInfo(uint8_t* output,
const nearby_platform_BatteryInfo* battery_info) {
uint8_t charging = battery_info->is_charging ? BATTERY_INFO_CHARGING
: BATTERY_INFO_NOT_CHARGING;
output[0] =
charging | (battery_info->left_bud_battery_level & BATTERY_LEVEL_MASK);
output[1] =
charging | (battery_info->right_bud_battery_level & BATTERY_LEVEL_MASK);
output[2] = charging |
(battery_info->charging_case_battery_level & BATTERY_LEVEL_MASK);
}
static void AddBatteryInfo(uint8_t* output, size_t length,
bool show_ui_indicator,
const nearby_platform_BatteryInfo* battery_info) {
NEARBY_ASSERT(length >= BATTERY_INFO_SIZE_BYTES);
output[0] = show_ui_indicator ? SHOW_BATTERY_INDICATION_BYTE
: DONT_SHOW_BATTERY_INDICATION_BYTE;
SerializeBatteryInfo(output + 1, battery_info);
}
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
static size_t CreateNondiscoverableAdvertisement(
uint8_t* output, size_t length, bool show_pairing_indicator
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
,
bool show_battery_indicator, const nearby_platform_BatteryInfo* battery_info
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
) {
NEARBY_TRACE(VERBOSE, "nearby_fp_CreateNondiscoverableAdvertisement");
const unsigned kHeaderSize = FP_SERVICE_UUID_SIZE + 2;
NEARBY_ASSERT(length >= kHeaderSize);
unsigned i = 1;
// service data UUID
output[i++] = GAP_DATA_TYPE_SERVICE_DATA_UUID;
// service uuid
nearby_utils_CopyLittleEndian(output + i, FP_SERVICE_UUID,
FP_SERVICE_UUID_SIZE);
i += FP_SERVICE_UUID_SIZE;
// service data
uint8_t salt = nearby_platform_Rand();
size_t n = nearby_fp_GetAccountKeyCount();
if (n == 0) {
const unsigned kMessageSize = 2;
NEARBY_ASSERT(length >= i + kMessageSize);
output[i++] = 0x00;
output[i++] = 0x00;
} else {
const unsigned kFlagFilterAndSaltSize = 4;
const size_t s = (6 * n + 15) / 5;
const size_t kAccountKeyDataSize = s + kFlagFilterAndSaltSize;
NEARBY_ASSERT(length >= i + kAccountKeyDataSize);
unsigned used_key_and_salt_size = ACCOUNT_KEY_SIZE_BYTES + SALT_SIZE_BYTES;
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
// We need to add the battery info first because it's used as salt
if (battery_info != NULL) {
uint8_t battery_chunk_offset = i + kAccountKeyDataSize;
uint8_t* battery_chunk = output + battery_chunk_offset;
used_key_and_salt_size += BATTERY_INFO_SIZE_BYTES;
AddBatteryInfo(battery_chunk, length - battery_chunk_offset,
show_battery_indicator, battery_info);
memcpy(key_and_salt + ACCOUNT_KEY_SIZE_BYTES + SALT_SIZE_BYTES,
battery_chunk, BATTERY_INFO_SIZE_BYTES);
}
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
// flags
output[i++] = 0;
// filter length and type
output[i++] = combineNibbles(s, show_pairing_indicator
? SHOW_PAIRING_INDICATION_BYTE
: DONT_SHOW_PAIRING_INDICATION_BYTE);
memset(output + i, 0, s);
key_and_salt[ACCOUNT_KEY_SIZE_BYTES] = salt;
unsigned k, j;
for (k = 0; k < n; k++) {
nearby_fp_CopyAccountKey(key_and_salt, k);
nearby_fp_Sha256(sha_buffer, key_and_salt, used_key_and_salt_size);
for (j = 0; j < 8; j++) {
uint32_t x = nearby_utils_GetBigEndian32(sha_buffer + 4 * j);
uint32_t m = x % (s * 8);
output[i + (m / 8)] |= (1 << (m % 8));
}
}
i += s;
output[i++] = SALT_FIELD_LENGTH_AND_TYPE_BYTE;
output[i++] = salt;
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
if (battery_info != NULL) {
i += BATTERY_INFO_SIZE_BYTES;
}
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
}
// service data size
output[0] = i - 1;
return i;
}
size_t nearby_fp_CreateNondiscoverableAdvertisement(
uint8_t* output, size_t length, bool show_pairing_indicator) {
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
return CreateNondiscoverableAdvertisement(
output, length, show_pairing_indicator, false, NULL);
#else
return CreateNondiscoverableAdvertisement(output, length,
show_pairing_indicator);
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
}
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
// |battery_info| can be NULL
size_t nearby_fp_CreateNondiscoverableAdvertisementWithBattery(
uint8_t* output, size_t length, bool show_pairing_indicator,
bool show_battery_indicator,
const nearby_platform_BatteryInfo* battery_info) {
return CreateNondiscoverableAdvertisement(
output, length, show_pairing_indicator, show_battery_indicator,
battery_info);
}
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
size_t nearby_fp_AppendTxPower(uint8_t* advertisement, size_t length,
int8_t tx_power) {
size_t offset = 0;
NEARBY_ASSERT(length >= 1 + TX_POWER_DATA_SIZE);
// tx power level data size
advertisement[offset++] = TX_POWER_DATA_SIZE;
// tx power level UUID
advertisement[offset++] = GAP_DATA_TYPE_TX_POWER_LEVEL_UUID;
// tx power level
advertisement[offset++] = tx_power;
return offset;
}
nearby_platform_status nearby_fp_LoadAccountKeys() {
size_t length = sizeof(account_key_list);
memset(account_key_list, 0, length);
return nearby_platform_LoadValue(kStoredKeyAccountKeyList, account_key_list,
&length);
}
nearby_platform_status nearby_fp_SaveAccountKeys() {
return nearby_platform_SaveValue(kStoredKeyAccountKeyList, account_key_list,
GetAccountKeyListUsedSize());
}
nearby_platform_status nearby_fp_GattReadModelId(uint8_t* output,
size_t* length) {
NEARBY_ASSERT(*length >= FP_MODEL_ID_SIZE);
uint32_t model = nearby_platform_GetModelId();
nearby_utils_CopyBigEndian(output, model, FP_MODEL_ID_SIZE);
*length = FP_MODEL_ID_SIZE;
return kNearbyStatusOK;
}
nearby_platform_status nearby_fp_CreateSharedSecret(
const uint8_t remote_public_key[64],
uint8_t output[ACCOUNT_KEY_SIZE_BYTES]) {
nearby_platform_status status;
uint8_t secret[32];
uint8_t hash[32];
status = nearby_platform_GenSec256r1Secret(remote_public_key, secret);
if (status != kNearbyStatusOK) return status;
status = nearby_fp_Sha256(hash, secret, sizeof(secret));
if (status != kNearbyStatusOK) return status;
memcpy(output, hash, ACCOUNT_KEY_SIZE_BYTES);
return status;
}
nearby_platform_status nearby_fp_CreateRawKeybasedPairingResponse(
uint8_t output[AES_MESSAGE_SIZE_BYTES]) {
output[0] = KEY_BASED_PAIRING_RESPONSE_FLAG;
nearby_utils_CopyBigEndian(output + 1, nearby_platform_GetPublicAddress(),
BT_ADDRESS_LENGTH);
int i;
for (i = 1 + BT_ADDRESS_LENGTH; i < AES_MESSAGE_SIZE_BYTES; i++) {
output[i] = nearby_platform_Rand();
}
return kNearbyStatusOK;
}
#ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA
#define HMAC_SHA256_KEY_SIZE 64
#define OPAD 0x5C
#define IPAD 0x36
#define NONCE_SIZE 8
#define ADDITIONAL_DATA_SHA_SIZE 8
static void PadKey(uint8_t output[HMAC_SHA256_KEY_SIZE], const uint8_t* key,
size_t key_length, uint8_t pad) {
int i;
for (i = 0; i < key_length; i++) {
*output++ = *key++ ^ pad;
}
memset(output, pad, HMAC_SHA256_KEY_SIZE - key_length);
}
#define RETURN_IF_ERROR(X) \
do { \
nearby_platform_status status = X; \
if (kNearbyStatusOK != status) return status; \
} while (0)
static nearby_platform_status HmacSha256(uint8_t out[32],
uint8_t hmac_key[HMAC_SHA256_KEY_SIZE],
const uint8_t* data,
size_t data_length) {
RETURN_IF_ERROR(nearby_platform_Sha256Start());
RETURN_IF_ERROR(nearby_platform_Sha256Update(hmac_key, HMAC_SHA256_KEY_SIZE));
RETURN_IF_ERROR(nearby_platform_Sha256Update(data, data_length));
RETURN_IF_ERROR(nearby_platform_Sha256Finish(out));
return kNearbyStatusOK;
}
nearby_platform_status nearby_fp_HmacSha256(uint8_t out[32], const uint8_t* key,
size_t key_length,
const uint8_t* data,
size_t data_length) {
uint8_t hmac_key[HMAC_SHA256_KEY_SIZE];
// out = HASH(Key XOR ipad, data)
PadKey(hmac_key, key, key_length, IPAD);
RETURN_IF_ERROR(HmacSha256(out, hmac_key, data, data_length));
// out = HASH(Key XOR opad, out)
PadKey(hmac_key, key, key_length, OPAD);
return HmacSha256(out, hmac_key, out, 32);
}
nearby_platform_status nearby_fp_AesCtr(
uint8_t* message, size_t message_length,
const uint8_t key[AES_MESSAGE_SIZE_BYTES]) {
uint8_t key_stream[AES_MESSAGE_SIZE_BYTES];
uint8_t iv[AES_MESSAGE_SIZE_BYTES];
size_t offset = NONCE_SIZE;
memset(iv, 0, AES_MESSAGE_SIZE_BYTES - NONCE_SIZE);
memcpy(iv + AES_MESSAGE_SIZE_BYTES - NONCE_SIZE, message, NONCE_SIZE);
while (offset < message_length) {
int i;
int bytes_left = message_length - offset;
RETURN_IF_ERROR(nearby_platform_Aes128Encrypt(iv, key_stream, key));
for (i = 0; i < bytes_left && i < sizeof(key_stream); i++) {
message[offset++] ^= key_stream[i];
}
iv[0]++;
}
return kNearbyStatusOK;
}
nearby_platform_status nearby_fp_DecodeAdditionalData(
uint8_t* data, size_t length, const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]) {
NEARBY_ASSERT(length > ADDITIONAL_DATA_SHA_SIZE + NONCE_SIZE);
uint8_t sha[32];
RETURN_IF_ERROR(nearby_fp_HmacSha256(sha, key, ACCOUNT_KEY_SIZE_BYTES,
data + ADDITIONAL_DATA_SHA_SIZE,
length - ADDITIONAL_DATA_SHA_SIZE));
if (memcmp(sha, data, ADDITIONAL_DATA_SHA_SIZE) != 0) {
NEARBY_TRACE(WARNING, "Additional Data SHA check failed");
return kNearbyStatusError;
}
return nearby_fp_AesCtr(data + ADDITIONAL_DATA_SHA_SIZE,
length - ADDITIONAL_DATA_SHA_SIZE, key);
}
nearby_platform_status nearby_fp_EncodeAdditionalData(
uint8_t* data, size_t length, const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]) {
NEARBY_ASSERT(length >= ADDITIONAL_DATA_SHA_SIZE + NONCE_SIZE);
uint8_t sha[32];
int i;
for (i = 0; i < NONCE_SIZE; i++) {
data[ADDITIONAL_DATA_SHA_SIZE + i] = nearby_platform_Rand();
}
RETURN_IF_ERROR(nearby_fp_AesCtr(data + ADDITIONAL_DATA_SHA_SIZE,
length - ADDITIONAL_DATA_SHA_SIZE, key));
RETURN_IF_ERROR(nearby_fp_HmacSha256(sha, key, ACCOUNT_KEY_SIZE_BYTES,
data + ADDITIONAL_DATA_SHA_SIZE,
length - ADDITIONAL_DATA_SHA_SIZE));
memcpy(data, sha, ADDITIONAL_DATA_SHA_SIZE);
return kNearbyStatusOK;
}
#endif /* NEARBY_FP_ENABLE_ADDITIONAL_DATA */
// Computes sha256 sum.
nearby_platform_status nearby_fp_Sha256(uint8_t out[32], const void* data,
size_t length) {
nearby_platform_status status = nearby_platform_Sha256Start();
if (status == kNearbyStatusOK) {
status = nearby_platform_Sha256Update(data, length);
if (status == kNearbyStatusOK) {
status = nearby_platform_Sha256Finish(out);
}
}
return status;
}
+187
View File
@@ -0,0 +1,187 @@
// 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.
#ifndef NEARBY_FP_LIBRARY_H
#define NEARBY_FP_LIBRARY_H
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby.h"
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
#include "nearby_platform_battery.h"
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define BT_ADDRESS_LENGTH 6
#define DISCOVERABLE_ADV_SIZE_BYTES 10
// Sufficient for 5 account keys and battery notification
#define NON_DISCOVERABLE_ADV_SIZE_BYTES 24
#define ADDITIONAL_DATA_HEADER_SIZE 16
// Creates advertisement with the Model Id. Returns the number of bytes written
// to |output|.
//
// output - Advertisement data output buffer.
// length - Length of output buffer.
size_t nearby_fp_CreateDiscoverableAdvertisement(uint8_t* output,
size_t length);
// Creates advertisement with Account Key Data. Returns the number of bytes
// written to |output|.
//
// output - Advertisement data output buffer.
// length - Length of data output buffer.
// show_ui_indicator - Ask seeker to show UI indication.
size_t nearby_fp_CreateNondiscoverableAdvertisement(
uint8_t* output, size_t length, bool show_pairing_indicator);
#ifdef NEARBY_FP_ENABLE_BATTERY_NOTIFICATION
void SerializeBatteryInfo(uint8_t* output,
const nearby_platform_BatteryInfo* battery_info);
// Creates advertisement with Account Key Data and optionally battery info.
// |battery_info| can be NULL. Returns the number of bytes written to |output|.
//
// output - Advertisement data output buffer.
// length - Length of output buffer.
// show_ui_indicator - Ask seeker to show UI indication.
// battery_info - Battery status data structure.
size_t nearby_fp_CreateNondiscoverableAdvertisementWithBattery(
uint8_t* output, size_t length, bool show_pairing_indicator,
bool show_battery_indicator,
const nearby_platform_BatteryInfo* battery_info);
#endif /* NEARBY_FP_ENABLE_BATTERY_NOTIFICATION */
// Appends TX power stanza to the advertisement. |Advertisement| should point
// past the end of advertisement created with one of the 'Create*' functions.
//
// advertisement - Pointer to end of advertising data.
// length - Remaining length of advertising data.
// power - Power level.
size_t nearby_fp_AppendTxPower(uint8_t* advertisement, size_t length,
int8_t tx_power);
// Loads account keys from NV storage.
nearby_platform_status nearby_fp_LoadAccountKeys();
// Saves account keys to NV storage.
nearby_platform_status nearby_fp_SaveAccountKeys();
// Gets Specific key by number to buffer
//
// dest - Buffer for key fetched.
// key_number - Number of key to fetch.
void nearby_fp_CopyAccountKey(uint8_t* dest, unsigned key_number);
// Gets number of active keys.
size_t nearby_fp_GetAccountKeyCount();
// Gets offset of key by key number. Returns the offset.
//
// key_number - ordinal number of key to return.
size_t nearby_fp_GetAccountKeyOffset(unsigned key_number);
// Gets pointer to given key by ordinal number.
//
// key_number - Ordinal number of key to return.
const uint8_t* nearby_fp_GetAccountKey(unsigned key_number);
// Marks account key as active by moving it to the top of the key list.
//
// key_number - Original number of key to mark active.
void nearby_fp_MarkAccountKeyAsActive(unsigned key_number);
// Adds key to key list. Inserts key to top of list.
//
// key - Buffer containing key to insert.
void nearby_fp_AddAccountKey(const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]);
// Gets fast pair model ID
//
// output - Buffer to hold model ID.
// length - On input, contains the length of the buffer.
// On output, returns the length of the model ID returned.
nearby_platform_status nearby_fp_GattReadModelId(uint8_t* output,
size_t* length);
// Creates shared secret from remote public key.
//
// remote_public_key - 512 bit peer public key.
// output - Buffer that returns the shared secret.
nearby_platform_status nearby_fp_CreateSharedSecret(
const uint8_t remote_public_key[64],
uint8_t output[ACCOUNT_KEY_SIZE_BYTES]);
// Creates raw key based paring response.
//
// output - Buffer returning the pairing response.
nearby_platform_status nearby_fp_CreateRawKeybasedPairingResponse(
uint8_t output[AES_MESSAGE_SIZE_BYTES]);
#ifdef NEARBY_FP_ENABLE_ADDITIONAL_DATA
// Calculates HMAC SHA256 hash.
//
// out - Output buffer for hash.
// key - Input key to calculate hash.
// key_length - Length of key.
// data - Data to calculate hash.
// data_lenth - Data length.
nearby_platform_status nearby_fp_HmacSha256(uint8_t out[32], const uint8_t* key,
size_t key_length,
const uint8_t* data,
size_t data_length);
// The first 8 bytes of the message are the nonce. The message is
// encrypted/decrypted in place.
//
// message - Message buffer in/out.
// message_length - Length of message.
// key - Key.
nearby_platform_status nearby_fp_AesCtr(
uint8_t* message, size_t message_length,
const uint8_t key[AES_MESSAGE_SIZE_BYTES]);
// Decodes data package read from Additional Data characteristics. The decoding
// happens in-place. Returns an error if HMAC-SHA checksum is invalid.
//
// data - Data to decode.
// length - Length of data.
// key - Key.
nearby_platform_status nearby_fp_DecodeAdditionalData(
uint8_t* data, size_t length, const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]);
// Encodes data package for writing to Additional Data characteristics. The
// encoding happens in-place. The first 16 bytes of the data buffer are used for
// checksum and nonce, so the actual message must begin at offset 16.
//
// data - Data to encode.
// length - Length of data.
// key - Key.
nearby_platform_status nearby_fp_EncodeAdditionalData(
uint8_t* data, size_t length, const uint8_t key[ACCOUNT_KEY_SIZE_BYTES]);
#endif /* NEARBY_FP_ENABLE_ADDITIONAL_DATA */
// Computes sha256 sum.
//
// out - Output 256 bit sum.
// data - Data to compute sha256 sum over.
// length - Length of data.
nearby_platform_status nearby_fp_Sha256(uint8_t out[32], const void* data,
size_t length);
#ifdef __cplusplus
}
#endif
#endif // NEARBY_FP_LIBRARY_H
@@ -0,0 +1,126 @@
// 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 "nearby_message_stream.h"
#include "nearby_platform_bt.h"
#define HEADER_SIZE 4
#define ACK_MESSAGE_SIZE 6
#define NACK_MESSAGE_SIZE 7
#define ACKNOWLEDGEMENT_GROUP 0xFF
#define ACK_CODE 1
#define NACK_CODE 2
#ifdef NEARBY_FP_MESSAGE_STREAM
void nearby_message_stream_Init(const nearby_message_stream_State* state) {
nearby_message_stream_Metadata* metadata =
(nearby_message_stream_Metadata*)state->buffer;
memset(metadata, 0, sizeof(nearby_message_stream_Metadata));
metadata->message.data =
state->buffer + sizeof(nearby_message_stream_Metadata);
}
void nearby_message_stream_Read(const nearby_message_stream_State* state,
const uint8_t* data, size_t length) {
nearby_message_stream_Metadata* metadata =
(nearby_message_stream_Metadata*)state->buffer;
nearby_message_stream_Message* message = &metadata->message;
uint16_t available_space =
state->length - sizeof(nearby_message_stream_Metadata);
while (length > 0) {
switch (metadata->bytes_read) {
case 0:
message->message_group = *data;
break;
case 1:
message->message_code = *data;
break;
case 2:
message->length = ((uint16_t)*data) << 8;
break;
case 3:
message->length += *data;
break;
default: {
uint16_t offset = metadata->bytes_read - HEADER_SIZE;
if (offset < message->length && offset < available_space) {
message->data[offset] = *data;
}
}
}
data++;
length--;
metadata->bytes_read++;
if (metadata->bytes_read - HEADER_SIZE == message->length) {
if (message->length > available_space) {
// Message truncated
message->length = available_space;
}
state->on_message_received(state->peer_address, message);
message->length = 0;
metadata->bytes_read = 0;
}
}
}
nearby_platform_status nearby_message_stream_Send(
uint64_t peer_address, const nearby_message_stream_Message* message) {
nearby_platform_status status;
uint8_t header[HEADER_SIZE];
header[0] = message->message_group;
header[1] = message->message_code;
header[2] = message->length >> 8;
header[3] = message->length;
status =
nearby_platform_SendMessageStream(peer_address, header, sizeof(header));
if (kNearbyStatusOK == status && message->length > 0) {
status = nearby_platform_SendMessageStream(peer_address, message->data,
message->length);
}
return status;
}
nearby_platform_status nearby_message_stream_SendAck(
uint64_t peer_address, const nearby_message_stream_Message* message) {
uint8_t ack[ACK_MESSAGE_SIZE];
ack[0] = ACKNOWLEDGEMENT_GROUP;
ack[1] = ACK_CODE;
ack[2] = 0;
ack[3] = ACK_MESSAGE_SIZE - HEADER_SIZE;
ack[4] = message->message_group;
ack[5] = message->message_code;
return nearby_platform_SendMessageStream(peer_address, ack, sizeof(ack));
}
nearby_platform_status nearby_message_stream_SendNack(
uint64_t peer_address, const nearby_message_stream_Message* message,
uint8_t fail_reason) {
uint8_t nack[NACK_MESSAGE_SIZE];
nack[0] = ACKNOWLEDGEMENT_GROUP;
nack[1] = NACK_CODE;
nack[2] = 0;
nack[3] = NACK_MESSAGE_SIZE - HEADER_SIZE;
nack[4] = fail_reason;
nack[5] = message->message_group;
nack[6] = message->message_code;
return nearby_platform_SendMessageStream(peer_address, nack, sizeof(nack));
}
uint16_t nearby_message_stream_GetMaxPayloadSize(
const nearby_message_stream_State* state) {
return state->length - sizeof(nearby_message_stream_Metadata);
}
#endif /* NEARBY_FP_MESSAGE_STREAM */
@@ -0,0 +1,100 @@
// 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.
#ifndef NEARBY_MESSAGE_STREAM_H
#define NEARBY_MESSAGE_STREAM_H
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// Message group and codes for ACK and NACK messages
#define ACKNOWLEDGEMENT_GROUP 0xFF
#define ACK_CODE 1
#define NACK_CODE 2
#define FAIL_REASON_REDUNDANT_DEVICE_ACTION 4
// Message sent and received over the message stream
// See: https://developers.google.com/nearby/fast-pair/spec#MessageStream
typedef struct {
uint8_t message_group;
uint8_t message_code;
// |data| length in native encoding
uint16_t length;
// message payload in big endian encoding
uint8_t* data;
} nearby_message_stream_Message;
// State and configuration of message stream parser
typedef struct {
// Callback triggered when a complete message is read from the input stream
void (*on_message_received)(uint64_t peer_address,
nearby_message_stream_Message* message);
// |peer_address| that is passed to |on_message_received|
uint64_t peer_address;
// Length of |buffer|
size_t length;
// The buffer used for storing an incoming message. The parser uses the buffer
// to store nearby_message_stream_Metadata, so not all of the space is
// available for message payload
uint8_t* buffer;
} nearby_message_stream_State;
typedef struct {
nearby_message_stream_Message message;
uint16_t bytes_read;
} nearby_message_stream_Metadata;
#ifdef NEARBY_FP_MESSAGE_STREAM
// Initializes the parser
void nearby_message_stream_Init(const nearby_message_stream_State* state);
// Reads and deserializes data from an input stream. It is OK to pass in
// incomplete packets. When the parses reads a complete message, it calls
// |on_message_received|. If the message payload is too big to fit the buffer -
// bigger than GetMaxPayloadSize(), then the payload is truncated.
void nearby_message_stream_Read(const nearby_message_stream_State* state,
const uint8_t* data, size_t length);
// Serializes and sends |message| out using nearby_platform_SendMessageStream()
nearby_platform_status nearby_message_stream_Send(
uint64_t peer_address, const nearby_message_stream_Message* message);
// Sends out an ACK message for a received |message|. Note that not all messages
// require an ACK
nearby_platform_status nearby_message_stream_SendAck(
uint64_t peer_address, const nearby_message_stream_Message* message);
// Sends out a NACK message for a received |message| with |fail_reason| reason.
nearby_platform_status nearby_message_stream_SendNack(
uint64_t peer_address, const nearby_message_stream_Message* message,
uint8_t fail_reason);
// Returns the maximum message payload size
uint16_t nearby_message_stream_GetMaxPayloadSize(
const nearby_message_stream_State* state);
#endif /* NEARBY_FP_MESSAGE_STREAM */
#ifdef __cplusplus
}
#endif
#endif // NEARBY_MESSAGE_STREAM_H
+84
View File
@@ -0,0 +1,84 @@
// 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.
#ifndef NEARBY_TRACE_H
#define NEARBY_TRACE_H
#include "nearby.h"
#include "nearby_platform_trace.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// Must be kept in sync with TraceLevel in nearby_platform_trace.h
#define NEARBY_TRACE_LEVEL_VERBOSE (1)
#define NEARBY_TRACE_LEVEL_DEBUG (2)
#define NEARBY_TRACE_LEVEL_INFO (3)
#define NEARBY_TRACE_LEVEL_WARNING (4)
#define NEARBY_TRACE_LEVEL_ERROR (5)
#define NEARBY_TRACE_LEVEL_OFF (6)
#if NEARBY_TRACE_LEVEL < NEARBY_TRACE_LEVEL_VERBOSE || \
NEARBY_TRACE_LEVEL > NEARBY_TRACE_LEVEL_OFF
#error "Invalid definition of NEARBY_TRACE_LEVEL."
#endif
#if NEARBY_TRACE_LEVEL > NEARBY_TRACE_LEVEL_VERBOSE
#define NEARBY_TRACE_VERBOSE(level, ...)
#else
#define NEARBY_TRACE_VERBOSE(level, ...) _NEARBY_TRACE(level, __VA_ARGS__)
#define NEARBY_ALL_ENUM_TO_STR_ENABLE 1
#endif
#if NEARBY_TRACE_LEVEL > NEARBY_TRACE_LEVEL_DEBUG
#define NEARBY_TRACE_DEBUG(level, ...)
#else
#define NEARBY_TRACE_DEBUG(level, ...) _NEARBY_TRACE(level, __VA_ARGS__)
#endif
#if NEARBY_TRACE_LEVEL > NEARBY_TRACE_LEVEL_INFO
#define NEARBY_TRACE_INFO(level, ...)
#else
#define NEARBY_TRACE_INFO(level, ...) _NEARBY_TRACE(level, __VA_ARGS__)
#endif
#if NEARBY_TRACE_LEVEL > NEARBY_TRACE_LEVEL_WARNING
#define NEARBY_TRACE_WARNING(level, ...)
#else
#define NEARBY_TRACE_WARNING(level, ...) _NEARBY_TRACE(level, __VA_ARGS__)
#endif
#if NEARBY_TRACE_LEVEL > NEARBY_TRACE_LEVEL_ERROR
#define NEARBY_TRACE_ERROR(level, ...)
#else
#define NEARBY_TRACE_ERROR(level, ...) _NEARBY_TRACE(level, __VA_ARGS__)
#endif
#ifndef __NEARBY_SHORT_FILE__
#define __NEARBY_SHORT_FILE__ __FILE__
#endif
#define _NEARBY_TRACE(level, ...) \
nearby_platform_Trace(level, __NEARBY_SHORT_FILE__, __LINE__, \
"@g " __VA_ARGS__);
#define NEARBY_TRACE(level, ...) \
NEARBY_TRACE_##level((nearby_platform_TraceLevel)NEARBY_TRACE_LEVEL_##level, \
__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_TRACE_H */
+96
View File
@@ -0,0 +1,96 @@
// 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 "nearby_utils.h"
uint8_t nearby_utils_GetByte(uint64_t source, int byteNumber) {
return source >> (8 * byteNumber);
}
void nearby_utils_CopyBigEndian(uint8_t* dest, uint64_t source, int bytes) {
int i;
for (i = 0; i < bytes; i++) {
dest[bytes - i - 1] = nearby_utils_GetByte(source, i);
}
}
void nearby_utils_CopyLittleEndian(uint8_t* dest, uint64_t source, int bytes) {
int i;
for (i = 0; i < bytes; i++) {
dest[i] = nearby_utils_GetByte(source, i);
}
}
uint32_t nearby_utils_GetBigEndian24(uint8_t* buffer) {
return (256 * 256 * buffer[0]) + (256 * buffer[1]) + buffer[2];
}
uint32_t nearby_utils_GetBigEndian32(uint8_t* buffer) {
return (256 * 256 * 256 * buffer[0]) + (256 * 256 * buffer[1]) +
(256 * buffer[2]) + buffer[3];
}
uint64_t nearby_utils_GetBigEndian48(uint8_t* b) {
return (((uint64_t)b[0]) << 40) | (((uint64_t)b[1]) << 32) |
(((uint64_t)b[2]) << 24) | (((uint64_t)b[3]) << 16) |
(((uint64_t)b[4]) << 8) | (uint64_t)b[5];
}
static char NibbleToHex(uint8_t value) {
if (value < 10) return '0' + value;
value -= 10;
if (value < 6) return 'A' + value;
return '?';
}
const char* nearby_utils_ArrayToString(const uint8_t* data, size_t length) {
static char buffer[80];
size_t buffer_size = sizeof(buffer);
size_t num_bytes;
if (2 * length < buffer_size - 1) {
num_bytes = 2 * length;
buffer[num_bytes] = 0;
} else {
// The buffer is too small to hold all of data. Let's truncate the data and
// append an ellipsis.
num_bytes = buffer_size - 4;
buffer[buffer_size - 1] = 0;
buffer[buffer_size - 2] = '.';
buffer[buffer_size - 3] = '.';
buffer[buffer_size - 4] = '.';
num_bytes = buffer_size - 4;
}
const uint8_t* input = data;
char* output = buffer;
for (size_t i = 0; i < num_bytes; i += 2) {
uint8_t x = *input++;
*output++ = NibbleToHex(x >> 4);
*output++ = NibbleToHex(x & 0x0F);
}
return buffer;
}
const char* nearby_utils_MacToString(uint64_t address) {
static char buffer[18];
char* p = buffer + sizeof(buffer) - 1;
*p-- = 0;
while (p > buffer) {
*p-- = NibbleToHex(address & 0xF);
address >>= 4;
*p-- = NibbleToHex(address & 0xF);
address >>= 4;
if (p >= buffer) *p-- = ':';
}
return buffer;
}
+14
View File
@@ -0,0 +1,14 @@
// 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.
+45
View File
@@ -0,0 +1,45 @@
// 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.
#ifndef NEARBY_UTILS_H
#define NEARBY_UTILS_H
#include "nearby_types.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
uint8_t nearby_utils_GetByte(uint64_t source, int byteNumber);
void nearby_utils_CopyBigEndian(uint8_t* dest, uint64_t source, int bytes);
void nearby_utils_CopyLittleEndian(uint8_t* dest, uint64_t source, int bytes);
uint32_t nearby_utils_GetBigEndian24(uint8_t* buffer);
uint32_t nearby_utils_GetBigEndian32(uint8_t* buffer);
uint64_t nearby_utils_GetBigEndian48(uint8_t* buffer);
// Converts the data to a hex string for debugging. Returns a pointer to a
// static buffer. Don't call it twice in the same log message!
const char* nearby_utils_ArrayToString(const uint8_t* data, size_t data_length);
// Converts BT MAC address to string for debugging. Returns a pointer to a
// static buffer. Don't call it twice in the same log message!
const char* nearby_utils_MacToString(uint64_t address);
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_UTILS_H */
@@ -0,0 +1,31 @@
// 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.
#ifndef NEARBY_TYPES_H
#define NEARBY_TYPES_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifdef __cplusplus
}
#endif
#endif // NEARBY_TYPES_H
+44
View File
@@ -0,0 +1,44 @@
// 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.
#ifndef NEARBY_H
#define NEARBY_H
#include "nearby_types.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define AES_MESSAGE_SIZE_BYTES 16
#define FP_SERVICE_UUID_SIZE 2
#define FP_MODEL_ID_SIZE 3
#define ACCOUNT_KEY_SIZE_BYTES AES_MESSAGE_SIZE_BYTES
typedef enum {
kNearbyStatusOK = 0,
kNearbyStatusError,
kNearbyStatusTimeout,
kNearbyStatusResourceExhausted,
kNearbyStatusUnimplemented,
kNearbyStatusUnsupported,
kNearbyStatusInvalidInput,
kNearbyStatusRedundantAction
} nearby_platform_status;
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_H */
@@ -0,0 +1,21 @@
// Copyright 2021 Google LLC.
#ifndef NEARBY_PLATFORM_AUDIO_H
#define NEARBY_PLATFORM_AUDIO_H
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// Returns true if right earbud is active
bool nearby_platform_GetEarbudRightStatus();
// Returns true if left earbud is active
bool nearby_platform_GetEarbudLeftStatus();
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_AUDIO_H */
@@ -0,0 +1,56 @@
// 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.
#ifndef NEARBY_PLATFORM_BATTERY_H
#define NEARBY_PLATFORM_BATTERY_H
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
// Set to true if the device is charging.
bool is_charging;
// Battery level of the right bud. 0 - 100 range.
uint8_t right_bud_battery_level;
// Battery level of the left bud. 0 - 100 range.
uint8_t left_bud_battery_level;
// Battery level of the charging case . 0 - 100 range.
uint8_t charging_case_battery_level;
// Battery remaining time in minutes, 0-65335 range.
uint16_t remaining_time_minutes;
} nearby_platform_BatteryInfo;
typedef struct {
void (*on_battery_changed)(void);
} nearby_platform_BatteryInterface;
// Gets battery and charging info
//
// battery_info - Battery status structure.
nearby_platform_status nearby_platform_GetBatteryInfo(
nearby_platform_BatteryInfo* battery_info);
// Initializes battery module
//
// battery_interface - Battery status callback events.
nearby_platform_status nearby_platform_BatteryInit(
nearby_platform_BatteryInterface* battery_interface);
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_BATTERY_H */
@@ -0,0 +1,94 @@
// 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.
#ifndef NEARBY_PLATFORM_BLE_H
#define NEARBY_PLATFORM_BLE_H
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef enum {
kModelId = 0, // FE2C1233-8366-4814-8EB0-01DE32100BEA (read)
kKeyBasedPairing, // FE2C1234-8366-4814-8EB0-01DE32100BEA (write, notify)
kPasskey, // FE2C1235-8366-4814-8EB0-01DE32100BEA (write, notify)
kAccountKey, // FE2C1236-8366-4814-8EB0-01DE32100BEA (write)
kFirmwareRevision, // 0x2A26
kAdditionalData, // FE2C1237-8366-4814-8EB0-01DE32100BEA
} nearby_fp_Characteristic;
typedef enum {
kDisabled,
kNoLargerThan100ms,
kNoLargerThan250ms
} nearby_fp_AvertisementInterval;
typedef struct {
nearby_platform_status (*on_gatt_write)(
uint64_t peer_address, nearby_fp_Characteristic characteristic,
const uint8_t* request, size_t length);
nearby_platform_status (*on_gatt_read)(
uint64_t peer_address, nearby_fp_Characteristic characteristic,
uint8_t* output, size_t* length);
} nearby_platform_BleInterface;
// Gets BLE address.
uint64_t nearby_platform_GetBleAddress();
// Sets BLE address. Returns address after change, which may be different than
// requested address.
//
// address - BLE address to set.
uint64_t nearby_platform_SetBleAddress(uint64_t address);
#ifdef NEARBY_FP_HAVE_BLE_ADDRESS_ROTATION
// Rotates BLE address to a random resolvable private address (RPA). Returns
// address after change.
uint64_t nearby_platform_RotateBleAddress();
#endif /* NEARBY_FP_HAVE_BLE_ADDRESS_ROTATION */
// Sends a notification to the connected GATT client.
//
// peer_address - Address of peer.
// characteristic - Characteristic UUID
// message - Message buffer to transmit.
// length - Length of message in buffer.
nearby_platform_status nearby_platform_GattNotify(
uint64_t peer_address, nearby_fp_Characteristic characteristic,
const uint8_t* message, size_t length);
// Sets the Fast Pair advertisement payload and starts advertising at a given
// interval.
//
// payload - Advertising data.
// length - Length of data.
// interval - Advertising interval code.
nearby_platform_status nearby_platform_SetAdvertisement(
const uint8_t* payload, size_t length,
nearby_fp_AvertisementInterval interval);
// Initializes BLE
//
// ble_interface - GATT read and write callbacks structure.
nearby_platform_status nearby_platform_BleInit(
const nearby_platform_BleInterface* ble_interface);
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_BLE_H */
+110
View File
@@ -0,0 +1,110 @@
// 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.
#ifndef NEARBY_PLATFORM_BT_H
#define NEARBY_PLATFORM_BT_H
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
void (*on_pairing_request)(uint64_t peer_address);
void (*on_paired)(uint64_t peer_address);
void (*on_pairing_failed)(uint64_t peer_address);
#ifdef NEARBY_FP_MESSAGE_STREAM
void (*on_message_stream_connected)(uint64_t peer_address);
void (*on_message_stream_disconnected)(uint64_t peer_address);
void (*on_message_stream_received)(uint64_t peer_address,
const uint8_t* message, size_t length);
#endif /* NEARBY_FP_MESSAGE_STREAM */
} nearby_platform_BtInterface;
// Returns Fast Pair Model Id.
uint32_t nearby_platform_GetModelId();
// Returns tx power level.
int8_t nearby_platform_GetTxLevel();
// Returns public BR/EDR address
uint64_t nearby_platform_GetPublicAddress();
// Returns passkey used during pairing
uint32_t nearby_platfrom_GetPairingPassKey();
// Provides the passkey received from the remote party.
// The system should compare local and remote party and accept/decline pairing
// request accordingly.
//
// passkey - Passkey
void nearby_platform_SetRemotePasskey(uint32_t passkey);
// Sends a pairing request to the Seeker
//
// remote_party_br_edr_address - BT address of peer.
nearby_platform_status nearby_platform_SendPairingRequest(
uint64_t remote_party_br_edr_address);
// Switches the device capabilities field back to default so that new
// pairings continue as expected.
nearby_platform_status nearby_platform_SetDefaultCapabilities();
// Switches the device capabilities field to Fast Pair required configuration:
// DisplayYes/No so that `confirm passkey` pairing method will be used.
nearby_platform_status nearby_platform_SetFastPairCapabilities();
// Sets null-terminated device name string in UTF-8 encoding
//
// name - Zero terminated string name of device.
nearby_platform_status nearby_platform_SetDeviceName(const char* name);
// Gets null-terminated device name string in UTF-8 encoding
// pass buffer size in char, and get string length in char.
//
// name - Buffer to return name string.
// length - On input, the size of the name buffer.
// On output, returns size of name in buffer.
nearby_platform_status nearby_platform_GetDeviceName(char* name,
size_t* length);
// Returns true if the device is in pairing mode (either fast-pair or manual).
bool nearby_platform_IsInPairingMode();
#ifdef NEARBY_FP_MESSAGE_STREAM
// Sends message stream through RFCOMM channel initiated by Seeker
//
// peer_address - Peer address.
// message - Contents of message.
// length - Length of message
nearby_platform_status nearby_platform_SendMessageStream(uint64_t peer_address,
const uint8_t* message,
size_t length);
#endif /* NEARBY_FP_MESSAGE_STREAM */
// Initializes BT module
//
// bt_interface - BT callbacks event structure.
nearby_platform_status nearby_platform_BtInit(
const nearby_platform_BtInterface* bt_interface);
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_BT_H */
@@ -0,0 +1,80 @@
// 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.
#ifndef NEARBY_PLATFORM_OS_H
#define NEARBY_PLATFORM_OS_H
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef enum {
kRingStateStarted = 0,
kRingStateFailedToStartOrStop,
kRingStateStoppedReasonTimeout,
kRingStateStoppedReasonUserAction,
kRingStateStoppedReasonNearbyRequest
} nearby_platform_RingState;
typedef struct {
nearby_platform_RingState ring_state;
// The number of components capable of ringing
// 0 - the device is incapable of ringing
// 1 - only a single component is capable of ringing
// 2 - left and right bud can ring
// 3 - left and right bud, and the case can ring
uint8_t num_components;
// A bitmap indicating what (sub)components are ringing. See
// `nearby_platform_Ring` for details
uint8_t components;
// Remaining ringing timeout in deciseconds
uint16_t timeout;
} nearby_platform_RingingInfo;
// Gets current time in ms.
unsigned int nearby_platform_GetCurrentTimeMs();
// Starts a timer. Returns an opaque timer handle or null on error.
//
// callback - Function to call when timer matures.
// delay_ms - Number of milliseconds to run the timer.
void* nearby_platform_StartTimer(void (*callback)(), unsigned int delay_ms);
// Cancels a timer
//
// timer - Timer handle returned by StartTimer.
nearby_platform_status nearby_platform_CancelTimer(void* timer);
// Initializes OS module
nearby_platform_status nearby_platform_OsInit();
// Starts ringing
//
// `command` - the requested ringing state as a bitmap:
// Bit 1 (0x01): ring right
// Bit 2 (0x02): ring left
// Bit 3 (0x04): ring case
// Alternatively, `command` hold a special value of 0xFF to ring all
// components that can ring.
// `timeout` - the timeout in deciseconds. The timeout overrides the one already
// in effect if any component of the device is already ringing.
nearby_platform_status nearby_platform_Ring(uint8_t command, uint16_t timeout);
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_OS_H */
@@ -0,0 +1,55 @@
// 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.
#ifndef NEARBY_PLATFORM_PERSISTENCE_H
#define NEARBY_PLATFORM_PERSISTENCE_H
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef enum {
kStoredKeyAccountKeyList,
kStoredKeyPersonalizedName
} nearby_fp_StoredKey;
// Loads stored key
//
// key - Type of key to fetch.
// output - Buffer to contain retrieved key.
// length - On input, contains the size of the output buffer.
// On output, contains the Length of key.
nearby_platform_status nearby_platform_LoadValue(nearby_fp_StoredKey key,
uint8_t* output,
size_t* length);
// Saves stored key
//
// key - Type of key to store.
// output - Buffer containing key to store.
// length - Length of key.
nearby_platform_status nearby_platform_SaveValue(nearby_fp_StoredKey key,
const uint8_t* input,
size_t length);
// Initializes persistence module
nearby_platform_status nearby_platform_PersistenceInit();
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_PERSISTENCE_H */
@@ -0,0 +1,80 @@
// 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.
#ifndef NEARBY_PLATFORM_SE_H
#define NEARBY_PLATFORM_SE_H
// clang-format off
#include "nearby_config.h"
// clang-format on
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// Generates a random number.
uint8_t nearby_platform_Rand();
// Computes the sha256 incrementally. Sha256Start() is called first, then
// Sha256Update() one or more times, and finally Sha256Finish().
nearby_platform_status nearby_platform_Sha256Start();
// Intermediate sha256 compute.
//
// data - Block of data to compute into sha256.
// length - Length of data to process.
nearby_platform_status nearby_platform_Sha256Update(const void* data,
size_t length);
// Finishes sha256 compute.
//
// out - Contains the final 256 bit sha.
nearby_platform_status nearby_platform_Sha256Finish(uint8_t out[32]);
// Encrypts a data block with AES128 in ECB mode.
//
// input - Input data block to be encrypted.
// output - Resulting encrypted block.
// key - 128 bit key to use for encryption.
nearby_platform_status nearby_platform_Aes128Encrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]);
// Decrypts a data block with AES128 in ECB mode.
//
// input - Input data block to be decrypted.
// output - Resulting decrypted block.
// key - 128 bit key to use for decryption.
nearby_platform_status nearby_platform_Aes128Decrypt(
const uint8_t input[AES_MESSAGE_SIZE_BYTES],
uint8_t output[AES_MESSAGE_SIZE_BYTES],
const uint8_t key[AES_MESSAGE_SIZE_BYTES]);
// Generates a shared sec256p1 secret using remote party public key and this
// device's private key.
//
// remote_party_public_key - Remote key.
// secret - 256 bit shared secret.
nearby_platform_status nearby_platform_GenSec256r1Secret(
const uint8_t remote_party_public_key[64], uint8_t secret[32]);
// Initializes secure element module
nearby_platform_status nearby_platform_SecureElementInit();
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_SE_H */
@@ -0,0 +1,60 @@
// 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.
#ifndef NEARBY_PLATFORM_TRACE_H
#define NEARBY_PLATFORM_TRACE_H
#include "nearby.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef enum {
kTraceLevelUnknown = 0,
kTraceLevelVerbose = 1,
kTraceLevelDebug = 2,
kTraceLevelInfo = 3,
kTraceLevelWarning = 4,
kTraceLevelError = 5,
} nearby_platform_TraceLevel;
// Generates conditional trace line. This is usually wrapped in a macro to
// provide compiler parameters.
//
// level - Debug level, higher is less messages.
// filename - Name of file calling trace.
// lineno - Source line of trace call.
// fmt - printf() style format string (%).
// ... - A series of parameters indicated by the fmt string.
void nearby_platform_Trace(nearby_platform_TraceLevel level,
const char *filename, int lineno, const char *fmt,
...);
// Processes assert. Processes a failed assertion.
//
// filename - Name of file calling assert.
// lineno - Source line of assert call
// reason - String message indicating reason for assert.
void nearby_platfrom_CrashOnAssert(const char *filename, int lineno,
const char *reason);
// Initializes trace module.
void nearby_platform_TraceInit(void);
#ifdef __cplusplus
}
#endif
#endif /* NEARBY_PLATFORM_TRACE_H */
+14
View File
@@ -0,0 +1,14 @@
### Nearby SDK for embedded systems release notes
## v1.0.0-embedded
Initial release supporting GFPS 3.1 per [specification](https://developers.google.com/nearby/fast-pair/specifications/introduction).
Supported features include:
* Initial pairing with discoverable advertisement
* Subsequent pairing with non-discoverable advertisement
* Retroactive Active Key for provisioning after manual pairing
* Battery Notification
* Personalized Name
* RFCOMM message stream
* Unit-tests on a simulated gLinux platform implementation
+28
View File
@@ -0,0 +1,28 @@
CFLAGS_EXTRA ?=
CFLAGS += -g \
-MD \
-Werror \
-Wno-deprecated-declarations \
-DARCH_GLINUX \
-fsanitize=address \
-fno-omit-frame-pointer \
-DARCH_GLINUX \
-DNEARBY_ALL_MODULE_DEBUG \
-DNEARBY_UNIT_TEST_ENABLED \
$(CFLAGS_EXTRA)
NEARBY_TRACE_LEVEL = VERBOSE
ifeq ($(OPTIMIZED_BUILD),1)
CFLAGS += -O2
else
CFLAGS += -O0
endif
TEST_INCLUDES = \
-I$(GTEST_DIR)/include -I$(GTEST_DIR) \
-I$(GMOCK_DIR)/include -I$(GMOCK_DIR) \
$(CLIENT_INCLUDES) \
-Iclient/tests/ \
-Iclient/tests/$(ARCH)/ \
-Iclient/tests/mocks/
+87
View File
@@ -0,0 +1,87 @@
ifneq ($(EXCLUDE_COMMON_TEST_SRCS),1)
TEST_SRCS += $(wildcard $(COMMON_DIR)/tests/*.cc)
endif
TEST_SRCS += $(wildcard client/tests/*.cc)
TEST_SRCS := $(filter-out $(TEST_SRCS_EXCLUDE), $(TEST_SRCS))
TEST_OBJS += $(patsubst %.cc,$(OUT_DIR)/%.o,$(TEST_SRCS))
GLINUX_TARGET_SRCS := $(wildcard client/tests/glinux/*.cc)
GTEST_SRCS = $(GTEST_DIR)/src/gtest-all.cc $(GMOCK_DIR)/src/gmock-all.cc
EMPTY_TARGET_SRCS := $(wildcard client/tests/empty_target/*.c)
GTEST_OBJS := $(patsubst %.cc,$(OUT_DIR)/%.o,$(GTEST_SRCS))
GLINUX_TARGET_OBJS := $(patsubst %.cc,$(OUT_DIR)/%.o,$(GLINUX_TARGET_SRCS))
EMPTY_TARGET_OBJS := $(patsubst %.c,$(OUT_DIR)/%.o,$(EMPTY_TARGET_SRCS))
ALL_TEST_OBJS += $(GLINUX_TARGET_OBJS)
ALL_TEST_OBJS += $(TEST_OBJS)
ALL_TEST_OBJS += $(GTEST_OBJS)
ALL_TEST_OBJS += $(EMPTY_TARGET_OBJS)
# The glinux target layer
$(GLINUX_TARGET_OBJS) : $(OUT_DIR)/%.o: %.cc
$(call compile_c,$(TEST_INCLUDES) -I. -std=c++14 -c $(CFLAGS))
$(EMPTY_TARGET_OBJS) : $(OUT_DIR)/%.o: %.c
$(info "compiling empty target object $<")
$(call compile_c,-I. $(EMPTY_TARGET_INC) -std=c99 -c $(CFLAGS))
$(TEST_OBJS) : $(FIRMWARE_VERSION_FILENAME)
$(TEST_OBJS) : $(OUT_DIR)/%.o: %.cc
$(call compile_c,$(TEST_INCLUDES) -I. -std=c++14 $(CFLAGS))
$(GTEST_OBJS) : $(OUT_DIR)/%.o : %.cc
$(call compile_c,$(TEST_INCLUDES) -std=c++14 $(CFLAGS))
TESTS_TO_RUN = $(patsubst %.cc,$(OUT_DIR)/%_run,$(TEST_SRCS))
.PHONY: $(TESTS_TO_RUN)
# Static pattern rule to execute the test binary.
$(TESTS_TO_RUN) : %_run : %
mkdir -p $(OUT_DIR)/test_logs/$(notdir $<)_logs/
./$< --gtest_output=xml:$(OUT_DIR)/test_logs/$(notdir $<)_logs/sponge_log.xml
TARGET_OBJS ?= $(GLINUX_TARGET_OBJS)
TARGET_OS_SRCS := $(wildcard client/tests/gLinux/*.cc)
TARGET_OS_OBJS ?= $(patsubst %.cc,$(OUT_DIR)/%.o,$(TARGET_OS_SRCS))
$(TARGET_OS_OBJS) : $(OUT_DIR)/%.o: %.cc
$(call compile_c,$(TEST_INCLUDES) -I. -std=c++14 $(CFLAGS))
ALL_TEST_OBJS += $(TEST_OBJS)
ALL_TEST_OBJS += $(TARGET_OS_OBJS)
ALL_TEST_OBJS += $(TARGET_OS_OBJS)
ALL_OBJS += $(ALL_TEST_OBJS)
# Must include the .d files for test sources here since gLinux.rules is included
# by makefile after after it includes $(DEPFILES).
-include $(ALL_TEST_OBJS:.o=.d)
EMPTY_TARGET_TEST = $(OUT_DIR)/client/tests/empty_target_test
$(EMPTY_TARGET_TEST) : TARGET_OBJS = $(EMPTY_TARGET_OBJS)
$(EMPTY_TARGET_TEST) : $(EMPTY_TARGET_OBJS)
TEST_BINARIES = $(patsubst %.cc,$(OUT_DIR)/%,$(TEST_SRCS))
$(TEST_BINARIES) : $(NAME) $(GTEST_OBJS) $(GLINUX_TARGET_OBJS)
$(TEST_BINARIES) : $(TARGET_OS_OBJS)
$(TEST_BINARIES) : % : %.o
mkdir -p $(dir $@)
$(CC) -o $@ $< \
$(CFLAGS) \
$(TEST_INCLUDES) \
$(GTEST_OBJS) \
$(TARGET_OBJS) \
$(TARGET_OS_OBJS) \
-L /usr/local/lib \
-std=c++14 \
-L $(OUT_DIR) -lnearby -lcrypto -lssl -lpthread -lstdc++
run_tests: $(TESTS_TO_RUN)
tests: $(TEST_BINARIES)
run_tests : tests
.PHONY : tests run_tests