mirror of
https://github.com/google/pebble.git
synced 2025-07-15 02:26:43 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
483
src/fw/services/normal/bluetooth/ble_hrm.c
Normal file
483
src/fw/services/normal/bluetooth/ble_hrm.c
Normal file
|
@ -0,0 +1,483 @@
|
|||
/*
|
||||
* 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 "ble_hrm.h"
|
||||
|
||||
#include "applib/event_service_client.h"
|
||||
#include "comm/ble/gap_le_connection.h"
|
||||
#include "comm/ble/gap_le_slave_reconnect.h"
|
||||
#include "comm/bt_lock.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/events.h"
|
||||
#include "popups/ble_hrm/ble_hrm_reminder_popup.h"
|
||||
#include "popups/ble_hrm/ble_hrm_sharing_popup.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "services/common/analytics/analytics_event.h"
|
||||
#include "services/common/hrm/hrm_manager_private.h"
|
||||
#include "services/common/regular_timer.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
#include "shell/system_app_ids.auto.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#include <bluetooth/gap_le_connect.h>
|
||||
#include <bluetooth/hrm_service.h>
|
||||
#include <btutil/bt_device.h>
|
||||
#include <util/list.h>
|
||||
#include <util/size.h>
|
||||
|
||||
#if CAPABILITY_HAS_BUILTIN_HRM
|
||||
|
||||
#define BLE_HRM_UPDATE_INTERVAL_SEC (1)
|
||||
|
||||
typedef struct BLEHRMSharingRequest {
|
||||
GAPLEConnection *connection;
|
||||
} BLEHRMSharingRequest;
|
||||
|
||||
static bool s_ble_hrm_is_inited;
|
||||
static int s_ble_hrm_subscription_count;
|
||||
static RegularTimerInfo s_ble_hrm_timer;
|
||||
static struct {
|
||||
EventServiceInfo service_info;
|
||||
HRMSessionRef manager_session;
|
||||
} s_ble_hrm_session;
|
||||
|
||||
typedef enum {
|
||||
HrmSharingPermission_Unknown,
|
||||
HrmSharingPermission_Granted,
|
||||
HrmSharingPermission_Declined,
|
||||
} HrmSharingPermission;
|
||||
|
||||
typedef struct BLEHRMSharingPermission {
|
||||
ListNode node;
|
||||
BTDeviceInternal device;
|
||||
|
||||
//! Whether the user has confirmed that sharing HRM data to this device is permitted.
|
||||
HrmSharingPermission permission;
|
||||
} BLEHRMSharingPermission;
|
||||
|
||||
static BLEHRMSharingPermission *s_permissions_head;
|
||||
|
||||
static bool prv_hw_and_sw_supports_hrm(void) {
|
||||
return (bt_driver_is_hrm_service_supported() &&
|
||||
sys_hrm_manager_is_hrm_present());
|
||||
}
|
||||
|
||||
bool ble_hrm_is_supported_and_enabled(void) {
|
||||
return (prv_hw_and_sw_supports_hrm() &&
|
||||
activity_prefs_heart_rate_is_enabled());
|
||||
}
|
||||
|
||||
static void prv_reset_subscriptions(void);
|
||||
|
||||
static bool prv_free_permission_for_each_cb(ListNode *node, void *unused) {
|
||||
kernel_free(node);
|
||||
return true; // continue iteration
|
||||
}
|
||||
|
||||
static void prv_free_all_permissions(void) {
|
||||
list_foreach((ListNode *)s_permissions_head, prv_free_permission_for_each_cb, NULL);
|
||||
s_permissions_head = NULL;
|
||||
}
|
||||
|
||||
static bool prv_find_permission_by_device_filter_cb(ListNode *found_node, void *data) {
|
||||
const BTDeviceInternal *device = data;
|
||||
BLEHRMSharingPermission *permission = (BLEHRMSharingPermission *)found_node;
|
||||
return bt_device_internal_equal(device, &permission->device);
|
||||
}
|
||||
|
||||
static BLEHRMSharingPermission *prv_find_permission_by_device(const BTDeviceInternal *device) {
|
||||
return (BLEHRMSharingPermission *)list_find((ListNode *)s_permissions_head,
|
||||
prv_find_permission_by_device_filter_cb,
|
||||
(void *)device);
|
||||
}
|
||||
|
||||
static void prv_upsert_permission(const BTDeviceInternal *device, HrmSharingPermission permission) {
|
||||
BLEHRMSharingPermission *p = prv_find_permission_by_device(device);
|
||||
if (!p) {
|
||||
p = kernel_zalloc_check(sizeof(*p));
|
||||
p->device = *device;
|
||||
s_permissions_head = (BLEHRMSharingPermission *)list_prepend((ListNode *)s_permissions_head,
|
||||
(ListNode *)p);
|
||||
}
|
||||
p->permission = permission;
|
||||
}
|
||||
|
||||
static HrmSharingPermission prv_get_permission_by_device(const BTDeviceInternal *device) {
|
||||
BLEHRMSharingPermission *p = prv_find_permission_by_device(device);
|
||||
if (!p) {
|
||||
return HrmSharingPermission_Unknown;
|
||||
}
|
||||
return p->permission;
|
||||
}
|
||||
|
||||
void ble_hrm_handle_activity_prefs_heart_rate_is_enabled(bool is_enabled) {
|
||||
if (!prv_hw_and_sw_supports_hrm()) {
|
||||
return;
|
||||
}
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing prefs updated: is_enabled=%u", is_enabled);
|
||||
|
||||
if (!is_enabled) {
|
||||
prv_reset_subscriptions();
|
||||
}
|
||||
bt_driver_hrm_service_enable(is_enabled);
|
||||
}
|
||||
|
||||
static bool prv_is_sharing(const GAPLEConnection *const connection) {
|
||||
return (connection->hrm_service_is_subscribed &&
|
||||
(prv_get_permission_by_device(&connection->device) == HrmSharingPermission_Granted));
|
||||
}
|
||||
|
||||
bool ble_hrm_is_sharing_to_connection(const GAPLEConnection *const connection) {
|
||||
bt_lock_assert_held(true);
|
||||
if (!connection) {
|
||||
return false;
|
||||
}
|
||||
return prv_is_sharing(connection);
|
||||
}
|
||||
|
||||
bool ble_hrm_is_sharing(void) {
|
||||
return (s_ble_hrm_subscription_count > 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
BTDeviceInternal *next_permitted_device;
|
||||
size_t slots_left;
|
||||
} CopySharingDevicesCtx;
|
||||
|
||||
static void prv_copy_sharing_devices_for_each_connection_cb(GAPLEConnection *connection,
|
||||
void *data) {
|
||||
CopySharingDevicesCtx *ctx = data;
|
||||
if (ctx->slots_left && prv_is_sharing(connection)) {
|
||||
*ctx->next_permitted_device = connection->device;
|
||||
++ctx->next_permitted_device;
|
||||
--ctx->slots_left;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t prv_copy_sharing_devices(BTDeviceInternal *devices_out,
|
||||
size_t max_devices) {
|
||||
bt_lock();
|
||||
CopySharingDevicesCtx ctx = {
|
||||
.next_permitted_device = devices_out,
|
||||
.slots_left = max_devices,
|
||||
};
|
||||
gap_le_connection_for_each(prv_copy_sharing_devices_for_each_connection_cb, &ctx);
|
||||
bt_unlock();
|
||||
return (max_devices - ctx.slots_left);
|
||||
}
|
||||
|
||||
static void prv_ble_hrm_handle_hrm_data(PebbleEvent *e, void *context) {
|
||||
if (!s_ble_hrm_is_inited) {
|
||||
return;
|
||||
}
|
||||
if (s_ble_hrm_subscription_count == 0) {
|
||||
return;
|
||||
}
|
||||
PBL_ASSERTN(e->type == PEBBLE_HRM_EVENT);
|
||||
const PebbleHRMEvent *const hrm_event = &e->hrm;
|
||||
if (hrm_event->event_type != HRMEvent_BPM) {
|
||||
return;
|
||||
}
|
||||
const BleHrmServiceMeasurement measurement = {
|
||||
.bpm = hrm_event->bpm.bpm,
|
||||
.is_on_wrist = (hrm_event->bpm.quality > HRMQuality_NoSignal),
|
||||
};
|
||||
|
||||
BTDeviceInternal sharing_to_devices[4];
|
||||
const size_t num_devices = prv_copy_sharing_devices(sharing_to_devices,
|
||||
ARRAY_LENGTH(sharing_to_devices));
|
||||
bt_driver_hrm_service_handle_measurement(&measurement, sharing_to_devices, num_devices);
|
||||
}
|
||||
|
||||
static void prv_start_hrm_kernel_main(void *unused) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing started");
|
||||
s_ble_hrm_session.service_info = (EventServiceInfo) {
|
||||
.type = PEBBLE_HRM_EVENT,
|
||||
.handler = prv_ble_hrm_handle_hrm_data,
|
||||
};
|
||||
event_service_client_subscribe(&s_ble_hrm_session.service_info);
|
||||
s_ble_hrm_session.manager_session =
|
||||
hrm_manager_subscribe_with_callback(INSTALL_ID_INVALID, 1 /*update_interval_s*/,
|
||||
0 /*expire_s*/, HRMFeature_BPM, NULL, NULL);
|
||||
|
||||
analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BLE_HRM_SHARING_TIME, AnalyticsClient_System);
|
||||
}
|
||||
|
||||
static void prv_stop_hrm_kernel_main(void *unused) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing stopped");
|
||||
sys_hrm_manager_unsubscribe(s_ble_hrm_session.manager_session);
|
||||
event_service_client_unsubscribe(&s_ble_hrm_session.service_info);
|
||||
|
||||
analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_HRM_SHARING_TIME);
|
||||
}
|
||||
|
||||
static void prv_execute_on_kernel_main(CallbackEventCallback cb) {
|
||||
if (pebble_task_get_current() != PebbleTask_KernelMain) {
|
||||
launcher_task_add_callback(cb, NULL);
|
||||
} else {
|
||||
cb(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_push_sharing_request_window_kernel_main_cb(void *ctx) {
|
||||
ble_hrm_push_sharing_request_window((BLEHRMSharingRequest *)ctx);
|
||||
}
|
||||
|
||||
static void prv_request_sharing_permission(GAPLEConnection *const connection) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Requesting BLE HRM sharing permission");
|
||||
BLEHRMSharingRequest *const sharing_request = kernel_zalloc_check(sizeof(*sharing_request));
|
||||
sharing_request->connection = connection;
|
||||
launcher_task_add_callback(prv_push_sharing_request_window_kernel_main_cb, sharing_request);
|
||||
}
|
||||
|
||||
static void prv_put_sharing_state_updated_event(int subscription_count) {
|
||||
// 2 purposes of this event:
|
||||
// - refresh the Settings/Bluetooth UI whenever a device (un)subscribes.
|
||||
// - present a "Sharing HRM" icon in the Settings app glance.
|
||||
PebbleEvent e = {
|
||||
.type = PEBBLE_BLE_HRM_SHARING_STATE_UPDATED_EVENT,
|
||||
.bluetooth = {
|
||||
.le = {
|
||||
.hrm_sharing_state = {
|
||||
.subscription_count = subscription_count,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
event_put(&e);
|
||||
}
|
||||
|
||||
static void prv_reschedule_popup_timer(void);
|
||||
|
||||
static void prv_push_reminder_popup_kernel_main_cb(void *unused) {
|
||||
bt_lock();
|
||||
if (s_ble_hrm_subscription_count > 0) {
|
||||
// Reschedule to show again after BLE_HRM_REMINDER_POPUP_DELAY_MINS
|
||||
prv_reschedule_popup_timer();
|
||||
}
|
||||
bt_unlock();
|
||||
|
||||
ble_hrm_push_reminder_popup();
|
||||
|
||||
analytics_event_ble_hrm(BleHrmEventSubtype_SharingTimeoutPopupPresented);
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing timeout fired!");
|
||||
}
|
||||
|
||||
//! @note executes on timer task
|
||||
static void prv_reminder_popup_timer_cb(void *unused) {
|
||||
prv_execute_on_kernel_main(prv_push_reminder_popup_kernel_main_cb);
|
||||
}
|
||||
|
||||
static void prv_stop_popup_timer(void) {
|
||||
if (regular_timer_is_scheduled(&s_ble_hrm_timer)) {
|
||||
regular_timer_remove_callback(&s_ble_hrm_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_reschedule_popup_timer(void) {
|
||||
prv_stop_popup_timer();
|
||||
s_ble_hrm_timer = (RegularTimerInfo) {
|
||||
.cb = prv_reminder_popup_timer_cb,
|
||||
};
|
||||
regular_timer_add_multiminute_callback(&s_ble_hrm_timer, BLE_HRM_REMINDER_POPUP_DELAY_MINS);
|
||||
}
|
||||
|
||||
static void prv_update_is_sharing(GAPLEConnection *connection, bool prev_is_sharing) {
|
||||
const bool is_sharing = prv_is_sharing(connection);
|
||||
if (is_sharing == prev_is_sharing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_sharing) {
|
||||
if (s_ble_hrm_subscription_count == 0) {
|
||||
prv_reschedule_popup_timer();
|
||||
prv_execute_on_kernel_main(prv_start_hrm_kernel_main);
|
||||
}
|
||||
++s_ble_hrm_subscription_count;
|
||||
} else {
|
||||
--s_ble_hrm_subscription_count;
|
||||
if (s_ble_hrm_subscription_count == 0) {
|
||||
prv_stop_popup_timer();
|
||||
prv_execute_on_kernel_main(prv_stop_hrm_kernel_main);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit for every subscription, so Settings/Bluetooth menu can update accordingly.
|
||||
prv_put_sharing_state_updated_event(s_ble_hrm_subscription_count);
|
||||
}
|
||||
|
||||
static void prv_update_permission(GAPLEConnection *connection, HrmSharingPermission permission) {
|
||||
bt_lock_assert_held(true);
|
||||
if (prv_get_permission_by_device(&connection->device) == permission) {
|
||||
return;
|
||||
}
|
||||
const bool prev_is_sharing = prv_is_sharing(connection);
|
||||
prv_upsert_permission(&connection->device, permission);
|
||||
prv_update_is_sharing(connection, prev_is_sharing);
|
||||
}
|
||||
|
||||
static void prv_disconnect_to_kill_subscription(GAPLEConnection *connection) {
|
||||
// Unfortunately, GATT does not offer a way to remove a subscription from the server side.
|
||||
// Only clients (subscribers) themselves can change the subscription state (write the CCCD).
|
||||
// When stopping sharing, we're disconnecting the LE link just to reset the remote subscription
|
||||
// state. Yes, a pretty big hammer... :( If we don't do this, the other end will stay subscribed.
|
||||
// Then when an app on the phone uses the HRM service "again", there won't be a new CCCD write
|
||||
// because the phone was already subscribed...
|
||||
// For declining to share up-front, we'll just leave the client subscribed and don't disconnect
|
||||
// to prevent reconnection-loops.
|
||||
bt_driver_gap_le_disconnect(&connection->device);
|
||||
}
|
||||
|
||||
void ble_hrm_revoke_sharing_permission_for_connection(GAPLEConnection *connection) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing: revoked for conn %p", connection);
|
||||
bt_lock();
|
||||
if (gap_le_connection_is_valid(connection)) {
|
||||
prv_update_permission(connection, HrmSharingPermission_Declined);
|
||||
prv_disconnect_to_kill_subscription(connection);
|
||||
}
|
||||
bt_unlock();
|
||||
|
||||
analytics_event_ble_hrm(BleHrmEventSubtype_SharingRevoked);
|
||||
}
|
||||
|
||||
static void prv_revoke_gap_le_connection_for_each_cb(GAPLEConnection *connection, void *unused) {
|
||||
prv_update_permission(connection, HrmSharingPermission_Declined);
|
||||
prv_disconnect_to_kill_subscription(connection);
|
||||
}
|
||||
|
||||
void ble_hrm_revoke_all(void) {
|
||||
bt_lock();
|
||||
gap_le_connection_for_each(prv_revoke_gap_le_connection_for_each_cb, NULL);
|
||||
bt_unlock();
|
||||
|
||||
// Counting as one -- it's one user action.
|
||||
analytics_event_ble_hrm(BleHrmEventSubtype_SharingRevoked);
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing: all revoked");
|
||||
}
|
||||
|
||||
static void prv_update_subscription(GAPLEConnection *connection, bool is_subscribed) {
|
||||
bt_lock_assert_held(true);
|
||||
if (connection->hrm_service_is_subscribed == is_subscribed) {
|
||||
return;
|
||||
}
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing: conn <%p> is_subscribed=%u", connection, is_subscribed);
|
||||
|
||||
const bool prev_is_sharing = prv_is_sharing(connection);
|
||||
connection->hrm_service_is_subscribed = is_subscribed;
|
||||
prv_update_is_sharing(connection, prev_is_sharing);
|
||||
|
||||
if (is_subscribed) {
|
||||
const HrmSharingPermission current_permission =
|
||||
prv_get_permission_by_device(&connection->device);
|
||||
switch (current_permission) {
|
||||
case HrmSharingPermission_Unknown:
|
||||
prv_request_sharing_permission(connection);
|
||||
break;
|
||||
case HrmSharingPermission_Granted:
|
||||
// Stop advertising the with the HR service in the adv payload.
|
||||
// Note: we're assuming this is the only device we were advertising for.
|
||||
gap_le_slave_reconnect_hrm_stop();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_reset_subscriptions(void) {
|
||||
bt_lock();
|
||||
if (s_ble_hrm_subscription_count) {
|
||||
s_ble_hrm_subscription_count = 0;
|
||||
prv_stop_popup_timer();
|
||||
bt_unlock();
|
||||
|
||||
prv_execute_on_kernel_main(prv_stop_hrm_kernel_main);
|
||||
} else {
|
||||
bt_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ble_hrm_handle_sharing_request_response(bool is_granted,
|
||||
BLEHRMSharingRequest *sharing_request) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "BLE HRM sharing permission is_granted=%u", is_granted);
|
||||
|
||||
bt_lock();
|
||||
GAPLEConnection *connection = sharing_request->connection;
|
||||
if (gap_le_connection_is_valid(connection)) {
|
||||
const HrmSharingPermission permission =
|
||||
(is_granted ? HrmSharingPermission_Granted : HrmSharingPermission_Declined);
|
||||
prv_update_permission(connection, permission);
|
||||
}
|
||||
bt_unlock();
|
||||
|
||||
kernel_free(sharing_request);
|
||||
|
||||
analytics_event_ble_hrm(is_granted ?
|
||||
BleHrmEventSubtype_SharingAccepted : BleHrmEventSubtype_SharingDeclined);
|
||||
}
|
||||
|
||||
void bt_driver_cb_hrm_service_update_subscription(const BTDeviceInternal *device,
|
||||
bool is_subscribed) {
|
||||
bt_lock();
|
||||
if (!s_ble_hrm_is_inited) {
|
||||
goto unlock;
|
||||
}
|
||||
GAPLEConnection *connection = gap_le_connection_by_device(device);
|
||||
if (!connection) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Subscription update but no connection?");
|
||||
goto unlock;
|
||||
}
|
||||
prv_update_subscription(connection, is_subscribed);
|
||||
unlock:
|
||||
bt_unlock();
|
||||
}
|
||||
|
||||
void ble_hrm_handle_disconnection(GAPLEConnection *connection) {
|
||||
if (!s_ble_hrm_is_inited) {
|
||||
return;
|
||||
}
|
||||
if (prv_is_sharing(connection)) {
|
||||
// Certain phone apps require the HR device to advertise with the HR service in the adv payload
|
||||
// in order to make reconnection work, regardless of whether the Pebble mobile app already takes
|
||||
// care of reconnecting... Therefore, advertise with the HR service for up to 60 seconds:
|
||||
gap_le_slave_reconnect_hrm_restart();
|
||||
}
|
||||
prv_update_subscription(connection, false /* is_subscribed */);
|
||||
|
||||
// Just leave the permission until we reboot, toggle airplane mode or the user manually revokes.
|
||||
}
|
||||
|
||||
void ble_hrm_init(void) {
|
||||
s_ble_hrm_is_inited = true;
|
||||
s_ble_hrm_timer = (RegularTimerInfo) {};
|
||||
}
|
||||
|
||||
void ble_hrm_deinit(void) {
|
||||
s_ble_hrm_is_inited = false;
|
||||
gap_le_slave_reconnect_hrm_stop();
|
||||
prv_reset_subscriptions();
|
||||
prv_free_all_permissions();
|
||||
}
|
||||
|
||||
// For unit testing
|
||||
RegularTimerInfo *ble_hrm_timer(void) {
|
||||
return &s_ble_hrm_timer;
|
||||
}
|
||||
|
||||
#endif
|
50
src/fw/services/normal/bluetooth/ble_hrm.h
Normal file
50
src/fw/services/normal/bluetooth/ble_hrm.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 "util/time/time.h"
|
||||
|
||||
#define BLE_HRM_REMINDER_POPUP_DELAY_MINS (2 * MINUTES_PER_HOUR)
|
||||
|
||||
typedef struct GAPLEConnection GAPLEConnection;
|
||||
|
||||
typedef struct BLEHRMSharingRequest BLEHRMSharingRequest;
|
||||
|
||||
//! Called by the ble_hrm_sharing_popup upon the user's action to grant or decline the sharing.
|
||||
//! @note Also cleans up the sharing_request object.
|
||||
void ble_hrm_handle_sharing_request_response(bool is_granted,
|
||||
BLEHRMSharingRequest *sharing_request);
|
||||
|
||||
bool ble_hrm_is_supported_and_enabled(void);
|
||||
|
||||
bool ble_hrm_is_sharing_to_connection(const GAPLEConnection *connection);
|
||||
|
||||
bool ble_hrm_is_sharing(void);
|
||||
|
||||
void ble_hrm_revoke_sharing_permission_for_connection(GAPLEConnection *connection);
|
||||
|
||||
void ble_hrm_revoke_all(void);
|
||||
|
||||
void ble_hrm_handle_activity_prefs_heart_rate_is_enabled(bool is_enabled);
|
||||
|
||||
void ble_hrm_handle_disconnection(GAPLEConnection *connection);
|
||||
|
||||
void ble_hrm_init(void);
|
||||
|
||||
void ble_hrm_deinit(void);
|
1606
src/fw/services/normal/bluetooth/bluetooth_persistent_storage.c
Normal file
1606
src/fw/services/normal/bluetooth/bluetooth_persistent_storage.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 <util/attributes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct PACKED BtPersistLEPairingInfo {
|
||||
struct PACKED {
|
||||
uint16_t div;
|
||||
uint16_t ediv;
|
||||
} local_encryption_info;
|
||||
|
||||
uint8_t rsvd1[4];
|
||||
|
||||
struct PACKED {
|
||||
SMLongTermKey ltk;
|
||||
uint64_t rand;
|
||||
uint16_t ediv;
|
||||
} remote_encryption_info;
|
||||
|
||||
uint8_t rsvd2[6];
|
||||
|
||||
SMIdentityResolvingKey irk;
|
||||
BTDeviceInternal identity;
|
||||
|
||||
SMConnectionSignatureResolvingKey csrk;
|
||||
|
||||
//! True if local_encryption_info is valid
|
||||
bool is_local_encryption_info_valid:1;
|
||||
|
||||
//! True if remote_encryption_info is valid
|
||||
bool is_remote_encryption_info_valid:1;
|
||||
|
||||
//! True if irk and identity are valid
|
||||
bool is_remote_identity_info_valid:1;
|
||||
|
||||
//! True if csrk is valid
|
||||
bool is_remote_signing_info_valid:1;
|
||||
|
||||
uint8_t rsvd3:4;
|
||||
uint8_t rsvd4[7];
|
||||
} BtPersistLEPairingInfo;
|
||||
|
||||
static void bt_persistent_storage_assign_persist_pairing_info(BtPersistLEPairingInfo *out,
|
||||
const SMPairingInfo *in) {
|
||||
*out = (BtPersistLEPairingInfo) {};
|
||||
out->local_encryption_info.div = in->local_encryption_info.div;
|
||||
out->local_encryption_info.ediv = in->local_encryption_info.ediv;
|
||||
out->remote_encryption_info.ltk = in->remote_encryption_info.ltk;
|
||||
out->remote_encryption_info.rand = in->remote_encryption_info.rand;
|
||||
out->remote_encryption_info.ediv = in->remote_encryption_info.ediv;
|
||||
out->irk = in->irk;
|
||||
out->identity = in->identity;
|
||||
out->csrk = in->csrk;
|
||||
out->is_local_encryption_info_valid = in->is_local_encryption_info_valid;
|
||||
out->is_remote_encryption_info_valid = in->is_remote_encryption_info_valid;
|
||||
out->is_remote_identity_info_valid = in->is_remote_identity_info_valid;
|
||||
out->is_remote_signing_info_valid = in->is_remote_signing_info_valid;
|
||||
}
|
||||
|
||||
static void bt_persistent_storage_assign_sm_pairing_info(SMPairingInfo *out,
|
||||
const BtPersistLEPairingInfo *in) {
|
||||
*out = (SMPairingInfo) {};
|
||||
out->local_encryption_info.div = in->local_encryption_info.div;
|
||||
out->local_encryption_info.ediv = in->local_encryption_info.ediv;
|
||||
out->remote_encryption_info.ltk = in->remote_encryption_info.ltk;
|
||||
out->remote_encryption_info.rand = in->remote_encryption_info.rand;
|
||||
out->remote_encryption_info.ediv = in->remote_encryption_info.ediv;
|
||||
out->irk = in->irk;
|
||||
out->identity = in->identity;
|
||||
out->csrk = in->csrk;
|
||||
out->is_local_encryption_info_valid = in->is_local_encryption_info_valid;
|
||||
out->is_remote_encryption_info_valid = in->is_remote_encryption_info_valid;
|
||||
out->is_remote_identity_info_valid = in->is_remote_identity_info_valid;
|
||||
out->is_remote_signing_info_valid = in->is_remote_signing_info_valid;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 <util/attributes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct PACKED BtPersistLEEncryptionInfo {
|
||||
SMLongTermKey ltk;
|
||||
uint16_t ediv;
|
||||
uint64_t rand;
|
||||
} BtPersistLEEncryptionInfo;
|
||||
|
||||
typedef struct PACKED BtPersistLEPairingInfo {
|
||||
BtPersistLEEncryptionInfo local_encryption_info;
|
||||
|
||||
BtPersistLEEncryptionInfo remote_encryption_info;
|
||||
|
||||
SMIdentityResolvingKey irk;
|
||||
BTDeviceInternal identity;
|
||||
|
||||
SMConnectionSignatureResolvingKey csrk;
|
||||
|
||||
//! True if local_encryption_info is valid
|
||||
bool is_local_encryption_info_valid:1;
|
||||
|
||||
//! True if remote_encryption_info is valid
|
||||
bool is_remote_encryption_info_valid:1;
|
||||
|
||||
//! True if irk and identity are valid
|
||||
bool is_remote_identity_info_valid:1;
|
||||
|
||||
//! True if csrk is valid
|
||||
bool is_remote_signing_info_valid:1;
|
||||
|
||||
//! True if Man-in-the-middle protection was enabled during the pairing process.
|
||||
bool is_mitm_protection_enabled:1;
|
||||
|
||||
uint8_t rsvd:3;
|
||||
} BtPersistLEPairingInfo;
|
||||
|
||||
static void bt_persistent_storage_assign_persist_pairing_info(BtPersistLEPairingInfo *out,
|
||||
const SMPairingInfo *in) {
|
||||
*out = (BtPersistLEPairingInfo) {
|
||||
.local_encryption_info = {
|
||||
.ltk = in->local_encryption_info.ltk,
|
||||
.rand = in->local_encryption_info.rand,
|
||||
.ediv = in->local_encryption_info.ediv,
|
||||
},
|
||||
.remote_encryption_info = {
|
||||
.ltk = in->remote_encryption_info.ltk,
|
||||
.rand = in->remote_encryption_info.rand,
|
||||
.ediv = in->remote_encryption_info.ediv,
|
||||
},
|
||||
.irk = in->irk,
|
||||
.identity = in->identity,
|
||||
.csrk = in->csrk,
|
||||
.is_local_encryption_info_valid = in->is_local_encryption_info_valid,
|
||||
.is_remote_encryption_info_valid = in->is_remote_encryption_info_valid,
|
||||
.is_remote_identity_info_valid = in->is_remote_identity_info_valid,
|
||||
.is_remote_signing_info_valid = in->is_remote_signing_info_valid,
|
||||
.is_mitm_protection_enabled = in->is_mitm_protection_enabled,
|
||||
};
|
||||
}
|
||||
|
||||
static void bt_persistent_storage_assign_sm_pairing_info(SMPairingInfo *out,
|
||||
const BtPersistLEPairingInfo *in) {
|
||||
*out = (SMPairingInfo) {
|
||||
.local_encryption_info = {
|
||||
.ltk = in->local_encryption_info.ltk,
|
||||
.rand = in->local_encryption_info.rand,
|
||||
.ediv = in->local_encryption_info.ediv,
|
||||
},
|
||||
.remote_encryption_info = {
|
||||
.ltk = in->remote_encryption_info.ltk,
|
||||
.rand = in->remote_encryption_info.rand,
|
||||
.ediv = in->remote_encryption_info.ediv,
|
||||
},
|
||||
.irk = in->irk,
|
||||
.identity = in->identity,
|
||||
.csrk = in->csrk,
|
||||
.is_local_encryption_info_valid = in->is_local_encryption_info_valid,
|
||||
.is_remote_encryption_info_valid = in->is_remote_encryption_info_valid,
|
||||
.is_remote_identity_info_valid = in->is_remote_identity_info_valid,
|
||||
.is_remote_signing_info_valid = in->is_remote_signing_info_valid,
|
||||
.is_mitm_protection_enabled = in->is_mitm_protection_enabled,
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue