【android bluetooth 框架分析 02】【Module详解 13】【CounterMetrics 模块介绍】
1. CounterMetrics 介绍
CounterMetrics 模块代码很少, 我简单介绍一下。
// system/gd/metrics/counter_metrics.cc
#define LOG_TAG "BluetoothCounterMetrics"#include "metrics/counter_metrics.h"#include "common/bind.h"
#include "os/log.h"
#include "os/metrics.h"namespace bluetooth {
namespace metrics {const int COUNTER_METRICS_PERDIOD_MINUTES = 360; // Drain counters every 6 hoursconst ModuleFactory CounterMetrics::Factory = ModuleFactory([]() { return new CounterMetrics(); });void CounterMetrics::ListDependencies(ModuleList* list) const {
}void CounterMetrics::Start() {alarm_ = std::make_unique<os::RepeatingAlarm>(GetHandler());alarm_->Schedule(common::Bind(&CounterMetrics::DrainBufferedCounters,bluetooth::common::Unretained(this)),std::chrono::minutes(COUNTER_METRICS_PERDIOD_MINUTES));LOG_INFO("Counter metrics initialized");initialized_ = true;
}void CounterMetrics::Stop() {DrainBufferedCounters();initialized_ = false;alarm_->Cancel();alarm_.reset();LOG_INFO("Counter metrics canceled");
}bool CounterMetrics::CacheCount(int32_t key, int64_t count) {if (!IsInitialized()) {LOG_WARN("Counter metrics isn't initialized");return false;}if (count <= 0) {LOG_WARN("count is not larger than 0. count: %s, key: %d", std::to_string(count).c_str(), key);return false;}int64_t total = 0;std::lock_guard<std::mutex> lock(mutex_);if (counters_.find(key) != counters_.end()) {total = counters_[key];}if (LLONG_MAX - total < count) {LOG_WARN("Counter metric overflows. count %s current total: %s key: %d",std::to_string(count).c_str(), std::to_string(total).c_str(), key);counters_[key] = LLONG_MAX;return false;}counters_[key] = total + count;return true;
}bool CounterMetrics::Count(int32_t key, int64_t count) {if (!IsInitialized()) {LOG_WARN("Counter metrics isn't initialized");return false;}if (count <= 0) {LOG_WARN("count is not larger than 0. count: %s, key: %d", std::to_string(count).c_str(), key);return false;}os::LogMetricBluetoothCodePathCounterMetrics(key, count);return true;
}void CounterMetrics::DrainBufferedCounters() {if (!IsInitialized()) {LOG_WARN("Counter metrics isn't initialized");return ;}std::lock_guard<std::mutex> lock(mutex_);LOG_INFO("Draining buffered counters");for (auto const& pair : counters_) {Count(pair.first, pair.second);}counters_.clear();
}} // namespace metrics
} // namespace bluetooth
如果你 看过我之前其他模块的介绍。应该很容易看明白, CounterMetrics 模块对应函数的触发流程。 我这里简单的介绍一下。CounterMetrics模块的作用。
2. CounterMetrics
的功能作用是什么?
该模块主要用于 收集并定期上报蓝牙栈内部的计数型指标(Counter Metrics),其核心职责包括:
功能点解析:
-
缓存统计项
- 使用
CacheCount(int32_t key, int64_t count)
将特定的计数项临时缓存在内存中(如某类事件的触发次数)。
- 使用
-
定时上报
- 通过
os::RepeatingAlarm
每 6 小时调用DrainBufferedCounters()
,将缓存中的计数上报给系统的 metrics 框架(如statsd
)。
- 通过
-
线程安全
- 内部通过
std::mutex
锁保护counters_
map,保证并发场景下的正确性。
- 内部通过
-
溢出保护
- 如果某个计数项的总值超出
LLONG_MAX
,将其钳制为LLONG_MAX
并发出警告。
- 如果某个计数项的总值超出
3. 设计该模块的目的是什么?
背后的设计理念:
设计需求 | 说明 |
---|---|
低频统计上传 | 某些蓝牙事件(如连接失败次数、重连次数、A2DP 播放错误等)不适合立即上报或日志输出。 |
延迟处理,降低性能影响 | 延迟上报避免频繁调用 I/O 操作(如写文件、上报 statsd)。 |
可扩展性强 | 只需新增对应的 key,即可添加新的统计项,无需修改上层逻辑。 |
帮助调优与问题排查 | 比如分析 Bluetooth 启动失败次数、配对错误数,辅助 Google 收集大数据做产品改进。 |
4. 如果去掉这个模块会怎样?
-
无法延迟上报
- 每次计数都需要即时上报,性能开销显著增加。
-
缺失统计数据
- 某些只有内部记录但未及时上报的数据将无法收集,影响用户反馈分析与 QA 问题定位。
-
不利于数据分析平台接入
- Google 的
statsd
、OEM 的大数据平台等依赖此模块的统一接口上传蓝牙计数指标。
- Google 的
5. 实际应用场景举例(车机)
场景 | CounterMetrics 的作用 |
---|---|
A2DP 播放中断 | 记录播放异常次数,辅助分析车机蓝牙不稳定问题 |
HFP 通话失败 | 统计通话连接失败次数,用于判断兼容性问题 |
蓝牙重启频率 | 如果蓝牙频繁崩溃,CounterMetrics 可以记录频率帮助开发定位根因 |
配对异常 | 统计不同品牌手机配对失败的频率,为白名单机制提供数据 |
6. 总结
项目 | 内容 |
---|---|
模块名称 | CounterMetrics |
主要功能 | 缓存并定期上报蓝牙内部计数数据 |
设计意义 | 降低性能开销、支持延迟上报、提高调试能力 |
可否去除 | 不能,否则将严重影响日志分析、问题定位及大数据支持能力 |
PS:
车机/手机 中 如果要对蓝牙服务 埋点, 诊断等。 其实可以结合这个模块来添加。