mirror of
https://github.com/google/pebble.git
synced 2025-11-25 16:52:25 -05:00
Import of the watch repository from Pebble
This commit is contained in:
281
src/fw/comm/bluetooth_analytics.c
Normal file
281
src/fw/comm/bluetooth_analytics.c
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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_analytics.h"
|
||||
|
||||
#include "comm/ble/gap_le_connection.h"
|
||||
#include "comm/bt_lock.h"
|
||||
#include "drivers/rtc.h"
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "services/common/bluetooth/bluetooth_ctl.h"
|
||||
#include "services/common/comm_session/session.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/bitset.h"
|
||||
#include "util/math.h"
|
||||
|
||||
#include <bluetooth/analytics.h>
|
||||
#include <bluetooth/gap_le_connect.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t slave_latency_events;
|
||||
uint32_t supervision_to_ms;
|
||||
int num_samps;
|
||||
} LeConnectionParams;
|
||||
|
||||
static LeConnectionParams s_le_conn_params = { 0 };
|
||||
|
||||
void bluetooth_analytics_get_param_averages(uint16_t *params) {
|
||||
int num_samps = s_le_conn_params.num_samps;
|
||||
if (num_samps != 0) {
|
||||
params[0] = s_le_conn_params.slave_latency_events / num_samps;
|
||||
params[1] = s_le_conn_params.supervision_to_ms / num_samps;
|
||||
}
|
||||
|
||||
s_le_conn_params = (LeConnectionParams){};
|
||||
}
|
||||
|
||||
static void prv_update_conn_params(uint16_t slave_latency_events,
|
||||
uint16_t supervision_to_10ms) {
|
||||
bt_lock();
|
||||
s_le_conn_params.slave_latency_events += slave_latency_events;
|
||||
s_le_conn_params.supervision_to_ms += (supervision_to_10ms * 10);
|
||||
s_le_conn_params.num_samps++;
|
||||
bt_unlock();
|
||||
}
|
||||
|
||||
static void prv_update_conn_event_timer(uint32_t interval_1_25ms, bool stop) {
|
||||
bt_lock();
|
||||
static bool s_analytic_conn_timer_running = false;
|
||||
if (stop || s_analytic_conn_timer_running) {
|
||||
analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_CONN_EVENT_COUNT);
|
||||
s_analytic_conn_timer_running = false;
|
||||
}
|
||||
|
||||
if (!stop) {
|
||||
// track (# connection attempts * 10^3) / sec
|
||||
uint32_t conn_attempts_per_sec = ((1000 * 1000 * 5) / (interval_1_25ms)) / 4;
|
||||
analytics_stopwatch_start_at_rate(
|
||||
ANALYTICS_DEVICE_METRIC_BLE_CONN_EVENT_COUNT,
|
||||
conn_attempts_per_sec, AnalyticsClient_System);
|
||||
s_analytic_conn_timer_running = true;
|
||||
}
|
||||
bt_unlock();
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_param_update_failed(void) {
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_CONN_PARAM_UPDATE_FAILED_COUNT,
|
||||
AnalyticsClient_System);
|
||||
}
|
||||
|
||||
//! only called when we are connected as a slave
|
||||
void bluetooth_analytics_handle_connection_params_update(const BleConnectionParams *params) {
|
||||
// When connected as a slave device, the 'Slave Latency' connection parameter allows
|
||||
// the controller to skip the connection sync for that number of connection events.
|
||||
uint32_t effective_interval = params->conn_interval_1_25ms * (1 + params->slave_latency_events);
|
||||
|
||||
prv_update_conn_event_timer(effective_interval, false);
|
||||
prv_update_conn_params(params->slave_latency_events, params->supervision_timeout_10ms);
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_connection_disconnection_event(
|
||||
AnalyticsEvent type, uint8_t reason, const BleRemoteVersionInfo *vers_info) {
|
||||
static uint32_t last_reset_counter_ticks = 0;
|
||||
static uint8_t num_events_logged = 0;
|
||||
|
||||
const uint32_t ticks_per_hour = RTC_TICKS_HZ * 60 * 60;
|
||||
|
||||
if ((rtc_get_ticks() - last_reset_counter_ticks) > ticks_per_hour) {
|
||||
num_events_logged = 0;
|
||||
last_reset_counter_ticks = rtc_get_ticks();
|
||||
}
|
||||
|
||||
if (num_events_logged > 100) { // don't log a ridiculous amount of tightly looped disconnects
|
||||
return;
|
||||
}
|
||||
|
||||
// It's okay to log to analytics directly from the BT02 callback thread
|
||||
// because flash writes are dispatched to KernelBG if the datalogging session
|
||||
// is buffered
|
||||
if (type != AnalyticsEvent_BtLeDisconnect) {
|
||||
analytics_event_bt_connection_or_disconnection(type, reason);
|
||||
} else {
|
||||
if (!vers_info) { // We expect version info
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Le Disconnect but no version info?");
|
||||
} else {
|
||||
analytics_event_bt_le_disconnection(reason, vers_info->version_number,
|
||||
vers_info->company_identifier,
|
||||
vers_info->subversion_number);
|
||||
}
|
||||
}
|
||||
|
||||
num_events_logged++;
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_connect(
|
||||
const BTDeviceInternal *peer_addr, const BleConnectionParams *conn_params) {
|
||||
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_COUNT, AnalyticsClient_System);
|
||||
analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_TIME, AnalyticsClient_System);
|
||||
|
||||
bluetooth_analytics_handle_connection_params_update(conn_params);
|
||||
|
||||
uint8_t link_quality = 0;
|
||||
int8_t rssi = 0;
|
||||
bool success = bt_driver_analytics_get_connection_quality(peer_addr, &link_quality, &rssi);
|
||||
|
||||
if (success) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Link quality: %x, RSSI: %d", link_quality, rssi);
|
||||
analytics_add(ANALYTICS_DEVICE_METRIC_BLE_LINK_QUALITY_SUM,
|
||||
link_quality, AnalyticsClient_System);
|
||||
analytics_add(ANALYTICS_DEVICE_METRIC_BLE_RSSI_SUM,
|
||||
ABS(rssi), AnalyticsClient_System);
|
||||
}
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_disconnect(bool local_is_master) {
|
||||
if (!local_is_master) {
|
||||
analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_TIME);
|
||||
analytics_stopwatch_stop(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_ENCRYPTED_TIME);
|
||||
|
||||
prv_update_conn_event_timer(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_encryption_change(void) {
|
||||
analytics_stopwatch_start(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_ENCRYPTED_TIME,
|
||||
AnalyticsClient_System);
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_no_intent_for_connection(void) {
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_CONNECT_NO_INTENT_COUNT, AnalyticsClient_System);
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_ble_pairing_request(void) {
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_BLE_PAIRING_COUNT, AnalyticsClient_System);
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_bt_classic_pairing_request(void) {
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_BT_PAIRING_COUNT, AnalyticsClient_System);
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_ble_pairing_error(uint32_t error) {
|
||||
analytics_event_bt_error(AnalyticsEvent_BtLePairingError, error);
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_bt_classic_pairing_error(uint32_t error) {
|
||||
analytics_event_bt_error(AnalyticsEvent_BtClassicPairingError, error);
|
||||
}
|
||||
|
||||
void bluetooth_analytics_ble_mic_error(uint32_t num_sequential_mic_errors) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "MIC Error detected ... %"PRIu32" packets", num_sequential_mic_errors);
|
||||
analytics_event_bt_error(AnalyticsEvent_BtLeMicError, num_sequential_mic_errors);
|
||||
}
|
||||
|
||||
static uint32_t prv_calc_other_errors(const SlaveConnEventStats *stats) {
|
||||
return (stats->num_type_errors + stats->num_len_errors + stats->num_crc_errors +
|
||||
stats->num_mic_errors);
|
||||
}
|
||||
|
||||
static bool prv_calc_stats_and_print(const SlaveConnEventStats *orig_stats,
|
||||
SlaveConnEventStats *stats_buf, bool is_putbytes) {
|
||||
if (bt_driver_analytics_get_conn_event_stats(stats_buf)) {
|
||||
stats_buf->num_conn_events =
|
||||
serial_distance32(orig_stats->num_conn_events, stats_buf->num_conn_events);
|
||||
stats_buf->num_sync_errors =
|
||||
serial_distance32(orig_stats->num_sync_errors, stats_buf->num_sync_errors);
|
||||
stats_buf->num_conn_events_skipped =
|
||||
serial_distance32(orig_stats->num_conn_events_skipped, stats_buf->num_conn_events_skipped);
|
||||
stats_buf->num_type_errors =
|
||||
serial_distance32(orig_stats->num_type_errors, stats_buf->num_type_errors);
|
||||
stats_buf->num_len_errors =
|
||||
serial_distance32(orig_stats->num_len_errors, stats_buf->num_len_errors);
|
||||
stats_buf->num_crc_errors =
|
||||
serial_distance32(orig_stats->num_crc_errors, stats_buf->num_crc_errors);
|
||||
stats_buf->num_mic_errors =
|
||||
serial_distance32(orig_stats->num_mic_errors, stats_buf->num_mic_errors);
|
||||
|
||||
PBL_LOG(LOG_LEVEL_INFO, "%sBytes Conn Stats: Events: %"PRIu32", Sync Errs: %"PRIu32
|
||||
", Skipped Events: %"PRIu32" Other Errs: %"PRIu32, is_putbytes ? "Put" : "Get",
|
||||
stats_buf->num_conn_events, stats_buf->num_sync_errors,
|
||||
stats_buf->num_conn_events_skipped, prv_calc_other_errors(stats_buf));
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_put_bytes_stats(bool successful, uint8_t type, uint32_t total_size,
|
||||
uint32_t elapsed_time_ms,
|
||||
const SlaveConnEventStats *orig_stats) {
|
||||
SlaveConnEventStats new_stats = {};
|
||||
prv_calc_stats_and_print(orig_stats, &new_stats, true /* is_putbytes */);
|
||||
|
||||
analytics_event_put_byte_stats(
|
||||
comm_session_get_system_session(), successful, type,
|
||||
total_size, elapsed_time_ms, new_stats.num_conn_events,
|
||||
new_stats.num_sync_errors, new_stats.num_conn_events_skipped,
|
||||
prv_calc_other_errors(&new_stats));
|
||||
}
|
||||
|
||||
void bluetooth_analytics_handle_get_bytes_stats(uint8_t type, uint32_t total_size,
|
||||
uint32_t elapsed_time_ms,
|
||||
const SlaveConnEventStats *orig_stats) {
|
||||
SlaveConnEventStats new_stats = {};
|
||||
prv_calc_stats_and_print(orig_stats, &new_stats, false /* is_putbytes */);
|
||||
|
||||
analytics_event_get_bytes_stats(
|
||||
comm_session_get_system_session(), type,
|
||||
total_size, elapsed_time_ms, new_stats.num_conn_events,
|
||||
new_stats.num_sync_errors, new_stats.num_conn_events_skipped,
|
||||
prv_calc_other_errors(&new_stats));
|
||||
}
|
||||
|
||||
void analytics_external_collect_ble_parameters(void) {
|
||||
bt_lock();
|
||||
{
|
||||
GAPLEConnection *connection = gap_le_connection_get_gateway();
|
||||
if (!connection) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
LEChannelMap le_channel_map;
|
||||
const bool success =
|
||||
bt_driver_analytics_collect_ble_parameters(&connection->device, &le_channel_map);
|
||||
if (success) {
|
||||
analytics_set(ANALYTICS_DEVICE_METRIC_BLE_CHAN_USE_COUNT,
|
||||
count_bits_set((uint8_t *)&le_channel_map, NUM_LE_CHANNELS),
|
||||
AnalyticsClient_System);
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
bt_unlock();
|
||||
}
|
||||
|
||||
void analytics_external_collect_chip_specific_parameters(void) {
|
||||
bt_lock();
|
||||
bt_driver_analytics_external_collect_chip_specific_parameters();
|
||||
bt_unlock();
|
||||
}
|
||||
|
||||
void analytics_external_collect_bt_chip_heartbeat(void) {
|
||||
// TODO: PBL-38365: Re-enable this once it is fixed :(
|
||||
#if 0
|
||||
if (bt_ctl_is_bluetooth_running()) {
|
||||
// No need for lock
|
||||
bt_driver_analytics_external_collect_bt_chip_heartbeat();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user