OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/base64.h" | 5 #include "base/base64.h" |
6 #include "base/i18n/time_formatting.h" | 6 #include "base/i18n/time_formatting.h" |
7 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
8 #include "base/sha1.h" | 8 #include "base/sha1.h" |
9 #include "base/strings/string_number_conversions.h" | 9 #include "base/strings/string_number_conversions.h" |
10 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
11 #if !defined(ANDROID) | 11 #if !defined(OS_ANDROID) |
12 // channel_common.proto defines ANDROID constant that conflicts with Android | 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 | 13 // build. At the same time TiclInvalidationService is not used on Android so it |
14 // is safe to exclude these protos from Android build. | 14 // is safe to exclude these protos from Android build. |
15 #include "google/cacheinvalidation/android_channel.pb.h" | 15 #include "google/cacheinvalidation/android_channel.pb.h" |
16 #include "google/cacheinvalidation/channel_common.pb.h" | 16 #include "google/cacheinvalidation/channel_common.pb.h" |
17 #endif | 17 #endif |
18 #include "google_apis/gaia/google_service_auth_error.h" | 18 #include "google_apis/gaia/google_service_auth_error.h" |
19 #include "net/http/http_status_code.h" | 19 #include "net/http/http_status_code.h" |
20 #include "net/url_request/url_fetcher.h" | 20 #include "net/url_request/url_fetcher.h" |
21 #include "net/url_request/url_request_status.h" | 21 #include "net/url_request/url_request_status.h" |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
93 } | 93 } |
94 | 94 |
95 void RecordOutgoingMessageStatus(OutgoingMessageStatus status) { | 95 void RecordOutgoingMessageStatus(OutgoingMessageStatus status) { |
96 UMA_HISTOGRAM_ENUMERATION(kOutgoingMessageStatusHistogram, | 96 UMA_HISTOGRAM_ENUMERATION(kOutgoingMessageStatusHistogram, |
97 MESSAGE_DISCARDED, | 97 MESSAGE_DISCARDED, |
98 OUTGOING_MESSAGE_STATUS_COUNT); | 98 OUTGOING_MESSAGE_STATUS_COUNT); |
99 } | 99 } |
100 | 100 |
101 } // namespace | 101 } // namespace |
102 | 102 |
103 GCMNetworkChannelDiagnostic::GCMNetworkChannelDiagnostic( | |
104 GCMNetworkChannel* parent) | |
105 : parent_(parent), | |
106 last_message_empty_echo_token_(false), | |
107 last_post_response_code_(0), | |
108 registration_result_(gcm::GCMClient::UNKNOWN_ERROR), | |
109 sent_messages_count_(0) {} | |
110 | |
111 scoped_ptr<base::DictionaryValue> | |
112 GCMNetworkChannelDiagnostic::CollectDebugData() const { | |
113 scoped_ptr<base::DictionaryValue> status(new base::DictionaryValue); | |
114 status->SetString("GCMNetworkChannel.Channel", "GCM"); | |
115 std::string reg_id_hash = base::SHA1HashString(registration_id_); | |
116 status->SetString("GCMNetworkChannel.HashedRegistrationID", | |
117 base::HexEncode(reg_id_hash.c_str(), reg_id_hash.size())); | |
118 status->SetString("GCMNetworkChannel.RegistrationResult", | |
119 GCMClientResultToString(registration_result_)); | |
120 status->SetBoolean("GCMNetworkChannel.HadLastMessageEmptyEchoToken", | |
121 last_message_empty_echo_token_); | |
122 status->SetString( | |
123 "GCMNetworkChannel.LastMessageReceivedTime", | |
124 base::TimeFormatShortDateAndTime(last_message_received_time_)); | |
125 status->SetInteger("GCMNetworkChannel.LastPostResponseCode", | |
126 last_post_response_code_); | |
127 status->SetInteger("GCMNetworkChannel.SentMessages", sent_messages_count_); | |
128 status->SetInteger("GCMNetworkChannel.ReceivedMessages", | |
129 parent_->GetReceivedMessagesCount()); | |
130 return status.Pass(); | |
131 } | |
132 | |
133 std::string GCMNetworkChannelDiagnostic::GCMClientResultToString( | |
134 const gcm::GCMClient::Result result) const { | |
135 #define ENUM_CASE(x) case x: return #x; break; | |
136 switch (result) { | |
137 ENUM_CASE(gcm::GCMClient::SUCCESS); | |
138 ENUM_CASE(gcm::GCMClient::NETWORK_ERROR); | |
139 ENUM_CASE(gcm::GCMClient::SERVER_ERROR); | |
140 ENUM_CASE(gcm::GCMClient::TTL_EXCEEDED); | |
141 ENUM_CASE(gcm::GCMClient::UNKNOWN_ERROR); | |
142 ENUM_CASE(gcm::GCMClient::NOT_SIGNED_IN); | |
143 ENUM_CASE(gcm::GCMClient::INVALID_PARAMETER); | |
144 ENUM_CASE(gcm::GCMClient::ASYNC_OPERATION_PENDING); | |
145 } | |
146 NOTREACHED(); | |
147 return ""; | |
148 } | |
149 | |
150 GCMNetworkChannel::GCMNetworkChannel( | 103 GCMNetworkChannel::GCMNetworkChannel( |
151 scoped_refptr<net::URLRequestContextGetter> request_context_getter, | 104 scoped_refptr<net::URLRequestContextGetter> request_context_getter, |
152 scoped_ptr<GCMNetworkChannelDelegate> delegate) | 105 scoped_ptr<GCMNetworkChannelDelegate> delegate) |
153 : request_context_getter_(request_context_getter), | 106 : request_context_getter_(request_context_getter), |
154 delegate_(delegate.Pass()), | 107 delegate_(delegate.Pass()), |
155 register_backoff_entry_(new net::BackoffEntry(&kRegisterBackoffPolicy)), | 108 register_backoff_entry_(new net::BackoffEntry(&kRegisterBackoffPolicy)), |
156 diagnostic_info_(this), | 109 diagnostic_info_(this), |
157 weak_factory_(this) { | 110 weak_factory_(this) { |
158 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); | 111 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); |
159 delegate_->Initialize(); | 112 delegate_->Initialize(); |
160 Register(); | 113 Register(); |
161 } | 114 } |
162 | 115 |
163 GCMNetworkChannel::~GCMNetworkChannel() { | 116 GCMNetworkChannel::~GCMNetworkChannel() { |
164 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); | 117 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
165 } | 118 } |
166 | 119 |
167 void GCMNetworkChannel::UpdateCredentials( | |
168 const std::string& email, | |
169 const std::string& token) { | |
170 // Do nothing. We get access token by requesting it for every message. | |
171 } | |
172 | |
173 void GCMNetworkChannel::RequestDetailedStatus( | |
174 base::Callback<void(const base::DictionaryValue&)> callback) { | |
175 callback.Run(*diagnostic_info_.CollectDebugData()); | |
176 } | |
177 | |
178 void GCMNetworkChannel::ResetRegisterBackoffEntryForTest( | |
179 const net::BackoffEntry::Policy* policy) { | |
180 register_backoff_entry_.reset(new net::BackoffEntry(policy)); | |
181 } | |
182 | |
183 void GCMNetworkChannel::Register() { | 120 void GCMNetworkChannel::Register() { |
184 delegate_->Register(base::Bind(&GCMNetworkChannel::OnRegisterComplete, | 121 delegate_->Register(base::Bind(&GCMNetworkChannel::OnRegisterComplete, |
185 weak_factory_.GetWeakPtr())); | 122 weak_factory_.GetWeakPtr())); |
186 } | 123 } |
187 | 124 |
188 void GCMNetworkChannel::OnRegisterComplete( | 125 void GCMNetworkChannel::OnRegisterComplete( |
189 const std::string& registration_id, | 126 const std::string& registration_id, |
190 gcm::GCMClient::Result result) { | 127 gcm::GCMClient::Result result) { |
191 DCHECK(CalledOnValidThread()); | 128 DCHECK(CalledOnValidThread()); |
192 if (result == gcm::GCMClient::SUCCESS) { | 129 if (result == gcm::GCMClient::SUCCESS) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 if (!cached_message_.empty()) { | 165 if (!cached_message_.empty()) { |
229 RecordOutgoingMessageStatus(MESSAGE_DISCARDED); | 166 RecordOutgoingMessageStatus(MESSAGE_DISCARDED); |
230 } | 167 } |
231 cached_message_ = message; | 168 cached_message_ = message; |
232 | 169 |
233 if (!registration_id_.empty()) { | 170 if (!registration_id_.empty()) { |
234 RequestAccessToken(); | 171 RequestAccessToken(); |
235 } | 172 } |
236 } | 173 } |
237 | 174 |
238 void GCMNetworkChannel::SetMessageReceiver( | |
239 invalidation::MessageCallback* incoming_receiver) { | |
240 delegate_->SetMessageReceiver(base::Bind( | |
241 &GCMNetworkChannel::OnIncomingMessage, weak_factory_.GetWeakPtr())); | |
242 SyncNetworkChannel::SetMessageReceiver(incoming_receiver); | |
243 } | |
244 | |
245 void GCMNetworkChannel::RequestAccessToken() { | 175 void GCMNetworkChannel::RequestAccessToken() { |
246 DCHECK(CalledOnValidThread()); | 176 DCHECK(CalledOnValidThread()); |
247 delegate_->RequestToken(base::Bind(&GCMNetworkChannel::OnGetTokenComplete, | 177 delegate_->RequestToken(base::Bind(&GCMNetworkChannel::OnGetTokenComplete, |
248 weak_factory_.GetWeakPtr())); | 178 weak_factory_.GetWeakPtr())); |
249 } | 179 } |
250 | 180 |
251 void GCMNetworkChannel::OnGetTokenComplete( | 181 void GCMNetworkChannel::OnGetTokenComplete( |
252 const GoogleServiceAuthError& error, | 182 const GoogleServiceAuthError& error, |
253 const std::string& token) { | 183 const std::string& token) { |
254 DCHECK(CalledOnValidThread()); | 184 DCHECK(CalledOnValidThread()); |
(...skipping 28 matching lines...) Expand all Loading... |
283 if (!echo_token_.empty()) { | 213 if (!echo_token_.empty()) { |
284 const std::string echo_header("echo-token: " + echo_token_); | 214 const std::string echo_header("echo-token: " + echo_token_); |
285 fetcher_->AddExtraRequestHeader(echo_header); | 215 fetcher_->AddExtraRequestHeader(echo_header); |
286 } | 216 } |
287 fetcher_->SetUploadData("application/x-protobuffer", cached_message_); | 217 fetcher_->SetUploadData("application/x-protobuffer", cached_message_); |
288 fetcher_->Start(); | 218 fetcher_->Start(); |
289 // Clear message to prevent accidentally resending it in the future. | 219 // Clear message to prevent accidentally resending it in the future. |
290 cached_message_.clear(); | 220 cached_message_.clear(); |
291 } | 221 } |
292 | 222 |
| 223 void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) { |
| 224 DCHECK(CalledOnValidThread()); |
| 225 DCHECK_EQ(fetcher_, source); |
| 226 // Free fetcher at the end of function. |
| 227 scoped_ptr<net::URLFetcher> fetcher = fetcher_.Pass(); |
| 228 |
| 229 net::URLRequestStatus status = fetcher->GetStatus(); |
| 230 diagnostic_info_.last_post_response_code_ = |
| 231 status.is_success() ? source->GetResponseCode() : status.error(); |
| 232 |
| 233 if (status.is_success() && |
| 234 fetcher->GetResponseCode() == net::HTTP_UNAUTHORIZED) { |
| 235 DVLOG(1) << "URLFetcher failure: HTTP_UNAUTHORIZED"; |
| 236 delegate_->InvalidateToken(access_token_); |
| 237 } |
| 238 |
| 239 if (!status.is_success() || |
| 240 (fetcher->GetResponseCode() != net::HTTP_OK && |
| 241 fetcher->GetResponseCode() != net::HTTP_NO_CONTENT)) { |
| 242 DVLOG(1) << "URLFetcher failure"; |
| 243 RecordOutgoingMessageStatus(POST_FAILURE); |
| 244 NotifyStateChange(TRANSIENT_INVALIDATION_ERROR); |
| 245 return; |
| 246 } |
| 247 |
| 248 RecordOutgoingMessageStatus(OUTGOING_MESSAGE_SUCCESS); |
| 249 NotifyStateChange(INVALIDATIONS_ENABLED); |
| 250 DVLOG(2) << "URLFetcher success"; |
| 251 } |
| 252 |
293 void GCMNetworkChannel::OnIncomingMessage(const std::string& message, | 253 void GCMNetworkChannel::OnIncomingMessage(const std::string& message, |
294 const std::string& echo_token) { | 254 const std::string& echo_token) { |
295 #if !defined(ANDROID) | 255 #if !defined(OS_ANDROID) |
296 if (!echo_token.empty()) | 256 if (!echo_token.empty()) |
297 echo_token_ = echo_token; | 257 echo_token_ = echo_token; |
298 diagnostic_info_.last_message_empty_echo_token_ = echo_token.empty(); | 258 diagnostic_info_.last_message_empty_echo_token_ = echo_token.empty(); |
299 diagnostic_info_.last_message_received_time_ = base::Time::Now(); | 259 diagnostic_info_.last_message_received_time_ = base::Time::Now(); |
300 | 260 |
301 if (message.empty()) { | 261 if (message.empty()) { |
302 RecordIncomingMessageStatus(MESSAGE_EMPTY); | 262 RecordIncomingMessageStatus(MESSAGE_EMPTY); |
303 return; | 263 return; |
304 } | 264 } |
305 std::string data; | 265 std::string data; |
306 if (!Base64DecodeURLSafe(message, &data)) { | 266 if (!Base64DecodeURLSafe(message, &data)) { |
307 RecordIncomingMessageStatus(INVALID_ENCODING); | 267 RecordIncomingMessageStatus(INVALID_ENCODING); |
308 return; | 268 return; |
309 } | 269 } |
310 ipc::invalidation::AddressedAndroidMessage android_message; | 270 ipc::invalidation::AddressedAndroidMessage android_message; |
311 if (!android_message.ParseFromString(data) || | 271 if (!android_message.ParseFromString(data) || |
312 !android_message.has_message()) { | 272 !android_message.has_message()) { |
313 RecordIncomingMessageStatus(INVALID_PROTO); | 273 RecordIncomingMessageStatus(INVALID_PROTO); |
314 return; | 274 return; |
315 } | 275 } |
316 DVLOG(2) << "Deliver incoming message"; | 276 DVLOG(2) << "Deliver incoming message"; |
317 RecordIncomingMessageStatus(INCOMING_MESSAGE_SUCCESS); | 277 RecordIncomingMessageStatus(INCOMING_MESSAGE_SUCCESS); |
318 DeliverIncomingMessage(android_message.message()); | 278 DeliverIncomingMessage(android_message.message()); |
319 #else | 279 #else |
320 // This code shouldn't be invoked on Android. | 280 // This code shouldn't be invoked on Android. |
321 NOTREACHED(); | 281 NOTREACHED(); |
322 #endif | 282 #endif |
323 } | 283 } |
324 | 284 |
325 void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) { | |
326 DCHECK(CalledOnValidThread()); | |
327 DCHECK_EQ(fetcher_, source); | |
328 // Free fetcher at the end of function. | |
329 scoped_ptr<net::URLFetcher> fetcher = fetcher_.Pass(); | |
330 | |
331 net::URLRequestStatus status = fetcher->GetStatus(); | |
332 diagnostic_info_.last_post_response_code_ = | |
333 status.is_success() ? source->GetResponseCode() : status.error(); | |
334 | |
335 if (status.is_success() && | |
336 fetcher->GetResponseCode() == net::HTTP_UNAUTHORIZED) { | |
337 DVLOG(1) << "URLFetcher failure: HTTP_UNAUTHORIZED"; | |
338 delegate_->InvalidateToken(access_token_); | |
339 } | |
340 | |
341 if (!status.is_success() || | |
342 (fetcher->GetResponseCode() != net::HTTP_OK && | |
343 fetcher->GetResponseCode() != net::HTTP_NO_CONTENT)) { | |
344 DVLOG(1) << "URLFetcher failure"; | |
345 RecordOutgoingMessageStatus(POST_FAILURE); | |
346 NotifyStateChange(TRANSIENT_INVALIDATION_ERROR); | |
347 return; | |
348 } | |
349 | |
350 RecordOutgoingMessageStatus(OUTGOING_MESSAGE_SUCCESS); | |
351 NotifyStateChange(INVALIDATIONS_ENABLED); | |
352 DVLOG(2) << "URLFetcher success"; | |
353 } | |
354 | |
355 void GCMNetworkChannel::OnNetworkChanged( | 285 void GCMNetworkChannel::OnNetworkChanged( |
356 net::NetworkChangeNotifier::ConnectionType connection_type) { | 286 net::NetworkChangeNotifier::ConnectionType connection_type) { |
357 // Network connection is restored. Let's notify cacheinvalidations so it has | 287 // Network connection is restored. Let's notify cacheinvalidations so it has |
358 // chance to retry. | 288 // chance to retry. |
359 if (connection_type != net::NetworkChangeNotifier::CONNECTION_NONE) | 289 if (connection_type != net::NetworkChangeNotifier::CONNECTION_NONE) |
360 NotifyStateChange(INVALIDATIONS_ENABLED); | 290 NotifyStateChange(INVALIDATIONS_ENABLED); |
361 } | 291 } |
362 | 292 |
363 GURL GCMNetworkChannel::BuildUrl(const std::string& registration_id) { | 293 GURL GCMNetworkChannel::BuildUrl(const std::string& registration_id) { |
364 DCHECK(!registration_id.empty()); | 294 DCHECK(!registration_id.empty()); |
365 | 295 |
366 #if !defined(ANDROID) | 296 #if !defined(OS_ANDROID) |
367 ipc::invalidation::EndpointId endpoint_id; | 297 ipc::invalidation::EndpointId endpoint_id; |
368 endpoint_id.set_c2dm_registration_id(registration_id); | 298 endpoint_id.set_c2dm_registration_id(registration_id); |
369 endpoint_id.set_client_key(std::string()); | 299 endpoint_id.set_client_key(std::string()); |
370 endpoint_id.set_package_name(kCacheInvalidationPackageName); | 300 endpoint_id.set_package_name(kCacheInvalidationPackageName); |
371 endpoint_id.mutable_channel_version()->set_major_version( | 301 endpoint_id.mutable_channel_version()->set_major_version( |
372 ipc::invalidation::INITIAL); | 302 ipc::invalidation::INITIAL); |
373 std::string endpoint_id_buffer; | 303 std::string endpoint_id_buffer; |
374 endpoint_id.SerializeToString(&endpoint_id_buffer); | 304 endpoint_id.SerializeToString(&endpoint_id_buffer); |
375 | 305 |
376 ipc::invalidation::NetworkEndpointId network_endpoint_id; | 306 ipc::invalidation::NetworkEndpointId network_endpoint_id; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
411 // Add padding. | 341 // Add padding. |
412 size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4; | 342 size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4; |
413 std::string padded_input(input); | 343 std::string padded_input(input); |
414 padded_input.resize(padded_size, '='); | 344 padded_input.resize(padded_size, '='); |
415 // Convert to standard base64 alphabet. | 345 // Convert to standard base64 alphabet. |
416 base::ReplaceChars(padded_input, "-", "+", &padded_input); | 346 base::ReplaceChars(padded_input, "-", "+", &padded_input); |
417 base::ReplaceChars(padded_input, "_", "/", &padded_input); | 347 base::ReplaceChars(padded_input, "_", "/", &padded_input); |
418 return base::Base64Decode(padded_input, output); | 348 return base::Base64Decode(padded_input, output); |
419 } | 349 } |
420 | 350 |
| 351 void GCMNetworkChannel::SetMessageReceiver( |
| 352 invalidation::MessageCallback* incoming_receiver) { |
| 353 delegate_->SetMessageReceiver(base::Bind( |
| 354 &GCMNetworkChannel::OnIncomingMessage, weak_factory_.GetWeakPtr())); |
| 355 SyncNetworkChannel::SetMessageReceiver(incoming_receiver); |
| 356 } |
| 357 |
| 358 void GCMNetworkChannel::RequestDetailedStatus( |
| 359 base::Callback<void(const base::DictionaryValue&)> callback) { |
| 360 callback.Run(*diagnostic_info_.CollectDebugData()); |
| 361 } |
| 362 |
| 363 void GCMNetworkChannel::UpdateCredentials(const std::string& email, |
| 364 const std::string& token) { |
| 365 // Do nothing. We get access token by requesting it for every message. |
| 366 } |
| 367 |
| 368 void GCMNetworkChannel::ResetRegisterBackoffEntryForTest( |
| 369 const net::BackoffEntry::Policy* policy) { |
| 370 register_backoff_entry_.reset(new net::BackoffEntry(policy)); |
| 371 } |
| 372 |
| 373 GCMNetworkChannelDiagnostic::GCMNetworkChannelDiagnostic( |
| 374 GCMNetworkChannel* parent) |
| 375 : parent_(parent), |
| 376 last_message_empty_echo_token_(false), |
| 377 last_post_response_code_(0), |
| 378 registration_result_(gcm::GCMClient::UNKNOWN_ERROR), |
| 379 sent_messages_count_(0) {} |
| 380 |
| 381 scoped_ptr<base::DictionaryValue> |
| 382 GCMNetworkChannelDiagnostic::CollectDebugData() const { |
| 383 scoped_ptr<base::DictionaryValue> status(new base::DictionaryValue); |
| 384 status->SetString("GCMNetworkChannel.Channel", "GCM"); |
| 385 std::string reg_id_hash = base::SHA1HashString(registration_id_); |
| 386 status->SetString("GCMNetworkChannel.HashedRegistrationID", |
| 387 base::HexEncode(reg_id_hash.c_str(), reg_id_hash.size())); |
| 388 status->SetString("GCMNetworkChannel.RegistrationResult", |
| 389 GCMClientResultToString(registration_result_)); |
| 390 status->SetBoolean("GCMNetworkChannel.HadLastMessageEmptyEchoToken", |
| 391 last_message_empty_echo_token_); |
| 392 status->SetString( |
| 393 "GCMNetworkChannel.LastMessageReceivedTime", |
| 394 base::TimeFormatShortDateAndTime(last_message_received_time_)); |
| 395 status->SetInteger("GCMNetworkChannel.LastPostResponseCode", |
| 396 last_post_response_code_); |
| 397 status->SetInteger("GCMNetworkChannel.SentMessages", sent_messages_count_); |
| 398 status->SetInteger("GCMNetworkChannel.ReceivedMessages", |
| 399 parent_->GetReceivedMessagesCount()); |
| 400 return status.Pass(); |
| 401 } |
| 402 |
| 403 std::string GCMNetworkChannelDiagnostic::GCMClientResultToString( |
| 404 const gcm::GCMClient::Result result) const { |
| 405 #define ENUM_CASE(x) case x: return #x; break; |
| 406 switch (result) { |
| 407 ENUM_CASE(gcm::GCMClient::SUCCESS); |
| 408 ENUM_CASE(gcm::GCMClient::NETWORK_ERROR); |
| 409 ENUM_CASE(gcm::GCMClient::SERVER_ERROR); |
| 410 ENUM_CASE(gcm::GCMClient::TTL_EXCEEDED); |
| 411 ENUM_CASE(gcm::GCMClient::UNKNOWN_ERROR); |
| 412 ENUM_CASE(gcm::GCMClient::NOT_SIGNED_IN); |
| 413 ENUM_CASE(gcm::GCMClient::INVALID_PARAMETER); |
| 414 ENUM_CASE(gcm::GCMClient::ASYNC_OPERATION_PENDING); |
| 415 } |
| 416 NOTREACHED(); |
| 417 return ""; |
| 418 } |
| 419 |
421 } // namespace syncer | 420 } // namespace syncer |
OLD | NEW |