Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / google_apis / gcm / engine / registration_request.cc
index 64f49a9..f0de831 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "net/base/escape.h"
@@ -31,14 +32,17 @@ const char kCertKey[] = "cert";
 const char kDeviceIdKey[] = "device";
 const char kLoginHeader[] = "AidLogin";
 const char kSenderKey[] = "sender";
-const char kUserAndroidIdKey[] = "X-GOOG.USER_AID";
-const char kUserSerialNumberKey[] = "device_user_id";
 
 // Request validation constants.
 const size_t kMaxSenders = 100;
 
 // Response constants.
+const char kErrorPrefix[] = "Error=";
 const char kTokenPrefix[] = "token=";
+const char kDeviceRegistrationError[] = "PHONE_REGISTRATION_ERROR";
+const char kAuthenticationFailed[] = "AUTHENTICATION_FAILED";
+const char kInvalidSender[] = "INVALID_SENDER";
+const char kInvalidParameters[] = "INVALID_PARAMETERS";
 
 void BuildFormEncoding(const std::string& key,
                        const std::string& value,
@@ -48,33 +52,60 @@ void BuildFormEncoding(const std::string& key,
   out->append(key + "=" + net::EscapeUrlEncodedData(value, true));
 }
 
+// Gets correct status from the error message.
+RegistrationRequest::Status GetStatusFromError(const std::string& error) {
+  if (error == kDeviceRegistrationError)
+    return RegistrationRequest::DEVICE_REGISTRATION_ERROR;
+  if (error == kAuthenticationFailed)
+    return RegistrationRequest::AUTHENTICATION_FAILED;
+  if (error == kInvalidSender)
+    return RegistrationRequest::INVALID_SENDER;
+  if (error == kInvalidParameters)
+    return RegistrationRequest::INVALID_PARAMETERS;
+  return RegistrationRequest::UNKNOWN_ERROR;
+}
+
+// Indicates whether a retry attempt should be made based on the status of the
+// last request.
+bool ShouldRetryWithStatus(RegistrationRequest::Status status) {
+  return status == RegistrationRequest::UNKNOWN_ERROR ||
+         status == RegistrationRequest::AUTHENTICATION_FAILED ||
+         status == RegistrationRequest::DEVICE_REGISTRATION_ERROR;
+}
+
+void RecordRegistrationStatusToUMA(RegistrationRequest::Status status) {
+  UMA_HISTOGRAM_ENUMERATION("GCM.RegistrationRequestStatus", status,
+                            RegistrationRequest::STATUS_COUNT);
+}
+
 }  // namespace
 
 RegistrationRequest::RequestInfo::RequestInfo(
     uint64 android_id,
     uint64 security_token,
-    uint64 user_android_id,
-    int64 user_serial_number,
     const std::string& app_id,
     const std::string& cert,
     const std::vector<std::string>& sender_ids)
     : android_id(android_id),
       security_token(security_token),
-      user_android_id(user_android_id),
-      user_serial_number(user_serial_number),
       app_id(app_id),
       cert(cert),
-      sender_ids(sender_ids) {}
+      sender_ids(sender_ids) {
+}
 
 RegistrationRequest::RequestInfo::~RequestInfo() {}
 
 RegistrationRequest::RegistrationRequest(
     const RequestInfo& request_info,
+    const net::BackoffEntry::Policy& backoff_policy,
     const RegistrationCallback& callback,
     scoped_refptr<net::URLRequestContextGetter> request_context_getter)
     : callback_(callback),
       request_info_(request_info),
-      request_context_getter_(request_context_getter) {}
+      backoff_entry_(&backoff_policy),
+      request_context_getter_(request_context_getter),
+      weak_ptr_factory_(this) {
+}
 
 RegistrationRequest::~RegistrationRequest() {}
 
@@ -111,46 +142,76 @@ void RegistrationRequest::Start() {
     DCHECK(!iter->empty());
     if (!senders.empty())
       senders.append(",");
-    senders.append(net::EscapeUrlEncodedData(*iter, true));
+    senders.append(*iter);
   }
   BuildFormEncoding(kSenderKey, senders, &body);
 
-  if (request_info_.user_serial_number != 0) {
-    DCHECK(request_info_.user_android_id != 0);
-    BuildFormEncoding(kUserSerialNumberKey,
-                      base::IntToString(request_info_.user_serial_number),
-                      &body);
-    BuildFormEncoding(kUserAndroidIdKey,
-                      base::Uint64ToString(request_info_.user_android_id),
-                      &body);
-  }
-
+  DVLOG(1) << "Performing registration for: " << request_info_.app_id;
   DVLOG(1) << "Registration request: " << body;
   url_fetcher_->SetUploadData(kRegistrationRequestContentType, body);
-
-  DVLOG(1) << "Performing registration for: " << request_info_.app_id;
   url_fetcher_->Start();
 }
 
+void RegistrationRequest::RetryWithBackoff(bool update_backoff) {
+  if (update_backoff) {
+    url_fetcher_.reset();
+    backoff_entry_.InformOfRequest(false);
+  }
+
+  if (backoff_entry_.ShouldRejectRequest()) {
+    DVLOG(1) << "Delaying GCM registration of app: "
+             << request_info_.app_id << ", for "
+             << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
+             << " milliseconds.";
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&RegistrationRequest::RetryWithBackoff,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   false),
+        backoff_entry_.GetTimeUntilRelease());
+    return;
+  }
+
+  Start();
+}
+
 void RegistrationRequest::OnURLFetchComplete(const net::URLFetcher* source) {
   std::string response;
   if (!source->GetStatus().is_success() ||
       source->GetResponseCode() != net::HTTP_OK ||
       !source->GetResponseAsString(&response)) {
-    // TODO(fgoski): Introduce retry logic.
-    LOG(ERROR) << "Failed to get registration response.";
-    callback_.Run("");
+    LOG(ERROR) << "Failed to get registration response: "
+               << source->GetStatus().is_success() << " "
+               << source->GetResponseCode();
+    RetryWithBackoff(true);
     return;
   }
 
   DVLOG(1) << "Parsing registration response: " << response;
   size_t token_pos = response.find(kTokenPrefix);
-  std::string token;
-  if (token_pos != std::string::npos)
-    token = response.substr(token_pos + strlen(kTokenPrefix));
-  else
-    LOG(ERROR) << "Failed to extract token.";
-  callback_.Run(token);
+  if (token_pos != std::string::npos) {
+    std::string token =
+        response.substr(token_pos + arraysize(kTokenPrefix) - 1);
+    RecordRegistrationStatusToUMA(SUCCESS);
+    callback_.Run(SUCCESS, token);
+    return;
+  }
+
+  size_t error_pos = response.find(kErrorPrefix);
+  Status status = UNKNOWN_ERROR;
+  if (error_pos != std::string::npos) {
+    std::string error =
+        response.substr(error_pos + arraysize(kErrorPrefix) - 1);
+    status = GetStatusFromError(error);
+  }
+  RecordRegistrationStatusToUMA(status);
+
+  if (ShouldRetryWithStatus(status)) {
+    RetryWithBackoff(true);
+    return;
+  }
+
+  callback_.Run(status, std::string());
 }
 
 }  // namespace gcm