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 "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 Loading... |
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 Loading... |
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 Loading... |
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 DCHECK(status != GCMMessageStatus::GCM_UNRESOLVED); |
| 1523 |
| 1524 // Only send message receipts to GCM if the trial is enabled. |
| 1525 int message_types_to_send = base::GetFieldTrialParamByFeatureAsInt( |
| 1526 kGCMMessageReceiptsFeature, kMessageReceiptsToSend, |
| 1527 MessageReceiptsToSend::SEND_NONE); |
| 1528 |
| 1529 if (message_types_to_send == MessageReceiptsToSend::SEND_NONE) |
| 1530 return; |
| 1531 |
| 1532 if (message_types_to_send == MessageReceiptsToSend::SEND_FAILURES && |
| 1533 (status == GCMMessageStatus::GCM_SUCCESS || |
| 1534 status == GCMMessageStatus::GCM_UNRESOLVED)) { |
| 1535 return; |
| 1536 } |
| 1537 |
| 1538 // Prepare a message to send to GCM which will log the status of the received |
| 1539 // message. This is aggregated by GCM to provide better error alerting. |
| 1540 OutgoingMessage message; |
| 1541 message.time_to_live = kReceiptTTLInSeconds; |
| 1542 message.id = |
| 1543 base::Int64ToString(base::Time::NowFromSystemTime().ToInternalValue()); |
| 1544 message.data[kMessageTypeKey] = kMessageReceiptType; |
| 1545 message.data[kReceiptMessageIdKey] = message_id; |
| 1546 message.data[kReceiptStatusKey] = base::IntToString(static_cast<int>(status)); |
| 1547 |
| 1548 Send(app_id, kReceiptGCMDestinationID, message); |
| 1549 } |
| 1550 |
1478 } // namespace gcm | 1551 } // namespace gcm |
OLD | NEW |