| // Copyright 2021 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "iioservice/daemon/sensor_metrics.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include <base/logging.h> |
| #include <base/stl_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/strings/string_util.h> |
| #include <libmems/common_types.h> |
| |
| #include "iioservice/include/common.h" |
| |
| namespace iioservice { |
| |
| namespace { |
| |
| constexpr base::TimeDelta kMetricsHourlyTimeOnlineSamplePeriod = |
| base::TimeDelta::FromHours(1); |
| |
| constexpr int kFrequencyThresholds[] = {0, 10, 50, 100}; |
| |
| // UMA metric names: |
| |
| // Device Type and Location |
| constexpr char kSensorUsage[] = "ChromeOS.IioService.SensorUsage.%iHz"; |
| |
| constexpr char kSensorObserver[] = "ChromeOS.IioService.SensorObserver"; |
| constexpr char kSensorObserverOpen[] = "ChromeOS.IioService.SensorObserverOpen"; |
| constexpr char kSensorClientConcurrent[] = |
| "ChromeOS.IioService.SensorClientConcurrent"; |
| |
| // UMA histogram ranges: |
| constexpr int kSensorUsageEnumMax = |
| (static_cast<int>(cros::mojom::DeviceType::kMaxValue) + 1) * |
| static_cast<int>(SensorMetrics::Location::kMax); |
| |
| constexpr int kSensorObserverMax = 20; |
| constexpr int kSensorObserverBuckets = 21; |
| |
| constexpr int kSensorObserverOpenMax = 100; |
| constexpr int kSensorObserverOpenBuckets = 50; |
| |
| constexpr int kSensorClientConcurrentMax = 10; |
| constexpr int kSensorClientConcurrentBuckets = 11; |
| |
| bool SensorTypeSupported(cros::mojom::DeviceType type) { |
| return type != cros::mojom::DeviceType::NONE && |
| type <= cros::mojom::DeviceType::kMaxValue; |
| } |
| |
| SensorMetrics* g_sensor_metrics = nullptr; |
| } // namespace |
| |
| // static |
| void SensorMetrics::Initialize() { |
| if (g_sensor_metrics) { |
| LOGF(WARNING) << "SensorMetrics was already initialized"; |
| return; |
| } |
| SetInstance(new SensorMetrics(std::make_unique<MetricsLibrary>())); |
| } |
| |
| // static |
| void SensorMetrics::Shutdown() { |
| if (!g_sensor_metrics) { |
| LOGF(WARNING) << "SensorMetrics::Shutdown() called with null metrics"; |
| return; |
| } |
| delete g_sensor_metrics; |
| SetInstance(nullptr); |
| } |
| |
| // static |
| SensorMetrics* SensorMetrics::GetInstance() { |
| return g_sensor_metrics; |
| } |
| |
| SensorMetrics::~SensorMetrics() { |
| summarize_timer_.Stop(); |
| } |
| |
| void SensorMetrics::SetConfigForDevice( |
| int iio_device_id, |
| const std::vector<cros::mojom::DeviceType>& types, |
| const std::string& location) { |
| if (device_configs_.find(iio_device_id) != device_configs_.end()) { |
| LOGF(WARNING) << "DeviceConfig already set for device with id: " |
| << iio_device_id; |
| return; |
| } |
| |
| auto& config = device_configs_[iio_device_id]; |
| config.types = types; |
| config.location = FilterLocationString(std::string(base::TrimString( |
| location, base::StringPiece("\0\n", 2), base::TRIM_TRAILING))); |
| } |
| |
| void SensorMetrics::SendSensorUsage(int iio_device_id, double frequency) { |
| DCHECK_GE(frequency, 0.0); |
| auto it = device_configs_.find(iio_device_id); |
| if (it == device_configs_.end()) |
| return; |
| |
| it->second.frequency = frequency; |
| it->second.max_frequency = std::max(it->second.max_frequency, frequency); |
| } |
| |
| void SensorMetrics::SendSensorObserverOpened() { |
| ++enable_sensor_observer_counter_; |
| ++sensor_observer_counter_; |
| max_sensor_observer_counter_ = |
| std::max(max_sensor_observer_counter_, sensor_observer_counter_); |
| } |
| |
| void SensorMetrics::SendSensorObserverClosed() { |
| DCHECK_GT(sensor_observer_counter_, 0); |
| --sensor_observer_counter_; |
| } |
| |
| void SensorMetrics::SendSensorClientConnected() { |
| ++sensor_client_counter_; |
| max_sensor_client_counter_ = |
| std::max(max_sensor_client_counter_, sensor_client_counter_); |
| } |
| |
| void SensorMetrics::SendSensorClientDisconnected() { |
| DCHECK_GT(sensor_client_counter_, 0); |
| --sensor_client_counter_; |
| } |
| |
| // static |
| void SensorMetrics::SetInstance(SensorMetrics* sensor_metrics) { |
| g_sensor_metrics = sensor_metrics; |
| } |
| |
| SensorMetrics::SensorMetrics( |
| std::unique_ptr<MetricsLibraryInterface> metrics_lib) |
| : metrics_lib_(std::move(metrics_lib)) { |
| summarize_timer_.Start(FROM_HERE, kMetricsHourlyTimeOnlineSamplePeriod, |
| base::BindRepeating(&SensorMetrics::SummarizeTime, |
| base::Unretained(this))); |
| } |
| |
| SensorMetrics::Location SensorMetrics::FilterLocationString( |
| std::string location) { |
| if (location == cros::mojom::kLocationBase) |
| return Location::kBase; |
| |
| if (location == cros::mojom::kLocationLid) |
| return Location::kLid; |
| |
| if (location == cros::mojom::kLocationCamera) |
| return Location::kCamera; |
| |
| return Location::kOthers; |
| } |
| |
| void SensorMetrics::SummarizeTime() { |
| for (auto& device_config : device_configs_) { |
| auto& config = device_config.second; |
| |
| for (auto type : config.types) { |
| if (!SensorTypeSupported(type)) |
| continue; |
| |
| int device_enum = |
| (static_cast<int>(type) - 1) * static_cast<int>(Location::kMax) + |
| static_cast<int>(config.location); |
| |
| for (int freq_threshold : kFrequencyThresholds) { |
| if (config.max_frequency < freq_threshold) |
| continue; |
| |
| std::string action_name = |
| base::StringPrintf(kSensorUsage, freq_threshold); |
| metrics_lib_->SendEnumToUMA(action_name, device_enum, |
| kSensorUsageEnumMax); |
| } |
| } |
| |
| config.max_frequency = config.frequency; |
| } |
| |
| metrics_lib_->SendToUMA(kSensorObserver, max_sensor_observer_counter_, 1, |
| kSensorObserverMax, kSensorObserverBuckets); |
| max_sensor_observer_counter_ = sensor_observer_counter_; |
| |
| metrics_lib_->SendToUMA(kSensorObserverOpen, enable_sensor_observer_counter_, |
| 1, kSensorObserverOpenMax, |
| kSensorObserverOpenBuckets); |
| |
| metrics_lib_->SendToUMA(kSensorClientConcurrent, max_sensor_client_counter_, |
| 1, kSensorClientConcurrentMax, |
| kSensorClientConcurrentBuckets); |
| max_sensor_client_counter_ = sensor_client_counter_; |
| } |
| |
| } // namespace iioservice |