[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / google_apis / gcm / engine / checkin_request.cc
blobe7490e5b47a9189dbd0d41c2f40d3cec93871f72
1 // Copyright 2014 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.
5 #include "google_apis/gcm/engine/checkin_request.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/metrics/histogram.h"
10 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
11 #include "google_apis/gcm/protocol/checkin.pb.h"
12 #include "net/http/http_status_code.h"
13 #include "net/url_request/url_fetcher.h"
14 #include "net/url_request/url_request_status.h"
16 namespace gcm {
18 namespace {
19 const char kRequestContentType[] = "application/x-protobuf";
20 const int kRequestVersionValue = 3;
21 const int kDefaultUserSerialNumber = 0;
23 // This enum is also used in an UMA histogram (GCMCheckinRequestStatus
24 // enum defined in tools/metrics/histograms/histogram.xml). Hence the entries
25 // here shouldn't be deleted or re-ordered and new ones should be added to
26 // the end, and update the GetCheckinRequestStatusString(...) below.
27 enum CheckinRequestStatus {
28 SUCCESS, // Checkin completed successfully.
29 URL_FETCHING_FAILED, // URL fetching failed.
30 HTTP_BAD_REQUEST, // The request was malformed.
31 HTTP_UNAUTHORIZED, // The security token didn't match the android id.
32 HTTP_NOT_OK, // HTTP status was not OK.
33 RESPONSE_PARSING_FAILED, // Check in response parsing failed.
34 ZERO_ID_OR_TOKEN, // Either returned android id or security token
35 // was zero.
36 // NOTE: always keep this entry at the end. Add new status types only
37 // immediately above this line. Make sure to update the corresponding
38 // histogram enum accordingly.
39 STATUS_COUNT
42 // Returns string representation of enum CheckinRequestStatus.
43 std::string GetCheckinRequestStatusString(CheckinRequestStatus status) {
44 switch (status) {
45 case SUCCESS:
46 return "SUCCESS";
47 case URL_FETCHING_FAILED:
48 return "URL_FETCHING_FAILED";
49 case HTTP_BAD_REQUEST:
50 return "HTTP_BAD_REQUEST";
51 case HTTP_UNAUTHORIZED:
52 return "HTTP_UNAUTHORIZED";
53 case HTTP_NOT_OK:
54 return "HTTP_NOT_OK";
55 case RESPONSE_PARSING_FAILED:
56 return "RESPONSE_PARSING_FAILED";
57 case ZERO_ID_OR_TOKEN:
58 return "ZERO_ID_OR_TOKEN";
59 default:
60 NOTREACHED();
61 return "UNKNOWN_STATUS";
65 // Records checkin status to both stats recorder and reports to UMA.
66 void RecordCheckinStatusAndReportUMA(CheckinRequestStatus status,
67 GCMStatsRecorder* recorder,
68 bool will_retry) {
69 UMA_HISTOGRAM_ENUMERATION("GCM.CheckinRequestStatus", status, STATUS_COUNT);
70 if (status == SUCCESS)
71 recorder->RecordCheckinSuccess();
72 else {
73 recorder->RecordCheckinFailure(GetCheckinRequestStatusString(status),
74 will_retry);
78 } // namespace
80 CheckinRequest::RequestInfo::RequestInfo(
81 uint64 android_id,
82 uint64 security_token,
83 const std::string& settings_digest,
84 const std::vector<std::string>& account_ids,
85 const checkin_proto::ChromeBuildProto& chrome_build_proto)
86 : android_id(android_id),
87 security_token(security_token),
88 settings_digest(settings_digest),
89 account_ids(account_ids),
90 chrome_build_proto(chrome_build_proto) {
93 CheckinRequest::RequestInfo::~RequestInfo() {}
95 CheckinRequest::CheckinRequest(
96 const GURL& checkin_url,
97 const RequestInfo& request_info,
98 const net::BackoffEntry::Policy& backoff_policy,
99 const CheckinRequestCallback& callback,
100 net::URLRequestContextGetter* request_context_getter,
101 GCMStatsRecorder* recorder)
102 : request_context_getter_(request_context_getter),
103 callback_(callback),
104 backoff_entry_(&backoff_policy),
105 checkin_url_(checkin_url),
106 request_info_(request_info),
107 recorder_(recorder),
108 weak_ptr_factory_(this) {
111 CheckinRequest::~CheckinRequest() {}
113 void CheckinRequest::Start() {
114 DCHECK(!url_fetcher_.get());
116 checkin_proto::AndroidCheckinRequest request;
117 request.set_id(request_info_.android_id);
118 request.set_security_token(request_info_.security_token);
119 request.set_user_serial_number(kDefaultUserSerialNumber);
120 request.set_version(kRequestVersionValue);
121 if (!request_info_.settings_digest.empty())
122 request.set_digest(request_info_.settings_digest);
124 checkin_proto::AndroidCheckinProto* checkin = request.mutable_checkin();
125 checkin->mutable_chrome_build()->CopyFrom(request_info_.chrome_build_proto);
126 #if defined(CHROME_OS)
127 checkin->set_type(checkin_proto::DEVICE_CHROME_OS);
128 #else
129 checkin->set_type(checkin_proto::DEVICE_CHROME_BROWSER);
130 #endif
132 for (std::vector<std::string>::const_iterator iter =
133 request_info_.account_ids.begin();
134 iter != request_info_.account_ids.end();
135 ++iter) {
136 request.add_account_cookie("[" + *iter + "]");
139 std::string upload_data;
140 CHECK(request.SerializeToString(&upload_data));
142 url_fetcher_.reset(
143 net::URLFetcher::Create(checkin_url_, net::URLFetcher::POST, this));
144 url_fetcher_->SetRequestContext(request_context_getter_);
145 url_fetcher_->SetUploadData(kRequestContentType, upload_data);
146 recorder_->RecordCheckinInitiated(request_info_.android_id);
147 request_start_time_ = base::TimeTicks::Now();
148 url_fetcher_->Start();
151 void CheckinRequest::RetryWithBackoff(bool update_backoff) {
152 if (update_backoff) {
153 backoff_entry_.InformOfRequest(false);
154 url_fetcher_.reset();
157 if (backoff_entry_.ShouldRejectRequest()) {
158 DVLOG(1) << "Delay GCM checkin for: "
159 << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
160 << " milliseconds.";
161 recorder_->RecordCheckinDelayedDueToBackoff(
162 backoff_entry_.GetTimeUntilRelease().InMilliseconds());
163 base::MessageLoop::current()->PostDelayedTask(
164 FROM_HERE,
165 base::Bind(&CheckinRequest::RetryWithBackoff,
166 weak_ptr_factory_.GetWeakPtr(),
167 false),
168 backoff_entry_.GetTimeUntilRelease());
169 return;
172 Start();
175 void CheckinRequest::OnURLFetchComplete(const net::URLFetcher* source) {
176 std::string response_string;
177 checkin_proto::AndroidCheckinResponse response_proto;
178 if (!source->GetStatus().is_success()) {
179 LOG(ERROR) << "Failed to get checkin response. Fetcher failed. Retrying.";
180 RecordCheckinStatusAndReportUMA(URL_FETCHING_FAILED, recorder_, true);
181 RetryWithBackoff(true);
182 return;
185 net::HttpStatusCode response_status = static_cast<net::HttpStatusCode>(
186 source->GetResponseCode());
187 if (response_status == net::HTTP_BAD_REQUEST ||
188 response_status == net::HTTP_UNAUTHORIZED) {
189 // BAD_REQUEST indicates that the request was malformed.
190 // UNAUTHORIZED indicates that security token didn't match the android id.
191 LOG(ERROR) << "No point retrying the checkin with status: "
192 << response_status << ". Checkin failed.";
193 CheckinRequestStatus status = response_status == net::HTTP_BAD_REQUEST ?
194 HTTP_BAD_REQUEST : HTTP_UNAUTHORIZED;
195 RecordCheckinStatusAndReportUMA(status, recorder_, false);
196 callback_.Run(response_proto);
197 return;
200 if (response_status != net::HTTP_OK ||
201 !source->GetResponseAsString(&response_string) ||
202 !response_proto.ParseFromString(response_string)) {
203 LOG(ERROR) << "Failed to get checkin response. HTTP Status: "
204 << response_status << ". Retrying.";
205 CheckinRequestStatus status = response_status != net::HTTP_OK ?
206 HTTP_NOT_OK : RESPONSE_PARSING_FAILED;
207 RecordCheckinStatusAndReportUMA(status, recorder_, true);
208 RetryWithBackoff(true);
209 return;
212 if (!response_proto.has_android_id() ||
213 !response_proto.has_security_token() ||
214 response_proto.android_id() == 0 ||
215 response_proto.security_token() == 0) {
216 LOG(ERROR) << "Android ID or security token is 0. Retrying.";
217 RecordCheckinStatusAndReportUMA(ZERO_ID_OR_TOKEN, recorder_, true);
218 RetryWithBackoff(true);
219 return;
222 RecordCheckinStatusAndReportUMA(SUCCESS, recorder_, false);
223 UMA_HISTOGRAM_COUNTS("GCM.CheckinRetryCount",
224 backoff_entry_.failure_count());
225 UMA_HISTOGRAM_TIMES("GCM.CheckinCompleteTime",
226 base::TimeTicks::Now() - request_start_time_);
227 callback_.Run(response_proto);
230 } // namespace gcm