// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/metrics/structured/external_metrics.h" #include #include "base/containers/fixed_flat_set.h" #include "base/files/file.h" #include "base/files/file_enumerator.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/threading/scoped_blocking_call.h" #include "base/threading/sequenced_task_runner_handle.h" #include "components/metrics/structured/storage.pb.h" #include "components/metrics/structured/structured_metrics_features.h" namespace metrics { namespace structured { namespace { // TODO(b/181724341): Remove this once the bluetooth metrics are fully enabled. void MaybeFilterBluetoothEvents( google::protobuf::RepeatedPtrField* events) { // Event name hashes of all bluetooth events listed in // src/platform2/metrics/structured/structured.xml. static constexpr auto kBluetoothEventHashes = base::MakeFixedFlatSet({ // BluetoothAdapterStateChanged UINT64_C(959829856916771459), // BluetoothPairingStateChanged UINT64_C(11839023048095184048), // BluetoothAclConnectionStateChanged UINT64_C(1880220404408566268), // BluetoothProfileConnectionStateChanged UINT64_C(7217682640379679663), // BluetoothDeviceInfoReport UINT64_C(1506471670382892394) }); if (base::FeatureList::IsEnabled(kBluetoothSessionizedMetrics)) return; // Remove all bluetooth events. auto it = events->begin(); while (it != events->end()) { if (kBluetoothEventHashes.contains(it->event_name_hash())) { it = events->erase(it); } else { ++it; } } } EventsProto ReadAndDeleteEvents(const base::FilePath& directory) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); EventsProto result; if (!base::DirectoryExists(directory)) return result; base::FileEnumerator enumerator(directory, false, base::FileEnumerator::FILES); for (base::FilePath path = enumerator.Next(); !path.empty(); path = enumerator.Next()) { std::string proto_str; EventsProto proto; bool read_ok = base::ReadFileToString(path, &proto_str) && proto.ParseFromString(proto_str); base::DeleteFile(path); if (!read_ok) continue; // MergeFrom performs a copy that could be a move if done manually. But all // the protos here are expected to be small, so let's keep it simple. result.mutable_uma_events()->MergeFrom(proto.uma_events()); result.mutable_non_uma_events()->MergeFrom(proto.non_uma_events()); } MaybeFilterBluetoothEvents(result.mutable_uma_events()); MaybeFilterBluetoothEvents(result.mutable_non_uma_events()); return result; } } // namespace ExternalMetrics::ExternalMetrics(const base::FilePath& events_directory, const base::TimeDelta& collection_interval, MetricsCollectedCallback callback) : events_directory_(events_directory), collection_interval_(collection_interval), callback_(std::move(callback)), task_runner_(base::ThreadPool::CreateSequencedTaskRunner( {base::TaskPriority::BEST_EFFORT, base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) { ScheduleCollector(); } ExternalMetrics::~ExternalMetrics() = default; void ExternalMetrics::CollectEventsAndReschedule() { CollectEvents(); ScheduleCollector(); } void ExternalMetrics::ScheduleCollector() { base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::BindOnce(&ExternalMetrics::CollectEventsAndReschedule, weak_factory_.GetWeakPtr()), collection_interval_); } void ExternalMetrics::CollectEvents() { task_runner_->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&ReadAndDeleteEvents, events_directory_), base::BindOnce(callback_)); } } // namespace structured } // namespace metrics