[email protected] | 04a830a | 2014-01-04 02:48:51 | [diff] [blame] | 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 | |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 5 | #include "base/base64.h" |
[email protected] | edfe19f | 2014-03-21 01:38:12 | [diff] [blame] | 6 | #include "base/i18n/time_formatting.h" |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 7 | #include "base/metrics/histogram.h" |
[email protected] | edfe19f | 2014-03-21 01:38:12 | [diff] [blame] | 8 | #include "base/sha1.h" |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 9 | #include "base/strings/string_number_conversions.h" |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 10 | #include "base/strings/string_util.h" |
[email protected] | 6ea3c2a | 2014-03-29 04:14:01 | [diff] [blame] | 11 | #if !defined(OS_ANDROID) |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 12 | // channel_common.proto defines ANDROID constant that conflicts with Android |
| 13 | // build. At the same time TiclInvalidationService is not used on Android so it |
| 14 | // is safe to exclude these protos from Android build. |
| 15 | #include "google/cacheinvalidation/android_channel.pb.h" |
| 16 | #include "google/cacheinvalidation/channel_common.pb.h" |
[email protected] | 5322cf7 | 2014-05-02 00:08:04 | [diff] [blame] | 17 | #include "google/cacheinvalidation/types.pb.h" |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 18 | #endif |
[email protected] | 4482877 | 2014-06-06 02:56:52 | [diff] [blame] | 19 | #include "components/invalidation/gcm_network_channel.h" |
| 20 | #include "components/invalidation/gcm_network_channel_delegate.h" |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 21 | #include "google_apis/gaia/google_service_auth_error.h" |
| 22 | #include "net/http/http_status_code.h" |
| 23 | #include "net/url_request/url_fetcher.h" |
| 24 | #include "net/url_request/url_request_status.h" |
[email protected] | 04a830a | 2014-01-04 02:48:51 | [diff] [blame] | 25 | |
| 26 | namespace syncer { |
| 27 | |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 28 | namespace { |
| 29 | |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 30 | const char kCacheInvalidationEndpointUrl[] = |
| 31 | "https://clients4.google.com/invalidation/android/request/"; |
| 32 | const char kCacheInvalidationPackageName[] = "com.google.chrome.invalidations"; |
| 33 | |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 34 | // Register backoff policy. |
| 35 | const net::BackoffEntry::Policy kRegisterBackoffPolicy = { |
| 36 | // Number of initial errors (in sequence) to ignore before applying |
| 37 | // exponential back-off rules. |
| 38 | 0, |
| 39 | |
| 40 | // Initial delay for exponential back-off in ms. |
| 41 | 2000, // 2 seconds. |
| 42 | |
| 43 | // Factor by which the waiting time will be multiplied. |
| 44 | 2, |
| 45 | |
| 46 | // Fuzzing percentage. ex: 10% will spread requests randomly |
| 47 | // between 90%-100% of the calculated time. |
| 48 | 0.2, // 20%. |
| 49 | |
| 50 | // Maximum amount of time we are willing to delay our request in ms. |
| 51 | 1000 * 3600 * 4, // 4 hours. |
| 52 | |
| 53 | // Time to keep an entry from being discarded even when it |
| 54 | // has no significant state, -1 to never discard. |
| 55 | -1, |
| 56 | |
| 57 | // Don't use initial delay unless the last request was an error. |
| 58 | false, |
| 59 | }; |
| 60 | |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 61 | // Incoming message status values for UMA_HISTOGRAM. |
| 62 | enum IncomingMessageStatus { |
| 63 | INCOMING_MESSAGE_SUCCESS, |
| 64 | MESSAGE_EMPTY, // GCM message's content is missing or empty. |
| 65 | INVALID_ENCODING, // Base64Decode failed. |
| 66 | INVALID_PROTO, // Parsing protobuf failed. |
| 67 | |
| 68 | // This enum is used in UMA_HISTOGRAM_ENUMERATION. Insert new values above |
| 69 | // this line. |
| 70 | INCOMING_MESSAGE_STATUS_COUNT |
| 71 | }; |
| 72 | |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 73 | // Outgoing message status values for UMA_HISTOGRAM. |
| 74 | enum OutgoingMessageStatus { |
| 75 | OUTGOING_MESSAGE_SUCCESS, |
| 76 | MESSAGE_DISCARDED, // New message started before old one was sent. |
| 77 | ACCESS_TOKEN_FAILURE, // Requeting access token failed. |
| 78 | POST_FAILURE, // HTTP Post failed. |
| 79 | |
| 80 | // This enum is used in UMA_HISTOGRAM_ENUMERATION. Insert new values above |
| 81 | // this line. |
| 82 | OUTGOING_MESSAGE_STATUS_COUNT |
| 83 | }; |
| 84 | |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 85 | const char kIncomingMessageStatusHistogram[] = |
| 86 | "GCMInvalidations.IncomingMessageStatus"; |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 87 | const char kOutgoingMessageStatusHistogram[] = |
| 88 | "GCMInvalidations.OutgoingMessageStatus"; |
| 89 | |
| 90 | void RecordIncomingMessageStatus(IncomingMessageStatus status) { |
| 91 | UMA_HISTOGRAM_ENUMERATION(kIncomingMessageStatusHistogram, |
| 92 | status, |
| 93 | INCOMING_MESSAGE_STATUS_COUNT); |
| 94 | } |
| 95 | |
| 96 | void RecordOutgoingMessageStatus(OutgoingMessageStatus status) { |
| 97 | UMA_HISTOGRAM_ENUMERATION(kOutgoingMessageStatusHistogram, |
[email protected] | a9a7d5e | 2014-05-07 15:48:19 | [diff] [blame] | 98 | status, |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 99 | OUTGOING_MESSAGE_STATUS_COUNT); |
| 100 | } |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 101 | |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 102 | } // namespace |
| 103 | |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 104 | GCMNetworkChannel::GCMNetworkChannel( |
| 105 | scoped_refptr<net::URLRequestContextGetter> request_context_getter, |
| 106 | scoped_ptr<GCMNetworkChannelDelegate> delegate) |
| 107 | : request_context_getter_(request_context_getter), |
| 108 | delegate_(delegate.Pass()), |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 109 | register_backoff_entry_(new net::BackoffEntry(&kRegisterBackoffPolicy)), |
[email protected] | afff175 | 2014-06-20 04:42:10 | [diff] [blame] | 110 | gcm_channel_online_(false), |
| 111 | http_channel_online_(false), |
[email protected] | edfe19f | 2014-03-21 01:38:12 | [diff] [blame] | 112 | diagnostic_info_(this), |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 113 | weak_factory_(this) { |
[email protected] | 9c497bf | 2014-03-28 20:59:14 | [diff] [blame] | 114 | net::NetworkChangeNotifier::AddNetworkChangeObserver(this); |
[email protected] | dd507f5 | 2014-06-19 01:27:56 | [diff] [blame] | 115 | delegate_->Initialize(base::Bind(&GCMNetworkChannel::OnConnectionStateChanged, |
| 116 | weak_factory_.GetWeakPtr())); |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 117 | Register(); |
[email protected] | 04a830a | 2014-01-04 02:48:51 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | GCMNetworkChannel::~GCMNetworkChannel() { |
[email protected] | 9c497bf | 2014-03-28 20:59:14 | [diff] [blame] | 121 | net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
[email protected] | 04a830a | 2014-01-04 02:48:51 | [diff] [blame] | 122 | } |
| 123 | |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 124 | void GCMNetworkChannel::Register() { |
| 125 | delegate_->Register(base::Bind(&GCMNetworkChannel::OnRegisterComplete, |
| 126 | weak_factory_.GetWeakPtr())); |
| 127 | } |
| 128 | |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 129 | void GCMNetworkChannel::OnRegisterComplete( |
| 130 | const std::string& registration_id, |
| 131 | gcm::GCMClient::Result result) { |
| 132 | DCHECK(CalledOnValidThread()); |
| 133 | if (result == gcm::GCMClient::SUCCESS) { |
| 134 | DCHECK(!registration_id.empty()); |
| 135 | DVLOG(2) << "Got registration_id"; |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 136 | register_backoff_entry_->Reset(); |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 137 | registration_id_ = registration_id; |
[email protected] | 3b73b25 | 2014-03-01 00:18:54 | [diff] [blame] | 138 | if (!cached_message_.empty()) |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 139 | RequestAccessToken(); |
| 140 | } else { |
[email protected] | 8499597 | 2014-02-05 06:15:55 | [diff] [blame] | 141 | DVLOG(2) << "Register failed: " << result; |
| 142 | // Retry in case of transient error. |
| 143 | switch (result) { |
| 144 | case gcm::GCMClient::NETWORK_ERROR: |
| 145 | case gcm::GCMClient::SERVER_ERROR: |
| 146 | case gcm::GCMClient::TTL_EXCEEDED: |
| 147 | case gcm::GCMClient::UNKNOWN_ERROR: { |
| 148 | register_backoff_entry_->InformOfRequest(false); |
| 149 | base::MessageLoop::current()->PostDelayedTask( |
| 150 | FROM_HERE, |
| 151 | base::Bind(&GCMNetworkChannel::Register, |
| 152 | weak_factory_.GetWeakPtr()), |
| 153 | register_backoff_entry_->GetTimeUntilRelease()); |
| 154 | break; |
| 155 | } |
| 156 | default: |
| 157 | break; |
| 158 | } |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 159 | } |
[email protected] | edfe19f | 2014-03-21 01:38:12 | [diff] [blame] | 160 | diagnostic_info_.registration_id_ = registration_id_; |
| 161 | diagnostic_info_.registration_result_ = result; |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 162 | } |
| 163 | |
[email protected] | 3b73b25 | 2014-03-01 00:18:54 | [diff] [blame] | 164 | void GCMNetworkChannel::SendMessage(const std::string& message) { |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 165 | DCHECK(CalledOnValidThread()); |
[email protected] | 3b73b25 | 2014-03-01 00:18:54 | [diff] [blame] | 166 | DCHECK(!message.empty()); |
| 167 | DVLOG(2) << "SendMessage"; |
[email protected] | edfe19f | 2014-03-21 01:38:12 | [diff] [blame] | 168 | diagnostic_info_.sent_messages_count_++; |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 169 | if (!cached_message_.empty()) { |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 170 | RecordOutgoingMessageStatus(MESSAGE_DISCARDED); |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 171 | } |
[email protected] | 3b73b25 | 2014-03-01 00:18:54 | [diff] [blame] | 172 | cached_message_ = message; |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 173 | |
| 174 | if (!registration_id_.empty()) { |
| 175 | RequestAccessToken(); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | void GCMNetworkChannel::RequestAccessToken() { |
| 180 | DCHECK(CalledOnValidThread()); |
| 181 | delegate_->RequestToken(base::Bind(&GCMNetworkChannel::OnGetTokenComplete, |
| 182 | weak_factory_.GetWeakPtr())); |
| 183 | } |
| 184 | |
| 185 | void GCMNetworkChannel::OnGetTokenComplete( |
| 186 | const GoogleServiceAuthError& error, |
[email protected] | 04a830a | 2014-01-04 02:48:51 | [diff] [blame] | 187 | const std::string& token) { |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 188 | DCHECK(CalledOnValidThread()); |
[email protected] | 3b73b25 | 2014-03-01 00:18:54 | [diff] [blame] | 189 | if (cached_message_.empty()) { |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 190 | // Nothing to do. |
| 191 | return; |
| 192 | } |
| 193 | |
| 194 | if (error.state() != GoogleServiceAuthError::NONE) { |
| 195 | // Requesting access token failed. Persistent errors will be reported by |
| 196 | // token service. Just drop this request, cacheinvalidations will retry |
| 197 | // sending message and at that time we'll retry requesting access token. |
| 198 | DVLOG(1) << "RequestAccessToken failed: " << error.ToString(); |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 199 | RecordOutgoingMessageStatus(ACCESS_TOKEN_FAILURE); |
[email protected] | afff175 | 2014-06-20 04:42:10 | [diff] [blame] | 200 | // Message won't get sent. Notify that http channel doesn't work. |
| 201 | UpdateHttpChannelState(false); |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 202 | cached_message_.clear(); |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 203 | return; |
| 204 | } |
| 205 | DCHECK(!token.empty()); |
| 206 | // Save access token in case POST fails and we need to invalidate it. |
| 207 | access_token_ = token; |
| 208 | |
| 209 | DVLOG(2) << "Got access token, sending message"; |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 210 | fetcher_.reset(net::URLFetcher::Create( |
| 211 | BuildUrl(registration_id_), net::URLFetcher::POST, this)); |
dcheng | f0fe8bf | 2014-08-27 18:32:36 | [diff] [blame] | 212 | fetcher_->SetRequestContext(request_context_getter_.get()); |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 213 | const std::string auth_header("Authorization: Bearer " + access_token_); |
| 214 | fetcher_->AddExtraRequestHeader(auth_header); |
[email protected] | 88af124 | 2014-03-12 22:02:09 | [diff] [blame] | 215 | if (!echo_token_.empty()) { |
| 216 | const std::string echo_header("echo-token: " + echo_token_); |
| 217 | fetcher_->AddExtraRequestHeader(echo_header); |
| 218 | } |
[email protected] | 3b73b25 | 2014-03-01 00:18:54 | [diff] [blame] | 219 | fetcher_->SetUploadData("application/x-protobuffer", cached_message_); |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 220 | fetcher_->Start(); |
| 221 | // Clear message to prevent accidentally resending it in the future. |
[email protected] | 3b73b25 | 2014-03-01 00:18:54 | [diff] [blame] | 222 | cached_message_.clear(); |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 223 | } |
| 224 | |
| 225 | void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) { |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 226 | DCHECK(CalledOnValidThread()); |
| 227 | DCHECK_EQ(fetcher_, source); |
| 228 | // Free fetcher at the end of function. |
| 229 | scoped_ptr<net::URLFetcher> fetcher = fetcher_.Pass(); |
| 230 | |
| 231 | net::URLRequestStatus status = fetcher->GetStatus(); |
[email protected] | edfe19f | 2014-03-21 01:38:12 | [diff] [blame] | 232 | diagnostic_info_.last_post_response_code_ = |
| 233 | status.is_success() ? source->GetResponseCode() : status.error(); |
[email protected] | 4c6b34ba | 2014-03-24 02:58:05 | [diff] [blame] | 234 | |
| 235 | if (status.is_success() && |
| 236 | fetcher->GetResponseCode() == net::HTTP_UNAUTHORIZED) { |
| 237 | DVLOG(1) << "URLFetcher failure: HTTP_UNAUTHORIZED"; |
| 238 | delegate_->InvalidateToken(access_token_); |
| 239 | } |
| 240 | |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 241 | if (!status.is_success() || |
| 242 | (fetcher->GetResponseCode() != net::HTTP_OK && |
| 243 | fetcher->GetResponseCode() != net::HTTP_NO_CONTENT)) { |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 244 | DVLOG(1) << "URLFetcher failure"; |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 245 | RecordOutgoingMessageStatus(POST_FAILURE); |
[email protected] | afff175 | 2014-06-20 04:42:10 | [diff] [blame] | 246 | // POST failed. Notify that http channel doesn't work. |
| 247 | UpdateHttpChannelState(false); |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 248 | return; |
| 249 | } |
| 250 | |
[email protected] | 0839ca8 | 2014-03-25 19:55:27 | [diff] [blame] | 251 | RecordOutgoingMessageStatus(OUTGOING_MESSAGE_SUCCESS); |
[email protected] | afff175 | 2014-06-20 04:42:10 | [diff] [blame] | 252 | // Successfully sent message. Http channel works. |
| 253 | UpdateHttpChannelState(true); |
[email protected] | df006cbc | 2014-01-22 18:36:20 | [diff] [blame] | 254 | DVLOG(2) << "URLFetcher success"; |
| 255 | } |
| 256 | |
[email protected] | 6ea3c2a | 2014-03-29 04:14:01 | [diff] [blame] | 257 | void GCMNetworkChannel::OnIncomingMessage(const std::string& message, |
| 258 | const std::string& echo_token) { |
| 259 | #if !defined(OS_ANDROID) |
| 260 | if (!echo_token.empty()) |
| 261 | echo_token_ = echo_token; |
| 262 | diagnostic_info_.last_message_empty_echo_token_ = echo_token.empty(); |
| 263 | diagnostic_info_.last_message_received_time_ = base::Time::Now(); |
| 264 | |
| 265 | if (message.empty()) { |
| 266 | RecordIncomingMessageStatus(MESSAGE_EMPTY); |
| 267 | return; |
| 268 | } |
| 269 | std::string data; |
| 270 | if (!Base64DecodeURLSafe(message, &data)) { |
| 271 | RecordIncomingMessageStatus(INVALID_ENCODING); |
| 272 | return; |
| 273 | } |
| 274 | ipc::invalidation::AddressedAndroidMessage android_message; |
| 275 | if (!android_message.ParseFromString(data) || |
| 276 | !android_message.has_message()) { |
| 277 | RecordIncomingMessageStatus(INVALID_PROTO); |
| 278 | return; |
| 279 | } |
| 280 | DVLOG(2) << "Deliver incoming message"; |
| 281 | RecordIncomingMessageStatus(INCOMING_MESSAGE_SUCCESS); |
[email protected] | afff175 | 2014-06-20 04:42:10 | [diff] [blame] | 282 | UpdateGcmChannelState(true); |
[email protected] | 6ea3c2a | 2014-03-29 04:14:01 | [diff] [blame] | 283 | DeliverIncomingMessage(android_message.message()); |
| 284 | #else |
| 285 | // This code shouldn't be invoked on Android. |
| 286 | NOTREACHED(); |
| 287 | #endif |
| 288 | } |
| 289 | |
[email protected] | afff175 | 2014-06-20 04:42:10 | [diff] [blame] | 290 | void GCMNetworkChannel::OnConnectionStateChanged(bool online) { |
| 291 | UpdateGcmChannelState(online); |
[email protected] | dd507f5 | 2014-06-19 01:27:56 | [diff] [blame] | 292 | } |
| 293 | |
[email protected] | 9c497bf | 2014-03-28 20:59:14 | [diff] [blame] | 294 | void GCMNetworkChannel::OnNetworkChanged( |
| 295 | net::NetworkChangeNotifier::ConnectionType connection_type) { |
| 296 | // Network connection is restored. Let's notify cacheinvalidations so it has |
| 297 | // chance to retry. |
[email protected] | afff175 | 2014-06-20 04:42:10 | [diff] [blame] | 298 | NotifyNetworkStatusChange( |
| 299 | connection_type != net::NetworkChangeNotifier::CONNECTION_NONE); |
| 300 | } |
| 301 | |
| 302 | void GCMNetworkChannel::UpdateGcmChannelState(bool online) { |
| 303 | if (gcm_channel_online_ == online) |
| 304 | return; |
| 305 | gcm_channel_online_ = online; |
| 306 | InvalidatorState channel_state = TRANSIENT_INVALIDATION_ERROR; |
| 307 | if (gcm_channel_online_ && http_channel_online_) |
| 308 | channel_state = INVALIDATIONS_ENABLED; |
| 309 | NotifyChannelStateChange(channel_state); |
| 310 | } |
| 311 | |
| 312 | void GCMNetworkChannel::UpdateHttpChannelState(bool online) { |
| 313 | if (http_channel_online_ == online) |
| 314 | return; |
| 315 | http_channel_online_ = online; |
| 316 | InvalidatorState channel_state = TRANSIENT_INVALIDATION_ERROR; |
| 317 | if (gcm_channel_online_ && http_channel_online_) |
| 318 | channel_state = INVALIDATIONS_ENABLED; |
| 319 | NotifyChannelStateChange(channel_state); |
[email protected] | 9c497bf | 2014-03-28 20:59:14 | [diff] [blame] | 320 | } |
| 321 | |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 322 | GURL GCMNetworkChannel::BuildUrl(const std::string& registration_id) { |
| 323 | DCHECK(!registration_id.empty()); |
| 324 | |
[email protected] | 6ea3c2a | 2014-03-29 04:14:01 | [diff] [blame] | 325 | #if !defined(OS_ANDROID) |
[email protected] | 2618807 | 2014-03-09 04:44:03 | [diff] [blame] | 326 | ipc::invalidation::EndpointId endpoint_id; |
| 327 | endpoint_id.set_c2dm_registration_id(registration_id); |
| 328 | endpoint_id.set_client_key(std::string()); |
| 329 | endpoint_id.set_package_name(kCacheInvalidationPackageName); |
| 330 | endpoint_id.mutable_channel_version()->set_major_version( |
| 331 | ipc::invalidation::INITIAL); |
| 332 | std::string endpoint_id_buffer; |
| 333 | endpoint_id.SerializeToString(&endpoint_id_buffer); |
| 334 | |
| 335 | ipc::invalidation::NetworkEndpointId network_endpoint_id; |
| 336 | network_endpoint_id.set_network_address( |
| 337 | ipc::invalidation::NetworkEndpointId_NetworkAddress_ANDROID); |
| 338 | network_endpoint_id.set_client_address(endpoint_id_buffer); |
| 339 | std::string network_endpoint_id_buffer; |
| 340 | network_endpoint_id.SerializeToString(&network_endpoint_id_buffer); |
| 341 | |
| 342 | std::string base64URLPiece; |
| 343 | Base64EncodeURLSafe(network_endpoint_id_buffer, &base64URLPiece); |
| 344 | |
| 345 | std::string url(kCacheInvalidationEndpointUrl); |
| 346 | url += base64URLPiece; |
| 347 | return GURL(url); |
| 348 | #else |
| 349 | // This code shouldn't be invoked on Android. |
| 350 | NOTREACHED(); |
| 351 | return GURL(); |
| 352 | #endif |
| 353 | } |
| 354 | |
| 355 | void GCMNetworkChannel::Base64EncodeURLSafe(const std::string& input, |
| 356 | std::string* output) { |
| 357 | base::Base64Encode(input, output); |
| 358 | // Covert to url safe alphabet. |
| 359 | base::ReplaceChars(*output, "+", "-", output); |
| 360 | base::ReplaceChars(*output, "/", "_", output); |
| 361 | // Trim padding. |
| 362 | size_t padding_size = 0; |
| 363 | for (size_t i = output->size(); i > 0 && (*output)[i - 1] == '='; --i) |
| 364 | ++padding_size; |
| 365 | output->resize(output->size() - padding_size); |
| 366 | } |
| 367 | |
| 368 | bool GCMNetworkChannel::Base64DecodeURLSafe(const std::string& input, |
| 369 | std::string* output) { |
| 370 | // Add padding. |
| 371 | size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4; |
| 372 | std::string padded_input(input); |
| 373 | padded_input.resize(padded_size, '='); |
| 374 | // Convert to standard base64 alphabet. |
| 375 | base::ReplaceChars(padded_input, "-", "+", &padded_input); |
| 376 | base::ReplaceChars(padded_input, "_", "/", &padded_input); |
| 377 | return base::Base64Decode(padded_input, output); |
[email protected] | 04a830a | 2014-01-04 02:48:51 | [diff] [blame] | 378 | } |
| 379 | |
[email protected] | 6ea3c2a | 2014-03-29 04:14:01 | [diff] [blame] | 380 | void GCMNetworkChannel::SetMessageReceiver( |
| 381 | invalidation::MessageCallback* incoming_receiver) { |
| 382 | delegate_->SetMessageReceiver(base::Bind( |
| 383 | &GCMNetworkChannel::OnIncomingMessage, weak_factory_.GetWeakPtr())); |
| 384 | SyncNetworkChannel::SetMessageReceiver(incoming_receiver); |
| 385 | } |
| 386 | |
| 387 | void GCMNetworkChannel::RequestDetailedStatus( |
| 388 | base::Callback<void(const base::DictionaryValue&)> callback) { |
| 389 | callback.Run(*diagnostic_info_.CollectDebugData()); |
| 390 | } |
| 391 | |
| 392 | void GCMNetworkChannel::UpdateCredentials(const std::string& email, |
| 393 | const std::string& token) { |
| 394 | // Do nothing. We get access token by requesting it for every message. |
| 395 | } |
| 396 | |
[email protected] | 5322cf7 | 2014-05-02 00:08:04 | [diff] [blame] | 397 | int GCMNetworkChannel::GetInvalidationClientType() { |
| 398 | #if defined(OS_IOS) |
| 399 | return ipc::invalidation::ClientType::CHROME_SYNC_GCM_IOS; |
| 400 | #else |
| 401 | return ipc::invalidation::ClientType::CHROME_SYNC_GCM_DESKTOP; |
| 402 | #endif |
| 403 | } |
| 404 | |
[email protected] | 6ea3c2a | 2014-03-29 04:14:01 | [diff] [blame] | 405 | void GCMNetworkChannel::ResetRegisterBackoffEntryForTest( |
| 406 | const net::BackoffEntry::Policy* policy) { |
| 407 | register_backoff_entry_.reset(new net::BackoffEntry(policy)); |
| 408 | } |
| 409 | |
| 410 | GCMNetworkChannelDiagnostic::GCMNetworkChannelDiagnostic( |
| 411 | GCMNetworkChannel* parent) |
| 412 | : parent_(parent), |
| 413 | last_message_empty_echo_token_(false), |
| 414 | last_post_response_code_(0), |
| 415 | registration_result_(gcm::GCMClient::UNKNOWN_ERROR), |
| 416 | sent_messages_count_(0) {} |
| 417 | |
| 418 | scoped_ptr<base::DictionaryValue> |
| 419 | GCMNetworkChannelDiagnostic::CollectDebugData() const { |
| 420 | scoped_ptr<base::DictionaryValue> status(new base::DictionaryValue); |
| 421 | status->SetString("GCMNetworkChannel.Channel", "GCM"); |
| 422 | std::string reg_id_hash = base::SHA1HashString(registration_id_); |
| 423 | status->SetString("GCMNetworkChannel.HashedRegistrationID", |
| 424 | base::HexEncode(reg_id_hash.c_str(), reg_id_hash.size())); |
| 425 | status->SetString("GCMNetworkChannel.RegistrationResult", |
| 426 | GCMClientResultToString(registration_result_)); |
| 427 | status->SetBoolean("GCMNetworkChannel.HadLastMessageEmptyEchoToken", |
| 428 | last_message_empty_echo_token_); |
| 429 | status->SetString( |
| 430 | "GCMNetworkChannel.LastMessageReceivedTime", |
| 431 | base::TimeFormatShortDateAndTime(last_message_received_time_)); |
| 432 | status->SetInteger("GCMNetworkChannel.LastPostResponseCode", |
| 433 | last_post_response_code_); |
| 434 | status->SetInteger("GCMNetworkChannel.SentMessages", sent_messages_count_); |
| 435 | status->SetInteger("GCMNetworkChannel.ReceivedMessages", |
| 436 | parent_->GetReceivedMessagesCount()); |
| 437 | return status.Pass(); |
| 438 | } |
| 439 | |
| 440 | std::string GCMNetworkChannelDiagnostic::GCMClientResultToString( |
| 441 | const gcm::GCMClient::Result result) const { |
| 442 | #define ENUM_CASE(x) case x: return #x; break; |
| 443 | switch (result) { |
| 444 | ENUM_CASE(gcm::GCMClient::SUCCESS); |
| 445 | ENUM_CASE(gcm::GCMClient::NETWORK_ERROR); |
| 446 | ENUM_CASE(gcm::GCMClient::SERVER_ERROR); |
| 447 | ENUM_CASE(gcm::GCMClient::TTL_EXCEEDED); |
| 448 | ENUM_CASE(gcm::GCMClient::UNKNOWN_ERROR); |
| 449 | ENUM_CASE(gcm::GCMClient::NOT_SIGNED_IN); |
| 450 | ENUM_CASE(gcm::GCMClient::INVALID_PARAMETER); |
| 451 | ENUM_CASE(gcm::GCMClient::ASYNC_OPERATION_PENDING); |
[email protected] | 9d7e5c0 | 2014-05-21 03:09:03 | [diff] [blame] | 452 | ENUM_CASE(gcm::GCMClient::GCM_DISABLED); |
[email protected] | 6ea3c2a | 2014-03-29 04:14:01 | [diff] [blame] | 453 | } |
| 454 | NOTREACHED(); |
| 455 | return ""; |
| 456 | } |
| 457 | |
[email protected] | 04a830a | 2014-01-04 02:48:51 | [diff] [blame] | 458 | } // namespace syncer |