| 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
|
|
|