Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: components/gcm_driver/gcm_client_impl.cc

Issue 2578583002: Provide a mechanism for the GCM driver to send message receipts to GCM.
Patch Set: Formatting Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 "components/gcm_driver/gcm_client_impl.h" 5 #include "components/gcm_driver/gcm_client_impl.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <memory> 9 #include <memory>
10 #include <utility> 10 #include <utility>
11 11
12 #include "base/bind.h" 12 #include "base/bind.h"
13 #include "base/feature_list.h"
13 #include "base/files/file_path.h" 14 #include "base/files/file_path.h"
14 #include "base/location.h" 15 #include "base/location.h"
15 #include "base/logging.h" 16 #include "base/logging.h"
16 #include "base/memory/ptr_util.h" 17 #include "base/memory/ptr_util.h"
18 #include "base/metrics/field_trial_params.h"
17 #include "base/metrics/histogram_macros.h" 19 #include "base/metrics/histogram_macros.h"
18 #include "base/sequenced_task_runner.h" 20 #include "base/sequenced_task_runner.h"
19 #include "base/single_thread_task_runner.h" 21 #include "base/single_thread_task_runner.h"
20 #include "base/stl_util.h" 22 #include "base/stl_util.h"
21 #include "base/strings/string_number_conversions.h" 23 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/stringprintf.h" 24 #include "base/strings/stringprintf.h"
23 #include "base/threading/thread_task_runner_handle.h" 25 #include "base/threading/thread_task_runner_handle.h"
24 #include "base/time/default_clock.h" 26 #include "base/time/default_clock.h"
27 #include "base/time/time.h"
25 #include "base/timer/timer.h" 28 #include "base/timer/timer.h"
26 #include "components/crx_file/id_util.h" 29 #include "components/crx_file/id_util.h"
27 #include "components/gcm_driver/gcm_account_mapper.h" 30 #include "components/gcm_driver/gcm_account_mapper.h"
28 #include "components/gcm_driver/gcm_backoff_policy.h" 31 #include "components/gcm_driver/gcm_backoff_policy.h"
32 #include "components/gcm_driver/gcm_message_status.h"
29 #include "google_apis/gcm/base/encryptor.h" 33 #include "google_apis/gcm/base/encryptor.h"
30 #include "google_apis/gcm/base/mcs_message.h" 34 #include "google_apis/gcm/base/mcs_message.h"
31 #include "google_apis/gcm/base/mcs_util.h" 35 #include "google_apis/gcm/base/mcs_util.h"
32 #include "google_apis/gcm/engine/checkin_request.h" 36 #include "google_apis/gcm/engine/checkin_request.h"
33 #include "google_apis/gcm/engine/connection_factory_impl.h" 37 #include "google_apis/gcm/engine/connection_factory_impl.h"
34 #include "google_apis/gcm/engine/gcm_registration_request_handler.h" 38 #include "google_apis/gcm/engine/gcm_registration_request_handler.h"
35 #include "google_apis/gcm/engine/gcm_store_impl.h" 39 #include "google_apis/gcm/engine/gcm_store_impl.h"
36 #include "google_apis/gcm/engine/gcm_unregistration_request_handler.h" 40 #include "google_apis/gcm/engine/gcm_unregistration_request_handler.h"
37 #include "google_apis/gcm/engine/instance_id_delete_token_request_handler.h" 41 #include "google_apis/gcm/engine/instance_id_delete_token_request_handler.h"
38 #include "google_apis/gcm/engine/instance_id_get_token_request_handler.h" 42 #include "google_apis/gcm/engine/instance_id_get_token_request_handler.h"
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 76
73 enum ResetStoreError { 77 enum ResetStoreError {
74 DESTROYING_STORE_FAILED, 78 DESTROYING_STORE_FAILED,
75 INFINITE_STORE_RESET, 79 INFINITE_STORE_RESET,
76 // NOTE: always keep this entry at the end. Add new value only immediately 80 // NOTE: always keep this entry at the end. Add new value only immediately
77 // above this line. Make sure to update the corresponding histogram enum 81 // above this line. Make sure to update the corresponding histogram enum
78 // accordingly. 82 // accordingly.
79 RESET_STORE_ERROR_COUNT 83 RESET_STORE_ERROR_COUNT
80 }; 84 };
81 85
86 // Indicates what type of message receipts should be sent to the GCM.
87 enum MessageReceiptsToSend {
88 SEND_NONE = 0,
89 SEND_FAILURES = 1,
90 SEND_ALL = 2,
91 };
92
82 const char kGCMScope[] = "GCM"; 93 const char kGCMScope[] = "GCM";
83 const int kMaxRegistrationRetries = 5; 94 const int kMaxRegistrationRetries = 5;
84 const int kMaxUnregistrationRetries = 5; 95 const int kMaxUnregistrationRetries = 5;
85 const char kDeletedCountKey[] = "total_deleted"; 96 const char kDeletedCountKey[] = "total_deleted";
86 const char kMessageTypeDataMessage[] = "gcm"; 97 const char kMessageTypeDataMessage[] = "gcm";
87 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages"; 98 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
88 const char kMessageTypeKey[] = "message_type"; 99 const char kMessageTypeKey[] = "message_type";
89 const char kMessageTypeSendErrorKey[] = "send_error"; 100 const char kMessageTypeSendErrorKey[] = "send_error";
90 const char kSendErrorMessageIdKey[] = "google.message_id"; 101 const char kSendErrorMessageIdKey[] = "google.message_id";
91 const char kSubtypeKey[] = "subtype"; 102 const char kSubtypeKey[] = "subtype";
92 const char kSendMessageFromValue[] = "gcm@chrome.com"; 103 const char kSendMessageFromValue[] = "gcm@chrome.com";
93 const int64_t kDefaultUserSerialNumber = 0LL; 104 const int64_t kDefaultUserSerialNumber = 0LL;
94 const int kDestroyGCMStoreDelayMS = 5 * 60 * 1000; // 5 minutes. 105 const int kDestroyGCMStoreDelayMS = 5 * 60 * 1000; // 5 minutes.
106 const char kMessageReceiptType[] = "message_receipt";
107 const char kReceiptMessageIdKey[] = "message_id";
108 const char kReceiptStatusKey[] = "status";
109 // kReceiptGCMDestinationID is the destination ID which GCM listens to for
110 // upstream receipt messages.
111 const char kReceiptGCMDestinationID[] = "1029510549786@google.com";
112 const int kReceiptTTLInSeconds = 10;
113 const base::Feature kGCMMessageReceiptsFeature{
114 "GCMMessageReceiptsFeature", base::FEATURE_ENABLED_BY_DEFAULT};
115 const char kMessageReceiptsToSend[] = "message_receipts_to_send";
95 116
96 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) { 117 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
97 switch (status) { 118 switch (status) {
98 case MCSClient::QUEUED: 119 case MCSClient::QUEUED:
99 return GCMClient::SUCCESS; 120 return GCMClient::SUCCESS;
100 case MCSClient::MESSAGE_TOO_LARGE: 121 case MCSClient::MESSAGE_TOO_LARGE:
101 return GCMClient::INVALID_PARAMETER; 122 return GCMClient::INVALID_PARAMETER;
102 case MCSClient::QUEUE_SIZE_LIMIT_REACHED: 123 case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
103 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED: 124 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
104 case MCSClient::NO_CONNECTION_ON_ZERO_TTL: 125 case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
(...skipping 1234 matching lines...) Expand 10 before | Expand all | Expand 10 after
1339 HandleIncomingDataMessage(app_id, use_subtype, data_message_stanza, 1360 HandleIncomingDataMessage(app_id, use_subtype, data_message_stanza,
1340 message_data); 1361 message_data);
1341 break; 1362 break;
1342 case DELETED_MESSAGES: 1363 case DELETED_MESSAGES:
1343 HandleIncomingDeletedMessages(app_id, data_message_stanza, message_data); 1364 HandleIncomingDeletedMessages(app_id, data_message_stanza, message_data);
1344 break; 1365 break;
1345 case SEND_ERROR: 1366 case SEND_ERROR:
1346 HandleIncomingSendError(app_id, data_message_stanza, message_data); 1367 HandleIncomingSendError(app_id, data_message_stanza, message_data);
1347 break; 1368 break;
1348 case UNKNOWN: 1369 case UNKNOWN:
1370 SendMessageReceipt(data_message_stanza.id(), app_id,
1371 GCMMessageStatus::GCM_UNKNOWN_MESSAGE_TYPE);
1349 DVLOG(1) << "Unknown message_type received. Message ignored. " 1372 DVLOG(1) << "Unknown message_type received. Message ignored. "
1350 << "App ID: " << app_id << "."; 1373 << "App ID: " << app_id << ".";
1351 break; 1374 break;
1352 } 1375 }
1353 } 1376 }
1354 1377
1355 void GCMClientImpl::HandleIncomingDataMessage( 1378 void GCMClientImpl::HandleIncomingDataMessage(
1356 const std::string& app_id, 1379 const std::string& app_id,
1357 bool was_subtype, 1380 bool was_subtype,
1358 const mcs_proto::DataMessageStanza& data_message_stanza, 1381 const mcs_proto::DataMessageStanza& data_message_stanza,
1359 MessageData& message_data) { 1382 MessageData& message_data) {
1360 std::string sender = data_message_stanza.from(); 1383 std::string sender = data_message_stanza.from();
1384 GCMMessageStatus status = GCMMessageStatus::GCM_NO_REGISTRATION;
1361 1385
1362 // Drop the message when the app is not registered for the sender of the 1386 // Drop the message when the app is not registered for the sender of the
1363 // message. 1387 // message.
1364 bool registered = false; 1388 bool registered = false;
1365 1389
1366 // First, find among all GCM registrations. 1390 // First, find among all GCM registrations.
1367 std::unique_ptr<GCMRegistrationInfo> gcm_registration( 1391 std::unique_ptr<GCMRegistrationInfo> gcm_registration(
1368 new GCMRegistrationInfo); 1392 new GCMRegistrationInfo);
1369 gcm_registration->app_id = app_id; 1393 gcm_registration->app_id = app_id;
1370 auto gcm_registration_iter = registrations_.find( 1394 auto gcm_registration_iter = registrations_.find(
1371 make_linked_ptr<RegistrationInfo>(gcm_registration.release())); 1395 make_linked_ptr<RegistrationInfo>(gcm_registration.release()));
1372 if (gcm_registration_iter != registrations_.end()) { 1396 if (gcm_registration_iter != registrations_.end()) {
1373 GCMRegistrationInfo* cached_gcm_registration = 1397 GCMRegistrationInfo* cached_gcm_registration =
1374 GCMRegistrationInfo::FromRegistrationInfo( 1398 GCMRegistrationInfo::FromRegistrationInfo(
1375 gcm_registration_iter->first.get()); 1399 gcm_registration_iter->first.get());
1376 if (cached_gcm_registration && 1400 if (cached_gcm_registration) {
1377 std::find(cached_gcm_registration->sender_ids.begin(), 1401 if (std::find(cached_gcm_registration->sender_ids.begin(),
1378 cached_gcm_registration->sender_ids.end(), 1402 cached_gcm_registration->sender_ids.end(),
1379 sender) != cached_gcm_registration->sender_ids.end()) { 1403 sender) != cached_gcm_registration->sender_ids.end()) {
1380 if (was_subtype) 1404 if (was_subtype) {
1381 DLOG(ERROR) << "GCM message for non-IID " << app_id << " used subtype"; 1405 DLOG(ERROR) << "GCM message for non-IID " << app_id
1382 else 1406 << " used subtype";
1383 registered = true; 1407 status = GCMMessageStatus::GCM_INVALID_SUBTYPE;
1408 } else {
1409 registered = true;
1410 }
1411 } else {
1412 DLOG(ERROR) << "GCM message for non-IID " << app_id
1413 << " had a registration but the sender_id " << sender
1414 << " was not valid.";
1415 status = GCMMessageStatus::GCM_INVALID_SENDER_ID;
1416 }
1384 } 1417 }
1385 } 1418 }
1386 1419
1387 // Then, find among all InstanceID registrations. 1420 // Then, find among all InstanceID registrations.
1388 if (!registered) { 1421 if (!registered) {
1389 std::unique_ptr<InstanceIDTokenInfo> instance_id_token( 1422 std::unique_ptr<InstanceIDTokenInfo> instance_id_token(
1390 new InstanceIDTokenInfo); 1423 new InstanceIDTokenInfo);
1391 instance_id_token->app_id = app_id; 1424 instance_id_token->app_id = app_id;
1392 instance_id_token->authorized_entity = sender; 1425 instance_id_token->authorized_entity = sender;
1393 instance_id_token->scope = kGCMScope; 1426 instance_id_token->scope = kGCMScope;
1394 auto instance_id_token_iter = registrations_.find( 1427 auto instance_id_token_iter = registrations_.find(
1395 make_linked_ptr<RegistrationInfo>(instance_id_token.release())); 1428 make_linked_ptr<RegistrationInfo>(instance_id_token.release()));
1396 if (instance_id_token_iter != registrations_.end()) { 1429 if (instance_id_token_iter != registrations_.end()) {
1397 if (was_subtype != InstanceIDUsesSubtypeForAppId(app_id)) { 1430 if (was_subtype != InstanceIDUsesSubtypeForAppId(app_id)) {
1398 DLOG(ERROR) << "GCM message for " << app_id 1431 DLOG(ERROR) << "GCM message for " << app_id
1399 << " incorrectly had was_subtype = " << was_subtype; 1432 << " incorrectly had was_subtype = " << was_subtype;
1433 status = GCMMessageStatus::GCM_INVALID_SUBTYPE;
1400 } else { 1434 } else {
1401 registered = true; 1435 registered = true;
1402 } 1436 }
1403 } 1437 }
1404 } 1438 }
1405 1439
1406 UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceivedHasRegisteredApp", registered); 1440 UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceivedHasRegisteredApp", registered);
1407 if (registered) { 1441 if (registered) {
1408 UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceived", true); 1442 UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceived", true);
1409 bool has_collapse_key = 1443 bool has_collapse_key =
1410 data_message_stanza.has_token() && !data_message_stanza.token().empty(); 1444 data_message_stanza.has_token() && !data_message_stanza.token().empty();
1411 UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceivedHasCollapseKey", 1445 UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceivedHasCollapseKey",
1412 has_collapse_key); 1446 has_collapse_key);
1413 } 1447 }
1414 recorder_.RecordDataMessageReceived(app_id, sender, 1448 recorder_.RecordDataMessageReceived(app_id, sender,
1415 data_message_stanza.ByteSize(), registered, 1449 data_message_stanza.ByteSize(), registered,
1416 GCMStatsRecorder::DATA_MESSAGE); 1450 GCMStatsRecorder::DATA_MESSAGE);
1417 if (!registered) 1451 if (!registered) {
1452 SendMessageReceipt(data_message_stanza.id(), app_id, status);
1418 return; 1453 return;
1454 }
1419 1455
1420 IncomingMessage incoming_message; 1456 IncomingMessage incoming_message;
1421 incoming_message.sender_id = data_message_stanza.from(); 1457 incoming_message.sender_id = data_message_stanza.from();
1422 if (data_message_stanza.has_token()) 1458 if (data_message_stanza.has_token())
1423 incoming_message.collapse_key = data_message_stanza.token(); 1459 incoming_message.collapse_key = data_message_stanza.token();
1424 incoming_message.data = message_data; 1460 incoming_message.data = message_data;
1425 incoming_message.raw_data = data_message_stanza.raw_data(); 1461 incoming_message.raw_data = data_message_stanza.raw_data();
1426 1462
1427 delegate_->OnMessageReceived(app_id, incoming_message); 1463 // Pass the message up to the GCMDriver. Also pass a callback which will send
1464 // a receipt for this message if invoked with a message status.
1465 delegate_->OnMessageReceived(app_id, incoming_message,
1466 base::Bind(&GCMClientImpl::SendMessageReceipt,
1467 weak_ptr_factory_.GetWeakPtr(),
1468 data_message_stanza.id(), app_id));
1428 } 1469 }
1429 1470
1430 void GCMClientImpl::HandleIncomingDeletedMessages( 1471 void GCMClientImpl::HandleIncomingDeletedMessages(
1431 const std::string& app_id, 1472 const std::string& app_id,
1432 const mcs_proto::DataMessageStanza& data_message_stanza, 1473 const mcs_proto::DataMessageStanza& data_message_stanza,
1433 MessageData& message_data) { 1474 MessageData& message_data) {
1434 int deleted_count = 0; 1475 int deleted_count = 0;
1435 MessageData::iterator count_iter = message_data.find(kDeletedCountKey); 1476 MessageData::iterator count_iter = message_data.find(kDeletedCountKey);
1436 if (count_iter != message_data.end()) { 1477 if (count_iter != message_data.end()) {
1437 if (!base::StringToInt(count_iter->second, &deleted_count)) 1478 if (!base::StringToInt(count_iter->second, &deleted_count))
(...skipping 30 matching lines...) Expand all
1468 1509
1469 bool GCMClientImpl::HasStandaloneRegisteredApp() const { 1510 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1470 if (registrations_.empty()) 1511 if (registrations_.empty())
1471 return false; 1512 return false;
1472 // Note that account mapper is not counted as a standalone app since it is 1513 // Note that account mapper is not counted as a standalone app since it is
1473 // automatically started when other app uses GCM. 1514 // automatically started when other app uses GCM.
1474 return registrations_.size() > 1 || 1515 return registrations_.size() > 1 ||
1475 !ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId); 1516 !ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId);
1476 } 1517 }
1477 1518
1519 void GCMClientImpl::SendMessageReceipt(const std::string& message_id,
1520 const std::string& app_id,
1521 GCMMessageStatus status) {
1522 // Only send message receipts to GCM if the trial is enabled.
1523 int message_types_to_send = base::GetFieldTrialParamByFeatureAsInt(
1524 kGCMMessageReceiptsFeature, kMessageReceiptsToSend,
1525 MessageReceiptsToSend::SEND_NONE);
1526
1527 if (message_types_to_send == MessageReceiptsToSend::SEND_NONE)
1528 return;
1529
1530 if (message_types_to_send == MessageReceiptsToSend::SEND_FAILURES &&
1531 (status == GCMMessageStatus::GCM_SUCCESS ||
1532 status == GCMMessageStatus::GCM_UNRESOLVED)) {
1533 return;
1534 }
1535
1536 // Prepare a message to send to GCM which will log the status of the received
1537 // message. This is aggregated by GCM to provide better error alerting.
1538 OutgoingMessage message;
1539 message.time_to_live = kReceiptTTLInSeconds;
1540 message.id =
1541 base::Int64ToString(base::Time::NowFromSystemTime().ToInternalValue());
1542 message.data[kMessageTypeKey] = kMessageReceiptType;
1543 message.data[kReceiptMessageIdKey] = message_id;
1544 message.data[kReceiptStatusKey] = base::IntToString(static_cast<int>(status));
1545
1546 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
1547 }
1548
1478 } // namespace gcm 1549 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698