Upstream version 7.36.149.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/http/http_status_code.h"
13 #include "net/url_request/url_fetcher.h"
14 #include "net/url_request/url_request_status.h"
15
16 namespace gcm {
17
18 namespace {
19 const char kRequestContentType[] = "application/x-protobuf";
20 const int kRequestVersionValue = 2;
21 const int kDefaultUserSerialNumber = 0;
22
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
40 };
41
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";
62   }
63 }
64
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);
75   }
76 }
77
78 }  // namespace
79
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) {
91 }
92
93 CheckinRequest::RequestInfo::~RequestInfo() {}
94
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) {
109 }
110
111 CheckinRequest::~CheckinRequest() {}
112
113 void CheckinRequest::Start() {
114   DCHECK(!url_fetcher_.get());
115
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);
123
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
131
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 + "]");
137   }
138
139   std::string upload_data;
140   CHECK(request.SerializeToString(&upload_data));
141
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();
149 }
150
151 void CheckinRequest::RetryWithBackoff(bool update_backoff) {
152   if (update_backoff) {
153     backoff_entry_.InformOfRequest(false);
154     url_fetcher_.reset();
155   }
156
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;
170   }
171
172   Start();
173 }
174
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;
183   }
184
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;
198   }
199
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;
210   }
211
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;
220   }
221
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);
228 }
229
230 }  // namespace gcm