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