/* * 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_state.h" #include #include #include #include #include #include #include #include "common/strings.h" #include "hci/address.h" #include "metrics/utils.h" #include "os/log.h" #include "os/metrics.h" namespace bluetooth { namespace metrics { using android::bluetooth::le::LeConnectionOriginType; using android::bluetooth::le::LeConnectionState; using android::bluetooth::le::LeConnectionType; // const static ClockTimePoint kInvalidTimePoint{}; /* * This is the device level metrics state, which will be modified based on * incoming state events. * */ void LEConnectionMetricState::AddStateChangedEvent( LeConnectionOriginType origin_type, LeConnectionType connection_type, LeConnectionState transaction_state, std::vector> argument_list) { LOG_INFO( "LEConnectionMetricState: Origin Type: %s, Connection Type: %s, Transaction State: " "%s", common::ToHexString(origin_type).c_str(), common::ToHexString(connection_type).c_str(), common::ToHexString(transaction_state).c_str()); ClockTimePoint current_timestamp = std::chrono::high_resolution_clock::now(); state = transaction_state; // Assign the origin of the connection if (connection_origin_type == LeConnectionOriginType::ORIGIN_UNSPECIFIED) { connection_origin_type = origin_type; } if (input_connection_type == LeConnectionType::CONNECTION_TYPE_UNSPECIFIED) { input_connection_type = connection_type; } if (start_timepoint == kInvalidTimePoint) { start_timepoint = current_timestamp; } end_timepoint = current_timestamp; switch (state) { case LeConnectionState::STATE_LE_ACL_START: { int connection_type_cid = GetArgumentTypeFromList(argument_list, os::ArgumentType::L2CAP_CID); if (connection_type_cid != -1) { LeConnectionType connection_type = GetLeConnectionTypeFromCID(connection_type_cid); if (connection_type != LeConnectionType::CONNECTION_TYPE_UNSPECIFIED) { LOG_INFO("LEConnectionMetricsRemoteDevice: Populating the connection type\n"); input_connection_type = connection_type; } } break; } case LeConnectionState::STATE_LE_ACL_END: { int acl_status_code_from_args = GetArgumentTypeFromList(argument_list, os::ArgumentType::ACL_STATUS_CODE); acl_status_code = static_cast(acl_status_code_from_args); acl_state = LeAclConnectionState::LE_ACL_SUCCESS; if (acl_status_code != android::bluetooth::hci::StatusEnum::STATUS_SUCCESS) { acl_state = LeAclConnectionState::LE_ACL_FAILED; } break; } case LeConnectionState::STATE_LE_ACL_TIMEOUT: { int acl_status_code_from_args = GetArgumentTypeFromList(argument_list, os::ArgumentType::ACL_STATUS_CODE); acl_status_code = static_cast(acl_status_code_from_args); acl_state = LeAclConnectionState::LE_ACL_FAILED; break; } case LeConnectionState::STATE_LE_ACL_CANCEL: { acl_state = LeAclConnectionState::LE_ACL_FAILED; is_cancelled = true; break; } [[fallthrough]]; default: { // do nothing } } } bool LEConnectionMetricState::IsEnded() { return acl_state == LeAclConnectionState::LE_ACL_SUCCESS || acl_state == LeAclConnectionState::LE_ACL_FAILED; } bool LEConnectionMetricState::IsStarted() { return state == LeConnectionState::STATE_LE_ACL_START; } bool LEConnectionMetricState::IsCancelled() { return is_cancelled; } // Initialize the LEConnectionMetricsRemoteDevice LEConnectionMetricsRemoteDevice::LEConnectionMetricsRemoteDevice() { metrics_logger_module = new MetricsLoggerModule(); } LEConnectionMetricsRemoteDevice::LEConnectionMetricsRemoteDevice( BaseMetricsLoggerModule* baseMetricsLoggerModule) { metrics_logger_module = baseMetricsLoggerModule; } // Uploading the session void LEConnectionMetricsRemoteDevice::UploadLEConnectionSession(const hci::Address& address) { auto it = opened_devices.find(address); if (it != opened_devices.end()) { os::LEConnectionSessionOptions session_options; session_options.acl_connection_state = it->second->acl_state; session_options.origin_type = it->second->connection_origin_type; session_options.transaction_type = it->second->input_connection_type; session_options.latency = bluetooth::metrics::get_timedelta_nanos( it->second->start_timepoint, it->second->end_timepoint); session_options.remote_address = address; session_options.status = it->second->acl_status_code; // TODO: keep the acl latency the same as the overall latency for now // When more events are added, we will an overall latency session_options.acl_latency = session_options.latency; session_options.is_cancelled = it->second->is_cancelled; metrics_logger_module->LogMetricBluetoothLESession(session_options); opened_devices.erase(it); } } // Implementation of metrics per remote device void LEConnectionMetricsRemoteDevice::AddStateChangedEvent( const hci::Address& address, LeConnectionOriginType origin_type, LeConnectionType connection_type, LeConnectionState transaction_state, std::vector> argument_list) { LOG_INFO( "LEConnectionMetricsRemoteDevice: Transaction State %s, Connection Type %s, Origin Type %s\n", common::ToHexString(transaction_state).c_str(), common::ToHexString(connection_type).c_str(), common::ToHexString(origin_type).c_str()); if (address.IsEmpty()) { LOG_INFO( "LEConnectionMetricsRemoteDevice: Empty Address Cancellation %s, %s, %s\n", common::ToHexString(transaction_state).c_str(), common::ToHexString(connection_type).c_str(), common::ToHexString(transaction_state).c_str()); for (auto& device_metric : device_metrics) { if (device_metric->IsStarted() && transaction_state == LeConnectionState::STATE_LE_ACL_CANCEL) { LOG_INFO("LEConnectionMetricsRemoteDevice: Cancellation Begin"); // cancel the connection device_metric->AddStateChangedEvent( origin_type, connection_type, transaction_state, argument_list); continue; } if (device_metric->IsCancelled() && transaction_state == LeConnectionState::STATE_LE_ACL_END) { LOG_INFO("LEConnectionMetricsRemoteDevice: Session is now complete after cancellation"); // complete the connection device_metric->AddStateChangedEvent( origin_type, connection_type, transaction_state, argument_list); UploadLEConnectionSession(address); continue; } } return; } auto it = opened_devices.find(address); if (it == opened_devices.end()) { device_metrics.push_back(std::make_unique(address)); it = opened_devices.insert(std::begin(opened_devices), {address, device_metrics.back().get()}); } it->second->AddStateChangedEvent(origin_type, connection_type, transaction_state, argument_list); // Connection is finished if (it->second->IsEnded()) { UploadLEConnectionSession(address); } } // MetricsLoggerModule class void MetricsLoggerModule::LogMetricBluetoothLESession( os::LEConnectionSessionOptions session_options) { os::LogMetricBluetoothLEConnection(session_options); } // Instance of Metrics Collector for LEConnectionMetricsRemoteDeviceImpl LEConnectionMetricsRemoteDevice* MetricsCollector::le_connection_metrics_remote_device = new LEConnectionMetricsRemoteDevice(); LEConnectionMetricsRemoteDevice* MetricsCollector::GetLEConnectionMetricsCollector() { return MetricsCollector::le_connection_metrics_remote_device; } } // namespace metrics } // namespace bluetooth