Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson
2025-01-27 11:38:16 -08:00
committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions
@@ -0,0 +1,69 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 "drivers/rng.h"
#include "services/common/bluetooth/ble_root_keys.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "system/hexdump.h"
#include "system/logging.h"
#include <bluetooth/sm_types.h>
static void prv_generate_root_keys(SM128BitKey *keys_out) {
uint8_t *rand_buffer = (uint8_t *)keys_out;
int tries_left = 20;
// rng_rand generates only 4 bytes of random data at a time. Loop to fill up the whole array:
for (uint32_t i = 0; i < ((sizeof(SM128BitKey) * SMRootKeyTypeNum) / sizeof(uint32_t)); ++i) {
while (tries_left) {
const bool success = rng_rand((uint32_t *) &rand_buffer[i * sizeof(uint32_t)]);
if (success) {
break;
}
--tries_left;
}
}
if (tries_left == 0) {
PBL_LOG(LOG_LEVEL_WARNING, "rng_rand() failed too many times, falling back to rand()");
// Fall back to rand():
for (uint32_t i = 0; i < (sizeof(SM128BitKey) * SMRootKeyTypeNum); ++i) {
rand_buffer[i] = rand();
}
}
}
void ble_root_keys_get_and_generate_if_needed(SM128BitKey *keys_out) {
SM128BitKey *enc_key = &keys_out[SMRootKeyTypeEncryption];
SM128BitKey *id_key = &keys_out[SMRootKeyTypeIdentity];
bool is_existing = false;
if (bt_persistent_storage_get_root_key(SMRootKeyTypeIdentity, id_key) &&
bt_persistent_storage_get_root_key(SMRootKeyTypeEncryption, enc_key)) {
is_existing = true;
goto finally;
}
prv_generate_root_keys(keys_out);
finally:
#ifndef RELEASE
PBL_LOG(LOG_LEVEL_INFO, "BLE Root Keys (existing=%u):", is_existing);
PBL_HEXDUMP(LOG_LEVEL_INFO, (const uint8_t *)keys_out, 2 * sizeof(SM128BitKey));
#endif
if (!is_existing) {
bt_persistent_storage_set_root_keys(keys_out);
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
#include <bluetooth/sm_types.h>
//! @param[out] keys_out Array of at least SMRootKeyTypeNum elements in which the root keys should
//! be copied.
void ble_root_keys_get_and_generate_if_needed(SM128BitKey *keys_out);
@@ -0,0 +1,286 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#define FILE_LOG_COLOR LOG_COLOR_BLUE
#include "system/logging.h"
#include "console/dbgserial.h"
#include "comm/ble/gap_le.h"
#include "comm/ble/gatt_client_subscriptions.h"
#include "drivers/clocksource.h"
#include "kernel/events.h"
#include "kernel/pbl_malloc.h"
#include "kernel/util/stop.h"
#include "pebble_errors.h"
#include "services/common/analytics/analytics.h"
#include "services/common/bluetooth/ble_root_keys.h"
#include "services/common/bluetooth/bluetooth_ctl.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/common/bluetooth/dis.h"
#include "services/common/bluetooth/local_addr.h"
#include "services/common/bluetooth/local_id.h"
#include "services/common/bluetooth/pairability.h"
#include "services/common/regular_timer.h"
#include "services/common/system_task.h"
#include "services/normal/bluetooth/ble_hrm.h"
#include "os/mutex.h"
#include <bluetooth/init.h>
#include <string.h>
static bool s_comm_initialized = false;
static bool s_comm_airplane_mode_on = false;
static bool s_comm_enabled = false;
static bool s_comm_is_running = false;
static bool s_comm_state_change_eval_is_scheduled;
static BtCtlModeOverride s_comm_override = BtCtlModeOverrideNone;
static PebbleMutex *s_comm_state_change_mutex;
bool bt_ctl_is_airplane_mode_on(void) {
return s_comm_airplane_mode_on;
}
bool bt_ctl_is_bluetooth_active(void) {
if (s_comm_enabled) {
if (s_comm_override == BtCtlModeOverrideRun) {
return true;
} else if (s_comm_override == BtCtlModeOverrideNone && !s_comm_airplane_mode_on) {
return true;
}
}
return false;
}
bool bt_ctl_is_bluetooth_running(void) {
return s_comm_is_running;
}
static void prv_put_disconnection_event(void) {
PebbleEvent event = (PebbleEvent) {
.type = PEBBLE_BT_CONNECTION_EVENT,
.bluetooth.connection = {
.is_ble = true,
.state = PebbleBluetoothConnectionEventStateDisconnected,
}
};
PBL_LOG(LOG_LEVEL_DEBUG, "New BT Conn change event, We are now disconnected");
event_put(&event);
}
static void prv_comm_start(void) {
if (s_comm_is_running) {
return;
}
stop_mode_disable(InhibitorCommMode);
// Heap allocated to reduce stack usage
BTDriverConfig *config = kernel_zalloc_check(sizeof(BTDriverConfig));
ble_root_keys_get_and_generate_if_needed(config->root_keys);
dis_get_info(&config->dis_info);
#if CAPABILITY_HAS_BUILTIN_HRM
config->is_hrm_supported_and_enabled = ble_hrm_is_supported_and_enabled();
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing prefs: is_enabled=%u",
config->is_hrm_supported_and_enabled);
#endif
s_comm_is_running = bt_driver_start(config);
kernel_free(config);
if (s_comm_is_running) {
bt_local_addr_init();
bt_persistent_storage_register_existing_ble_bondings();
gap_le_init();
bt_local_id_configure_driver();
#if CAPABILITY_HAS_BUILTIN_HRM
ble_hrm_init();
#endif
bt_pairability_init();
analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BT_OFF_TIME);
} else {
PBL_LOG(LOG_LEVEL_ERROR, "BT driver failed to start!");
// FIXME: PBL-36163 -- handle this better
}
stop_mode_enable(InhibitorCommMode);
}
static void prv_comm_stop(void) {
if (!s_comm_is_running) {
return;
}
stop_mode_disable(InhibitorCommMode);
#if CAPABILITY_HAS_BUILTIN_HRM
ble_hrm_deinit();
#endif
gap_le_deinit();
// Should be the last thing to happen that touches the Bluetooth controller directly
bt_driver_stop();
stop_mode_enable(InhibitorCommMode);
analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BT_OFF_TIME, AnalyticsClient_System);
s_comm_is_running = false;
// This is a legacy event used to update the Settings app.
prv_put_disconnection_event();
}
static void prv_send_state_change_event(void) {
PBL_LOG(LOG_LEVEL_DEBUG, "----> Sending a BT state event");
PebbleEvent event = {
.type = PEBBLE_BT_STATE_EVENT,
.bluetooth = {
.state = {
.airplane = s_comm_airplane_mode_on,
.enabled = s_comm_enabled,
.override = s_comm_override,
},
},
};
event_put(&event);
}
static void prv_comm_state_change(void *context) {
static bool s_first_run = true;
mutex_lock(s_comm_state_change_mutex);
s_comm_state_change_eval_is_scheduled = false;
bool is_active_mode = bt_ctl_is_bluetooth_active();
if (is_active_mode != s_comm_is_running) {
if (is_active_mode) {
prv_comm_start();
} else {
prv_comm_stop();
}
// Only send event if state changed successfully:
if (is_active_mode == s_comm_is_running) {
prv_send_state_change_event();
}
} else if (!s_comm_is_running && s_first_run) {
PBL_LOG(LOG_LEVEL_DEBUG, "Shutting down the BT stack on boot");
bt_driver_power_down_controller_on_boot();
}
s_first_run = false;
mutex_unlock(s_comm_state_change_mutex);
}
void bt_ctl_set_enabled(bool enabled) {
if (!s_comm_initialized) {
PBL_LOG(LOG_LEVEL_ERROR, "Error: Bluetooth isn't initialized yet");
return;
}
mutex_lock(s_comm_state_change_mutex);
s_comm_enabled = enabled;
mutex_unlock(s_comm_state_change_mutex);
prv_comm_state_change(NULL);
}
void bt_ctl_set_override_mode(BtCtlModeOverride override) {
if (!s_comm_initialized) {
PBL_LOG(LOG_LEVEL_ERROR, "Error: Bluetooth isn't initialized yet");
return;
}
mutex_lock(s_comm_state_change_mutex);
s_comm_override = override;
mutex_unlock(s_comm_state_change_mutex);
prv_comm_state_change(NULL);
}
static void prv_track_quick_airplane_mode_toggles(bool is_airplane_mode_currently_on) {
// Track when coming out of airplane mode and we've gone into airplane mode less than 30 secs ago:
static RtcTicks s_airplane_mode_last_toggle_ticks;
const RtcTicks now_ticks = rtc_get_ticks();
const uint64_t max_interval_secs = 30;
if (((now_ticks - s_airplane_mode_last_toggle_ticks) < (max_interval_secs * RTC_TICKS_HZ)) &&
is_airplane_mode_currently_on) {
PBL_LOG(LOG_LEVEL_INFO, "Quick airplane mode toggle detected!");
analytics_inc(ANALYTICS_DEVICE_METRIC_BT_AIRPLANE_MODE_QUICK_TOGGLE_COUNT,
AnalyticsClient_System);
}
s_airplane_mode_last_toggle_ticks = now_ticks;
}
void bt_ctl_set_airplane_mode_async(bool enabled) {
if (!s_comm_initialized) {
PBL_LOG(LOG_LEVEL_ERROR, "Error: Bluetooth isn't initialized yet");
return;
}
mutex_lock(s_comm_state_change_mutex);
prv_track_quick_airplane_mode_toggles(!enabled);
bt_persistent_storage_set_airplane_mode_enabled(enabled);
s_comm_airplane_mode_on = enabled;
bool should_schedule_eval = false;
if (!s_comm_state_change_eval_is_scheduled) {
should_schedule_eval = true;
s_comm_state_change_eval_is_scheduled = true;
}
mutex_unlock(s_comm_state_change_mutex);
if (should_schedule_eval) {
system_task_add_callback(prv_comm_state_change, NULL);
}
}
void bt_ctl_init(void) {
s_comm_state_change_mutex = mutex_create();
s_comm_airplane_mode_on = bt_persistent_storage_get_airplane_mode_enabled();
s_comm_initialized = true;
analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BT_OFF_TIME, AnalyticsClient_System);
gatt_client_subscription_boot();
}
static void prv_bt_ctl_reset_bluetooth_callback(void *context) {
PBL_LOG(LOG_LEVEL_DEBUG, "Resetting Bluetooth");
mutex_lock(s_comm_state_change_mutex);
bool was_already_running = s_comm_is_running;
prv_comm_stop();
prv_comm_start();
// It's possible a reset was triggered because the stack failed to boot up
// correctly in which case we have never generated an event about the stack
// booting up. Don't bother sending events if we are just returning the stack
// to the state it is already in
if (!was_already_running && s_comm_is_running) {
prv_send_state_change_event();
}
mutex_unlock(s_comm_state_change_mutex);
}
void bt_ctl_reset_bluetooth(void) {
if (bt_ctl_is_bluetooth_active()) {
system_task_add_callback(prv_bt_ctl_reset_bluetooth_callback, NULL);
} else {
PBL_LOG(LOG_LEVEL_DEBUG, "Bluetooth is disabled, reset aborted");
}
}
void command_bt_airplane_mode(const char* new_mode) {
// as tests run using command_bt_airplane_mode, will retain nomenclature
// but work as override mode change
BtCtlModeOverride override = BtCtlModeOverrideStop;
if (strcmp(new_mode, "exit") == 0) {
override = BtCtlModeOverrideNone;
}
bt_ctl_set_override_mode(override);
bool new_state = bt_ctl_is_bluetooth_active();
if (!new_state) {
dbgserial_putstr("Entered airplane mode");
} else {
dbgserial_putstr("Left airplane mode");
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
#include <stdbool.h>
typedef enum {
BtCtlModeOverrideNone,
BtCtlModeOverrideStop,
BtCtlModeOverrideRun
} BtCtlModeOverride;
void bt_ctl_init(void);
//! returns the airplane mode state
bool bt_ctl_is_airplane_mode_on(void);
//! Returns whether the bluetooth stack is supposed to be up and running (but might not because it's
//! still starting or in the middle of resetting).
bool bt_ctl_is_bluetooth_active(void);
//! Returns whether the bluetooth stack is up and running or not.
bool bt_ctl_is_bluetooth_running(void);
// The following three functions are used for setting the flags that define the state of
// the bluetooth stack.
//! Sets the airplane mode flag. The flag is persisted across reboots
void bt_ctl_set_airplane_mode_async(bool enabled);
//! Sets enable flag (used by the runlevel system).
void bt_ctl_set_enabled(bool enabled);
//! Sets the override mode used to stop and start the bluetooth independent of the airplane mode.
void bt_ctl_set_override_mode(BtCtlModeOverride override);
//! Reset bluetoosh using sequential calls to comm_stop() and comm_start()
void bt_ctl_reset_bluetooth(void);
@@ -0,0 +1,203 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
#include "services/common/comm_session/session_remote_version.h"
#include <bluetooth/bluetooth_types.h>
#include <bluetooth/id.h>
#include <bluetooth/sm_types.h>
//! @file bluetooth_persistent_storage.h
//! Future file-based database for Bluetooth related credentials, cached data, etc.
//! The idea is to replace the deprecated, registry-based "remote_prefs.c" and "known_device_list.c"
//! For now this module contains temporary implementations that use the legacy registry.
typedef enum {
BtPersistBondingOpInvalid = -1,
BtPersistBondingOpDidAdd,
BtPersistBondingOpDidChange,
BtPersistBondingOpWillDelete,
} BtPersistBondingOp;
typedef enum {
BtPersistBondingTypeBTClassic,
BtPersistBondingTypeBLE,
BtPersistBondingNumTypes
} BtPersistBondingType;
//! Signature of function that handles changes in the pairing database
typedef void (*BtPersistBondingChangeHandler)(BTBondingID affected_bonding,
BtPersistBondingOp operation);
typedef void (*BtPersistBondingDBEachBLE)(BTDeviceInternal *device, SMIdentityResolvingKey *irk,
const char *name, BTBondingID *id, void *context);
typedef void (*BtPersistBondingDBEachBTClassic)(BTDeviceAddress *addr, SM128BitKey *link_key,
const char *name, uint8_t *platform_bits, void *context);
///////////////////////////////////////////////////////////////////////////////////////////////////
//! BLE Pairing Info
bool bt_persistent_storage_has_pinned_ble_pairings(void);
bool bt_persistent_storage_set_ble_pinned_address(const BTDeviceAddress *address);
bool bt_persistent_storage_get_ble_pinned_address(BTDeviceAddress *address_out);
BTBondingID bt_persistent_storage_store_ble_pairing(const SMPairingInfo *pairing_info,
bool is_gateway, const char *device_name,
bool requires_address_pinning,
uint8_t flags);
bool bt_persistent_storage_update_ble_device_name(BTBondingID bonding, const char *device_name);
void bt_persistent_storage_delete_ble_pairing_by_id(BTBondingID);
bool bt_persistent_storage_get_ble_pairing_by_id(BTBondingID bonding,
SMIdentityResolvingKey *IRK_out,
BTDeviceInternal *device_out,
char name[BT_DEVICE_NAME_BUFFER_SIZE]);
bool bt_persistent_storage_get_ble_pairing_by_addr(const BTDeviceInternal *device,
SMIdentityResolvingKey *IRK_out,
char name_out[BT_DEVICE_NAME_BUFFER_SIZE]);
//! Returns the first ANCS supported bonding that is found
//! The case of having multiple supported ANCS bondings isn't handled well yet.
//! When this happens this could easily be changed to a for_each_ancs_supported_bonding(cb)
BTBondingID bt_persistent_storage_get_ble_ancs_bonding(void);
//! Returns true if the bondings is BLE and supports ANCS
bool bt_persistent_storage_is_ble_ancs_bonding(BTBondingID bonding);
//! Returns true if there exists a BLE bonding which supports ANCS
bool bt_persistent_storage_has_ble_ancs_bonding(void);
//! Returns true if the active gateway uses BLE
//! [PG]: This will currently always return false until PPoGATT is supported
bool bt_persistent_storage_has_active_ble_gateway_bonding(void);
//! Runs the callback for each BLE pairing
//! The callback is NOT allowed to aquire the bt_lock() (or we could deadlock).
void bt_persistent_storage_for_each_ble_pairing(BtPersistBondingDBEachBLE cb, void *context);
//! Registers all the existing BLE bondings with the BT driver lib.
void bt_persistent_storage_register_existing_ble_bondings(void);
///////////////////////////////////////////////////////////////////////////////////////////////////
//! BT Classic Pairing Info
BTBondingID bt_persistent_storage_store_bt_classic_pairing(BTDeviceAddress *address,
SM128BitKey *key, char *name,
uint8_t *platform_bits);
void bt_persistent_storage_delete_bt_classic_pairing_by_id(BTBondingID bonding);
void bt_persistent_storage_delete_bt_classic_pairing_by_addr(const BTDeviceAddress *bd_addr);
bool bt_persistent_storage_get_bt_classic_pairing_by_id(BTBondingID bonding,
BTDeviceAddress *address_out,
SM128BitKey *link_key_out,
char *name_out,
uint8_t *platform_bits_out);
BTBondingID bt_persistent_storage_get_bt_classic_pairing_by_addr(BTDeviceAddress *addr_in,
SM128BitKey *link_key_out,
char *name_out,
uint8_t *platform_bits_out);
//! Returns true if the active gateway uses BT Classic
bool bt_persistent_storage_has_active_bt_classic_gateway_bonding(void);
//! Runs the callback for each BT Classic pairing
//! The callback is NOT allowed to aquire the bt_lock() (or we could deadlock).
void bt_persistent_storage_for_each_bt_classic_pairing(BtPersistBondingDBEachBTClassic cb,
void *context);
///////////////////////////////////////////////////////////////////////////////////////////////////
//! Local Device Info
//! Updates the active gateway (the gateway which implements PP)
//! This bonding is used for BT Classic reconnection as well
//! @param bonding The desired active gateway
void bt_persistent_storage_set_active_gateway(BTBondingID bonding);
//! Returns false if no active gateway exists, true if one does exist
//! bonding_out and type_out are only valid when this function returns true;
bool bt_persistent_storage_get_active_gateway(BTBondingID *bonding_out,
BtPersistBondingType *type_out);
//! Returns true when the active gateway is changed until a sync happens
bool bt_persistent_storage_is_unfaithful(void);
//! Marks the device as being unfaithful
void bt_persistent_storage_set_unfaithful(bool is_unfaithful);
//! Copies the BLE Encryption Root (ER) or Identity Root (IR) keys out of storage
//! @param key_out Storage into which ER or IR should be copied.
//! @param key_type The type of key to copy
//! @return true if ER and IR are copied, false if there are no keys have been found to copy.
bool bt_persistent_storage_get_root_key(SMRootKeyType key_type, SM128BitKey *key_out);
//! Stores new BLE Encryption Root (ER) and Identity Root (IR) keys
void bt_persistent_storage_set_root_keys(SM128BitKey *keys_in);
//! @param local_device_name_out Storage for the local device name.
//! @param max_size Size of the local_device_name_out buffer
//! @return true if there is a valid local device name stored, otherwise false (a zero-length string
bool bt_persistent_storage_get_local_device_name(char *local_device_name_out, size_t max_size);
//! Stores the customized local device name
//! @param local_device_name The device name to store
//! @param size The size of the string
void bt_persistent_storage_set_local_device_name(char *local_device_name, size_t max_size);
//! Retrieve the airplane mode setting
//! @return the stored airplane mode flag
bool bt_persistent_storage_get_airplane_mode_enabled(void);
//! Store the airplane mode setting
//! @param the airplane mode state to be saved
void bt_persistent_storage_set_airplane_mode_enabled(bool enable);
///////////////////////////////////////////////////////////////////////////////////////////////////
//! Remote Device Info
//! Retrieve the most recent system session capabilities
//! @param capabilities_out Storage for system session capabilities
//! @note It's preferable to use \ref comm_session_get_capabilities when possible
void bt_persistent_storage_get_cached_system_capabilities(
PebbleProtocolCapabilities *capabilities_out);
//! Store the most recent system session capabilities
//! @param capabilities The capability flags to be saved (cache will be cleared if NULL)
void bt_persistent_storage_set_cached_system_capabilities(
const PebbleProtocolCapabilities *capabilities);
///////////////////////////////////////////////////////////////////////////////////////////////////
//! Common
void bt_persistent_storage_init(void);
//! This will not delete the local device info, only pairings
void bt_persistent_storage_delete_all_pairings(void);
///////////////////////////////////////////////////////////////////////////////////////////////////
//! Unit testing
int bt_persistent_storage_get_raw_data(const void *key, size_t key_len,
void *data_out, size_t buf_len);
@@ -0,0 +1,112 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 "bluetooth_persistent_storage_debug.h"
#include "console/prompt.h"
#include "services/common/shared_prf_storage/shared_prf_storage_debug.h"
#include "system/hexdump.h"
#include "system/logging.h"
#include "util/string.h"
#include <bluetooth/bluetooth_types.h>
#include <bluetooth/features.h>
#include <bluetooth/sm_types.h>
#include <btutil/bt_device.h>
#include <btutil/sm_util.h>
//
// Strictly for debug. Pretty-prints most of the pairing information saved
// in the gap bonding db and shared PRF.
//
void bluetooth_persistent_storage_debug_dump_ble_pairing_info(
char *display_buf, const SMPairingInfo *info) {
prompt_send_response(" Local Encryption Info: ");
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG,
(uint8_t *)&info->local_encryption_info,
sizeof(info->local_encryption_info));
prompt_send_response(" Remote Encryption Info: ");
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG,
(uint8_t *)&info->remote_encryption_info,
sizeof(info->remote_encryption_info));
prompt_send_response(" SMIdentityResolvingKey: ");
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG,
(uint8_t *)&info->irk,
sizeof(info->irk));
prompt_send_response(" BTDeviceInternal: ");
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG,
(uint8_t *)&info->identity,
sizeof(BTDeviceInternal));
prompt_send_response(" SMConnectionSignatureResolvingKey: ");
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG,
(uint8_t *)&info->csrk,
sizeof(SMConnectionSignatureResolvingKey));
prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN,
" local encryption valid: %s\n"
" remote encryption valid: %s\n"
" remote identity valid: %s\n"
" remote signature valid: %s\n",
bool_to_str(info->is_local_encryption_info_valid),
bool_to_str(info->is_remote_encryption_info_valid),
bool_to_str(info->is_remote_encryption_info_valid),
bool_to_str(info->is_remote_signing_info_valid));
}
void bluetooth_persistent_storage_debug_dump_classic_pairing_info(
char *display_buf, BTDeviceAddress *addr, char *device_name, SM128BitKey *link_key,
uint8_t platform_bits) {
prompt_send_response(" Link Key:");
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)link_key, sizeof(SM128BitKey));
prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " BT ADDR: " BD_ADDR_FMT,
BT_DEVICE_ADDRESS_XPLODE(*addr));
prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Name: %s",
device_name);
prompt_send_response_fmt(display_buf, DISPLAY_BUF_LEN, " Platform Bits: 0x%x",
(int)platform_bits);
}
void bluetooth_persistent_storage_debug_dump_root_keys(SM128BitKey *irk, SM128BitKey *erk) {
prompt_send_response("Root Key hexdumps:");
prompt_send_response(" IRK:");
if (irk) {
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)irk, sizeof(SM128BitKey));
} else {
prompt_send_response(" None");
};
prompt_send_response(" ERK:");
if (erk) {
PBL_HEXDUMP_D_PROMPT(LOG_LEVEL_DEBUG, (uint8_t *)erk, sizeof(SM128BitKey));
} else {
prompt_send_response(" None");
};
}
extern void bluetooth_persistent_storage_dump_contents(void);
void command_gapdb_dump(void) {
#if !RECOVERY_FW
bluetooth_persistent_storage_dump_contents();
#endif
shared_prf_storage_dump_contents();
}
@@ -0,0 +1,32 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
#include <bluetooth/bluetooth_types.h>
#include <bluetooth/sm_types.h>
#include <btutil/sm_util.h>
#define DISPLAY_BUF_LEN 160
void bluetooth_persistent_storage_debug_dump_ble_pairing_info(
char *display_buf, const SMPairingInfo *info);
void bluetooth_persistent_storage_debug_dump_classic_pairing_info(
char *display_buf, BTDeviceAddress *addr, char *device_name, SM128BitKey *link_key,
uint8_t platform_bits);
void bluetooth_persistent_storage_debug_dump_root_keys(SM128BitKey *irk, SM128BitKey *erk);
@@ -0,0 +1,134 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 "comm/ble/gap_le_connection.h"
#include "comm/bt_lock.h"
#include "console/prompt.h"
#include "services/common/bluetooth/bluetooth_ctl.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/common/bluetooth/bt_compliance_tests.h"
#include "services/common/bluetooth/local_id.h"
#include "services/common/bluetooth/pairability.h"
#include "services/common/shared_prf_storage/shared_prf_storage.h"
#include "util/string.h"
#include <bluetooth/bluetooth_types.h>
#include <bluetooth/bt_test.h>
#include <bluetooth/classic_connect.h>
#include <bluetooth/id.h>
#include <stdlib.h>
void command_bt_print_mac(void) {
char addr_hex_str[BT_ADDR_FMT_BUFFER_SIZE_BYTES];
bt_local_id_copy_address_hex_string(addr_hex_str);
prompt_send_response(addr_hex_str);
}
//! Overrides the BD ADDR of the Bluetooth controller for test automation purposes
//! @param bd_addr String of 12 hex characters (6 bytes) of the Bluetooth device address
//! @note To undo the change, call this with all zeroes.
//! @note The change will take effect when the Bluetooth is (re)enabled.
void command_bt_set_addr(const char *bd_addr_str) {
char buffer[32];
BTDeviceAddress bd_addr;
if (convert_bt_addr_hex_str_to_bd_addr(bd_addr_str, (uint8_t *) &bd_addr, sizeof(bd_addr))) {
bt_driver_test_set_spoof_address(&bd_addr);
prompt_send_response_fmt(buffer, 32, BT_DEVICE_ADDRESS_FMT, BT_DEVICE_ADDRESS_XPLODE(bd_addr));
} else {
prompt_send_response("?");
}
}
//! @param bt_name A custom Bluetooth device name.
void command_bt_set_name(const char *bt_name) {
bt_local_id_set_device_name(bt_name);
}
// BT FCC tests
void command_bt_test_start(void) {
// take down the BT stack and put the OS in a mode where it will not
// interfere with the BT testing.
bt_test_start();
}
void command_bt_test_stop(void) {
// restore the watch to normal operation
bt_test_stop();
}
void command_bt_test_hci_passthrough(void) {
bt_test_enter_hci_passthrough();
}
void command_bt_test_bt_sig_rf_mode(void) {
if (bt_test_bt_sig_rf_test_mode()) {
prompt_send_response("BT SIG RF Test Mode Enabled");
} else {
prompt_send_response("Failed to enter BT SIG RF Test Mode");
}
}
void command_bt_prefs_wipe(void) {
bt_driver_classic_disconnect(NULL);
bt_persistent_storage_delete_all_pairings();
}
void command_bt_sprf_nuke(void) {
shared_prf_storage_wipe_all();
#if RECOVERY_FW
// Reset system to get caches (in s_intents, s_connections and controller-side caches) in sync.
extern void factory_reset_set_reason_and_reset(void);
factory_reset_set_reason_and_reset();
#endif
}
#ifdef RECOVERY_FW
void command_bt_status(void) {
char buffer[64];
prompt_send_response_fmt(buffer, sizeof(buffer), "Alive: %s",
bt_ctl_is_bluetooth_running() ? "yes" : "no");
const char *prefix = "BT Chip Info: ";
size_t prefix_length = strlen(prefix);
strncpy(buffer, prefix, sizeof(buffer));
bt_driver_id_copy_chip_info_string(buffer + prefix_length,
sizeof(buffer) - prefix_length);
prompt_send_response(buffer);
char name[BT_DEVICE_NAME_BUFFER_SIZE];
bt_lock();
bool connected = bt_driver_classic_copy_connected_device_name(name);
if (!connected) {
// Try LE:
GAPLEConnection *connection = gap_le_connection_any();
if (connection) {
const char *device_name = connection->device_name ?: "<Unknown>";
strncpy(name, device_name, BT_DEVICE_NAME_BUFFER_SIZE);
name[BT_DEVICE_NAME_BUFFER_SIZE - 1] = '\0';
connected = true;
}
}
bt_unlock();
prompt_send_response_fmt(buffer, sizeof(buffer), "Connected: %s", connected ? "yes" : "no");
if (connected) {
prompt_send_response_fmt(buffer, sizeof(buffer), "Device: %s", name);
}
}
#endif // RECOVERY_FW
@@ -0,0 +1,67 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 "comm/ble/gap_le_connection.h"
#include "comm/ble/gap_le_device_name.h"
#include "comm/bt_lock.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/common/bluetooth/local_addr.h"
#include "system/logging.h"
#include <bluetooth/bonding_sync.h>
#include <bluetooth/bluetooth_types.h>
#include <bluetooth/sm_types.h>
void bt_driver_cb_handle_create_bonding(const BleBonding *bonding,
const BTDeviceAddress *addr) {
#if !defined(PLATFORM_TINTIN)
PBL_LOG(LOG_LEVEL_INFO, "Creating new bonding for "BT_DEVICE_ADDRESS_FMT,
BT_DEVICE_ADDRESS_XPLODE(bonding->pairing_info.identity.address));
#endif
const bool should_pin_address = bonding->should_pin_address;
if (should_pin_address) {
bt_local_addr_pin(&bonding->pinned_address);
}
const uint8_t flags = bonding->flags;
if (flags) {
PBL_LOG(LOG_LEVEL_INFO, "flags: 0x02%x", flags);
}
BTBondingID bonding_id = bt_persistent_storage_store_ble_pairing(&bonding->pairing_info,
bonding->is_gateway, NULL,
should_pin_address,
flags);
bt_lock();
{
GAPLEConnection *connection = gap_le_connection_by_addr(addr);
if (connection) {
// Associate the connection with the bonding:
connection->bonding_id = bonding_id;
connection->is_gateway = bonding->is_gateway;
if (!connection->is_gateway) {
PBL_LOG(LOG_LEVEL_DEBUG, "New bonding is not gateway?");
}
// Request device name. iOS returns an "anonymized" device name before encryption, like
// "iPhone" and only returns the real name i.e. "Martijn's iPhone" after encryption is set up.
gap_le_device_name_request(&connection->device);
} else {
PBL_LOG(LOG_LEVEL_ERROR, "Couldn't find connection for bonding!");
}
}
bt_unlock();
}
@@ -0,0 +1,64 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 "console/console_internal.h"
#include "console/prompt.h"
#include "services/common/bluetooth/bluetooth_ctl.h"
#include "services/common/bluetooth/bt_compliance_tests.h"
#include "kernel/util/stop.h"
#include <bluetooth/bt_test.h>
static bool s_test_mode_enabled = false;
void bt_test_start(void) {
if (s_test_mode_enabled) {
prompt_send_response("Invalid operation: Run 'bt test stop' first");
return;
}
s_test_mode_enabled = true;
bt_ctl_set_override_mode(BtCtlModeOverrideStop);
stop_mode_disable(InhibitorBluetooth);
bt_driver_test_start();
}
bool bt_test_bt_sig_rf_test_mode(void) {
return bt_driver_test_enter_rf_test_mode();
}
void bt_test_enter_hci_passthrough(void) {
// redirect all communications to the BT module
serial_console_set_state(SERIAL_CONSOLE_STATE_HCI_PASSTHROUGH);
bt_driver_test_enter_hci_passthrough();
}
void bt_test_stop(void) {
if (!s_test_mode_enabled) {
prompt_send_response("Invalid operation: Run 'bt test start' first");
return;
}
bt_driver_test_stop();
stop_mode_enable(InhibitorBluetooth);
// Bring the normal BT stack back up - airplane mode makes this simple
bt_ctl_set_override_mode(BtCtlModeOverrideNone);
s_test_mode_enabled = false;
}
@@ -0,0 +1,26 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
void bt_test_start(void);
void bt_test_stop(void);
bool bt_test_bt_sig_rf_test_mode(void);
void bt_test_enter_hci_passthrough(void);
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 <bluetooth/dis.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "process_management/pebble_process_info.h"
#include "mfg/mfg_info.h"
#include "mfg/mfg_serials.h"
#include "system/version.h"
#define MANUFACTURER_STR "Pebble Technology"
_Static_assert(MODEL_NUMBER_LEN >= MFG_HW_VERSION_SIZE + 1, "Size mismatch");
_Static_assert(MANUFACTURER_LEN >= sizeof(MANUFACTURER_STR), "Size mismatch");
_Static_assert(SERIAL_NUMBER_LEN >= MFG_SERIAL_NUMBER_SIZE + 1, "Size mismatch");
_Static_assert(FW_REVISION_LEN >= sizeof(TINTIN_METADATA.version_tag), "Size mismatch");
static void prv_set_model_number(DisInfo *info) {
mfg_info_get_hw_version(info->model_number, MODEL_NUMBER_LEN);
}
static void prv_set_manufacturer_name(DisInfo *info) {
strncpy(info->manufacturer, MANUFACTURER_STR, MANUFACTURER_LEN);
}
static void prv_set_serial_number(DisInfo *info) {
mfg_info_get_serialnumber(info->serial_number, SERIAL_NUMBER_LEN);
}
static void prv_set_firmware_revision(DisInfo *info) {
strncpy(info->fw_revision, (char*)TINTIN_METADATA.version_tag, FW_REVISION_LEN);
}
static void prv_set_software_revision(DisInfo *info) {
// Fmt: xx.xx\0
char sdk_version[SW_REVISION_LEN];
sniprintf(sdk_version, SW_REVISION_LEN, "%2u.%02u",
PROCESS_INFO_CURRENT_SDK_VERSION_MAJOR,
PROCESS_INFO_CURRENT_SDK_VERSION_MINOR);
strncpy(info->sw_revision, sdk_version, SW_REVISION_LEN);
}
void dis_get_info(DisInfo *info) {
prv_set_model_number(info);
prv_set_manufacturer_name(info);
prv_set_serial_number(info);
prv_set_firmware_revision(info);
prv_set_software_revision(info);
}
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
typedef struct DisInfo DisInfo;
void dis_get_info(DisInfo *info);
@@ -0,0 +1,130 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 "local_addr.h"
#include "comm/bt_lock.h"
#include "system/logging.h"
#include "system/passert.h"
#include <bluetooth/bluetooth_types.h>
#include <bluetooth/id.h>
#include <btutil/bt_device.h>
static uint32_t s_pra_cycling_pause_count;
static BTDeviceAddress s_pinned_addr;
static bool s_cycling_paused_due_to_dependent_bondings;
static void prv_allow_cycling(bool allow_cycling) {
bt_driver_set_local_address(allow_cycling, allow_cycling ? NULL : &s_pinned_addr);
}
void bt_local_addr_pause_cycling(void) {
bt_lock();
{
if (s_pra_cycling_pause_count == 0) {
PBL_LOG(LOG_LEVEL_INFO, "Pausing address cycling (pinned_addr="BT_DEVICE_ADDRESS_FMT")",
BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr));
prv_allow_cycling(false);
}
++s_pra_cycling_pause_count;
}
bt_unlock();
}
void bt_local_addr_resume_cycling(void) {
bt_lock();
{
PBL_ASSERTN(s_pra_cycling_pause_count);
--s_pra_cycling_pause_count;
if (s_pra_cycling_pause_count == 0) {
PBL_LOG(LOG_LEVEL_INFO, "Resuming address cycling (pinned_addr="BT_DEVICE_ADDRESS_FMT")",
BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr));
prv_allow_cycling(true);
}
}
bt_unlock();
}
void bt_local_addr_pin(const BTDeviceAddress *addr) {
// In a previous version of the code, the main FW would not know yet what address would be used
// for pinning until the BT driver would give the address to pin when a pairing was added.
// A single, persistent pinned address is now generated up front in bt_local_addr_init().
// Getting the address back in this call from the BT driver currently only serves as a
// consistency check.
// It is possible that the addresses do not match in the following scenario:
// 1. No bondings that require pinning present. Cycling address 'C' is used.
// 2. Device A is connected.
// 3. Become discoverable: cycling is requested to be paused at address 'P' but can't be granted
// yet because device A is still connected.
// 4. Device B connects (using 'C' as connection address)
// 5. Device B requests pin + pairs => the remote bonding is stored with 'C' as key instead of 'P'
// 6. We'll print here there's a mismatch.
// 7. Once Device A & B disconnect, device B won't be able to recognize us because 'P' is used...
bt_lock();
bool addresses_match = bt_device_address_equal(addr, &s_pinned_addr);
bt_unlock();
PBL_LOG(LOG_LEVEL_INFO,
"Requested to pin address to "BT_DEVICE_ADDRESS_FMT " match=%u",
BT_DEVICE_ADDRESS_XPLODE_PTR(addr), addresses_match);
}
void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op) {
bool has_pinned_ble_pairings = bt_persistent_storage_has_pinned_ble_pairings();
if (has_pinned_ble_pairings != s_cycling_paused_due_to_dependent_bondings) {
if (has_pinned_ble_pairings) {
bt_local_addr_pause_cycling();
} else {
bt_local_addr_resume_cycling();
}
s_cycling_paused_due_to_dependent_bondings = has_pinned_ble_pairings;
}
}
void bt_local_addr_init(void) {
s_pra_cycling_pause_count = 0;
s_cycling_paused_due_to_dependent_bondings = false;
// Load pinned address from settings file or generate one if it hasn't happened before:
if (!bt_persistent_storage_get_ble_pinned_address(&s_pinned_addr)) {
if (bt_driver_id_generate_private_resolvable_address(&s_pinned_addr)) {
bt_persistent_storage_set_ble_pinned_address(&s_pinned_addr);
} else {
PBL_LOG(LOG_LEVEL_ERROR, "Failed to generate PRA... :(");
}
}
PBL_LOG(LOG_LEVEL_INFO, "Pinned address: " BT_DEVICE_ADDRESS_FMT,
BT_DEVICE_ADDRESS_XPLODE(s_pinned_addr));
if (bt_persistent_storage_has_pinned_ble_pairings()) {
PBL_LOG(LOG_LEVEL_INFO, "Bonding that requires address pinning exists, applying pinned addr!");
bt_local_addr_pause_cycling();
s_cycling_paused_due_to_dependent_bondings = true;
} else {
#if RECOVERY_FW
PBL_LOG(LOG_LEVEL_INFO, "Pausing address cycling because PRF!");
bt_local_addr_pause_cycling();
#elif BT_CONTROLLER_CC2564X && !RELEASE
PBL_LOG(LOG_LEVEL_INFO, "Pausing address cycling because cc2564x and beta build!");
bt_local_addr_pause_cycling();
#else
PBL_LOG(LOG_LEVEL_INFO, "No bondings found that require address pinning!");
bt_driver_set_local_address(true /* allow_cycling */, NULL);
#endif
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
typedef struct BTDeviceAddress BTDeviceAddress;
//! Pauses cycling of local Private Resolvable Address (ref counted).
//! As long as the cycling is paused, the address that is used "on air" will be stable for the
//! duration that the BT stack is up (so the address can be expected to have changed after rebooting
//! or resetting the stack).
//! In case the local address is currently pinned, this function will be a no-op.
void bt_local_addr_pause_cycling(void);
//! Resumes cycling of local Private Resolvable Address (ref counted).
//! In case the local address is currently pinned, this function will be a no-op.
void bt_local_addr_resume_cycling(void);
//! Called by BT driver to indicate what the local address was that was used during the pairing
//! and pinning was requested. See comment in the implementation for more details.
void bt_local_addr_pin(const BTDeviceAddress *addr);
//! Handler for bonding changes (deletions primarily).
void bt_local_addr_handle_bonding_change(BTBondingID bonding, BtPersistBondingOp op);
//! Called during the BT stack initialization.
void bt_local_addr_init(void);
+158
View File
@@ -0,0 +1,158 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 "local_id.h"
#include "mfg/mfg_serials.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "util/attributes.h"
#include "util/hash.h"
#include "util/size.h"
#include "util/string.h"
#include <bluetooth/features.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
// Caches of the local address and device name.
// Some clients (i.e. Settings app) make a lot of calls to this module. By caching this info,
// we avoid having to reach out to the BT driver every time.
static BTDeviceAddress s_local_address;
static char s_local_device_name[BT_DEVICE_NAME_BUFFER_SIZE];
static char s_local_le_device_name[BT_DEVICE_NAME_BUFFER_SIZE];
static void prv_populate_name(char name[BT_DEVICE_NAME_BUFFER_SIZE], const char *name_fmt) {
sprintf(name, name_fmt, s_local_address.octets[1], s_local_address.octets[0]);
}
static void prv_set_default_device_name(void) {
#if (PLATFORM_SNOWY || PLATFORM_SPALDING || PLATFORM_ROBERT)
const char *s_local_default_device_name_format = "Pebble Time %02X%02X";
const char *s_local_default_le_device_name_format = "Pebble Time LE %02X%02X";
#else
const char *s_local_default_device_name_format = "Pebble %02X%02X";
const char *s_local_default_le_device_name_format = "Pebble-LE %02X%02X";
#endif
// Pebble + hex last 2 bytes of the device address:
prv_populate_name(s_local_device_name, s_local_default_device_name_format);
prv_populate_name(s_local_le_device_name, s_local_default_le_device_name_format);
}
static bool prv_has_device_name(void) {
return (s_local_device_name[0] != '\0');
}
static void prv_configure_device_name(void) {
bt_driver_id_set_local_device_name(s_local_device_name);
}
void bt_local_id_configure_driver(void) {
// Request the local address from the BT driver and cache it:
bt_driver_id_copy_local_identity_address(&s_local_address);
if (!prv_has_device_name()) {
if (!bt_persistent_storage_get_local_device_name(s_local_device_name,
sizeof(s_local_device_name))) {
prv_set_default_device_name();
}
}
prv_configure_device_name();
}
void bt_local_id_set_device_name(const char *device_name) {
strncpy(s_local_device_name, device_name, sizeof(s_local_device_name));
s_local_device_name[sizeof(s_local_device_name) - 1] = '\0';
prv_configure_device_name();
}
void bt_local_id_copy_device_name(char name_out[BT_DEVICE_NAME_BUFFER_SIZE], bool is_le) {
char *name = (is_le && bt_driver_supports_bt_classic()) ? s_local_le_device_name :
s_local_device_name;
strncpy(name_out, name, BT_DEVICE_NAME_BUFFER_SIZE);
}
void bt_local_id_copy_address(BTDeviceAddress *addr_out) {
*addr_out = s_local_address;
}
void bt_local_id_copy_address_hex_string(char addr_hex_str_out[BT_ADDR_FMT_BUFFER_SIZE_BYTES]) {
static const BTDeviceAddress null_addr = {};
if (0 != memcmp(&null_addr, &s_local_address, sizeof(s_local_address))) {
sniprintf(addr_hex_str_out, BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE,
BD_ADDR_FMT, BT_DEVICE_ADDRESS_XPLODE(s_local_address));
} else {
sniprintf(addr_hex_str_out, BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE, "Unknown");
}
}
void bt_local_id_copy_address_mac_string(char addr_mac_str_out[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]) {
sniprintf(addr_mac_str_out, BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE,
BT_DEVICE_ADDRESS_FMT, BT_DEVICE_ADDRESS_XPLODE(s_local_address));
}
T_STATIC void prv_generate_address(BTDeviceAddress *addr_out) {
const char *serial = mfg_get_serial_number();
const uint32_t full_len = strlen(serial);
const uint32_t half_len = (full_len / 2);
// Hash of the normal serial
const uint32_t serial_hash = hash((uint8_t *)serial, full_len);
// Hash of the serial reversed
char tmp[full_len + 1];
strncpy(tmp, serial, sizeof(tmp));
string_reverse(tmp);
const uint32_t reverse_hash = hash((uint8_t *)tmp, full_len);
struct PACKED {
union {
BTDeviceAddress bt_addr;
struct PACKED {
uint16_t a;
uint32_t b;
};
};
} addr = {
.a = (uint16_t) reverse_hash,
.b = (serial_hash ^ reverse_hash),
};
*addr_out = addr.bt_addr;
}
void bt_local_id_generate_address_from_serial(BTDeviceAddress *addr_out) {
prv_generate_address(addr_out);
addr_out->octets[ARRAY_LENGTH(addr_out->octets) - 1] |= 0b11000000;
// Addresses with all 0's or 1's
const BTDeviceAddress zero_addr = {.octets = {0x00, 0x00, 0x00, 0x00, 0x00, 0xC0}};
const BTDeviceAddress one_addr = {.octets = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
// NOTE: It already has the two most sig. bits set.
const BTDeviceAddress fallback_addr = {.octets = {0x3c, 0x08, 0x55, 0xaf, 0xd3, 0xc4}};
// Compare (the first 5 bytes) the generated one with the invalid ones. If they are equal,
// fall back to this address.
if (!memcmp(addr_out, &zero_addr, sizeof(BTDeviceAddress))
|| !memcmp(addr_out, &one_addr, sizeof(BTDeviceAddress))) {
*addr_out = fallback_addr;
}
memcpy(addr_out, (uint8_t *)addr_out, sizeof(*addr_out));
}
@@ -0,0 +1,47 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 <bluetooth/bluetooth_types.h>
#include <bluetooth/id.h>
//! Called by bl_ctl right after the stack starts, to configure the local device name and address.
void bt_local_id_configure_driver(void);
//! Sets a new device name, overriding the existing (default) one.
//! The name will be truncated to BT_DEVICE_NAME_BUFFER_SIZE - 1 characters.
void bt_local_id_set_device_name(const char *device_name);
//! Copies the name of the local device into the given buffer.
//! @param is_le Only consumed if the device used is dual mode. If so,
// this changes the name returned
void bt_local_id_copy_device_name(char name_out[BT_DEVICE_NAME_BUFFER_SIZE], bool is_le);
//! Copies the address of the local device.
void bt_local_id_copy_address(BTDeviceAddress *addr_out);
//! Copies a hex-formatted string representation ("0x000000000000") of the device address into the
//! given buffer. The buffer should be at least BT_ADDR_FMT_BUFFER_SIZE_BYTES bytes in size to fit
//! the address string.
//! If there is no local address known, the string "Unknown" will be copied into the buffer.
void bt_local_id_copy_address_hex_string(char addr_hex_str_out[BT_ADDR_FMT_BUFFER_SIZE_BYTES]);
//! Copies a MAC-formatted string representation ("00:00:00:00:00:00") of the device address into
//! the given buffer. The buffer should be at least BT_ADDR_FMT_BUFFER_SIZE_BYTES bytes in size to
//! fit the address string.
void bt_local_id_copy_address_mac_string(char addr_mac_str_out[BT_DEVICE_ADDRESS_FMT_BUFFER_SIZE]);
//! Generates a BTDeviceAddress from the serial number of the watch
void bt_local_id_generate_address_from_serial(BTDeviceAddress *addr_out);
@@ -0,0 +1,155 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#define FILE_LOG_COLOR LOG_COLOR_BLUE
#include "system/logging.h"
#include "system/passert.h"
#include "comm/ble/gap_le_slave_discovery.h"
#include "kernel/pebble_tasks.h"
#include "services/common/bluetooth/bluetooth_ctl.h"
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
#include "services/common/bluetooth/local_addr.h"
#include "services/common/bluetooth/pairability.h"
#include "services/common/regular_timer.h"
#include "services/common/system_task.h"
#include <bluetooth/connectability.h>
#include <bluetooth/features.h>
#include <bluetooth/pairability.h>
static void prv_pairability_timer_cb(void *unused);
static int s_allow_bt_pairing_refcount = 0;
static int s_allow_ble_pairing_refcount = 0;
static RegularTimerInfo s_pairability_timer_info = {
.cb = prv_pairability_timer_cb,
};
static void evaluate_pairing_refcount(void *data) {
PBL_ASSERT_TASK(PebbleTask_KernelBackground);
if (!bt_ctl_is_bluetooth_running()) {
return;
}
PBL_LOG(LOG_LEVEL_DEBUG, "Pairabilty state: LE=%u, Classic=%u",
s_allow_ble_pairing_refcount, s_allow_bt_pairing_refcount);
bool is_ble_pairable_and_discoverable = (s_allow_ble_pairing_refcount > 0);
bt_driver_le_pairability_set_enabled(is_ble_pairable_and_discoverable);
if (gap_le_slave_is_discoverable() != is_ble_pairable_and_discoverable) {
if (is_ble_pairable_and_discoverable) {
bt_local_addr_pause_cycling();
} else {
bt_local_addr_resume_cycling();
}
gap_le_slave_set_discoverable(is_ble_pairable_and_discoverable);
}
if (bt_driver_supports_bt_classic()) {
bt_driver_classic_pairability_set_enabled((s_allow_bt_pairing_refcount > 0));
bt_driver_classic_update_connectability();
}
}
static void prv_schedule_evaluation(void) {
// We used to sparingly schedule the evaluation and had a bug because of this:
// https://pebbletechnology.atlassian.net/browse/PBL-22884
// Because this pretty much only happens in response to user input, don't bother limiting this,
// and always evaluate, even though the state might not have changed:
system_task_add_callback(evaluate_pairing_refcount, NULL);
}
void bt_pairability_use(void) {
++s_allow_bt_pairing_refcount;
++s_allow_ble_pairing_refcount;
prv_schedule_evaluation();
}
void bt_pairability_use_bt(void) {
++s_allow_bt_pairing_refcount;
prv_schedule_evaluation();
}
void bt_pairability_use_ble(void) {
++s_allow_ble_pairing_refcount;
prv_schedule_evaluation();
}
static void prv_pairability_timer_cb(void *unused) {
regular_timer_remove_callback(&s_pairability_timer_info);
bt_pairability_release_ble();
}
void bt_pairability_use_ble_for_period(uint16_t duration_secs) {
if (!regular_timer_is_scheduled(&s_pairability_timer_info)) {
// If this function is called multiple times before the timer is unscheduled, limit to calling
// "use" only once:
bt_pairability_use_ble();
}
// Always reschedule, even if the duration is shorter than the one that might already be
// scheduled:
regular_timer_add_multisecond_callback(&s_pairability_timer_info, duration_secs);
}
void bt_pairability_release(void) {
PBL_ASSERT(s_allow_bt_pairing_refcount != 0 && s_allow_ble_pairing_refcount != 0, "");
--s_allow_bt_pairing_refcount;
--s_allow_ble_pairing_refcount;
prv_schedule_evaluation();
}
void bt_pairability_release_bt(void) {
PBL_ASSERT(s_allow_bt_pairing_refcount != 0, "");
--s_allow_bt_pairing_refcount;
prv_schedule_evaluation();
}
void bt_pairability_release_ble(void) {
PBL_ASSERT(s_allow_ble_pairing_refcount != 0, "");
--s_allow_ble_pairing_refcount;
prv_schedule_evaluation();
}
//! Call this whenever we modify the number of saved bondings we have.
void bt_pairability_update_due_to_bonding_change(void) {
static bool s_pairable_due_to_no_gateway_bondings = false;
const bool has_classic_bonding =
(bt_driver_supports_bt_classic() &&
bt_persistent_storage_has_active_bt_classic_gateway_bonding());
if (!has_classic_bonding &&
!bt_persistent_storage_has_active_ble_gateway_bonding() &&
!bt_persistent_storage_has_ble_ancs_bonding()) {
if (!s_pairable_due_to_no_gateway_bondings) {
bt_pairability_use();
s_pairable_due_to_no_gateway_bondings = true;
}
} else {
if (s_pairable_due_to_no_gateway_bondings) {
bt_pairability_release();
s_pairable_due_to_no_gateway_bondings = false;
}
}
}
void bt_pairability_init(void) {
bt_pairability_update_due_to_bonding_change();
prv_schedule_evaluation();
}
@@ -0,0 +1,45 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
//! Reference counted request to allow us to be discovered and paired with over BT Classic & LE.
void bt_pairability_use(void);
//! Reference counted request to allow us to be discovered and paired with over BT Classic.
void bt_pairability_use_bt(void);
//! Reference counted request to allow us to be discovered and paired with over BLE.
void bt_pairability_use_ble(void);
//! Reference counted request to allow us to be discovered and paired with over BLE for a specific
//! period, after which bt_pairability_release_ble will be called automatically.
void bt_pairability_use_ble_for_period(uint16_t duration_secs);
//! Reference counted request to disallow us to be discovered and paired with over BT Classic & LE.
void bt_pairability_release(void);
//! Reference counted request to disallow us to be discovered and paired with over BT Classic.
void bt_pairability_release_bt(void);
//! Reference counted request to disallow us to be discovered and paired with over BLE.
void bt_pairability_release_ble(void);
//! Evaluates whether there are any bondings to gateways. If there are none, make the system
//! discoverable and pairable.
void bt_pairability_update_due_to_bonding_change(void);
void bt_pairability_init(void);
@@ -0,0 +1,130 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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 <bluetooth/pebble_pairing_service.h>
#include "comm/ble/gap_le_connect_params.h"
#include "comm/ble/gap_le_connection.h"
#include "comm/ble/kernel_le_client/app_launch/app_launch.h"
#include "comm/bt_conn_mgr.h"
#include "comm/bt_lock.h"
#include "kernel/pbl_malloc.h"
#include "system/logging.h"
extern void gap_le_connect_params_re_evaluate(GAPLEConnection *connection);
static void prv_convert_pps_request_params(const PebblePairingServiceConnParamSet *pps_params_in,
GAPLEConnectRequestParams *params_out) {
const uint16_t min_1_25ms = pps_params_in->interval_min_1_25ms;
params_out->connection_interval_min_1_25ms = min_1_25ms;
params_out->connection_interval_max_1_25ms =
min_1_25ms + pps_params_in->interval_max_delta_1_25ms;
#if RECOVERY_FW || BT_CONTROLLER_DA14681
if (pps_params_in->slave_latency_events != 0) {
# if RECOVERY_FW
PBL_LOG(LOG_LEVEL_DEBUG, "Overriding requested slave latency with 0 because PRF");
# else
PBL_LOG(LOG_LEVEL_DEBUG, "Overriding requested slave latency with 0 because Dialog");
# endif
}
params_out->slave_latency_events = 0;
#else
params_out->slave_latency_events = pps_params_in->slave_latency_events;
#endif
params_out->supervision_timeout_10ms = pps_params_in->supervision_timeout_30ms * 3;
}
static void prv_handle_set_remote_param_mgmt_settings(GAPLEConnection *connection,
const PebblePairingServiceRemoteParamMgmtSettings *settings, size_t settings_length) {
bool is_remote_device_managing_connection_parameters =
settings->is_remote_device_managing_connection_parameters;
connection->is_remote_device_managing_connection_parameters =
is_remote_device_managing_connection_parameters;
PBL_LOG(LOG_LEVEL_INFO, "PPS: is_remote_mgmt=%u",
is_remote_device_managing_connection_parameters);
if (settings_length >= PEBBLE_PAIRING_SERVICE_REMOTE_PARAM_MGTM_SETTINGS_SIZE_WITH_PARAM_SETS) {
if (!connection->connection_parameter_sets) {
const size_t size = sizeof(GAPLEConnectRequestParams) * NumResponseTimeState;
connection->connection_parameter_sets =
(GAPLEConnectRequestParams *) kernel_zalloc_check(size);
}
for (ResponseTimeState s = ResponseTimeMax; s < NumResponseTimeState; ++s) {
const PebblePairingServiceConnParamSet *pps_params =
&settings->connection_parameter_sets[s];
GAPLEConnectRequestParams *params = &connection->connection_parameter_sets[s];
prv_convert_pps_request_params(pps_params, params);
PBL_LOG(LOG_LEVEL_INFO,
"PPS: Updated param set %u: %u-%u, slave lat: %u, supervision timeout: %u",
s, params->connection_interval_min_1_25ms, params->connection_interval_max_1_25ms,
params->slave_latency_events, params->supervision_timeout_10ms);
}
}
// Always just re-evaluate, should be idempotent:
gap_le_connect_params_re_evaluate(connection);
}
static void prv_handle_set_remote_desired_state(GAPLEConnection *connection,
const PebblePairingServiceRemoteDesiredState *desired_state) {
const ResponseTimeState remote_desired_state = (ResponseTimeState)desired_state->state;
PBL_LOG(LOG_LEVEL_INFO, "PPS: desired_state=%u", remote_desired_state);
// "As a safety measure, the watch will reset it back to ResponseTimeMax after 5 minutes."
const uint16_t max_period_secs = 5 * 60;
conn_mgr_set_ble_conn_response_time(connection, BtConsumerPebblePairingServiceRemoteDevice,
remote_desired_state, max_period_secs);
}
void bt_driver_cb_pebble_pairing_service_handle_connection_parameter_write(
const BTDeviceInternal *device,
const PebblePairingServiceConnParamsWrite *conn_params,
size_t conn_params_length) {
bt_lock();
{
GAPLEConnection *connection = gap_le_connection_by_device(device);
if (!connection) {
goto unlock;
}
const size_t length = (conn_params_length - offsetof(PebblePairingServiceConnParamsWrite,
remote_desired_state));
switch (conn_params->cmd) {
case PebblePairingServiceConnParamsWriteCmd_SetRemoteParamMgmtSettings:
prv_handle_set_remote_param_mgmt_settings(connection,
&conn_params->remote_param_mgmt_settings, length);
break;
case PebblePairingServiceConnParamsWriteCmd_SetRemoteDesiredState:
prv_handle_set_remote_desired_state(connection, &conn_params->remote_desired_state);
break;
case PebblePairingServiceConnParamsWriteCmd_EnablePacketLengthExtension:
PBL_LOG(LOG_LEVEL_INFO, "Enabling BLE Packet Length Extension");
break;
case PebblePairingServiceConnParamsWriteCmd_InhibitBLESleep:
PBL_LOG(LOG_LEVEL_INFO, "BLE Sleep Mode inhibited!");
break;
default:
PBL_LOG(LOG_LEVEL_ERROR, "Unknown write_cmd %d", conn_params->cmd);
break;
}
}
unlock:
bt_unlock();
}
void bt_driver_cb_pebble_pairing_service_handle_ios_app_termination_detected(void) {
app_launch_trigger();
}
@@ -0,0 +1,72 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#define FILE_LOG_COLOR LOG_COLOR_BLUE
#include "pp_ble_control.h"
#include "services/common/bluetooth/pairability.h"
#include "system/logging.h"
#include "system/passert.h"
#include "util/attributes.h"
typedef enum {
// Values 0 - 3 are deprecated, do not use.
BLEControlCommandTypeSetDiscoverablePairable = 4,
} BLEControlCommandType;
typedef struct PACKED {
uint8_t opcode;
bool discoverable_pairable;
uint16_t duration;
} BLEControlCommandSetDiscoverablePairable;
// -----------------------------------------------------------------------------
//! Handler for the "Set Discoverable & Pairable" command
static void prv_handle_set_discoverable_pairable(
BLEControlCommandSetDiscoverablePairable *cmd_data) {
bt_pairability_use_ble_for_period(cmd_data->duration);
PBL_LOG(LOG_LEVEL_INFO, "Set Discoverable Pairable: %u, %u",
cmd_data->discoverable_pairable, cmd_data->duration);
}
// -----------------------------------------------------------------------------
//! Pebble protocol handler for the BLE control endpoint
void pp_ble_control_protocol_msg_callback(
CommSession* session, const uint8_t *data, unsigned int length) {
PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(PebbleTask_KernelBackground);
if (length < sizeof(BLEControlCommandSetDiscoverablePairable)) {
PBL_LOG(LOG_LEVEL_WARNING, "Invalid pp_ble_control_protocol_msg_callback message: %d", length);
return;
}
const uint8_t opcode = *(const uint8_t *) data;
switch (opcode) {
case 0 ... 3:
PBL_LOG(LOG_LEVEL_INFO, "Deprecated & unsupported opcode: %u", opcode);
break;
case BLEControlCommandTypeSetDiscoverablePairable: {
BLEControlCommandSetDiscoverablePairable *cmd_data =
(BLEControlCommandSetDiscoverablePairable *)data;
prv_handle_set_discoverable_pairable(cmd_data);
break;
}
default:
PBL_LOG(LOG_LEVEL_DEBUG, "Unknown opcode %u", opcode);
break;
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
#pragma once
#include "services/common/comm_session/session.h"
void pp_ble_control_protocol_msg_callback(CommSession* session,
const uint8_t *data,
unsigned int length);