pebble/src/fw/services/normal/analytics/analytics_event.c
Josh Soref 848a68c241 spelling: specific
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-29 00:03:28 -05:00

701 lines
25 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 <string.h>
#include <inttypes.h>
#include "services/common/analytics/analytics.h"
#include "services/common/analytics/analytics_event.h"
#include "services/common/analytics/analytics_logging.h"
#include "services/common/analytics/analytics_storage.h"
#include "apps/system_apps/launcher/launcher_app.h"
#include "comm/ble/gap_le_connection.h"
#include "comm/bt_lock.h"
#include "comm/ble/gap_le_connection.h"
#include "kernel/pbl_malloc.h"
#include "services/common/comm_session/session_internal.h"
#include "services/normal/alarms/alarm.h"
#include "services/normal/timeline/timeline.h"
#include "syscall/syscall.h"
#include "syscall/syscall_internal.h"
#include "system/logging.h"
#include "system/passert.h"
#include "util/size.h"
#include "util/time/time.h"
_Static_assert(sizeof(AnalyticsEventBlob) == 36,
"When the blob format or size changes, be sure to bump up ANALYTICS_EVENT_BLOB_VERSION");
// ------------------------------------------------------------------------------------------
// Log an app launch event
static bool prv_send_uuid(AnalyticsEvent event_enum, const Uuid *uuid) {
if (uuid_is_invalid(uuid) || uuid_is_system(uuid)) {
// No need to log apps with invalid uuids. This is typically built-in test apps like "Light
// config" that we don't bother to declare a UUID for
return false;
}
// FIXME: The sdkshell doesn't have a launcher menu so this causes a linker error. Maybe the
// mapping of events to analytics should also be shell-specific?
#ifndef SHELL_SDK
// No need to log the launcher menu app
if (uuid_equal(uuid, &launcher_menu_app_get_app_info()->uuid)) {
return false;
}
#endif
return true;
}
// ------------------------------------------------------------------------------------------
// Log an out-of-memory situation for an app.
void analytics_event_app_oom(AnalyticsEvent type,
uint32_t requested_size, uint32_t total_size, uint32_t total_free,
uint32_t largest_free_block) {
PBL_ASSERTN(type == AnalyticsEvent_AppOOMNative || type == AnalyticsEvent_AppOOMRocky);
AnalyticsEventBlob event_blob = {
.event = type,
.app_oom = {
.requested_size = requested_size,
.total_size = total_size,
.total_free = MIN(total_free, UINT16_MAX),
.largest_free_block = MIN(largest_free_block, UINT16_MAX),
},
};
if (!sys_process_manager_get_current_process_uuid(&event_blob.app_oom.app_uuid)) {
// Process has no UUID
return;
}
#if LOG_DOMAIN_ANALYTICS
ANALYTICS_LOG_DEBUG("app oom: is_rocky=%u, req_sz=%"PRIu32" tot_sz=%"PRIu32" free=%"PRIu32
" max_free=%"PRIu32,
(type == AnalyticsEvent_AppOOMRocky),
requested_size, total_size, total_free, largest_free_block);
#endif
sys_analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a generic app launch event
void analytics_event_app_launch(const Uuid *uuid) {
if (!prv_send_uuid(AnalyticsEvent_AppLaunch, uuid)) {
return;
}
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_AppLaunch,
.app_launch.uuid = *uuid,
};
#if LOG_DOMAIN_ANALYTICS
char uuid_string[UUID_STRING_BUFFER_LENGTH];
uuid_to_string(uuid, uuid_string);
ANALYTICS_LOG_DEBUG("app launch event: uuid %s", uuid_string);
#endif
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a pin open/create/update event.
static void prv_simple_pin_event(time_t timestamp, const Uuid *parent_id,
AnalyticsEvent event_enum, const char *verb) {
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = event_enum,
.pin_open_create_update.time_utc = timestamp,
.pin_open_create_update.parent_id = *parent_id,
};
#if LOG_DOMAIN_ANALYTICS
char uuid_string[UUID_STRING_BUFFER_LENGTH];
uuid_to_string(&event_blob.pin_open_create_update.parent_id, uuid_string);
ANALYTICS_LOG_DEBUG("pin %s event: timestamp: %"PRIu32", uuid:%s", verb,
event_blob.pin_open_create_update.time_utc, uuid_string);
#endif
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a pin open event.
void analytics_event_pin_open(time_t timestamp, const Uuid *parent_id) {
prv_simple_pin_event(timestamp, parent_id, AnalyticsEvent_PinOpen, "open");
}
// ------------------------------------------------------------------------------------------
// Log a pin created event.
void analytics_event_pin_created(time_t timestamp, const Uuid *parent_id) {
prv_simple_pin_event(timestamp, parent_id, AnalyticsEvent_PinCreated, "created");
}
// ------------------------------------------------------------------------------------------
// Log a pin updated event.
void analytics_event_pin_updated(time_t timestamp, const Uuid *parent_id) {
prv_simple_pin_event(timestamp, parent_id, AnalyticsEvent_PinUpdated, "updated");
}
// ------------------------------------------------------------------------------------------
// Log a pin action event.
void analytics_event_pin_action(time_t timestamp, const Uuid *parent_id,
TimelineItemActionType action_type) {
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_PinAction,
.pin_action.time_utc = timestamp,
.pin_action.parent_id = *parent_id,
.pin_action.type = action_type,
};
#if LOG_DOMAIN_ANALYTICS
char uuid_string[UUID_STRING_BUFFER_LENGTH];
uuid_to_string(&event_blob.pin_action.parent_id, uuid_string);
ANALYTICS_LOG_DEBUG("pin action event: timestamp: %"PRIu32", uuid:%s, action:%"PRIu8,
event_blob.pin_action.time_utc, uuid_string, action_type);
#endif
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a pin app launch event.
void analytics_event_pin_app_launch(time_t timestamp, const Uuid *parent_id) {
if (!prv_send_uuid(AnalyticsEvent_PinAppLaunch, parent_id)) {
return;
}
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_PinAppLaunch,
.pin_app_launch.time_utc = timestamp,
.pin_app_launch.parent_id = *parent_id,
};
#if LOG_DOMAIN_ANALYTICS
char uuid_string[UUID_STRING_BUFFER_LENGTH];
uuid_to_string(&event_blob.pin_app_launch.parent_id, uuid_string);
ANALYTICS_LOG_DEBUG("pin app launch event: timestamp: %"PRIu32", uuid:%s",
event_blob.pin_app_launch.time_utc, uuid_string);
#endif
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a canned response event
void analytics_event_canned_response(const char *response, bool successfully_sent) {
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = successfully_sent ? AnalyticsEvent_CannedResponseSent
: AnalyticsEvent_CannedResponseFailed,
};
if (!response) {
event_blob.canned_response.response_size_bytes = 0;
} else {
event_blob.canned_response.response_size_bytes = strlen(response);
}
if (successfully_sent) {
ANALYTICS_LOG_DEBUG("canned response sent event: response_size_bytes:%d",
event_blob.canned_response.response_size_bytes);
} else {
ANALYTICS_LOG_DEBUG("canned response failed event: response_size_bytes:%d",
event_blob.canned_response.response_size_bytes);
}
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a voice response event
void analytics_event_voice_response(AnalyticsEvent event_type, uint16_t response_size_bytes,
uint16_t response_len_chars, uint32_t response_len_ms,
uint8_t error_count, uint8_t num_sessions, Uuid *app_uuid) {
PBL_ASSERTN((event_type >= AnalyticsEvent_VoiceTranscriptionAccepted) &&
(event_type <= AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted));
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = event_type,
};
event_blob.voice_response = (AnalyticsEventVoiceResponse) {
.response_size_bytes = response_size_bytes,
.response_len_chars = response_len_chars,
.response_len_ms = response_len_ms,
.num_sessions = num_sessions,
.error_count = error_count,
.app_uuid = *app_uuid,
};
const char *msg = "Other";
switch (event_type) {
case AnalyticsEvent_VoiceTranscriptionAccepted:
msg = "Accepted";
break;
case AnalyticsEvent_VoiceTranscriptionRejected:
msg = "Rejected";
break;
case AnalyticsEvent_VoiceTranscriptionAutomaticallyAccepted:
msg = "Automatically accepted";
break;
default:
break;
}
ANALYTICS_LOG_DEBUG("voice response %s event: size: %"PRIu16"; length (chars): %"PRIu16
"; length (ms): %"PRIu32"; Errors: %"PRIu8"; Sessions: %"PRIu8, msg,
event_blob.voice_response.response_size_bytes, event_blob.voice_response.response_len_chars,
event_blob.voice_response.response_len_ms, event_blob.voice_response.error_count,
event_blob.voice_response.num_sessions);
// Use syscall because this is called by voice_window
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a BLE HRM event
void analytics_event_ble_hrm(BleHrmEventSubtype subtype) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_BleHrmEvent,
.ble_hrm = {
.subtype = subtype,
},
};
ANALYTICS_LOG_DEBUG("BLE HRM Event %u", subtype);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a bluetooth disconnection event
void analytics_event_bt_connection_or_disconnection(AnalyticsEvent type, uint8_t reason) {
AnalyticsEventBlob event_blob = {
.event = type,
};
event_blob.bt_connection_disconnection.reason = reason;
ANALYTICS_LOG_DEBUG("Event %d - BT (dis)connection: Reason: %"PRIu8,
event_blob.event,
event_blob.bt_connection_disconnection.reason);
analytics_logging_log_event(&event_blob);
}
void analytics_event_bt_le_disconnection(uint8_t reason, uint8_t remote_bt_version,
uint16_t remote_bt_company_id,
uint16_t remote_bt_subversion) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_BtLeDisconnect,
.ble_disconnection = {
.reason = reason,
.remote_bt_version = remote_bt_version,
.remote_bt_company_id = remote_bt_company_id,
.remote_bt_subversion_number = remote_bt_subversion,
}
};
ANALYTICS_LOG_DEBUG("Event %d - BT disconnection: Reason: %"PRIu8, event_blob.event,
event_blob.bt_connection_disconnection.reason);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a bluetooth error
void analytics_event_bt_error(AnalyticsEvent type, uint32_t error) {
AnalyticsEventBlob event_blob = {};
event_blob.event = type,
event_blob.bt_error.error_code = error;
ANALYTICS_LOG_DEBUG("bluetooth event %d - error: %"PRIu32,
event_blob.event,
event_blob.bt_error.error_code);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
//! Log when app_launch trigger failed.
void analytics_event_bt_app_launch_error(uint8_t gatt_error) {
analytics_event_bt_error(AnalyticsEvent_BtAppLaunchError, gatt_error);
}
// ------------------------------------------------------------------------------------------
//! Log when a Pebble Protocol session is closed.
void analytics_event_session_close(bool is_system_session, const Uuid *optional_app_uuid,
CommSessionCloseReason reason, uint16_t session_duration_mins) {
AnalyticsEventBlob event_blob = {};
event_blob.event = (is_system_session ? AnalyticsEvent_PebbleProtocolSystemSessionEnd :
AnalyticsEvent_PebbleProtocolAppSessionEnd);
event_blob.pp_common_session_close.close_reason = reason;
event_blob.pp_common_session_close.duration_minutes = session_duration_mins;
if (!is_system_session && optional_app_uuid) {
memcpy(&event_blob.pp_app_session_close.app_uuid, optional_app_uuid, sizeof(Uuid));
}
#if LOG_DOMAIN_ANALYTICS
char uuid_str[UUID_STRING_BUFFER_LENGTH];
uuid_to_string(optional_app_uuid, uuid_str);
ANALYTICS_LOG_DEBUG("Session close event. is_system_session=%u, uuid=%s, "
"reason=%u, duration_mins=%"PRIu16,
is_system_session, uuid_str, reason, session_duration_mins);
#endif
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
//! Log when the CC2564x BT chip becomes unresponsive
void analytics_event_bt_cc2564x_lockup_error(void) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_BtLockupError,
};
ANALYTICS_LOG_DEBUG("CC2564x lockup event");
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a crash event
void analytics_event_crash(uint8_t crash_code, uint32_t link_register) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_Crash,
.crash_report.crash_code = crash_code,
.crash_report.link_register = link_register
};
ANALYTICS_LOG_DEBUG("Crash occurred: Code %"PRIu8" / LR: %"PRIu32,
event_blob.crash_report.crash_code, event_blob.crash_report.link_register);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log local bluetooth disconnection reason
void analytics_event_local_bt_disconnect(uint16_t conn_handle, uint32_t lr) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_LocalBtDisconnect,
};
event_blob.local_bt_disconnect.lr = lr;
event_blob.local_bt_disconnect.conn_handle = conn_handle;
ANALYTICS_LOG_DEBUG("Event %d - BT Disconnect: Handle:%"PRIu16" LR: %"PRIu32,
event_blob.event,
conn_handle,
lr);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log an Apple Media Service event.
void analytics_event_ams(uint8_t type, int32_t aux_info) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_BtLeAMS,
.ams = {
.type = type,
.aux_info = aux_info,
},
};
ANALYTICS_LOG_DEBUG("Event %d - AMS: type:%d aux_info: %"PRId32, event_blob.event, type, aux_info);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log stationary mode events
void analytics_event_stationary_state_change(time_t timestamp, uint8_t state_change) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_StationaryModeSwitch,
.sd = {
.timestamp = timestamp,
.state_change = state_change,
}
};
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a health insight created event.
void analytics_event_health_insight_created(time_t timestamp,
ActivityInsightType insight_type,
PercentTier pct_tier) {
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_HealthInsightCreated,
.health_insight_created = {
.time_utc = timestamp,
.insight_type = insight_type,
.percent_tier = pct_tier,
}
};
#if LOG_DOMAIN_ANALYTICS
ANALYTICS_LOG_DEBUG("health insight created event: timestamp: %"PRIu32", type:%"PRIu8,
timestamp, insight_type);
#endif
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log a health insight response event.
void analytics_event_health_insight_response(time_t timestamp, ActivityInsightType insight_type,
ActivitySessionType activity_type,
ActivityInsightResponseType response_id) {
// Format the event specific info in the blob. The analytics_logging_log_event() method will fill
// in the common fields
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_HealthInsightResponse,
.health_insight_response = {
.time_utc = timestamp,
.insight_type = insight_type,
.activity_type = activity_type,
.response_id = response_id,
}
};
#if LOG_DOMAIN_ANALYTICS
ANALYTICS_LOG_DEBUG("health insight response event: timestamp: %"PRIu32", type:%"PRIu8 \
", response:%"PRIu8, timestamp, insight_type, response_id);
#endif
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
// Log an App Crash event
void analytics_event_app_crash(const Uuid *uuid, uint32_t pc, uint32_t lr,
const uint8_t *build_id, bool is_rocky_app) {
AnalyticsEventBlob event_blob = {
.event = (is_rocky_app ? AnalyticsEvent_RockyAppCrash : AnalyticsEvent_AppCrash),
.app_crash_report = {
.uuid = *uuid,
.pc = pc,
.lr = lr,
},
};
if (build_id) {
memcpy(event_blob.app_crash_report.build_id_slice, build_id,
sizeof(event_blob.app_crash_report.build_id_slice));
}
#if LOG_DOMAIN_ANALYTICS
char uuid_string[UUID_STRING_BUFFER_LENGTH];
uuid_to_string(uuid, uuid_string);
ANALYTICS_LOG_DEBUG("App Crash event: uuid:%s, pc: %p, lr: %p",
uuid_string, (void *)pc, (void *)lr);
#endif
analytics_logging_log_event(&event_blob);
}
extern bool comm_session_is_valid(const CommSession *session);
// ------------------------------------------------------------------------------------------
static bool prv_get_connection_details(CommSession *session, bool *is_ppogatt,
uint16_t *conn_interval) {
bt_lock();
if (!session || !comm_session_is_valid(session)) {
bt_unlock();
return false;
}
const bool tmp_is_ppogatt =
(comm_session_analytics_get_transport_type(session) == CommSessionTransportType_PPoGATT);
uint16_t tmp_conn_interval = 0;
if (tmp_is_ppogatt) {
GAPLEConnection *conn = gap_le_connection_get_gateway();
if (conn) {
tmp_conn_interval = conn->conn_params.conn_interval_1_25ms;
}
}
bt_unlock();
if (is_ppogatt) {
*is_ppogatt = tmp_is_ppogatt;
}
if (conn_interval) {
*conn_interval = tmp_conn_interval;
}
return true;
}
void analytics_event_put_byte_stats(
CommSession *session, bool crc_good, uint8_t type,
uint32_t bytes_transferred, uint32_t elapsed_time_ms,
uint32_t conn_events, uint32_t sync_errors, uint32_t skip_errors, uint32_t other_errors) {
bool is_ppogatt = false;
uint16_t conn_interval = 0;
if (!prv_get_connection_details(session, &is_ppogatt, &conn_interval)) {
return;
}
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_PutByteTime,
.pb_time = {
.ppogatt = is_ppogatt,
.conn_intvl_1_25ms = MIN(conn_interval, UINT8_MAX),
.crc_good = crc_good,
.type = type,
.bytes_transferred = bytes_transferred,
.elapsed_time_ms = elapsed_time_ms,
.conn_events = MIN(conn_events, UINT32_MAX),
.sync_errors = MIN(sync_errors, UINT16_MAX),
.skip_errors = MIN(skip_errors, UINT16_MAX),
.other_errors = MIN(other_errors, UINT16_MAX),
},
};
ANALYTICS_LOG_DEBUG("PutBytes event: is_ppogatt: %d, bytes: %d, time ms: %d",
(int)event_blob.pb_time.ppogatt,
(int)event_blob.pb_time.bytes_transferred,
(int)event_blob.pb_time.elapsed_time_ms);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
#if !PLATFORM_TINTIN
void analytics_event_vibe_access(VibePatternFeature vibe_feature, VibeScoreId pattern_id) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_VibeAccess,
. vibe_access_data = {
.feature = (uint8_t) vibe_feature,
.vibe_pattern_id = (uint8_t) pattern_id
}
};
analytics_logging_log_event(&event_blob);
}
#endif
// ------------------------------------------------------------------------------------------
void analytics_event_alarm(AnalyticsEvent event_type, const AlarmInfo *info) {
AnalyticsEventBlob event_blob = {
.event = event_type,
.alarm = {
.hour = info->hour,
.minute = info->minute,
.is_smart = info->is_smart,
.kind = info->kind,
},
};
memcpy(event_blob.alarm.scheduled_days, info->scheduled_days,
sizeof(event_blob.alarm.scheduled_days));
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
void analytics_event_bt_chip_boot(uint8_t build_id[BUILD_ID_EXPECTED_LEN],
uint32_t crash_lr, uint32_t reboot_reason_code) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_BtChipBoot,
.bt_chip_boot = {
.crash_lr = crash_lr,
.reboot_reason = reboot_reason_code,
},
};
memcpy(event_blob.bt_chip_boot.build_id, build_id, sizeof(BUILD_ID_EXPECTED_LEN));
ANALYTICS_LOG_DEBUG("BtChipBoot event: crash_lr: 0x%x, reboot_reason: %"PRIu32,
(int)event_blob.bt_chip_boot.crash_lr,
event_blob.bt_chip_boot.reboot_reason);
analytics_logging_log_event(&event_blob);
}
// ------------------------------------------------------------------------------------------
void analytics_event_PPoGATT_disconnect(time_t timestamp, bool successful_reconnect) {
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_PPoGATTDisconnect,
.ppogatt_disconnect = {
.successful_reconnect = successful_reconnect,
.time_utc = timestamp,
},
};
analytics_logging_log_event(&event_blob);
}
void analytics_event_get_bytes_stats(CommSession *session, uint8_t type,
uint32_t bytes_transferred, uint32_t elapsed_time_ms,
uint32_t conn_events, uint32_t sync_errors,
uint32_t skip_errors, uint32_t other_errors) {
bool is_ppogatt = false;
uint16_t conn_interval = 0;
if (!prv_get_connection_details(session, &is_ppogatt, &conn_interval)) {
return;
}
AnalyticsEventBlob event_blob = {
.event = AnalyticsEvent_GetBytesStats,
.get_bytes_stats = {
.ppogatt = is_ppogatt,
.conn_intvl_1_25ms = MIN(conn_interval, UINT8_MAX),
.type = type,
.bytes_transferred = bytes_transferred,
.elapsed_time_ms = elapsed_time_ms,
.conn_events = conn_events,
.sync_errors = MIN(sync_errors, UINT16_MAX),
.skip_errors = MIN(skip_errors, UINT16_MAX),
.other_errors = MIN(other_errors, UINT16_MAX),
},
};
ANALYTICS_LOG_DEBUG("GetBytesStats event: type: 0x%x, num_bytes: %"PRIu32", elapsed_ms: %"PRIu32,
type, bytes_transferred, elapsed_time_ms);
analytics_logging_log_event(&event_blob);
}