Upstream version 6.35.121.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/protocol/checkin.pb.h"
11 #include "net/http/http_status_code.h"
12 #include "net/url_request/url_fetcher.h"
13 #include "net/url_request/url_request_status.h"
14 #include "url/gurl.h"
15
16 namespace gcm {
17
18 namespace {
19 const char kCheckinURL[] = "https://android.clients.google.com/checkin";
20 const char kRequestContentType[] = "application/x-protobuf";
21 const int kRequestVersionValue = 2;
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.
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 void RecordCheckinStatusToUMA(CheckinRequestStatus status) {
44   UMA_HISTOGRAM_ENUMERATION("GCM.CheckinRequestStatus", status, STATUS_COUNT);
45 }
46
47 }  // namespace
48
49 CheckinRequest::CheckinRequest(
50     const CheckinRequestCallback& callback,
51     const net::BackoffEntry::Policy& backoff_policy,
52     const checkin_proto::ChromeBuildProto& chrome_build_proto,
53     uint64 android_id,
54     uint64 security_token,
55     const std::vector<std::string>& account_ids,
56     net::URLRequestContextGetter* request_context_getter)
57     : request_context_getter_(request_context_getter),
58       callback_(callback),
59       backoff_entry_(&backoff_policy),
60       chrome_build_proto_(chrome_build_proto),
61       android_id_(android_id),
62       security_token_(security_token),
63       account_ids_(account_ids),
64       weak_ptr_factory_(this) {
65 }
66
67 CheckinRequest::~CheckinRequest() {}
68
69 void CheckinRequest::Start() {
70   DCHECK(!url_fetcher_.get());
71
72   checkin_proto::AndroidCheckinRequest request;
73   request.set_id(android_id_);
74   request.set_security_token(security_token_);
75   request.set_user_serial_number(kDefaultUserSerialNumber);
76   request.set_version(kRequestVersionValue);
77
78   checkin_proto::AndroidCheckinProto* checkin = request.mutable_checkin();
79   checkin->mutable_chrome_build()->CopyFrom(chrome_build_proto_);
80 #if defined(CHROME_OS)
81   checkin->set_type(checkin_proto::DEVICE_CHROME_OS);
82 #else
83   checkin->set_type(checkin_proto::DEVICE_CHROME_BROWSER);
84 #endif
85
86   for (std::vector<std::string>::const_iterator iter = account_ids_.begin();
87        iter != account_ids_.end();
88        ++iter) {
89     request.add_account_cookie("[" + *iter + "]");
90   }
91
92   std::string upload_data;
93   CHECK(request.SerializeToString(&upload_data));
94
95   url_fetcher_.reset(
96       net::URLFetcher::Create(GURL(kCheckinURL), net::URLFetcher::POST, this));
97   url_fetcher_->SetRequestContext(request_context_getter_);
98   url_fetcher_->SetUploadData(kRequestContentType, upload_data);
99   url_fetcher_->Start();
100 }
101
102 void CheckinRequest::RetryWithBackoff(bool update_backoff) {
103   if (update_backoff) {
104     backoff_entry_.InformOfRequest(false);
105     url_fetcher_.reset();
106   }
107
108   if (backoff_entry_.ShouldRejectRequest()) {
109     DVLOG(1) << "Delay GCM checkin for: "
110              << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
111              << " milliseconds.";
112     base::MessageLoop::current()->PostDelayedTask(
113         FROM_HERE,
114         base::Bind(&CheckinRequest::RetryWithBackoff,
115                    weak_ptr_factory_.GetWeakPtr(),
116                    false),
117         backoff_entry_.GetTimeUntilRelease());
118     return;
119   }
120
121   Start();
122 }
123
124 void CheckinRequest::OnURLFetchComplete(const net::URLFetcher* source) {
125   std::string response_string;
126   checkin_proto::AndroidCheckinResponse response_proto;
127   if (!source->GetStatus().is_success()) {
128     LOG(ERROR) << "Failed to get checkin response. Fetcher failed. Retrying.";
129     RecordCheckinStatusToUMA(URL_FETCHING_FAILED);
130     RetryWithBackoff(true);
131     return;
132   }
133
134   net::HttpStatusCode response_status = static_cast<net::HttpStatusCode>(
135       source->GetResponseCode());
136   if (response_status == net::HTTP_BAD_REQUEST ||
137       response_status == net::HTTP_UNAUTHORIZED) {
138     // BAD_REQUEST indicates that the request was malformed.
139     // UNAUTHORIZED indicates that security token didn't match the android id.
140     LOG(ERROR) << "No point retrying the checkin with status: "
141                << response_status << ". Checkin failed.";
142     RecordCheckinStatusToUMA(response_status == net::HTTP_BAD_REQUEST ?
143         HTTP_BAD_REQUEST : HTTP_UNAUTHORIZED);
144     callback_.Run(0,0);
145     return;
146   }
147
148   if (response_status != net::HTTP_OK ||
149       !source->GetResponseAsString(&response_string) ||
150       !response_proto.ParseFromString(response_string)) {
151     LOG(ERROR) << "Failed to get checkin response. HTTP Status: "
152                << response_status << ". Retrying.";
153     RecordCheckinStatusToUMA(response_status != net::HTTP_OK ?
154         HTTP_NOT_OK : RESPONSE_PARSING_FAILED);
155     RetryWithBackoff(true);
156     return;
157   }
158
159   if (!response_proto.has_android_id() ||
160       !response_proto.has_security_token() ||
161       response_proto.android_id() == 0 ||
162       response_proto.security_token() == 0) {
163     LOG(ERROR) << "Android ID or security token is 0. Retrying.";
164     RecordCheckinStatusToUMA(ZERO_ID_OR_TOKEN);
165     RetryWithBackoff(true);
166     return;
167   }
168
169   RecordCheckinStatusToUMA(SUCCESS);
170   callback_.Run(response_proto.android_id(), response_proto.security_token());
171 }
172
173 }  // namespace gcm