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"
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"
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;
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
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
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.
43 void RecordCheckinStatusToUMA(CheckinRequestStatus status) {
44 UMA_HISTOGRAM_ENUMERATION("GCM.CheckinRequestStatus", status, STATUS_COUNT);
49 CheckinRequest::CheckinRequest(
50 const CheckinRequestCallback& callback,
51 const net::BackoffEntry::Policy& backoff_policy,
52 const checkin_proto::ChromeBuildProto& chrome_build_proto,
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),
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) {
67 CheckinRequest::~CheckinRequest() {}
69 void CheckinRequest::Start() {
70 DCHECK(!url_fetcher_.get());
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);
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);
83 checkin->set_type(checkin_proto::DEVICE_CHROME_BROWSER);
86 for (std::vector<std::string>::const_iterator iter = account_ids_.begin();
87 iter != account_ids_.end();
89 request.add_account_cookie("[" + *iter + "]");
92 std::string upload_data;
93 CHECK(request.SerializeToString(&upload_data));
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();
102 void CheckinRequest::RetryWithBackoff(bool update_backoff) {
103 if (update_backoff) {
104 backoff_entry_.InformOfRequest(false);
105 url_fetcher_.reset();
108 if (backoff_entry_.ShouldRejectRequest()) {
109 DVLOG(1) << "Delay GCM checkin for: "
110 << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
112 base::MessageLoop::current()->PostDelayedTask(
114 base::Bind(&CheckinRequest::RetryWithBackoff,
115 weak_ptr_factory_.GetWeakPtr(),
117 backoff_entry_.GetTimeUntilRelease());
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);
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);
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);
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);
169 RecordCheckinStatusToUMA(SUCCESS);
170 callback_.Run(response_proto.android_id(), response_proto.security_token());