fcm_network_handler.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/invalidation/impl/fcm_network_handler.h"
  5. #include <memory>
  6. #include <string>
  7. #include "base/base64url.h"
  8. #include "base/bind.h"
  9. #include "base/callback.h"
  10. #include "base/feature_list.h"
  11. #include "base/i18n/time_formatting.h"
  12. #include "base/metrics/histogram_functions.h"
  13. #include "base/metrics/histogram_macros.h"
  14. #include "base/strings/string_number_conversions.h"
  15. #include "base/strings/string_piece.h"
  16. #include "base/strings/string_util.h"
  17. #include "build/build_config.h"
  18. #include "components/gcm_driver/gcm_driver.h"
  19. #include "components/gcm_driver/gcm_profile_service.h"
  20. #include "components/gcm_driver/instance_id/instance_id.h"
  21. #include "components/gcm_driver/instance_id/instance_id_driver.h"
  22. #include "components/invalidation/impl/invalidation_switches.h"
  23. #include "components/invalidation/impl/status.h"
  24. #include "components/invalidation/public/invalidator_state.h"
  25. using instance_id::InstanceID;
  26. namespace invalidation {
  27. namespace {
  28. const char kPayloadKey[] = "payload";
  29. const char kPublicTopic[] = "external_name";
  30. const char kVersionKey[] = "version";
  31. // OAuth2 Scope passed to getToken to obtain GCM registration tokens.
  32. // Must match Java GoogleCloudMessaging.INSTANCE_ID_SCOPE.
  33. const char kGCMScope[] = "GCM";
  34. // Lower bound time between two token validations when listening.
  35. const int kTokenValidationPeriodMinutesDefault = 60 * 24;
  36. // Returns the TTL (time-to-live) for the Instance ID token, or 0 if no TTL
  37. // should be specified.
  38. base::TimeDelta GetTimeToLive(const std::string& sender_id) {
  39. // This magic value is identical to kInvalidationGCMSenderId, i.e. the value
  40. // that Sync uses for its invalidations.
  41. if (sender_id == "8181035976") {
  42. if (!base::FeatureList::IsEnabled(switches::kSyncInstanceIDTokenTTL)) {
  43. return base::TimeDelta();
  44. }
  45. return base::Seconds(switches::kSyncInstanceIDTokenTTLSeconds.Get());
  46. }
  47. // This magic value is identical to kPolicyFCMInvalidationSenderID, i.e. the
  48. // value that ChromeOS policy uses for its invalidations.
  49. if (sender_id == "1013309121859") {
  50. if (!base::FeatureList::IsEnabled(switches::kPolicyInstanceIDTokenTTL)) {
  51. return base::TimeDelta();
  52. }
  53. return base::Seconds(switches::kPolicyInstanceIDTokenTTLSeconds.Get());
  54. }
  55. // The default for all other FCM clients is no TTL.
  56. return base::TimeDelta();
  57. }
  58. std::string GetValueFromMessage(const gcm::IncomingMessage& message,
  59. const std::string& key) {
  60. std::string value;
  61. auto it = message.data.find(key);
  62. if (it != message.data.end())
  63. value = it->second;
  64. return value;
  65. }
  66. // Unpacks the private topic included in messages to the form returned for
  67. // subscription requests.
  68. //
  69. // Subscriptions for private topics generate a private topic from the public
  70. // topic of the form "/private/${public_topic}-${something}. Messages include
  71. // this as the sender in the form
  72. // "/topics/private/${public_topic}-${something}". For such messages, strip the
  73. // "/topics" prefix.
  74. //
  75. // Subscriptions for public topics pass-through the public topic unchanged:
  76. // "${public_topic}". Messages include the sender in the form
  77. // "/topics/${public_topic}". For these messages, strip the "/topics/" prefix.
  78. //
  79. // If the provided sender does not match either pattern, return it unchanged.
  80. std::string UnpackPrivateTopic(base::StringPiece private_topic) {
  81. if (base::StartsWith(private_topic, "/topics/private/")) {
  82. return std::string(private_topic.substr(strlen("/topics")));
  83. } else if (base::StartsWith(private_topic, "/topics/")) {
  84. return std::string(private_topic.substr(strlen("/topics/")));
  85. } else {
  86. return std::string(private_topic);
  87. }
  88. }
  89. InvalidationParsingStatus ParseIncomingMessage(
  90. const gcm::IncomingMessage& message,
  91. std::string* payload,
  92. std::string* private_topic,
  93. std::string* public_topic,
  94. int64_t* version) {
  95. *payload = GetValueFromMessage(message, kPayloadKey);
  96. std::string version_str = GetValueFromMessage(message, kVersionKey);
  97. // Version must always be there, and be an integer.
  98. if (version_str.empty())
  99. return InvalidationParsingStatus::kVersionEmpty;
  100. if (!base::StringToInt64(version_str, version))
  101. return InvalidationParsingStatus::kVersionInvalid;
  102. *public_topic = GetValueFromMessage(message, kPublicTopic);
  103. *private_topic = UnpackPrivateTopic(message.sender_id);
  104. if (private_topic->empty())
  105. return InvalidationParsingStatus::kPrivateTopicEmpty;
  106. return InvalidationParsingStatus::kSuccess;
  107. }
  108. void RecordFCMMessageStatus(InvalidationParsingStatus status,
  109. const std::string& sender_id) {
  110. // These histograms are recorded quite frequently, so use the macros rather
  111. // than the functions.
  112. UMA_HISTOGRAM_ENUMERATION("FCMInvalidations.FCMMessageStatus", status);
  113. // Also split the histogram by a few well-known senders. The actual constants
  114. // aren't accessible here (they're defined in higher layers), so we simply
  115. // duplicate them here, strictly only for the purpose of metrics.
  116. constexpr char kInvalidationGCMSenderId[] = "8181035976";
  117. constexpr char kDriveFcmSenderId[] = "947318989803";
  118. constexpr char kPolicyFCMInvalidationSenderID[] = "1013309121859";
  119. if (sender_id == kInvalidationGCMSenderId) {
  120. UMA_HISTOGRAM_ENUMERATION("FCMInvalidations.FCMMessageStatus.Sync", status);
  121. } else if (sender_id == kDriveFcmSenderId) {
  122. UMA_HISTOGRAM_ENUMERATION("FCMInvalidations.FCMMessageStatus.Drive",
  123. status);
  124. } else if (sender_id == kPolicyFCMInvalidationSenderID) {
  125. UMA_HISTOGRAM_ENUMERATION("FCMInvalidations.FCMMessageStatus.Policy",
  126. status);
  127. }
  128. }
  129. } // namespace
  130. FCMNetworkHandler::FCMNetworkHandler(
  131. gcm::GCMDriver* gcm_driver,
  132. instance_id::InstanceIDDriver* instance_id_driver,
  133. const std::string& sender_id,
  134. const std::string& app_id)
  135. : gcm_driver_(gcm_driver),
  136. instance_id_driver_(instance_id_driver),
  137. token_validation_timer_(std::make_unique<base::OneShotTimer>()),
  138. sender_id_(sender_id),
  139. app_id_(app_id) {}
  140. FCMNetworkHandler::~FCMNetworkHandler() {
  141. StopListening();
  142. }
  143. // static
  144. std::unique_ptr<FCMNetworkHandler> FCMNetworkHandler::Create(
  145. gcm::GCMDriver* gcm_driver,
  146. instance_id::InstanceIDDriver* instance_id_driver,
  147. const std::string& sender_id,
  148. const std::string& app_id) {
  149. return std::make_unique<FCMNetworkHandler>(gcm_driver, instance_id_driver,
  150. sender_id, app_id);
  151. }
  152. void FCMNetworkHandler::StartListening() {
  153. if (IsListening()) {
  154. StopListening();
  155. }
  156. // Adding ourselves as Handler means start listening.
  157. // Being the listener is pre-requirement for token operations.
  158. gcm_driver_->AddAppHandler(app_id_, this);
  159. diagnostic_info_.instance_id_token_requested = base::Time::Now();
  160. instance_id_driver_->GetInstanceID(app_id_)->GetToken(
  161. sender_id_, kGCMScope, GetTimeToLive(sender_id_),
  162. /*flags=*/{InstanceID::Flags::kIsLazy},
  163. base::BindRepeating(&FCMNetworkHandler::DidRetrieveToken,
  164. weak_ptr_factory_.GetWeakPtr()));
  165. }
  166. void FCMNetworkHandler::StopListening() {
  167. if (IsListening())
  168. gcm_driver_->RemoveAppHandler(app_id_);
  169. }
  170. bool FCMNetworkHandler::IsListening() const {
  171. return gcm_driver_->GetAppHandler(app_id_);
  172. }
  173. void FCMNetworkHandler::DidRetrieveToken(const std::string& subscription_token,
  174. InstanceID::Result result) {
  175. base::UmaHistogramEnumeration("FCMInvalidations.InitialTokenRetrievalStatus",
  176. result);
  177. diagnostic_info_.registration_result = result;
  178. diagnostic_info_.token = subscription_token;
  179. diagnostic_info_.instance_id_token_was_received = base::Time::Now();
  180. switch (result) {
  181. case InstanceID::SUCCESS:
  182. // The received token is assumed to be valid, therefore, we reschedule
  183. // validation.
  184. DeliverToken(subscription_token);
  185. token_ = subscription_token;
  186. UpdateChannelState(FcmChannelState::ENABLED);
  187. break;
  188. case InstanceID::INVALID_PARAMETER:
  189. case InstanceID::DISABLED:
  190. case InstanceID::ASYNC_OPERATION_PENDING:
  191. case InstanceID::SERVER_ERROR:
  192. case InstanceID::UNKNOWN_ERROR:
  193. case InstanceID::NETWORK_ERROR:
  194. DLOG(WARNING) << "Messaging subscription failed; InstanceID::Result = "
  195. << result;
  196. UpdateChannelState(FcmChannelState::NO_INSTANCE_ID_TOKEN);
  197. break;
  198. }
  199. ScheduleNextTokenValidation();
  200. }
  201. void FCMNetworkHandler::ScheduleNextTokenValidation() {
  202. DCHECK(IsListening());
  203. token_validation_timer_->Start(
  204. FROM_HERE, base::Minutes(kTokenValidationPeriodMinutesDefault),
  205. base::BindOnce(&FCMNetworkHandler::StartTokenValidation,
  206. weak_ptr_factory_.GetWeakPtr()));
  207. }
  208. void FCMNetworkHandler::StartTokenValidation() {
  209. DCHECK(IsListening());
  210. diagnostic_info_.instance_id_token_verification_requested = base::Time::Now();
  211. diagnostic_info_.token_validation_requested_num++;
  212. instance_id_driver_->GetInstanceID(app_id_)->GetToken(
  213. sender_id_, kGCMScope, GetTimeToLive(sender_id_),
  214. /*flags=*/{InstanceID::Flags::kIsLazy},
  215. base::BindOnce(&FCMNetworkHandler::DidReceiveTokenForValidation,
  216. weak_ptr_factory_.GetWeakPtr()));
  217. }
  218. void FCMNetworkHandler::DidReceiveTokenForValidation(
  219. const std::string& new_token,
  220. InstanceID::Result result) {
  221. if (!IsListening()) {
  222. // After we requested the token, |StopListening| has been called. Thus,
  223. // ignore the token.
  224. return;
  225. }
  226. diagnostic_info_.instance_id_token_verified = base::Time::Now();
  227. diagnostic_info_.token_verification_result = result;
  228. if (result == InstanceID::SUCCESS) {
  229. UpdateChannelState(FcmChannelState::ENABLED);
  230. if (token_ != new_token) {
  231. diagnostic_info_.token_changed = true;
  232. token_ = new_token;
  233. DeliverToken(new_token);
  234. }
  235. }
  236. ScheduleNextTokenValidation();
  237. }
  238. void FCMNetworkHandler::UpdateChannelState(FcmChannelState state) {
  239. if (channel_state_ == state)
  240. return;
  241. channel_state_ = state;
  242. NotifyChannelStateChange(channel_state_);
  243. }
  244. void FCMNetworkHandler::ShutdownHandler() {}
  245. void FCMNetworkHandler::OnStoreReset() {}
  246. void FCMNetworkHandler::OnMessage(const std::string& app_id,
  247. const gcm::IncomingMessage& message) {
  248. DCHECK_EQ(app_id, app_id_);
  249. std::string payload;
  250. std::string private_topic;
  251. std::string public_topic;
  252. int64_t version = 0;
  253. InvalidationParsingStatus status = ParseIncomingMessage(
  254. message, &payload, &private_topic, &public_topic, &version);
  255. RecordFCMMessageStatus(status, sender_id_);
  256. if (status == InvalidationParsingStatus::kSuccess)
  257. DeliverIncomingMessage(payload, private_topic, public_topic, version);
  258. }
  259. void FCMNetworkHandler::OnMessagesDeleted(const std::string& app_id) {
  260. DCHECK_EQ(app_id, app_id_);
  261. // Note: As of 2020-02, this doesn't actually happen in practice.
  262. }
  263. void FCMNetworkHandler::OnSendError(
  264. const std::string& app_id,
  265. const gcm::GCMClient::SendErrorDetails& details) {
  266. // Should never be called because we don't send GCM messages to
  267. // the server.
  268. NOTREACHED() << "FCMNetworkHandler doesn't send GCM messages.";
  269. }
  270. void FCMNetworkHandler::OnSendAcknowledged(const std::string& app_id,
  271. const std::string& message_id) {
  272. // Should never be called because we don't send GCM messages to
  273. // the server.
  274. NOTREACHED() << "FCMNetworkHandler doesn't send GCM messages.";
  275. }
  276. void FCMNetworkHandler::SetTokenValidationTimerForTesting(
  277. std::unique_ptr<base::OneShotTimer> token_validation_timer) {
  278. token_validation_timer_ = std::move(token_validation_timer);
  279. }
  280. void FCMNetworkHandler::RequestDetailedStatus(
  281. const base::RepeatingCallback<void(base::Value::Dict)>& callback) {
  282. callback.Run(diagnostic_info_.CollectDebugData());
  283. }
  284. FCMNetworkHandler::FCMNetworkHandlerDiagnostic::FCMNetworkHandlerDiagnostic() =
  285. default;
  286. base::Value::Dict
  287. FCMNetworkHandler::FCMNetworkHandlerDiagnostic::CollectDebugData() const {
  288. base::Value::Dict status;
  289. status.SetByDottedPath("NetworkHandler.Registration-result-code",
  290. RegistrationResultToString(registration_result));
  291. status.SetByDottedPath("NetworkHandler.Token", token);
  292. status.SetByDottedPath(
  293. "NetworkHandler.Token-was-requested",
  294. base::TimeFormatShortDateAndTime(instance_id_token_requested));
  295. status.SetByDottedPath(
  296. "NetworkHandler.Token-was-received",
  297. base::TimeFormatShortDateAndTime(instance_id_token_was_received));
  298. status.SetByDottedPath("NetworkHandler.Token-verification-started",
  299. base::TimeFormatShortDateAndTime(
  300. instance_id_token_verification_requested));
  301. status.SetByDottedPath(
  302. "NetworkHandler.Token-was-verified",
  303. base::TimeFormatShortDateAndTime(instance_id_token_verified));
  304. status.SetByDottedPath("NetworkHandler.Verification-result-code",
  305. RegistrationResultToString(token_verification_result));
  306. status.SetByDottedPath("NetworkHandler.Token-changed-when-verified",
  307. token_changed);
  308. status.SetByDottedPath("NetworkHandler.Token-validation-requests",
  309. token_validation_requested_num);
  310. return status;
  311. }
  312. std::string
  313. FCMNetworkHandler::FCMNetworkHandlerDiagnostic::RegistrationResultToString(
  314. const instance_id::InstanceID::Result result) const {
  315. switch (registration_result) {
  316. case instance_id::InstanceID::SUCCESS:
  317. return "InstanceID::SUCCESS";
  318. case instance_id::InstanceID::INVALID_PARAMETER:
  319. return "InstanceID::INVALID_PARAMETER";
  320. case instance_id::InstanceID::DISABLED:
  321. return "InstanceID::DISABLED";
  322. case instance_id::InstanceID::ASYNC_OPERATION_PENDING:
  323. return "InstanceID::ASYNC_OPERATION_PENDING";
  324. case instance_id::InstanceID::SERVER_ERROR:
  325. return "InstanceID::SERVER_ERROR";
  326. case instance_id::InstanceID::UNKNOWN_ERROR:
  327. return "InstanceID::UNKNOWN_ERROR";
  328. case instance_id::InstanceID::NETWORK_ERROR:
  329. return "InstanceID::NETWORK_ERROR";
  330. }
  331. }
  332. } // namespace invalidation