mirror of
https://github.com/google/pebble.git
synced 2025-05-05 09:21:40 -04:00
281 lines
11 KiB
C
281 lines
11 KiB
C
/*
|
|
* 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
|
|
}
|