holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // ReportingService handles uploading serialized logs to a server. |
| 6 | |
| 7 | #include "components/metrics/reporting_service.h" |
| 8 | |
| 9 | #include "base/bind.h" |
| 10 | #include "base/callback.h" |
Carlos IL | 75b352f | 2017-11-07 01:43:02 | [diff] [blame] | 11 | #include "base/command_line.h" |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 12 | #include "base/strings/string_number_conversions.h" |
| 13 | #include "components/metrics/data_use_tracker.h" |
| 14 | #include "components/metrics/log_store.h" |
| 15 | #include "components/metrics/metrics_log_uploader.h" |
| 16 | #include "components/metrics/metrics_service_client.h" |
| 17 | #include "components/metrics/metrics_upload_scheduler.h" |
| 18 | |
| 19 | namespace metrics { |
| 20 | |
| 21 | // static |
| 22 | void ReportingService::RegisterPrefs(PrefRegistrySimple* registry) { |
| 23 | DataUseTracker::RegisterPrefs(registry); |
| 24 | } |
| 25 | |
| 26 | ReportingService::ReportingService(MetricsServiceClient* client, |
| 27 | PrefService* local_state, |
| 28 | size_t max_retransmit_size) |
| 29 | : client_(client), |
| 30 | max_retransmit_size_(max_retransmit_size), |
| 31 | reporting_active_(false), |
| 32 | log_upload_in_progress_(false), |
| 33 | data_use_tracker_(DataUseTracker::Create(local_state)), |
| 34 | self_ptr_factory_(this) { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 35 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 36 | DCHECK(client_); |
| 37 | DCHECK(local_state); |
| 38 | } |
| 39 | |
| 40 | ReportingService::~ReportingService() { |
| 41 | DisableReporting(); |
| 42 | } |
| 43 | |
| 44 | void ReportingService::Initialize() { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 45 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | 5576ba8 | 2017-03-15 21:26:43 | [diff] [blame] | 46 | DCHECK(!upload_scheduler_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 47 | log_store()->LoadPersistedUnsentLogs(); |
| 48 | base::Closure send_next_log_callback = base::Bind( |
| 49 | &ReportingService::SendNextLog, self_ptr_factory_.GetWeakPtr()); |
| 50 | upload_scheduler_.reset(new MetricsUploadScheduler(send_next_log_callback)); |
| 51 | } |
| 52 | |
| 53 | void ReportingService::Start() { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 54 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 55 | if (reporting_active_) |
| 56 | upload_scheduler_->Start(); |
| 57 | } |
| 58 | |
| 59 | void ReportingService::Stop() { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 60 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 61 | if (upload_scheduler_) |
| 62 | upload_scheduler_->Stop(); |
| 63 | } |
| 64 | |
| 65 | void ReportingService::EnableReporting() { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 66 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 67 | if (reporting_active_) |
| 68 | return; |
| 69 | reporting_active_ = true; |
| 70 | Start(); |
| 71 | } |
| 72 | |
| 73 | void ReportingService::DisableReporting() { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 74 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 75 | reporting_active_ = false; |
| 76 | Stop(); |
| 77 | } |
| 78 | |
| 79 | bool ReportingService::reporting_active() const { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 80 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 81 | return reporting_active_; |
| 82 | } |
| 83 | |
rajendrant | dce70a0 | 2018-10-12 06:24:24 | [diff] [blame] | 84 | void ReportingService::UpdateMetricsUsagePrefs(int message_size, |
| 85 | bool is_cellular, |
| 86 | bool is_metrics_service_usage) { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 87 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 88 | if (data_use_tracker_) { |
rajendrant | dce70a0 | 2018-10-12 06:24:24 | [diff] [blame] | 89 | data_use_tracker_->UpdateMetricsUsagePrefs(message_size, is_cellular, |
| 90 | is_metrics_service_usage); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 91 | } |
| 92 | } |
| 93 | |
| 94 | //------------------------------------------------------------------------------ |
| 95 | // private methods |
| 96 | //------------------------------------------------------------------------------ |
| 97 | |
| 98 | void ReportingService::SendNextLog() { |
| 99 | DVLOG(1) << "SendNextLog"; |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 100 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
Alexei Svitkine | b9c82601 | 2018-10-13 00:19:26 | [diff] [blame] | 101 | |
Alexei Svitkine | 01f7205e | 2018-11-01 23:45:04 | [diff] [blame] | 102 | const base::TimeTicks now = base::TimeTicks::Now(); |
Alexei Svitkine | b9c82601 | 2018-10-13 00:19:26 | [diff] [blame] | 103 | LogActualUploadInterval(last_upload_finish_time_.is_null() |
| 104 | ? base::TimeDelta() |
Alexei Svitkine | 01f7205e | 2018-11-01 23:45:04 | [diff] [blame] | 105 | : now - last_upload_finish_time_); |
| 106 | last_upload_finish_time_ = now; |
Alexei Svitkine | b9c82601 | 2018-10-13 00:19:26 | [diff] [blame] | 107 | |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 108 | if (!reporting_active()) { |
| 109 | upload_scheduler_->StopAndUploadCancelled(); |
| 110 | return; |
| 111 | } |
| 112 | if (!log_store()->has_unsent_logs()) { |
| 113 | // Should only get here if serializing the log failed somehow. |
| 114 | upload_scheduler_->Stop(); |
| 115 | // Reset backoff interval |
| 116 | upload_scheduler_->UploadFinished(true); |
| 117 | return; |
| 118 | } |
Steven Holte | 72428db | 2017-10-13 19:47:22 | [diff] [blame] | 119 | if (!log_store()->has_staged_log()) { |
| 120 | reporting_info_.set_attempt_count(0); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 121 | log_store()->StageNextLog(); |
Steven Holte | 72428db | 2017-10-13 19:47:22 | [diff] [blame] | 122 | } |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 123 | |
| 124 | // Proceed to stage the log for upload if log size satisfies cellular log |
| 125 | // upload constrains. |
| 126 | bool upload_canceled = false; |
| 127 | bool is_cellular_logic = client_->IsUMACellularUploadLogicEnabled(); |
| 128 | if (is_cellular_logic && data_use_tracker_ && |
| 129 | !data_use_tracker_->ShouldUploadLogOnCellular( |
| 130 | log_store()->staged_log_hash().size())) { |
| 131 | upload_scheduler_->UploadOverDataUsageCap(); |
| 132 | upload_canceled = true; |
| 133 | } else { |
| 134 | SendStagedLog(); |
| 135 | } |
| 136 | if (is_cellular_logic) { |
| 137 | LogCellularConstraint(upload_canceled); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | void ReportingService::SendStagedLog() { |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 142 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 143 | DCHECK(log_store()->has_staged_log()); |
| 144 | if (!log_store()->has_staged_log()) |
| 145 | return; |
| 146 | |
| 147 | DCHECK(!log_upload_in_progress_); |
| 148 | log_upload_in_progress_ = true; |
| 149 | |
| 150 | if (!log_uploader_) { |
| 151 | log_uploader_ = client_->CreateUploader( |
Carlos IL | 75b352f | 2017-11-07 01:43:02 | [diff] [blame] | 152 | GetUploadUrl(), GetInsecureUploadUrl(), upload_mime_type(), |
| 153 | service_type(), |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 154 | base::Bind(&ReportingService::OnLogUploadComplete, |
| 155 | self_ptr_factory_.GetWeakPtr())); |
| 156 | } |
| 157 | |
Steven Holte | 72428db | 2017-10-13 19:47:22 | [diff] [blame] | 158 | reporting_info_.set_attempt_count(reporting_info_.attempt_count() + 1); |
| 159 | |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 160 | const std::string hash = |
| 161 | base::HexEncode(log_store()->staged_log_hash().data(), |
| 162 | log_store()->staged_log_hash().size()); |
Steven Holte | 72428db | 2017-10-13 19:47:22 | [diff] [blame] | 163 | log_uploader_->UploadLog(log_store()->staged_log(), hash, reporting_info_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 164 | } |
| 165 | |
Carlos IL | 75b352f | 2017-11-07 01:43:02 | [diff] [blame] | 166 | void ReportingService::OnLogUploadComplete(int response_code, |
| 167 | int error_code, |
| 168 | bool was_https) { |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 169 | DVLOG(1) << "OnLogUploadComplete:" << response_code; |
Steven Holte | 971b059 | 2017-10-12 15:28:26 | [diff] [blame] | 170 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 171 | DCHECK(log_upload_in_progress_); |
| 172 | log_upload_in_progress_ = false; |
| 173 | |
Steven Holte | 72428db | 2017-10-13 19:47:22 | [diff] [blame] | 174 | reporting_info_.set_last_response_code(response_code); |
Carlos IL | 75b352f | 2017-11-07 01:43:02 | [diff] [blame] | 175 | reporting_info_.set_last_error_code(error_code); |
| 176 | reporting_info_.set_last_attempt_was_https(was_https); |
Steven Holte | 72428db | 2017-10-13 19:47:22 | [diff] [blame] | 177 | |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 178 | // Log a histogram to track response success vs. failure rates. |
Carlos IL | e3fe868a | 2017-11-07 23:56:38 | [diff] [blame] | 179 | LogResponseOrErrorCode(response_code, error_code, was_https); |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 180 | |
| 181 | bool upload_succeeded = response_code == 200; |
| 182 | |
holte | 77d815b | 2017-03-27 21:27:37 | [diff] [blame] | 183 | // Staged log could have been removed already (such as by Purge() in some |
| 184 | // implementations), otherwise we may remove it here. |
| 185 | if (log_store()->has_staged_log()) { |
| 186 | // Provide boolean for error recovery (allow us to ignore response_code). |
| 187 | bool discard_log = false; |
| 188 | const size_t log_size = log_store()->staged_log().length(); |
| 189 | if (upload_succeeded) { |
| 190 | LogSuccess(log_size); |
| 191 | } else if (log_size > max_retransmit_size_) { |
| 192 | LogLargeRejection(log_size); |
| 193 | discard_log = true; |
| 194 | } else if (response_code == 400) { |
| 195 | // Bad syntax. Retransmission won't work. |
| 196 | discard_log = true; |
| 197 | } |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 198 | |
holte | 77d815b | 2017-03-27 21:27:37 | [diff] [blame] | 199 | if (upload_succeeded || discard_log) { |
| 200 | log_store()->DiscardStagedLog(); |
| 201 | // Store the updated list to disk now that the removed log is uploaded. |
| 202 | log_store()->PersistUnsentLogs(); |
| 203 | } |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | // Error 400 indicates a problem with the log, not with the server, so |
| 207 | // don't consider that a sign that the server is in trouble. |
| 208 | bool server_is_healthy = upload_succeeded || response_code == 400; |
Carlos IL | 75b352f | 2017-11-07 01:43:02 | [diff] [blame] | 209 | |
holte | a3b2411 | 2017-03-14 02:08:24 | [diff] [blame] | 210 | if (!log_store()->has_unsent_logs()) { |
| 211 | DVLOG(1) << "Stopping upload_scheduler_."; |
| 212 | upload_scheduler_->Stop(); |
| 213 | } |
| 214 | upload_scheduler_->UploadFinished(server_is_healthy); |
| 215 | } |
| 216 | |
| 217 | } // namespace metrics |