1 // Copyright 2019 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_METRICS_STRUCTURED_STRUCTURED_METRICS_PROVIDER_H_ 6 #define COMPONENTS_METRICS_STRUCTURED_STRUCTURED_METRICS_PROVIDER_H_ 7 8 #include <deque> 9 #include <memory> 10 11 #include "base/containers/flat_set.h" 12 #include "base/files/file_path.h" 13 #include "base/memory/weak_ptr.h" 14 #include "base/time/time.h" 15 #include "components/metrics/metrics_provider.h" 16 #include "components/metrics/structured/event.h" 17 #include "components/metrics/structured/key_data.h" 18 #include "components/metrics/structured/project_validator.h" 19 #include "components/metrics/structured/recorder.h" 20 21 namespace metrics { 22 namespace structured { 23 24 class EventsProto; 25 class ExternalMetrics; 26 27 // StructuredMetricsProvider is responsible for filling out the 28 // |structured_metrics_event| section of the UMA proto. This class should not be 29 // instantiated except by the ChromeMetricsServiceClient. This class is not 30 // thread safe and should only be called on the browser UI sequence, because 31 // calls from the metrics service come on the UI sequence. 32 // 33 // Initialization of the StructuredMetricsProvider must wait until a profile is 34 // added, because state is stored within the profile directory. Initialization 35 // happens in several steps: 36 // 37 // 1. A StructuredMetricsProvider instance is constructed and owned by the 38 // MetricsService. It registers itself as an observer of 39 // metrics::structured::Recorder. 40 // 41 // 2. When a profile is added that is eligible for recording, 42 // ChromeMetricsServiceClient calls Recorder::ProfileAdded, which notifies 43 // this class. 44 // 45 // 3. This class then begins initialization by asynchronously reading keys and 46 // unsent logs from the cryptohome. 47 // 48 // 4. If the read succeeds, initialization is complete and this class starts 49 // accepting events to record. 50 // 51 // After initialization, this class accepts events to record from 52 // StructuredMetricsProvider::OnRecord via Recorder::Record via 53 // Event::Record. These events are not uploaded immediately, and are cached 54 // in ready-to-upload form. 55 // 56 // On a call to ProvideCurrentSessionData, the cache of unsent logs is added to 57 // a ChromeUserMetricsExtension for upload, and is then cleared. 58 class StructuredMetricsProvider : public metrics::MetricsProvider, 59 public Recorder::RecorderImpl { 60 public: 61 explicit StructuredMetricsProvider( 62 metrics::MetricsProvider* system_profile_provider); 63 ~StructuredMetricsProvider() override; 64 StructuredMetricsProvider(const StructuredMetricsProvider&) = delete; 65 StructuredMetricsProvider& operator=(const StructuredMetricsProvider&) = 66 delete; 67 68 private: 69 friend class Recorder; 70 friend class StructuredMetricsProviderTest; 71 friend class StructuredMetricsProviderHwidTest; 72 friend class TestStructuredMetricsProvider; 73 74 // State machine for step 4 of initialization. These are stored in three files 75 // that are asynchronously read from disk at startup. When all files have 76 // been read, the provider has been initialized. 77 enum class InitState { 78 kUninitialized = 1, 79 // Set after we observe the recorder, which happens on construction. 80 kProfileAdded = 2, 81 // Set after all key and event files are read from disk. 82 kInitialized = 3, 83 }; 84 85 // Should only be used for tests. 86 // 87 // TODO(crbug/1350322): Use this ctor to replace existing ctor. 88 StructuredMetricsProvider(const base::FilePath& device_key_path, 89 base::TimeDelta write_delay, 90 base::TimeDelta min_independent_metrics_interval, 91 metrics::MetricsProvider* system_profile_provider); 92 93 void OnKeyDataInitialized(); 94 void OnRead(ReadStatus status); 95 void OnWrite(WriteStatus status); 96 void OnExternalMetricsCollected(const EventsProto& events); 97 void Purge(); 98 99 // Recorder::RecorderImpl: 100 void OnProfileAdded(const base::FilePath& profile_path) override; 101 void OnEventRecord(const Event& event) override; 102 void OnReportingStateChanged(bool enabled) override; 103 void OnSystemProfileInitialized() override; 104 absl::optional<int> LastKeyRotation(uint64_t project_name_hash) override; 105 106 // metrics::MetricsProvider: 107 void OnRecordingEnabled() override; 108 void OnRecordingDisabled() override; 109 void ProvideCurrentSessionData( 110 metrics::ChromeUserMetricsExtension* uma_proto) override; 111 bool HasIndependentMetrics() override; 112 void ProvideIndependentMetrics(base::OnceCallback<void(bool)> done_callback, 113 ChromeUserMetricsExtension* uma_proto, 114 base::HistogramSnapshotManager*) override; 115 116 void WriteNowForTest(); 117 void SetExternalMetricsDirForTest(const base::FilePath& dir); 118 119 // Records events before |init_state_| is kInitialized. 120 void RecordEventBeforeInitialization(const Event& event); 121 122 // Records |event| to persistent disk to be eventually sent. 123 void RecordEvent(const Event& event); 124 125 // Hashes events and persists the events to disk. Should be called once |this| 126 // has been initialized. 127 void HashUnhashedEventsAndPersist(); 128 129 // Populates system profile needed for Structured Metrics. 130 // Independent metric uploads will rely on a SystemProfileProvider 131 // to supply the system profile since ChromeOSMetricsProvider will 132 // not be called to populate the SystemProfile. 133 void ProvideSystemProfile(SystemProfileProto* system_profile); 134 135 // Checks if |project_name_hash| can be uploaded. 136 bool CanUploadProject(uint64_t project_name_hash) const; 137 138 // Builds a cache of disallow projects from the Finch controlled variable. 139 void CacheDisallowedProjectsSet(); 140 141 // Adds a project to the diallowed list for testing. 142 void AddDisallowedProjectForTest(uint64_t project_name_hash); 143 144 // Beyond this number of logging events between successive calls to 145 // ProvideCurrentSessionData, we stop recording events. 146 static int kMaxEventsPerUpload; 147 148 // The path used to store per-profile keys. Relative to the user's 149 // cryptohome. This file is created by chromium. 150 static char kProfileKeyDataPath[]; 151 152 // The path used to store per-device keys. This file is created by tmpfiles.d 153 // on start and has its permissions and ownership set such that it is writable 154 // by chronos. 155 static char kDeviceKeyDataPath[]; 156 157 // The directory used to store unsent logs. Relative to the user's cryptohome. 158 // This file is created by chromium. 159 static char kUnsentLogsPath[]; 160 161 // Whether the metrics provider has completed initialization. Initialization 162 // occurs across OnProfileAdded and OnInitializationCompleted. No incoming 163 // events are recorded until initialization has succeeded. 164 // 165 // Execution is: 166 // - A profile is added. 167 // - OnProfileAdded is called, which constructs |storage_| and 168 // asynchronously reads events and keys. 169 // - OnInitializationCompleted is called once reading from disk is complete, 170 // which sets |init_count_| to kInitialized. 171 // 172 // The metrics provider does not handle multiprofile: initialization happens 173 // only once, for the first-logged-in account aka. primary user. 174 // 175 // After a profile is added, three files need to be read from disk: 176 // per-profile keys, per-device keys, and unsent events. |init_count_| tracks 177 // how many of these have been read and, when it reaches 3, we set 178 // |init_state_| to kInitialized. 179 InitState init_state_ = InitState::kUninitialized; 180 int init_count_ = 0; 181 static constexpr int kTargetInitCount = 3; 182 183 // Tracks the recording state signalled to the metrics provider by 184 // OnRecordingEnabled and OnRecordingDisabled. This is false until 185 // OnRecordingEnabled is called, which sets it true if structured metrics' 186 // feature flag is enabled. 187 bool recording_enabled_ = false; 188 189 // Set by OnReportingStateChanged if all keys and events should be deleted, 190 // but the files backing that state haven't been initialized yet. If set, 191 // state will be purged upon initialization. 192 bool purge_state_on_init_ = false; 193 194 // The last time we provided independent metrics. 195 base::Time last_provided_independent_metrics_; 196 197 // Periodically reports metrics from cros. 198 std::unique_ptr<ExternalMetrics> external_metrics_; 199 200 // On-device storage within the user's cryptohome for unsent logs. 201 std::unique_ptr<PersistentProto<EventsProto>> events_; 202 203 // Store for events that were recorded before user/device keys are loaded. 204 std::deque<Event> unhashed_events_; 205 206 // Storage for all event's keys, and hashing logic for values. This stores 207 // keys on disk. |profile_key_data_| stores keys for per-profile projects, 208 // and |device_key_data_| stores keys for per-device projects. 209 std::unique_ptr<KeyData> profile_key_data_; 210 std::unique_ptr<KeyData> device_key_data_; 211 212 // Whether the system profile has been initialized. 213 bool system_profile_initialized_ = false; 214 215 // File path where device keys will be persisted. 216 const base::FilePath device_key_path_; 217 218 // Delay period for PersistentProto writes. Default value of 1000 ms used if 219 // not specified in ctor. 220 base::TimeDelta write_delay_; 221 222 // The minimum waiting time between successive deliveries of independent 223 // metrics to the metrics service via ProvideIndependentMetrics. This is set 224 // carefully: metrics logs are stored in a queue of limited size, and are 225 // uploaded roughly every 30 minutes. 226 // 227 // If this value is 0, then there will be no waiting time and events will be 228 // available on every ProvideIndependentMetrics. 229 base::TimeDelta min_independent_metrics_interval_; 230 231 // Interface for providing the SystemProfile to metrics. 232 // See chrome/browser/metrics/chrome_metrics_service_client.h 233 base::raw_ptr<metrics::MetricsProvider> system_profile_provider_; 234 235 // A set of projects that are not allowed to be recorded. This is a cache of 236 // GetDisabledProjects(). 237 base::flat_set<uint64_t> disallowed_projects_; 238 239 // The number of scans of external metrics that occurred since the last 240 // upload. This is only incremented if events were added by the scan. 241 int external_metrics_scans_ = 0; 242 243 base::WeakPtrFactory<StructuredMetricsProvider> weak_factory_{this}; 244 }; 245 246 } // namespace structured 247 } // namespace metrics 248 249 #endif // COMPONENTS_METRICS_STRUCTURED_STRUCTURED_METRICS_PROVIDER_H_ 250