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..6f4913e9e9daa74efd4dada2d982e8c507c55af8 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,36 @@ bool GCMClientImpl::HasStandaloneRegisteredApp() const { |
!ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId); |
} |
+void GCMClientImpl::SendMessageReceipt(const std::string& message_id, |
+ const std::string& app_id, |
+ GCMMessageStatus status) { |
+ DCHECK(status != GCMMessageStatus::GCM_UNRESOLVED); |
+ |
+ // 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); |
+} |
+ |
} // namespace gcm |