Chromium Code Reviews| Index: components/gcm_driver/gcm_client_impl.cc | 
| diff --git a/components/gcm_driver/gcm_client_impl.cc b/components/gcm_driver/gcm_client_impl.cc | 
| index 5420646009c631e2568e754586fe0e6360884ad9..e610a6c323b67f5aae5465b1967647a0b6b94403 100644 | 
| --- a/components/gcm_driver/gcm_client_impl.cc | 
| +++ b/components/gcm_driver/gcm_client_impl.cc | 
| @@ -10,10 +10,12 @@ | 
| #include <utility> | 
| #include "base/bind.h" | 
| +#include "base/feature_list.h" | 
| #include "base/files/file_path.h" | 
| #include "base/location.h" | 
| #include "base/logging.h" | 
| #include "base/memory/ptr_util.h" | 
| +#include "base/metrics/field_trial_params.h" | 
| #include "base/metrics/histogram_macros.h" | 
| #include "base/sequenced_task_runner.h" | 
| #include "base/single_thread_task_runner.h" | 
| @@ -22,10 +24,12 @@ | 
| #include "base/strings/stringprintf.h" | 
| #include "base/threading/thread_task_runner_handle.h" | 
| #include "base/time/default_clock.h" | 
| +#include "base/time/time.h" | 
| #include "base/timer/timer.h" | 
| #include "components/crx_file/id_util.h" | 
| #include "components/gcm_driver/gcm_account_mapper.h" | 
| #include "components/gcm_driver/gcm_backoff_policy.h" | 
| +#include "components/gcm_driver/gcm_message_status.h" | 
| #include "google_apis/gcm/base/encryptor.h" | 
| #include "google_apis/gcm/base/mcs_message.h" | 
| #include "google_apis/gcm/base/mcs_util.h" | 
| @@ -79,6 +83,13 @@ enum ResetStoreError { | 
| RESET_STORE_ERROR_COUNT | 
| }; | 
| +// Indicates what type of message receipts should be sent to the GCM. | 
| +enum MessageReceiptsToSend { | 
| + SEND_NONE = 0, | 
| + SEND_FAILURES = 1, | 
| + SEND_ALL = 2, | 
| +}; | 
| + | 
| const char kGCMScope[] = "GCM"; | 
| const int kMaxRegistrationRetries = 5; | 
| const int kMaxUnregistrationRetries = 5; | 
| @@ -92,6 +103,16 @@ const char kSubtypeKey[] = "subtype"; | 
| const char kSendMessageFromValue[] = "gcm@chrome.com"; | 
| const int64_t kDefaultUserSerialNumber = 0LL; | 
| const int kDestroyGCMStoreDelayMS = 5 * 60 * 1000; // 5 minutes. | 
| +const char kMessageReceiptType[] = "message_receipt"; | 
| +const char kReceiptMessageIdKey[] = "message_id"; | 
| +const char kReceiptStatusKey[] = "status"; | 
| +// kReceiptGCMDestinationID is the destination ID which GCM listens to for | 
| +// upstream receipt messages. | 
| +const char kReceiptGCMDestinationID[] = "1029510549786@google.com"; | 
| +const int kReceiptTTLInSeconds = 10; | 
| +const base::Feature kGCMMessageReceiptsFeature{ | 
| + "GCMMessageReceiptsFeature", base::FEATURE_ENABLED_BY_DEFAULT}; | 
| +const char kMessageReceiptsToSend[] = "message_receipts_to_send"; | 
| GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) { | 
| switch (status) { | 
| @@ -1346,6 +1367,8 @@ void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) { | 
| HandleIncomingSendError(app_id, data_message_stanza, message_data); | 
| break; | 
| case UNKNOWN: | 
| + SendMessageReceipt(data_message_stanza.id(), app_id, | 
| + GCMMessageStatus::GCM_UNKNOWN_MESSAGE_TYPE); | 
| DVLOG(1) << "Unknown message_type received. Message ignored. " | 
| << "App ID: " << app_id << "."; | 
| break; | 
| @@ -1358,6 +1381,7 @@ void GCMClientImpl::HandleIncomingDataMessage( | 
| const mcs_proto::DataMessageStanza& data_message_stanza, | 
| MessageData& message_data) { | 
| std::string sender = data_message_stanza.from(); | 
| + GCMMessageStatus status = GCMMessageStatus::GCM_NO_REGISTRATION; | 
| // Drop the message when the app is not registered for the sender of the | 
| // message. | 
| @@ -1373,14 +1397,23 @@ void GCMClientImpl::HandleIncomingDataMessage( | 
| GCMRegistrationInfo* cached_gcm_registration = | 
| GCMRegistrationInfo::FromRegistrationInfo( | 
| gcm_registration_iter->first.get()); | 
| - if (cached_gcm_registration && | 
| - std::find(cached_gcm_registration->sender_ids.begin(), | 
| - cached_gcm_registration->sender_ids.end(), | 
| - sender) != cached_gcm_registration->sender_ids.end()) { | 
| - if (was_subtype) | 
| - DLOG(ERROR) << "GCM message for non-IID " << app_id << " used subtype"; | 
| - else | 
| - registered = true; | 
| + if (cached_gcm_registration) { | 
| + if (std::find(cached_gcm_registration->sender_ids.begin(), | 
| + cached_gcm_registration->sender_ids.end(), | 
| + sender) != cached_gcm_registration->sender_ids.end()) { | 
| + if (was_subtype) { | 
| + DLOG(ERROR) << "GCM message for non-IID " << app_id | 
| + << " used subtype"; | 
| + status = GCMMessageStatus::GCM_INVALID_SUBTYPE; | 
| + } else { | 
| + registered = true; | 
| + } | 
| + } else { | 
| + DLOG(ERROR) << "GCM message for non-IID " << app_id | 
| + << " had a registration but the sender_id " << sender | 
| + << " was not valid."; | 
| + status = GCMMessageStatus::GCM_INVALID_SENDER_ID; | 
| + } | 
| } | 
| } | 
| @@ -1397,6 +1430,7 @@ void GCMClientImpl::HandleIncomingDataMessage( | 
| if (was_subtype != InstanceIDUsesSubtypeForAppId(app_id)) { | 
| DLOG(ERROR) << "GCM message for " << app_id | 
| << " incorrectly had was_subtype = " << was_subtype; | 
| + status = GCMMessageStatus::GCM_INVALID_SUBTYPE; | 
| } else { | 
| registered = true; | 
| } | 
| @@ -1414,8 +1448,10 @@ void GCMClientImpl::HandleIncomingDataMessage( | 
| recorder_.RecordDataMessageReceived(app_id, sender, | 
| data_message_stanza.ByteSize(), registered, | 
| GCMStatsRecorder::DATA_MESSAGE); | 
| - if (!registered) | 
| + if (!registered) { | 
| + SendMessageReceipt(data_message_stanza.id(), app_id, status); | 
| return; | 
| + } | 
| IncomingMessage incoming_message; | 
| incoming_message.sender_id = data_message_stanza.from(); | 
| @@ -1424,7 +1460,12 @@ void GCMClientImpl::HandleIncomingDataMessage( | 
| incoming_message.data = message_data; | 
| incoming_message.raw_data = data_message_stanza.raw_data(); | 
| - delegate_->OnMessageReceived(app_id, incoming_message); | 
| + // Pass the message up to the GCMDriver. Also pass a callback which will send | 
| + // a receipt for this message if invoked with a message status. | 
| + delegate_->OnMessageReceived(app_id, incoming_message, | 
| + base::Bind(&GCMClientImpl::SendMessageReceipt, | 
| + weak_ptr_factory_.GetWeakPtr(), | 
| + data_message_stanza.id(), app_id)); | 
| } | 
| void GCMClientImpl::HandleIncomingDeletedMessages( | 
| @@ -1475,4 +1516,34 @@ bool GCMClientImpl::HasStandaloneRegisteredApp() const { | 
| !ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId); | 
| } | 
| +void GCMClientImpl::SendMessageReceipt(const std::string& message_id, | 
| + const std::string& app_id, | 
| + GCMMessageStatus status) { | 
| + // Only send message receipts to GCM if the trial is enabled. | 
| + int message_types_to_send = base::GetFieldTrialParamByFeatureAsInt( | 
| + kGCMMessageReceiptsFeature, kMessageReceiptsToSend, | 
| + MessageReceiptsToSend::SEND_NONE); | 
| + | 
| + if (message_types_to_send == MessageReceiptsToSend::SEND_NONE) | 
| + return; | 
| + | 
| + if (message_types_to_send == MessageReceiptsToSend::SEND_FAILURES && | 
| + (status == GCMMessageStatus::GCM_SUCCESS || | 
| + status == GCMMessageStatus::GCM_UNRESOLVED)) { | 
| + return; | 
| + } | 
| + | 
| + // Prepare a message to send to GCM which will log the status of the received | 
| + // message. This is aggregated by GCM to provide better error alerting. | 
| + OutgoingMessage message; | 
| + message.time_to_live = kReceiptTTLInSeconds; | 
| + message.id = | 
| + base::Int64ToString(base::Time::NowFromSystemTime().ToInternalValue()); | 
| + message.data[kMessageTypeKey] = kMessageReceiptType; | 
| + message.data[kReceiptMessageIdKey] = message_id; | 
| + message.data[kReceiptStatusKey] = base::IntToString(static_cast<int>(status)); | 
| + | 
| + Send(app_id, kReceiptGCMDestinationID, message); | 
| 
 
Peter Beverloo
2017/02/15 15:17:15
Does the server ack this? (I.e. do we bail out in
 
harkness
2017/02/22 17:19:32
At the moment, this will be acknowledged and the A
 
Peter Beverloo
2017/02/22 17:24:23
That is not OK per the contract of OnSendAcknowled
 
 | 
| +} | 
| + | 
| } // namespace gcm |