| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/ukm/ukm_service.h" | 5 #include "components/ukm/ukm_service.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/feature_list.h" | 12 #include "base/feature_list.h" |
| 13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/metrics/field_trial.h" | 14 #include "base/metrics/field_trial.h" |
| 15 #include "base/metrics/field_trial_params.h" | 15 #include "base/metrics/field_trial_params.h" |
| 16 #include "base/metrics/histogram_macros.h" | 16 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/metrics/metrics_hashes.h" | 17 #include "base/metrics/metrics_hashes.h" |
| 18 #include "base/rand_util.h" | 18 #include "base/rand_util.h" |
| 19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/string_split.h" | 20 #include "base/strings/string_split.h" |
| 21 #include "base/time/time.h" | 21 #include "base/time/time.h" |
| 22 #include "components/metrics/metrics_log.h" | 22 #include "components/metrics/metrics_log.h" |
| 23 #include "components/metrics/metrics_log_uploader.h" | 23 #include "components/metrics/metrics_log_uploader.h" |
| 24 #include "components/metrics/metrics_service_client.h" | 24 #include "components/metrics/metrics_service_client.h" |
| 25 #include "components/metrics/proto/ukm/entry.pb.h" | 25 #include "components/metrics/proto/ukm/entry.pb.h" |
| 26 #include "components/metrics/proto/ukm/report.pb.h" | 26 #include "components/metrics/proto/ukm/report.pb.h" |
| 27 #include "components/metrics/proto/ukm/source.pb.h" | 27 #include "components/metrics/proto/ukm/source.pb.h" |
| 28 #include "components/prefs/pref_registry_simple.h" | 28 #include "components/prefs/pref_registry_simple.h" |
| 29 #include "components/prefs/pref_service.h" | 29 #include "components/prefs/pref_service.h" |
| 30 #include "components/ukm/metrics_reporting_scheduler.h" | |
| 31 #include "components/ukm/persisted_logs_metrics_impl.h" | 30 #include "components/ukm/persisted_logs_metrics_impl.h" |
| 32 #include "components/ukm/ukm_entry.h" | 31 #include "components/ukm/ukm_entry.h" |
| 33 #include "components/ukm/ukm_entry_builder.h" | 32 #include "components/ukm/ukm_entry_builder.h" |
| 34 #include "components/ukm/ukm_pref_names.h" | 33 #include "components/ukm/ukm_pref_names.h" |
| 34 #include "components/ukm/ukm_rotation_scheduler.h" |
| 35 #include "components/ukm/ukm_source.h" | 35 #include "components/ukm/ukm_source.h" |
| 36 | 36 |
| 37 namespace ukm { | 37 namespace ukm { |
| 38 | 38 |
| 39 namespace { | 39 namespace { |
| 40 | 40 |
| 41 constexpr char kMimeType[] = "application/vnd.chrome.ukm"; | |
| 42 | |
| 43 // The delay, in seconds, after starting recording before doing expensive | 41 // The delay, in seconds, after starting recording before doing expensive |
| 44 // initialization work. | 42 // initialization work. |
| 45 constexpr int kInitializationDelaySeconds = 5; | 43 constexpr int kInitializationDelaySeconds = 5; |
| 46 | 44 |
| 47 // The number of UKM logs that will be stored in PersistedLogs before logs | |
| 48 // start being dropped. | |
| 49 constexpr int kMinPersistedLogs = 8; | |
| 50 | |
| 51 // The number of bytes UKM logs that will be stored in PersistedLogs before | |
| 52 // logs start being dropped. | |
| 53 // This ensures that a reasonable amount of history will be stored even if there | |
| 54 // is a long series of very small logs. | |
| 55 constexpr int kMinPersistedBytes = 300000; | |
| 56 | |
| 57 // If an upload fails, and the transmission was over this byte count, then we | |
| 58 // will discard the log, and not try to retransmit it. We also don't persist | |
| 59 // the log to the prefs for transmission during the next chrome session if this | |
| 60 // limit is exceeded. | |
| 61 constexpr size_t kMaxLogRetransmitSize = 100 * 1024; | |
| 62 | |
| 63 // Gets the UKM Server URL. | |
| 64 std::string GetServerUrl() { | |
| 65 constexpr char kDefaultServerUrl[] = "https://clients4.google.com/ukm"; | |
| 66 std::string server_url = | |
| 67 base::GetFieldTrialParamValueByFeature(kUkmFeature, "ServerUrl"); | |
| 68 if (!server_url.empty()) | |
| 69 return server_url; | |
| 70 return kDefaultServerUrl; | |
| 71 } | |
| 72 | |
| 73 // Gets the list of whitelisted Entries as string. Format is a comma seperated | 45 // Gets the list of whitelisted Entries as string. Format is a comma seperated |
| 74 // list of Entry names (as strings). | 46 // list of Entry names (as strings). |
| 75 std::string GetWhitelistEntries() { | 47 std::string GetWhitelistEntries() { |
| 76 return base::GetFieldTrialParamValueByFeature(kUkmFeature, | 48 return base::GetFieldTrialParamValueByFeature(kUkmFeature, |
| 77 "WhitelistEntries"); | 49 "WhitelistEntries"); |
| 78 } | 50 } |
| 79 | 51 |
| 80 // Gets the maximum number of Sources we'll keep in memory before discarding any | 52 // Gets the maximum number of Sources we'll keep in memory before discarding any |
| 81 // new ones being added. | 53 // new ones being added. |
| 82 size_t GetMaxSources() { | 54 size_t GetMaxSources() { |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 155 | 127 |
| 156 const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT}; | 128 const base::Feature kUkmFeature = {"Ukm", base::FEATURE_DISABLED_BY_DEFAULT}; |
| 157 | 129 |
| 158 UkmService::UkmService(PrefService* pref_service, | 130 UkmService::UkmService(PrefService* pref_service, |
| 159 metrics::MetricsServiceClient* client) | 131 metrics::MetricsServiceClient* client) |
| 160 : pref_service_(pref_service), | 132 : pref_service_(pref_service), |
| 161 recording_enabled_(false), | 133 recording_enabled_(false), |
| 162 client_id_(0), | 134 client_id_(0), |
| 163 session_id_(0), | 135 session_id_(0), |
| 164 client_(client), | 136 client_(client), |
| 165 persisted_logs_(std::unique_ptr<ukm::PersistedLogsMetricsImpl>( | 137 reporting_service_(client, pref_service), |
| 166 new ukm::PersistedLogsMetricsImpl()), | |
| 167 pref_service, | |
| 168 prefs::kUkmPersistedLogs, | |
| 169 kMinPersistedLogs, | |
| 170 kMinPersistedBytes, | |
| 171 kMaxLogRetransmitSize), | |
| 172 initialize_started_(false), | 138 initialize_started_(false), |
| 173 initialize_complete_(false), | 139 initialize_complete_(false), |
| 174 log_upload_in_progress_(false), | |
| 175 self_ptr_factory_(this) { | 140 self_ptr_factory_(this) { |
| 176 DCHECK(pref_service_); | 141 DCHECK(pref_service_); |
| 177 DCHECK(client_); | 142 DCHECK(client_); |
| 178 DVLOG(1) << "UkmService::Constructor"; | 143 DVLOG(1) << "UkmService::Constructor"; |
| 179 | 144 |
| 180 persisted_logs_.LoadPersistedUnsentLogs(); | 145 reporting_service_.Initialize(); |
| 181 | 146 |
| 182 base::Closure rotate_callback = | 147 base::Closure rotate_callback = |
| 183 base::Bind(&UkmService::RotateLog, self_ptr_factory_.GetWeakPtr()); | 148 base::Bind(&UkmService::RotateLog, self_ptr_factory_.GetWeakPtr()); |
| 184 // MetricsServiceClient outlives UkmService, and | 149 // MetricsServiceClient outlives UkmService, and |
| 185 // MetricsReportingScheduler is tied to the lifetime of |this|. | 150 // MetricsReportingScheduler is tied to the lifetime of |this|. |
| 186 const base::Callback<base::TimeDelta(void)>& get_upload_interval_callback = | 151 const base::Callback<base::TimeDelta(void)>& get_upload_interval_callback = |
| 187 base::Bind(&metrics::MetricsServiceClient::GetStandardUploadInterval, | 152 base::Bind(&metrics::MetricsServiceClient::GetStandardUploadInterval, |
| 188 base::Unretained(client_)); | 153 base::Unretained(client_)); |
| 189 scheduler_.reset(new ukm::MetricsReportingScheduler( | 154 scheduler_.reset(new ukm::UkmRotationScheduler(rotate_callback, |
| 190 rotate_callback, get_upload_interval_callback)); | 155 get_upload_interval_callback)); |
| 191 | 156 |
| 192 for (auto& provider : metrics_providers_) | 157 for (auto& provider : metrics_providers_) |
| 193 provider->Init(); | 158 provider->Init(); |
| 194 | 159 |
| 195 StoreWhitelistedEntries(); | 160 StoreWhitelistedEntries(); |
| 196 } | 161 } |
| 197 | 162 |
| 198 UkmService::~UkmService() { | 163 UkmService::~UkmService() { |
| 199 DisableReporting(); | 164 DisableReporting(); |
| 200 } | 165 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 215 recording_enabled_ = true; | 180 recording_enabled_ = true; |
| 216 } | 181 } |
| 217 | 182 |
| 218 void UkmService::DisableRecording() { | 183 void UkmService::DisableRecording() { |
| 219 recording_enabled_ = false; | 184 recording_enabled_ = false; |
| 220 } | 185 } |
| 221 | 186 |
| 222 void UkmService::EnableReporting() { | 187 void UkmService::EnableReporting() { |
| 223 DCHECK(thread_checker_.CalledOnValidThread()); | 188 DCHECK(thread_checker_.CalledOnValidThread()); |
| 224 DVLOG(1) << "UkmService::EnableReporting"; | 189 DVLOG(1) << "UkmService::EnableReporting"; |
| 190 if (reporting_service_.reporting_active()) |
| 191 return; |
| 225 | 192 |
| 226 for (auto& provider : metrics_providers_) | 193 for (auto& provider : metrics_providers_) |
| 227 provider->OnRecordingEnabled(); | 194 provider->OnRecordingEnabled(); |
| 228 | 195 |
| 229 if (!initialize_started_) | 196 if (!initialize_started_) |
| 230 Initialize(); | 197 Initialize(); |
| 231 scheduler_->Start(); | 198 scheduler_->Start(); |
| 199 reporting_service_.EnableReporting(); |
| 232 } | 200 } |
| 233 | 201 |
| 234 void UkmService::DisableReporting() { | 202 void UkmService::DisableReporting() { |
| 235 DCHECK(thread_checker_.CalledOnValidThread()); | 203 DCHECK(thread_checker_.CalledOnValidThread()); |
| 236 DVLOG(1) << "UkmService::DisableReporting"; | 204 DVLOG(1) << "UkmService::DisableReporting"; |
| 237 | 205 |
| 206 reporting_service_.DisableReporting(); |
| 207 |
| 238 for (auto& provider : metrics_providers_) | 208 for (auto& provider : metrics_providers_) |
| 239 provider->OnRecordingDisabled(); | 209 provider->OnRecordingDisabled(); |
| 240 | 210 |
| 241 scheduler_->Stop(); | 211 scheduler_->Stop(); |
| 242 Flush(); | 212 Flush(); |
| 243 } | 213 } |
| 244 | 214 |
| 245 #if defined(OS_ANDROID) || defined(OS_IOS) | 215 #if defined(OS_ANDROID) || defined(OS_IOS) |
| 246 void UkmService::OnAppEnterForeground() { | 216 void UkmService::OnAppEnterForeground() { |
| 247 DCHECK(thread_checker_.CalledOnValidThread()); | 217 DCHECK(thread_checker_.CalledOnValidThread()); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 269 provider->OnAppEnterBackground(); | 239 provider->OnAppEnterBackground(); |
| 270 | 240 |
| 271 Flush(); | 241 Flush(); |
| 272 } | 242 } |
| 273 #endif | 243 #endif |
| 274 | 244 |
| 275 void UkmService::Flush() { | 245 void UkmService::Flush() { |
| 276 DCHECK(thread_checker_.CalledOnValidThread()); | 246 DCHECK(thread_checker_.CalledOnValidThread()); |
| 277 if (initialize_complete_) | 247 if (initialize_complete_) |
| 278 BuildAndStoreLog(); | 248 BuildAndStoreLog(); |
| 279 persisted_logs_.PersistUnsentLogs(); | 249 reporting_service_.ukm_log_store()->PersistUnsentLogs(); |
| 280 } | 250 } |
| 281 | 251 |
| 282 void UkmService::Purge() { | 252 void UkmService::Purge() { |
| 283 DCHECK(thread_checker_.CalledOnValidThread()); | 253 DCHECK(thread_checker_.CalledOnValidThread()); |
| 284 DVLOG(1) << "UkmService::Purge"; | 254 DVLOG(1) << "UkmService::Purge"; |
| 285 | 255 reporting_service_.ukm_log_store()->Purge(); |
| 286 persisted_logs_.Purge(); | |
| 287 sources_.clear(); | 256 sources_.clear(); |
| 288 entries_.clear(); | 257 entries_.clear(); |
| 289 } | 258 } |
| 290 | 259 |
| 291 // TODO(bmcquade): rename this to something more generic, like | 260 // TODO(bmcquade): rename this to something more generic, like |
| 292 // ResetClientState. Consider resetting all prefs here. | 261 // ResetClientState. Consider resetting all prefs here. |
| 293 void UkmService::ResetClientId() { | 262 void UkmService::ResetClientId() { |
| 294 client_id_ = GenerateClientId(pref_service_); | 263 client_id_ = GenerateClientId(pref_service_); |
| 295 session_id_ = LoadSessionId(pref_service_); | 264 session_id_ = LoadSessionId(pref_service_); |
| 296 } | 265 } |
| 297 | 266 |
| 298 void UkmService::RegisterMetricsProvider( | 267 void UkmService::RegisterMetricsProvider( |
| 299 std::unique_ptr<metrics::MetricsProvider> provider) { | 268 std::unique_ptr<metrics::MetricsProvider> provider) { |
| 300 metrics_providers_.push_back(std::move(provider)); | 269 metrics_providers_.push_back(std::move(provider)); |
| 301 } | 270 } |
| 302 | 271 |
| 303 // static | 272 // static |
| 304 void UkmService::RegisterPrefs(PrefRegistrySimple* registry) { | 273 void UkmService::RegisterPrefs(PrefRegistrySimple* registry) { |
| 305 registry->RegisterInt64Pref(prefs::kUkmClientId, 0); | 274 registry->RegisterInt64Pref(prefs::kUkmClientId, 0); |
| 306 registry->RegisterIntegerPref(prefs::kUkmSessionId, 0); | 275 registry->RegisterIntegerPref(prefs::kUkmSessionId, 0); |
| 307 registry->RegisterListPref(prefs::kUkmPersistedLogs); | 276 UkmReportingService::RegisterPrefs(registry); |
| 308 } | 277 } |
| 309 | 278 |
| 310 void UkmService::StartInitTask() { | 279 void UkmService::StartInitTask() { |
| 311 DCHECK(thread_checker_.CalledOnValidThread()); | 280 DCHECK(thread_checker_.CalledOnValidThread()); |
| 312 DVLOG(1) << "UkmService::StartInitTask"; | 281 DVLOG(1) << "UkmService::StartInitTask"; |
| 313 client_id_ = LoadOrGenerateClientId(pref_service_); | 282 client_id_ = LoadOrGenerateClientId(pref_service_); |
| 314 session_id_ = LoadSessionId(pref_service_); | 283 session_id_ = LoadSessionId(pref_service_); |
| 315 client_->InitializeSystemProfileMetrics(base::Bind( | 284 client_->InitializeSystemProfileMetrics(base::Bind( |
| 316 &UkmService::FinishedInitTask, self_ptr_factory_.GetWeakPtr())); | 285 &UkmService::FinishedInitTask, self_ptr_factory_.GetWeakPtr())); |
| 317 } | 286 } |
| 318 | 287 |
| 319 void UkmService::FinishedInitTask() { | 288 void UkmService::FinishedInitTask() { |
| 320 DCHECK(thread_checker_.CalledOnValidThread()); | 289 DCHECK(thread_checker_.CalledOnValidThread()); |
| 321 DVLOG(1) << "UkmService::FinishedInitTask"; | 290 DVLOG(1) << "UkmService::FinishedInitTask"; |
| 322 initialize_complete_ = true; | 291 initialize_complete_ = true; |
| 323 scheduler_->InitTaskComplete(); | 292 scheduler_->InitTaskComplete(); |
| 324 } | 293 } |
| 325 | 294 |
| 326 void UkmService::RotateLog() { | 295 void UkmService::RotateLog() { |
| 327 DCHECK(thread_checker_.CalledOnValidThread()); | 296 DCHECK(thread_checker_.CalledOnValidThread()); |
| 328 DCHECK(!log_upload_in_progress_); | |
| 329 DVLOG(1) << "UkmService::RotateLog"; | 297 DVLOG(1) << "UkmService::RotateLog"; |
| 330 if (!persisted_logs_.has_unsent_logs()) | 298 if (!reporting_service_.ukm_log_store()->has_unsent_logs()) |
| 331 BuildAndStoreLog(); | 299 BuildAndStoreLog(); |
| 332 StartScheduledUpload(); | 300 reporting_service_.Start(); |
| 333 } | 301 } |
| 334 | 302 |
| 335 void UkmService::BuildAndStoreLog() { | 303 void UkmService::BuildAndStoreLog() { |
| 336 DCHECK(thread_checker_.CalledOnValidThread()); | 304 DCHECK(thread_checker_.CalledOnValidThread()); |
| 337 DVLOG(1) << "UkmService::BuildAndStoreLog"; | 305 DVLOG(1) << "UkmService::BuildAndStoreLog"; |
| 338 | 306 |
| 339 // Suppress generating a log if we have no new data to include. | 307 // Suppress generating a log if we have no new data to include. |
| 340 // TODO(zhenw): add a histogram here to debug if this case is hitting a lot. | 308 // TODO(zhenw): add a histogram here to debug if this case is hitting a lot. |
| 341 if (sources_.empty() && entries_.empty()) | 309 if (sources_.empty() && entries_.empty()) |
| 342 return; | 310 return; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 364 | 332 |
| 365 metrics::MetricsLog::RecordCoreSystemProfile(client_, | 333 metrics::MetricsLog::RecordCoreSystemProfile(client_, |
| 366 report.mutable_system_profile()); | 334 report.mutable_system_profile()); |
| 367 | 335 |
| 368 for (auto& provider : metrics_providers_) { | 336 for (auto& provider : metrics_providers_) { |
| 369 provider->ProvideSystemProfileMetrics(report.mutable_system_profile()); | 337 provider->ProvideSystemProfileMetrics(report.mutable_system_profile()); |
| 370 } | 338 } |
| 371 | 339 |
| 372 std::string serialized_log; | 340 std::string serialized_log; |
| 373 report.SerializeToString(&serialized_log); | 341 report.SerializeToString(&serialized_log); |
| 374 persisted_logs_.StoreLog(serialized_log); | 342 reporting_service_.ukm_log_store()->StoreLog(serialized_log); |
| 375 } | |
| 376 | |
| 377 void UkmService::StartScheduledUpload() { | |
| 378 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 379 DCHECK(!log_upload_in_progress_); | |
| 380 if (!persisted_logs_.has_unsent_logs()) { | |
| 381 // No logs to send, so early out and schedule the next rotation. | |
| 382 scheduler_->UploadFinished(true, /* has_unsent_logs */ false); | |
| 383 return; | |
| 384 } | |
| 385 if (!persisted_logs_.has_staged_log()) | |
| 386 persisted_logs_.StageNextLog(); | |
| 387 // TODO(holte): Handle data usage on cellular, etc. | |
| 388 if (!log_uploader_) { | |
| 389 log_uploader_ = client_->CreateUploader( | |
| 390 GetServerUrl(), kMimeType, metrics::MetricsLogUploader::UKM, | |
| 391 base::Bind(&UkmService::OnLogUploadComplete, | |
| 392 self_ptr_factory_.GetWeakPtr())); | |
| 393 } | |
| 394 log_upload_in_progress_ = true; | |
| 395 | |
| 396 const std::string hash = | |
| 397 base::HexEncode(persisted_logs_.staged_log_hash().data(), | |
| 398 persisted_logs_.staged_log_hash().size()); | |
| 399 log_uploader_->UploadLog(persisted_logs_.staged_log(), hash); | |
| 400 } | |
| 401 | |
| 402 void UkmService::OnLogUploadComplete(int response_code) { | |
| 403 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 404 DCHECK(log_upload_in_progress_); | |
| 405 DVLOG(1) << "UkmService::OnLogUploadComplete"; | |
| 406 log_upload_in_progress_ = false; | |
| 407 | |
| 408 UMA_HISTOGRAM_SPARSE_SLOWLY("UKM.Upload.ResponseCode", response_code); | |
| 409 | |
| 410 bool upload_succeeded = response_code == 200; | |
| 411 | |
| 412 // Staged log may have been deleted by Purge already, otherwise we may | |
| 413 // remove it from the log store here. | |
| 414 if (persisted_logs_.has_staged_log()) { | |
| 415 // Provide boolean for error recovery (allow us to ignore response_code). | |
| 416 bool discard_log = false; | |
| 417 const size_t log_size_bytes = persisted_logs_.staged_log().length(); | |
| 418 if (upload_succeeded) { | |
| 419 UMA_HISTOGRAM_COUNTS_10000("UKM.LogSize.OnSuccess", | |
| 420 log_size_bytes / 1024); | |
| 421 } else if (response_code == 400) { | |
| 422 // Bad syntax. Retransmission won't work. | |
| 423 discard_log = true; | |
| 424 } | |
| 425 | |
| 426 if (upload_succeeded || discard_log) { | |
| 427 persisted_logs_.DiscardStagedLog(); | |
| 428 // Store the updated list to disk now that the removed log is uploaded. | |
| 429 persisted_logs_.PersistUnsentLogs(); | |
| 430 } | |
| 431 } | |
| 432 | |
| 433 // Error 400 indicates a problem with the log, not with the server, so | |
| 434 // don't consider that a sign that the server is in trouble. | |
| 435 bool server_is_healthy = upload_succeeded || response_code == 400; | |
| 436 scheduler_->UploadFinished(server_is_healthy, | |
| 437 persisted_logs_.has_unsent_logs()); | |
| 438 } | 343 } |
| 439 | 344 |
| 440 // static | 345 // static |
| 441 int32_t UkmService::GetNewSourceID() { | 346 int32_t UkmService::GetNewSourceID() { |
| 442 static int32_t next_source_id = 0; | 347 static int32_t next_source_id = 0; |
| 443 return next_source_id++; | 348 return next_source_id++; |
| 444 } | 349 } |
| 445 | 350 |
| 446 std::unique_ptr<UkmEntryBuilder> UkmService::GetEntryBuilder( | 351 std::unique_ptr<UkmEntryBuilder> UkmService::GetEntryBuilder( |
| 447 int32_t source_id, | 352 int32_t source_id, |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 501 void UkmService::StoreWhitelistedEntries() { | 406 void UkmService::StoreWhitelistedEntries() { |
| 502 const auto entries = | 407 const auto entries = |
| 503 base::SplitString(GetWhitelistEntries(), ",", base::TRIM_WHITESPACE, | 408 base::SplitString(GetWhitelistEntries(), ",", base::TRIM_WHITESPACE, |
| 504 base::SPLIT_WANT_NONEMPTY); | 409 base::SPLIT_WANT_NONEMPTY); |
| 505 for (const auto& entry_string : entries) { | 410 for (const auto& entry_string : entries) { |
| 506 whitelisted_entry_hashes_.insert(base::HashMetricName(entry_string)); | 411 whitelisted_entry_hashes_.insert(base::HashMetricName(entry_string)); |
| 507 } | 412 } |
| 508 } | 413 } |
| 509 | 414 |
| 510 } // namespace ukm | 415 } // namespace ukm |
| OLD | NEW |