// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef COMPONENTS_METRICS_STRUCTURED_STRUCTURED_METRICS_RECORDER_H_ #define COMPONENTS_METRICS_STRUCTURED_STRUCTURED_METRICS_RECORDER_H_ #include #include #include "base/containers/enum_set.h" #include "base/containers/flat_set.h" #include "base/functional/callback_forward.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "components/metrics/metrics_provider.h" #include "components/metrics/structured/event.h" #include "components/metrics/structured/event_storage.h" #include "components/metrics/structured/key_data.h" #include "components/metrics/structured/key_data_provider.h" #include "components/metrics/structured/project_validator.h" #include "components/metrics/structured/recorder.h" namespace metrics::structured { // StructuredMetricsRecorder is responsible for storing and managing the all // Structured Metrics events recorded on-device. This class is not thread safe // and should only be called on the browser UI sequence, because calls from the // metrics service come on the UI sequence. // // This is to be used as a base class for different platform implementations. // The subclass will instantiate the desired KeyDataProvider and EventStorage. // // This class accepts events to record from // StructuredMetricsRecorder::OnRecord via Recorder::Record via // Event::Record. These events are not uploaded immediately, and are cached // in ready-to-upload form. class StructuredMetricsRecorder : public Recorder::RecorderImpl, KeyDataProvider::Observer { public: StructuredMetricsRecorder(std::unique_ptr key_data_provider, std::unique_ptr event_storage); ~StructuredMetricsRecorder() override; StructuredMetricsRecorder(const StructuredMetricsRecorder&) = delete; StructuredMetricsRecorder& operator=(const StructuredMetricsRecorder&) = delete; // Manages whether or not Structured Metrics is recording. // If these functions are overloaded, make sure they are explicitly called in // the overriding function. virtual void EnableRecording(); virtual void DisableRecording(); void Purge(); bool recording_enabled() const { return recording_enabled_; } void ProvideUmaEventMetrics(ChromeUserMetricsExtension& uma_proto); // Provides event metrics stored in the recorder into |uma_proto|. // // This calls OnIndependentMetrics() to populate |uma_proto| with metadata // fields. virtual void ProvideEventMetrics(ChromeUserMetricsExtension& uma_proto); // Returns true if ready to provide metrics via ProvideEventMetrics. bool CanProvideMetrics(); // Returns true if there are metrics to provide. bool HasMetricsToProvide(); // KeyDataProvider::Observer: void OnKeyReady() override; EventStorage* event_storage() { return event_storage_.get(); } KeyDataProvider* key_data_provider() { return key_data_provider_.get(); } protected: friend class TestStructuredMetricsProvider; friend class StructuredMetricsMixin; // Recorder::RecorderImpl: void OnProfileAdded(const base::FilePath& profile_path) override; void OnEventRecord(const Event& event) override; // Different initialization states for the recorder. enum State { kUninitialized, // Set once OnKeyReady has been called once. kKeyDataInitialized, // Set once OnProfileAdded has been called once. kProfileAdded, // Set once the profile key data has been initialized. kProfileKeyDataInitialized, kMaxValue = kProfileKeyDataInitialized, }; // Collection of InitValues that represents the current initialization state // of the recorder. // // For events to be persisted, both kKeyDataInitialized, kEventsInitialized, // and kProfileKeyDataInitialized msut be set for events to be recorded. using InitState = base::EnumSet; bool HasState(State state) const; private: friend class Recorder; friend class StructuredMetricsMixin; friend class StructuredMetricsProviderTest; friend class StructuredMetricsRecorderTest; friend class StructuredMetricsRecorderHwidTest; friend class TestStructuredMetricsRecorder; friend class TestStructuredMetricsProvider; friend class StructuredMetricsServiceTest; // Recorder::RecorderImpl: void OnReportingStateChanged(bool enabled) override; // Records events before IsInitialized(). void RecordEventBeforeInitialization(const Event& event); // Records events before IsProfileInitialized(). void RecordProfileEventBeforeInitialization(const Event& event); // Records |event| to persistent disk to be eventually sent. void RecordEvent(const Event& event); // Sets the event and project fields and the identification fields. void InitializeEventProto(StructuredEventProto* proto, const Event& event, const ProjectValidator& project_validator, const EventValidator& event_validator); // Processes the events metric to proto format. void AddMetricsToProto(StructuredEventProto* proto, const Event& event, const ProjectValidator& project_validator, const EventValidator& validator); // Adds sequence metadata to the event. virtual void AddSequenceMetadata(StructuredEventProto* proto, const Event& event, const ProjectValidator& project_validator, const KeyData& key_data) {} // Populates system profile needed for Structured Metrics. // Independent metric uploads will rely on a SystemProfileProvider // to supply the system profile since ChromeOSMetricsProvider will // not be called to populate the SystemProfile. void ProvideSystemProfile(SystemProfileProto* system_profile); // Hashes events and persists the events to disk. Should be called once |this| // has been initialized. void HashUnhashedEventsAndPersist(); // Checks if |project_name_hash| can be uploaded. bool CanUploadProject(uint64_t project_name_hash) const; // Builds a cache of disallow projects from the Finch controlled variable. void CacheDisallowedProjectsSet(); // Returns true if key data is ready to use. bool IsKeyDataInitialized(); // Returns true if ready to record events. bool IsInitialized(); // Returns true if ready to record profile events. bool IsProfileInitialized(); // Returns whether the |event| can be recorded event if metrics is opted-out. // Note that uploading is still guarded by metrics opt-in state and that these // events will never be uploaded. In the event that a user opts-in, these // events will be purged. bool CanForceRecord(const Event& event) const; // Helper functions to determine scope of the event. bool IsDeviceEvent(const Event& event) const; bool IsProfileEvent(const Event& event) const; // Helper function to get the validators for |event|. absl::optional> GetEventValidators(const Event& event) const; void SetOnReadyToRecord(base::OnceClosure callback); // Sets a callback to be made every time an event is recorded. This is exposed // so that tests can check if a specific event is recorded since recording // happens asynchronously. void SetEventRecordCallbackForTest(base::RepeatingClosure callback); // Adds a project to the diallowed list for testing. void AddDisallowedProjectForTest(uint64_t project_name_hash); protected: // Key data provider that provides device and profile keys. std::unique_ptr key_data_provider_; // Storage for events while on device. std::unique_ptr event_storage_; // Whether the metrics provider has completed initialization. Initialization // occurs across OnProfileAdded and OnKeyReady. No incoming // events are recorded until initialization has succeeded. // // Execution is: // - A profile is added. // - OnProfileAdded is called, which constructs |storage_| and // asynchronously reads events and keys are loaded. // // The metrics provider does not handle multiprofile: initialization happens // only once, for the first-logged-in account aka. primary user. // // After a profile is added, two files need to be read from disk: // per-profile keys and per-device keys. |init_count_| tracks // how many of these have been read and, when it reaches 2, we set // |init_state_| to kInitialized. InitState init_state_; private: // Tracks the recording state signalled to the metrics provider by // OnRecordingEnabled and OnRecordingDisabled. This is false until // OnRecordingEnabled is called, which sets it true if structured metrics' // feature flag is enabled. bool recording_enabled_ = false; // Set by OnReportingStateChanged if all keys and events should be deleted, // but the files backing that state haven't been initialized yet. If set, // state will be purged upon initialization. bool purge_state_on_init_ = false; // Store for events that were recorded before keys are loaded. std::deque unhashed_events_; // Store for events that were recorded before profile keys are loaded. std::deque unhashed_profile_events_; // A set of projects that are not allowed to be recorded. This is a cache of // GetDisabledProjects(). base::flat_set disallowed_projects_; // Callbacks for tests whenever an event is recorded. base::RepeatingClosure test_callback_on_record_ = base::DoNothing(); // Callback to be made once recorder is ready to persist events to disk. base::OnceClosure on_ready_callback_ = base::DoNothing(); base::WeakPtrFactory weak_factory_{this}; }; } // namespace metrics::structured #endif // COMPONENTS_METRICS_STRUCTURED_STRUCTURED_METRICS_RECORDER_H_