summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--system/bta/Android.bp4
-rw-r--r--system/bta/le_audio/client.cc31
-rw-r--r--system/bta/le_audio/devices.cc3
-rw-r--r--system/bta/le_audio/metrics_collector.cc294
-rw-r--r--system/bta/le_audio/metrics_collector.h136
-rw-r--r--system/bta/le_audio/metrics_collector_linux.cc45
-rw-r--r--system/bta/le_audio/metrics_collector_test.cc370
-rw-r--r--system/common/metrics.cc49
-rw-r--r--system/common/metrics.h15
-rw-r--r--system/common/metrics_linux.cc13
-rw-r--r--system/test/mock/mock_common_metrics.cc15
11 files changed, 974 insertions, 1 deletions
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 020a06d9e0..1da4e04c6f 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -101,6 +101,7 @@ cc_library_static {
"le_audio/le_audio_set_configuration_provider.cc",
"le_audio/le_audio_set_configuration_provider_json.cc",
"le_audio/le_audio_types.cc",
+ "le_audio/metrics_collector.cc",
"has/has_client.cc",
"has/has_ctp.cc",
"has/has_journal.cc",
@@ -550,6 +551,7 @@ cc_test {
"le_audio/le_audio_set_configuration_provider_json.cc",
"le_audio/le_audio_types.cc",
"le_audio/le_audio_types_test.cc",
+ "le_audio/metrics_collector_linux.cc",
"le_audio/mock_iso_manager.cc",
"test/common/mock_controller.cc",
"le_audio/state_machine.cc",
@@ -611,6 +613,8 @@ cc_test {
"le_audio/le_audio_client_test.cc",
"le_audio/le_audio_set_configuration_provider_json.cc",
"le_audio/le_audio_types.cc",
+ "le_audio/metrics_collector.cc",
+ "le_audio/metrics_collector_test.cc",
"le_audio/mock_iso_manager.cc",
"le_audio/mock_state_machine.cc",
"test/common/btm_api_mock.cc",
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 287a9acfa9..2f877f0097 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -39,6 +39,7 @@
#include "gd/common/strings.h"
#include "le_audio_set_configuration_provider.h"
#include "le_audio_types.h"
+#include "metrics_collector.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
#include "osi/include/properties.h"
@@ -880,6 +881,10 @@ class LeAudioClientImpl : public LeAudioClient {
leAudioDevices_.Add(address, true);
} else {
leAudioDevice->connecting_actively_ = true;
+
+ le_audio::MetricsCollector::Get()->OnConnectionStateChanged(
+ leAudioDevice->group_id_, address, ConnectionState::CONNECTING,
+ le_audio::ConnectionStatus::SUCCESS);
}
BTA_GATTC_Open(gatt_if_, address, true, false);
@@ -1253,6 +1258,9 @@ class LeAudioClientImpl : public LeAudioClient {
LOG(ERROR) << "Failed to connect to LeAudio leAudioDevice, status: "
<< +status;
callbacks_->OnConnectionState(ConnectionState::DISCONNECTED, address);
+ le_audio::MetricsCollector::Get()->OnConnectionStateChanged(
+ leAudioDevice->group_id_, address, ConnectionState::CONNECTED,
+ le_audio::ConnectionStatus::FAILED);
return;
}
@@ -1303,6 +1311,9 @@ class LeAudioClientImpl : public LeAudioClient {
}
LOG(ERROR) << __func__ << " Encryption error";
+ le_audio::MetricsCollector::Get()->OnConnectionStateChanged(
+ leAudioDevice->group_id_, address, ConnectionState::CONNECTED,
+ le_audio::ConnectionStatus::FAILED);
}
void RegisterKnownNotifications(LeAudioDevice* leAudioDevice) {
@@ -1358,6 +1369,9 @@ class LeAudioClientImpl : public LeAudioClient {
BTA_GATTC_Close(leAudioDevice->conn_id_);
if (leAudioDevice->connecting_actively_) {
callbacks_->OnConnectionState(ConnectionState::DISCONNECTED, address);
+ le_audio::MetricsCollector::Get()->OnConnectionStateChanged(
+ leAudioDevice->group_id_, address, ConnectionState::CONNECTED,
+ le_audio::ConnectionStatus::FAILED);
}
return;
}
@@ -1402,6 +1416,10 @@ class LeAudioClientImpl : public LeAudioClient {
leAudioDevice->closing_stream_for_disconnection_ = false;
leAudioDevice->encrypted_ = false;
+ le_audio::MetricsCollector::Get()->OnConnectionStateChanged(
+ leAudioDevice->group_id_, address, ConnectionState::DISCONNECTED,
+ le_audio::ConnectionStatus::SUCCESS);
+
if (leAudioDevice->removing_device_) {
if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) {
auto group = aseGroups_.FindById(leAudioDevice->group_id_);
@@ -1953,6 +1971,9 @@ class LeAudioClientImpl : public LeAudioClient {
btif_storage_set_leaudio_autoconnect(leAudioDevice->address_, true);
leAudioDevice->first_connection_ = false;
}
+ le_audio::MetricsCollector::Get()->OnConnectionStateChanged(
+ leAudioDevice->group_id_, leAudioDevice->address_,
+ ConnectionState::CONNECTED, le_audio::ConnectionStatus::SUCCESS);
}
bool IsAseAcceptingAudioData(struct ase* ase) {
@@ -2799,8 +2820,10 @@ class LeAudioClientImpl : public LeAudioClient {
/* Last suspends group - triggers group stop */
if ((audio_receiver_state_ == AudioState::IDLE) ||
- (audio_receiver_state_ == AudioState::READY_TO_RELEASE))
+ (audio_receiver_state_ == AudioState::READY_TO_RELEASE)) {
OnAudioSuspend();
+ le_audio::MetricsCollector::Get()->OnStreamEnded(active_group_id_);
+ }
DLOG(INFO) << __func__
<< " OUT: audio_receiver_state_: " << audio_receiver_state_
@@ -2898,6 +2921,8 @@ class LeAudioClientImpl : public LeAudioClient {
if (alarm_is_scheduled(suspend_timeout_))
alarm_cancel(suspend_timeout_);
leAudioClientAudioSource->ConfirmStreamingRequest();
+ le_audio::MetricsCollector::Get()->OnStreamStarted(
+ active_group_id_, current_context_type_);
break;
case AudioState::RELEASING:
/* Keep wainting. After release is done, Audio Hal will be notified
@@ -3443,6 +3468,8 @@ class LeAudioClientImpl : public LeAudioClient {
stream_setup_end_timestamp_ =
bluetooth::common::time_get_os_boottime_us();
+ le_audio::MetricsCollector::Get()->OnStreamStarted(
+ active_group_id_, current_context_type_);
break;
case GroupStreamStatus::SUSPENDED:
stream_setup_end_timestamp_ = 0;
@@ -3575,6 +3602,7 @@ class LeAudioClientImpl : public LeAudioClient {
leAudioClientAudioSink->Release(audio_sink_instance_);
audio_sink_instance_ = nullptr;
}
+ le_audio::MetricsCollector::Get()->OnStreamEnded(active_group_id_);
}
};
@@ -3855,6 +3883,7 @@ void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) {
CodecManager::GetInstance()->Stop();
LeAudioGroupStateMachine::Cleanup();
IsoManager::GetInstance()->Stop();
+ le_audio::MetricsCollector::Get()->Flush();
}
void LeAudioClient::InitializeAudioClients(
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index ba7f462cdd..6943f3efe7 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -30,6 +30,7 @@
#include "device/include/controller.h"
#include "gd/common/strings.h"
#include "le_audio_set_configuration_provider.h"
+#include "metrics_collector.h"
#include "osi/include/log.h"
#include "stack/include/acl_api.h"
@@ -57,6 +58,7 @@ void LeAudioDeviceGroup::AddNode(
const std::shared_ptr<LeAudioDevice>& leAudioDevice) {
leAudioDevice->group_id_ = group_id_;
leAudioDevices_.push_back(std::weak_ptr<LeAudioDevice>(leAudioDevice));
+ MetricsCollector::Get()->OnGroupSizeUpdate(group_id_, leAudioDevices_.size());
}
void LeAudioDeviceGroup::RemoveNode(
@@ -73,6 +75,7 @@ void LeAudioDeviceGroup::RemoveNode(
leAudioDevices_.begin(), leAudioDevices_.end(),
[&leAudioDevice](auto& d) { return d.lock() == leAudioDevice; }),
leAudioDevices_.end());
+ MetricsCollector::Get()->OnGroupSizeUpdate(group_id_, leAudioDevices_.size());
}
bool LeAudioDeviceGroup::IsEmpty(void) { return leAudioDevices_.size() == 0; }
diff --git a/system/bta/le_audio/metrics_collector.cc b/system/bta/le_audio/metrics_collector.cc
new file mode 100644
index 0000000000..4a00d2da84
--- /dev/null
+++ b/system/bta/le_audio/metrics_collector.cc
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 "metrics_collector.h"
+
+#include <chrono>
+#include <memory>
+#include <vector>
+
+#include "common/metrics.h"
+
+namespace le_audio {
+
+using ClockTimePoint =
+ std::chrono::time_point<std::chrono::high_resolution_clock>;
+using bluetooth::le_audio::ConnectionState;
+using le_audio::types::LeAudioContextType;
+
+const static ClockTimePoint kInvalidTimePoint{};
+
+MetricsCollector* MetricsCollector::instance = nullptr;
+
+inline int64_t get_timedelta_nanos(const ClockTimePoint& t1,
+ const ClockTimePoint& t2) {
+ if (t1 == kInvalidTimePoint || t2 == kInvalidTimePoint) {
+ return -1;
+ }
+ return std::abs(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t2).count());
+}
+
+const static std::unordered_map<LeAudioContextType, LeAudioMetricsContextType>
+ kContextTypeTable = {
+ {LeAudioContextType::UNINITIALIZED, LeAudioMetricsContextType::INVALID},
+ {LeAudioContextType::UNSPECIFIED,
+ LeAudioMetricsContextType::UNSPECIFIED},
+ {LeAudioContextType::CONVERSATIONAL,
+ LeAudioMetricsContextType::COMMUNICATION},
+ {LeAudioContextType::MEDIA, LeAudioMetricsContextType::MEDIA},
+ {LeAudioContextType::GAME, LeAudioMetricsContextType::GAME},
+ {LeAudioContextType::INSTRUCTIONAL,
+ LeAudioMetricsContextType::INSTRUCTIONAL},
+ {LeAudioContextType::VOICEASSISTANTS,
+ LeAudioMetricsContextType::MAN_MACHINE},
+ {LeAudioContextType::LIVE, LeAudioMetricsContextType::LIVE},
+ {LeAudioContextType::SOUNDEFFECTS,
+ LeAudioMetricsContextType::ATTENTION_SEEKING},
+ {LeAudioContextType::NOTIFICATIONS,
+ LeAudioMetricsContextType::ATTENTION_SEEKING},
+ {LeAudioContextType::RINGTONE, LeAudioMetricsContextType::RINGTONE},
+ {LeAudioContextType::ALERTS,
+ LeAudioMetricsContextType::IMMEDIATE_ALERT},
+ {LeAudioContextType::EMERGENCYALARM,
+ LeAudioMetricsContextType::EMERGENCY_ALERT},
+ {LeAudioContextType::RFU, LeAudioMetricsContextType::RFU},
+};
+
+inline int32_t to_atom_context_type(const LeAudioContextType stack_type) {
+ auto it = kContextTypeTable.find(stack_type);
+ if (it != kContextTypeTable.end()) {
+ return static_cast<int32_t>(it->second);
+ }
+ return static_cast<int32_t>(LeAudioMetricsContextType::INVALID);
+}
+
+class DeviceMetrics {
+ public:
+ RawAddress address_;
+ ClockTimePoint connecting_timepoint_ = kInvalidTimePoint;
+ ClockTimePoint connected_timepoint_ = kInvalidTimePoint;
+ ClockTimePoint disconnected_timepoint_ = kInvalidTimePoint;
+ int32_t connection_status_ = 0;
+ int32_t disconnection_status_ = 0;
+
+ DeviceMetrics(const RawAddress& address) : address_(address) {}
+
+ void AddStateChangedEvent(ConnectionState state, ConnectionStatus status) {
+ switch (state) {
+ case ConnectionState::CONNECTING:
+ connecting_timepoint_ = std::chrono::high_resolution_clock::now();
+ break;
+ case ConnectionState::CONNECTED:
+ connected_timepoint_ = std::chrono::high_resolution_clock::now();
+ connection_status_ = static_cast<int32_t>(status);
+ break;
+ case ConnectionState::DISCONNECTED:
+ disconnected_timepoint_ = std::chrono::high_resolution_clock::now();
+ disconnection_status_ = static_cast<int32_t>(status);
+ break;
+ case ConnectionState::DISCONNECTING:
+ // Ignore
+ break;
+ }
+ }
+};
+
+class GroupMetricsImpl : public GroupMetrics {
+ private:
+ static constexpr int32_t kInvalidGroupId = -1;
+ int32_t group_id_;
+ int32_t group_size_;
+ std::vector<std::unique_ptr<DeviceMetrics>> device_metrics_;
+ std::unordered_map<RawAddress, DeviceMetrics*> opened_devices_;
+ ClockTimePoint beginning_timepoint_;
+ std::vector<int64_t> streaming_offset_nanos_;
+ std::vector<int64_t> streaming_duration_nanos_;
+ std::vector<int32_t> streaming_context_type_;
+
+ public:
+ GroupMetricsImpl() : group_id_(kInvalidGroupId), group_size_(0) {
+ beginning_timepoint_ = std::chrono::high_resolution_clock::now();
+ }
+ GroupMetricsImpl(int32_t group_id, int32_t group_size)
+ : group_id_(group_id), group_size_(group_size) {
+ beginning_timepoint_ = std::chrono::high_resolution_clock::now();
+ }
+
+ void AddStateChangedEvent(const RawAddress& address,
+ le_audio::ConnectionState state,
+ ConnectionStatus status) override {
+ auto it = opened_devices_.find(address);
+ if (it == opened_devices_.end()) {
+ device_metrics_.push_back(std::make_unique<DeviceMetrics>(address));
+ it = opened_devices_.insert(std::begin(opened_devices_),
+ {address, device_metrics_.back().get()});
+ }
+ it->second->AddStateChangedEvent(state, status);
+ if (state == le_audio::ConnectionState::DISCONNECTED ||
+ (state == le_audio::ConnectionState::CONNECTED &&
+ status != ConnectionStatus::SUCCESS)) {
+ opened_devices_.erase(it);
+ }
+ }
+
+ void AddStreamStartedEvent(
+ le_audio::types::LeAudioContextType context_type) override {
+ int32_t atom_context_type = to_atom_context_type(context_type);
+ // Make sure events aligned
+ if (streaming_offset_nanos_.size() - streaming_duration_nanos_.size() !=
+ 0) {
+ // Allow type switching
+ if (!streaming_context_type_.empty() &&
+ streaming_context_type_.back() != atom_context_type) {
+ AddStreamEndedEvent();
+ } else {
+ return;
+ }
+ }
+ streaming_offset_nanos_.push_back(get_timedelta_nanos(
+ std::chrono::high_resolution_clock::now(), beginning_timepoint_));
+ streaming_context_type_.push_back(atom_context_type);
+ }
+
+ void AddStreamEndedEvent() override {
+ // Make sure events aligned
+ if (streaming_offset_nanos_.size() - streaming_duration_nanos_.size() !=
+ 1) {
+ return;
+ }
+ streaming_duration_nanos_.push_back(
+ get_timedelta_nanos(std::chrono::high_resolution_clock::now(),
+ beginning_timepoint_) -
+ streaming_offset_nanos_.back());
+ }
+
+ void SetGroupSize(int32_t group_size) override { group_size_ = group_size; }
+
+ bool IsClosed() override { return opened_devices_.empty(); }
+
+ void WriteStats() override {
+ int64_t connection_duration_nanos = get_timedelta_nanos(
+ beginning_timepoint_, std::chrono::high_resolution_clock::now());
+
+ int len = device_metrics_.size();
+ std::vector<int64_t> device_connecting_offset_nanos(len);
+ std::vector<int64_t> device_connected_offset_nanos(len);
+ std::vector<int64_t> device_connection_duration_nanos(len);
+ std::vector<int32_t> device_connection_statuses(len);
+ std::vector<int32_t> device_disconnection_statuses(len);
+ std::vector<RawAddress> device_address(len);
+
+ while (streaming_duration_nanos_.size() < streaming_offset_nanos_.size()) {
+ AddStreamEndedEvent();
+ }
+
+ for (int i = 0; i < len; i++) {
+ auto device_metric = device_metrics_[i].get();
+ device_connecting_offset_nanos[i] = get_timedelta_nanos(
+ device_metric->connecting_timepoint_, beginning_timepoint_);
+ device_connected_offset_nanos[i] = get_timedelta_nanos(
+ device_metric->connected_timepoint_, beginning_timepoint_);
+ device_connection_duration_nanos[i] =
+ get_timedelta_nanos(device_metric->disconnected_timepoint_,
+ device_metric->connected_timepoint_);
+ device_connection_statuses[i] = device_metric->connection_status_;
+ device_disconnection_statuses[i] = device_metric->disconnection_status_;
+ device_address[i] = device_metric->address_;
+ }
+
+ bluetooth::common::LogLeAudioConnectionSessionReported(
+ group_size_, group_id_, connection_duration_nanos,
+ device_connecting_offset_nanos, device_connected_offset_nanos,
+ device_connection_duration_nanos, device_connection_statuses,
+ device_disconnection_statuses, device_address, streaming_offset_nanos_,
+ streaming_duration_nanos_, streaming_context_type_);
+ }
+
+ void Flush() {
+ for (auto& p : opened_devices_) {
+ p.second->AddStateChangedEvent(
+ bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+ }
+ WriteStats();
+ }
+};
+
+/* Metrics Colloctor */
+
+MetricsCollector* MetricsCollector::Get() {
+ if (MetricsCollector::instance == nullptr) {
+ MetricsCollector::instance = new MetricsCollector();
+ }
+ return MetricsCollector::instance;
+}
+
+void MetricsCollector::OnGroupSizeUpdate(int32_t group_id, int32_t group_size) {
+ group_size_table_[group_id] = group_size;
+ auto it = opened_groups_.find(group_id);
+ if (it != opened_groups_.end()) {
+ it->second->SetGroupSize(group_size);
+ }
+}
+
+void MetricsCollector::OnConnectionStateChanged(
+ int32_t group_id, const RawAddress& address,
+ bluetooth::le_audio::ConnectionState state, ConnectionStatus status) {
+ if (address.IsEmpty() || group_id <= 0) {
+ return;
+ }
+ auto it = opened_groups_.find(group_id);
+ if (it == opened_groups_.end()) {
+ it = opened_groups_.insert(
+ std::begin(opened_groups_),
+ {group_id, std::make_unique<GroupMetricsImpl>(
+ group_id, group_size_table_[group_id])});
+ }
+ it->second->AddStateChangedEvent(address, state, status);
+
+ if (it->second->IsClosed()) {
+ it->second->WriteStats();
+ opened_groups_.erase(it);
+ }
+}
+
+void MetricsCollector::OnStreamStarted(
+ int32_t group_id, le_audio::types::LeAudioContextType context_type) {
+ if (group_id <= 0) return;
+ auto it = opened_groups_.find(group_id);
+ if (it != opened_groups_.end()) {
+ it->second->AddStreamStartedEvent(context_type);
+ }
+}
+
+void MetricsCollector::OnStreamEnded(int32_t group_id) {
+ if (group_id <= 0) return;
+ auto it = opened_groups_.find(group_id);
+ if (it != opened_groups_.end()) {
+ it->second->AddStreamEndedEvent();
+ }
+}
+
+void MetricsCollector::Flush() {
+ LOG(INFO) << __func__;
+ for (auto& p : opened_groups_) {
+ p.second->Flush();
+ }
+ opened_groups_.clear();
+}
+
+} // namespace le_audio
diff --git a/system/bta/le_audio/metrics_collector.h b/system/bta/le_audio/metrics_collector.h
new file mode 100644
index 0000000000..f988364734
--- /dev/null
+++ b/system/bta/le_audio/metrics_collector.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 <hardware/bt_le_audio.h>
+
+#include <cstdint>
+#include <memory>
+#include <unordered_map>
+
+#include "le_audio_types.h"
+#include "types/raw_address.h"
+
+namespace le_audio {
+
+enum ConnectionStatus : int32_t {
+ UNKNOWN = 0,
+ SUCCESS = 1,
+ FAILED = 2,
+};
+
+/* android.bluetooth.leaudio.ContextType */
+enum class LeAudioMetricsContextType : int32_t {
+ INVALID = 0,
+ UNSPECIFIED = 1,
+ COMMUNICATION = 2,
+ MEDIA = 3,
+ INSTRUCTIONAL = 4,
+ ATTENTION_SEEKING = 5,
+ IMMEDIATE_ALERT = 6,
+ MAN_MACHINE = 7,
+ EMERGENCY_ALERT = 8,
+ RINGTONE = 9,
+ TV = 10,
+ LIVE = 11,
+ GAME = 12,
+ RFU = 13,
+};
+
+class GroupMetrics {
+ public:
+ GroupMetrics() {}
+
+ virtual ~GroupMetrics() {}
+
+ virtual void AddStateChangedEvent(const RawAddress& address,
+ bluetooth::le_audio::ConnectionState state,
+ ConnectionStatus status) = 0;
+
+ virtual void AddStreamStartedEvent(
+ le_audio::types::LeAudioContextType context_type) = 0;
+
+ virtual void AddStreamEndedEvent() = 0;
+
+ virtual void SetGroupSize(int32_t group_size) = 0;
+
+ virtual bool IsClosed() = 0;
+
+ virtual void WriteStats() = 0;
+
+ virtual void Flush() = 0;
+};
+
+class MetricsCollector {
+ public:
+ static MetricsCollector* Get();
+
+ /**
+ * Update the size of given group which will be used in the
+ * LogMetricBluetoothLeAudioConnectionStateChanged()
+ *
+ * @param group_id ID of target group
+ * @param group_size Size of target group
+ */
+ void OnGroupSizeUpdate(int32_t group_id, int32_t group_size);
+
+ /**
+ * When there is a change in Bluetooth LE Audio connection state
+ *
+ * @param group_id Group ID of the associated device.
+ * @param address Address of the associated device.
+ * @param state New LE Audio connetion state.
+ * @param status status or reason of the state transition. Ignored at
+ * CONNECTING states.
+ */
+ void OnConnectionStateChanged(int32_t group_id, const RawAddress& address,
+ bluetooth::le_audio::ConnectionState state,
+ ConnectionStatus status);
+
+ /**
+ * When there is a change in LE Audio stream started
+ *
+ * @param group_id Group ID of the associated stream.
+ */
+ void OnStreamStarted(int32_t group_id,
+ le_audio::types::LeAudioContextType context_type);
+
+ /**
+ * When there is a change in LE Audio stream started
+ *
+ * @param group_id Group ID of the associated stream.
+ */
+ void OnStreamEnded(int32_t group_id);
+
+ /**
+ * Flush all log to statsd
+ *
+ * @param group_id Group ID of the associated stream.
+ */
+ void Flush();
+
+ protected:
+ MetricsCollector() {}
+
+ private:
+ static MetricsCollector* instance;
+
+ std::unordered_map<int32_t, std::unique_ptr<GroupMetrics>> opened_groups_;
+ std::unordered_map<int32_t, int32_t> group_size_table_;
+};
+
+} // namespace le_audio
diff --git a/system/bta/le_audio/metrics_collector_linux.cc b/system/bta/le_audio/metrics_collector_linux.cc
new file mode 100644
index 0000000000..2ac63a567b
--- /dev/null
+++ b/system/bta/le_audio/metrics_collector_linux.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 "metrics_collector.h"
+
+namespace le_audio {
+
+/* Metrics Colloctor */
+MetricsCollector* MetricsCollector::instance = nullptr;
+
+MetricsCollector* MetricsCollector::Get() {
+ if (MetricsCollector::instance == nullptr) {
+ MetricsCollector::instance = new MetricsCollector();
+ }
+ return MetricsCollector::instance;
+}
+
+void MetricsCollector::OnGroupSizeUpdate(int32_t group_id, int32_t group_size) {
+}
+
+void MetricsCollector::OnConnectionStateChanged(
+ int32_t group_id, const RawAddress& address,
+ bluetooth::le_audio::ConnectionState state, ConnectionStatus status) {}
+
+void MetricsCollector::OnStreamStarted(
+ int32_t group_id, le_audio::types::LeAudioContextType context_type) {}
+
+void MetricsCollector::OnStreamEnded(int32_t group_id) {}
+
+void MetricsCollector::Flush() {}
+
+} // namespace le_audio
diff --git a/system/bta/le_audio/metrics_collector_test.cc b/system/bta/le_audio/metrics_collector_test.cc
new file mode 100644
index 0000000000..ee295d56d7
--- /dev/null
+++ b/system/bta/le_audio/metrics_collector_test.cc
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * 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 "metrics_collector.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <vector>
+
+#include "types/raw_address.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::AtMost;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Mock;
+using testing::MockFunction;
+using testing::NotNull;
+using testing::Return;
+using testing::SaveArg;
+using testing::SetArgPointee;
+using testing::Test;
+using testing::WithArg;
+
+int log_count = 0;
+int32_t last_group_size;
+int32_t last_group_metric_id;
+int64_t last_connection_duration_nanos;
+std::vector<int64_t> last_device_connecting_offset_nanos;
+std::vector<int64_t> last_device_connected_offset_nanos;
+std::vector<int64_t> last_device_connection_duration_nanos;
+std::vector<int32_t> last_device_connection_status;
+std::vector<int32_t> last_device_disconnection_status;
+std::vector<RawAddress> last_device_address;
+std::vector<int64_t> last_streaming_offset_nanos;
+std::vector<int64_t> last_streaming_duration_nanos;
+std::vector<int32_t> last_streaming_context_type;
+
+namespace bluetooth {
+namespace common {
+
+void LogLeAudioConnectionSessionReported(
+ int32_t group_size, int32_t group_metric_id,
+ int64_t connection_duration_nanos,
+ std::vector<int64_t>& device_connecting_offset_nanos,
+ std::vector<int64_t>& device_connected_offset_nanos,
+ std::vector<int64_t>& device_connection_duration_nanos,
+ std::vector<int32_t>& device_connection_status,
+ std::vector<int32_t>& device_disconnection_status,
+ std::vector<RawAddress>& device_address,
+ std::vector<int64_t>& streaming_offset_nanos,
+ std::vector<int64_t>& streaming_duration_nanos,
+ std::vector<int32_t>& streaming_context_type) {
+ log_count++;
+ last_group_size = group_size;
+ last_group_metric_id = group_metric_id;
+ last_connection_duration_nanos = connection_duration_nanos;
+ last_device_connecting_offset_nanos = device_connecting_offset_nanos;
+ last_device_connected_offset_nanos = device_connected_offset_nanos;
+ last_device_connection_duration_nanos = device_connection_duration_nanos;
+ last_device_connection_status = device_connection_status;
+ last_device_disconnection_status = device_disconnection_status;
+ last_device_address = device_address;
+ last_streaming_offset_nanos = streaming_offset_nanos;
+ last_streaming_duration_nanos = streaming_duration_nanos;
+ last_streaming_context_type = streaming_context_type;
+}
+
+} // namespace common
+} // namespace bluetooth
+
+namespace le_audio {
+
+const int32_t group_id1 = 1;
+const RawAddress device1 = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
+
+const int32_t group_id2 = 2;
+const RawAddress device2 = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x67});
+const RawAddress device3 = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x68});
+
+class MockMetricsCollector : public MetricsCollector {
+ public:
+ MockMetricsCollector() {}
+};
+
+class MetricsCollectorTest : public Test {
+ protected:
+ std::unique_ptr<MetricsCollector> collector;
+
+ void SetUp() override {
+ collector = std::make_unique<MockMetricsCollector>();
+
+ log_count = 0;
+ last_group_size = 0;
+ last_group_metric_id = 0;
+ last_connection_duration_nanos = 0;
+ last_device_connecting_offset_nanos = {};
+ last_device_connected_offset_nanos = {};
+ last_device_connection_duration_nanos = {};
+ last_device_connection_status = {};
+ last_device_disconnection_status = {};
+ last_device_address = {};
+ last_streaming_offset_nanos = {};
+ last_streaming_duration_nanos = {};
+ last_streaming_context_type = {};
+ }
+
+ void TearDown() override { collector = nullptr; }
+};
+
+TEST_F(MetricsCollectorTest, Initialize) { ASSERT_EQ(log_count, 0); }
+
+TEST_F(MetricsCollectorTest, ConnectionFailed) {
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::FAILED);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id1);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.back(), ConnectionStatus::FAILED);
+}
+
+TEST_F(MetricsCollectorTest, ConnectingConnectedDisconnected) {
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id1);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.size(), 1UL);
+ ASSERT_EQ(last_device_disconnection_status.size(), 1UL);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connected_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_duration_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.back(), ConnectionStatus::SUCCESS);
+ ASSERT_EQ(last_device_disconnection_status.back(), ConnectionStatus::SUCCESS);
+}
+
+TEST_F(MetricsCollectorTest, SingleDeviceTwoConnections) {
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id1);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.size(), 1UL);
+ ASSERT_EQ(last_device_disconnection_status.size(), 1UL);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connected_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_duration_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.back(), ConnectionStatus::SUCCESS);
+ ASSERT_EQ(last_device_disconnection_status.back(), ConnectionStatus::SUCCESS);
+
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 2);
+ ASSERT_EQ(last_group_metric_id, group_id1);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.size(), 1UL);
+ ASSERT_EQ(last_device_disconnection_status.size(), 1UL);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connected_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_duration_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.back(), ConnectionStatus::SUCCESS);
+ ASSERT_EQ(last_device_disconnection_status.back(), ConnectionStatus::SUCCESS);
+}
+
+TEST_F(MetricsCollectorTest, StereoGroupBasicTest) {
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id2);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 2UL);
+ ASSERT_EQ(last_device_connection_status.size(), 2UL);
+ ASSERT_EQ(last_device_disconnection_status.size(), 2UL);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 2UL);
+ ASSERT_EQ(last_device_connected_offset_nanos.size(), 2UL);
+ ASSERT_EQ(last_device_connection_duration_nanos.size(), 2UL);
+}
+
+TEST_F(MetricsCollectorTest, StereoGroupMultiReconnections) {
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id2);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 3UL);
+ ASSERT_EQ(last_device_connection_status.size(), 3UL);
+ ASSERT_EQ(last_device_disconnection_status.size(), 3UL);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 3UL);
+ ASSERT_EQ(last_device_connected_offset_nanos.size(), 3UL);
+ ASSERT_EQ(last_device_connection_duration_nanos.size(), 3UL);
+}
+
+TEST_F(MetricsCollectorTest, MixGroups) {
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device3, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id2, device2, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id2);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 2UL);
+ ASSERT_EQ(last_device_connection_status.size(), 2UL);
+ ASSERT_EQ(last_device_disconnection_status.size(), 2UL);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 2UL);
+ ASSERT_EQ(last_device_connected_offset_nanos.size(), 2UL);
+ ASSERT_EQ(last_device_connection_duration_nanos.size(), 2UL);
+
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 2);
+ ASSERT_EQ(last_group_metric_id, group_id1);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_status.size(), 1UL);
+ ASSERT_EQ(last_device_disconnection_status.size(), 1UL);
+ ASSERT_EQ(last_device_connecting_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connected_offset_nanos.size(), 1UL);
+ ASSERT_EQ(last_device_connection_duration_nanos.size(), 1UL);
+}
+
+TEST_F(MetricsCollectorTest, GroupSizeUpdated) {
+ collector->OnGroupSizeUpdate(group_id2, 1);
+ collector->OnGroupSizeUpdate(group_id1, 2);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id1);
+ ASSERT_EQ(last_group_size, 2);
+}
+
+TEST_F(MetricsCollectorTest, StreamingSessions) {
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTING,
+ ConnectionStatus::UNKNOWN);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::CONNECTED,
+ ConnectionStatus::SUCCESS);
+ collector->OnStreamStarted(group_id1,
+ le_audio::types::LeAudioContextType::MEDIA);
+ collector->OnStreamEnded(group_id1);
+ collector->OnStreamStarted(
+ group_id1, le_audio::types::LeAudioContextType::CONVERSATIONAL);
+ collector->OnStreamEnded(group_id1);
+ collector->OnConnectionStateChanged(
+ group_id1, device1, bluetooth::le_audio::ConnectionState::DISCONNECTED,
+ ConnectionStatus::SUCCESS);
+
+ ASSERT_EQ(log_count, 1);
+ ASSERT_EQ(last_group_metric_id, group_id1);
+ ASSERT_EQ(last_streaming_offset_nanos.size(), 2UL);
+ ASSERT_EQ(last_streaming_duration_nanos.size(), 2UL);
+ ASSERT_EQ(last_streaming_context_type.size(), 2UL);
+
+ ASSERT_GT(last_streaming_offset_nanos[0], 0L);
+ ASSERT_GT(last_streaming_offset_nanos[1], 0L);
+ ASSERT_GT(last_streaming_duration_nanos[0], 0L);
+ ASSERT_GT(last_streaming_duration_nanos[1], 0L);
+ ASSERT_EQ(last_streaming_context_type[0],
+ static_cast<int32_t>(LeAudioMetricsContextType::MEDIA));
+ ASSERT_EQ(last_streaming_context_type[1],
+ static_cast<int32_t>(LeAudioMetricsContextType::COMMUNICATION));
+}
+
+} // namespace le_audio \ No newline at end of file
diff --git a/system/common/metrics.cc b/system/common/metrics.cc
index 47870e0402..be1d1b3bac 100644
--- a/system/common/metrics.cc
+++ b/system/common/metrics.cc
@@ -908,6 +908,55 @@ void LogBluetoothHalCrashReason(const RawAddress& address, uint32_t error_code,
}
}
+void LogLeAudioConnectionSessionReported(
+ int32_t group_size, int32_t group_metric_id,
+ int64_t connection_duration_nanos,
+ std::vector<int64_t>& device_connecting_offset_nanos,
+ std::vector<int64_t>& device_connected_offset_nanos,
+ std::vector<int64_t>& device_connection_duration_nanos,
+ std::vector<int32_t>& device_connection_status,
+ std::vector<int32_t>& device_disconnection_status,
+ std::vector<RawAddress>& device_address,
+ std::vector<int64_t>& streaming_offset_nanos,
+ std::vector<int64_t>& streaming_duration_nanos,
+ std::vector<int32_t>& streaming_context_type) {
+ std::vector<int32_t> device_metric_id(device_address.size());
+ for (uint64_t i = 0; i < device_address.size(); i++) {
+ if (!device_address[i].IsEmpty()) {
+ device_metric_id[i] =
+ MetricIdAllocator::GetInstance().AllocateId(device_address[i]);
+ } else {
+ device_metric_id[i] = 0;
+ }
+ }
+ int ret = stats_write(
+ LE_AUDIO_CONNECTION_SESSION_REPORTED, group_size, group_metric_id,
+ connection_duration_nanos, device_connecting_offset_nanos,
+ device_connected_offset_nanos, device_connection_duration_nanos,
+ device_connection_status, device_disconnection_status, device_metric_id,
+ streaming_offset_nanos, streaming_duration_nanos, streaming_context_type);
+ if (ret < 0) {
+ LOG(WARNING) << __func__ << ": failed for group " << group_metric_id
+ << "device_connecting_offset_nanos["
+ << device_connecting_offset_nanos.size() << "], "
+ << "device_connected_offset_nanos["
+ << device_connected_offset_nanos.size() << "], "
+ << "device_connection_duration_nanos["
+ << device_connection_duration_nanos.size() << "], "
+ << "device_connection_status["
+ << device_connection_status.size() << "], "
+ << "device_disconnection_status["
+ << device_disconnection_status.size() << "], "
+ << "device_metric_id[" << device_metric_id.size() << "], "
+ << "streaming_offset_nanos[" << streaming_offset_nanos.size()
+ << "], "
+ << "streaming_duration_nanos["
+ << streaming_duration_nanos.size() << "], "
+ << "streaming_context_type[" << streaming_context_type.size()
+ << "]";
+ }
+}
+
} // namespace common
} // namespace bluetooth
diff --git a/system/common/metrics.h b/system/common/metrics.h
index c224734b07..53cad5c3d0 100644
--- a/system/common/metrics.h
+++ b/system/common/metrics.h
@@ -25,6 +25,7 @@
#include <memory>
#include <string>
+#include <vector>
#include "types/raw_address.h"
@@ -502,6 +503,20 @@ void LogManufacturerInfo(const RawAddress& address,
*/
void LogBluetoothHalCrashReason(const RawAddress& address, uint32_t error_code,
uint32_t vendor_error_code);
+
+void LogLeAudioConnectionSessionReported(
+ int32_t group_size, int32_t group_metric_id,
+ int64_t connection_duration_nanos,
+ std::vector<int64_t>& device_connecting_offset_nanos,
+ std::vector<int64_t>& device_connected_offset_nanos,
+ std::vector<int64_t>& device_connection_duration_nanos,
+ std::vector<int32_t>& device_connection_status,
+ std::vector<int32_t>& device_disconnection_status,
+ std::vector<RawAddress>& device_address,
+ std::vector<int64_t>& streaming_offset_nanos,
+ std::vector<int64_t>& streaming_duration_nanos,
+ std::vector<int32_t>& streaming_context_type);
+
} // namespace common
} // namespace bluetooth
diff --git a/system/common/metrics_linux.cc b/system/common/metrics_linux.cc
index 46cab49f2a..fa8c6a0497 100644
--- a/system/common/metrics_linux.cc
+++ b/system/common/metrics_linux.cc
@@ -154,6 +154,19 @@ void LogSmpPairingEvent(const RawAddress& address, uint8_t smp_cmd,
android::bluetooth::DirectionEnum direction,
uint8_t smp_fail_reason) {}
+void LogLeAudioConnectionSessionReported(
+ int32_t group_size, int32_t group_metric_id,
+ int64_t connection_duration_nanos,
+ std::vector<int64_t>& device_connecting_offset_nanos,
+ std::vector<int64_t>& device_connected_offset_nanos,
+ std::vector<int64_t>& device_connection_duration_nanos,
+ std::vector<int32_t>& device_connection_status,
+ std::vector<int32_t>& device_disconnection_status,
+ std::vector<RawAddress>& device_address,
+ std::vector<int64_t>& streaming_offset_nanos,
+ std::vector<int64_t>& streaming_duration_nanos,
+ std::vector<int32_t>& streaming_context_type) {}
+
} // namespace common
} // namespace bluetooth
diff --git a/system/test/mock/mock_common_metrics.cc b/system/test/mock/mock_common_metrics.cc
index ce8a7fb686..766fec2747 100644
--- a/system/test/mock/mock_common_metrics.cc
+++ b/system/test/mock/mock_common_metrics.cc
@@ -204,5 +204,20 @@ void LogSocketConnectionState(
mock_function_count_map[__func__]++;
}
+void LogLeAudioConnectionSessionReported(
+ int32_t group_size, int32_t group_metric_id,
+ int64_t connection_duration_nanos,
+ std::vector<int64_t>& device_connecting_offset_nanos,
+ std::vector<int64_t>& device_connected_offset_nanos,
+ std::vector<int64_t>& device_connection_duration_nanos,
+ std::vector<int32_t>& device_connection_status,
+ std::vector<int32_t>& device_disconnection_status,
+ std::vector<RawAddress>& device_address,
+ std::vector<int64_t>& streaming_offset_nanos,
+ std::vector<int64_t>& streaming_duration_nanos,
+ std::vector<int32_t>& streaming_context_type) {
+ mock_function_count_map[__func__]++;
+}
+
} // namespace common
} // namespace bluetooth