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