per_user_topic_subscription_manager.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  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/per_user_topic_subscription_manager.h"
  5. #include <stdint.h>
  6. #include <algorithm>
  7. #include <cstddef>
  8. #include <iterator>
  9. #include <memory>
  10. #include <string>
  11. #include <utility>
  12. #include "base/bind.h"
  13. #include "base/memory/raw_ptr.h"
  14. #include "base/metrics/histogram_functions.h"
  15. #include "base/observer_list.h"
  16. #include "base/rand_util.h"
  17. #include "base/strings/strcat.h"
  18. #include "base/strings/stringprintf.h"
  19. #include "base/values.h"
  20. #include "components/gcm_driver/instance_id/instance_id_driver.h"
  21. #include "components/invalidation/public/identity_provider.h"
  22. #include "components/invalidation/public/invalidation_util.h"
  23. #include "components/prefs/pref_registry_simple.h"
  24. #include "components/prefs/pref_service.h"
  25. #include "components/prefs/scoped_user_pref_update.h"
  26. #include "google_apis/gaia/gaia_constants.h"
  27. namespace invalidation {
  28. namespace {
  29. const char kTypeSubscribedForInvalidationsDeprecated[] =
  30. "invalidation.registered_for_invalidation";
  31. const char kTypeSubscribedForInvalidations[] =
  32. "invalidation.per_sender_registered_for_invalidation";
  33. const char kActiveRegistrationTokenDeprecated[] =
  34. "invalidation.active_registration_token";
  35. const char kActiveRegistrationTokens[] =
  36. "invalidation.per_sender_active_registration_tokens";
  37. const char kInvalidationRegistrationScope[] =
  38. "https://firebaseperusertopics-pa.googleapis.com";
  39. // Note: Taking |topic| and |private_topic_name| by value (rather than const
  40. // ref) because the caller (in practice, SubscriptionEntry) may be destroyed by
  41. // the callback.
  42. // This is a RepeatingCallback because in case of failure, the request will get
  43. // retried, so it might actually run multiple times.
  44. using SubscriptionFinishedCallback = base::RepeatingCallback<void(
  45. Topic topic,
  46. Status code,
  47. std::string private_topic_name,
  48. PerUserTopicSubscriptionRequest::RequestType type)>;
  49. static const net::BackoffEntry::Policy kBackoffPolicy = {
  50. // Number of initial errors (in sequence) to ignore before applying
  51. // exponential back-off rules.
  52. 0,
  53. // Initial delay for exponential back-off in ms.
  54. 2000,
  55. // Factor by which the waiting time will be multiplied.
  56. 2,
  57. // Fuzzing percentage. ex: 10% will spread requests randomly
  58. // between 90%-100% of the calculated time.
  59. 0.2, // 20%
  60. // Maximum amount of time we are willing to delay our request in ms.
  61. 1000 * 3600 * 4, // 4 hours.
  62. // Time to keep an entry from being discarded even when it
  63. // has no significant state, -1 to never discard.
  64. -1,
  65. // Don't use initial delay unless the last request was an error.
  66. false,
  67. };
  68. class PerProjectDictionaryPrefUpdate {
  69. public:
  70. explicit PerProjectDictionaryPrefUpdate(PrefService* prefs,
  71. const std::string& project_id)
  72. : update_(prefs, kTypeSubscribedForInvalidations) {
  73. per_sender_pref_ = update_->FindDictKey(project_id);
  74. if (!per_sender_pref_) {
  75. update_->SetKey(project_id, base::Value(base::Value::Type::DICTIONARY));
  76. per_sender_pref_ = update_->FindDictKey(project_id);
  77. }
  78. DCHECK(per_sender_pref_);
  79. }
  80. base::Value& operator*() { return *per_sender_pref_; }
  81. base::Value* operator->() { return per_sender_pref_; }
  82. private:
  83. DictionaryPrefUpdate update_;
  84. raw_ptr<base::Value> per_sender_pref_;
  85. };
  86. // Added in M76.
  87. void MigratePrefs(PrefService* prefs, const std::string& project_id) {
  88. if (!prefs->HasPrefPath(kActiveRegistrationTokenDeprecated)) {
  89. return;
  90. }
  91. {
  92. DictionaryPrefUpdate token_update(prefs, kActiveRegistrationTokens);
  93. token_update->SetStringKey(
  94. project_id, prefs->GetString(kActiveRegistrationTokenDeprecated));
  95. }
  96. const auto& old_subscriptions =
  97. prefs->GetValueDict(kTypeSubscribedForInvalidationsDeprecated);
  98. {
  99. PerProjectDictionaryPrefUpdate update(prefs, project_id);
  100. *update = base::Value(old_subscriptions.Clone());
  101. }
  102. prefs->ClearPref(kActiveRegistrationTokenDeprecated);
  103. prefs->ClearPref(kTypeSubscribedForInvalidationsDeprecated);
  104. }
  105. } // namespace
  106. // static
  107. void PerUserTopicSubscriptionManager::RegisterProfilePrefs(
  108. PrefRegistrySimple* registry) {
  109. registry->RegisterDictionaryPref(kTypeSubscribedForInvalidationsDeprecated);
  110. registry->RegisterStringPref(kActiveRegistrationTokenDeprecated,
  111. std::string());
  112. registry->RegisterDictionaryPref(kTypeSubscribedForInvalidations);
  113. registry->RegisterDictionaryPref(kActiveRegistrationTokens);
  114. }
  115. // static
  116. void PerUserTopicSubscriptionManager::RegisterPrefs(
  117. PrefRegistrySimple* registry) {
  118. // Same as RegisterProfilePrefs; see comment in the header.
  119. RegisterProfilePrefs(registry);
  120. }
  121. // State of the instance ID token when subscription is requested.
  122. // Used by UMA histogram, so entries shouldn't be reordered or removed.
  123. enum class PerUserTopicSubscriptionManager::TokenStateOnSubscriptionRequest {
  124. kTokenWasEmpty = 0,
  125. kTokenUnchanged = 1,
  126. kTokenChanged = 2,
  127. kTokenCleared = 3,
  128. kMaxValue = kTokenCleared,
  129. };
  130. struct PerUserTopicSubscriptionManager::SubscriptionEntry {
  131. SubscriptionEntry(const Topic& topic,
  132. SubscriptionFinishedCallback completion_callback,
  133. PerUserTopicSubscriptionRequest::RequestType type,
  134. bool topic_is_public = false);
  135. SubscriptionEntry(const SubscriptionEntry&) = delete;
  136. SubscriptionEntry& operator=(const SubscriptionEntry&) = delete;
  137. // Destruction of this object causes cancellation of the request.
  138. ~SubscriptionEntry();
  139. void SubscriptionFinished(const Status& code,
  140. const std::string& private_topic_name);
  141. // The object for which this is the status.
  142. const Topic topic;
  143. const bool topic_is_public;
  144. SubscriptionFinishedCallback completion_callback;
  145. PerUserTopicSubscriptionRequest::RequestType type;
  146. base::OneShotTimer request_retry_timer_;
  147. net::BackoffEntry request_backoff_;
  148. std::unique_ptr<PerUserTopicSubscriptionRequest> request;
  149. std::string last_request_access_token;
  150. bool has_retried_on_auth_error = false;
  151. };
  152. PerUserTopicSubscriptionManager::SubscriptionEntry::SubscriptionEntry(
  153. const Topic& topic,
  154. SubscriptionFinishedCallback completion_callback,
  155. PerUserTopicSubscriptionRequest::RequestType type,
  156. bool topic_is_public)
  157. : topic(topic),
  158. topic_is_public(topic_is_public),
  159. completion_callback(std::move(completion_callback)),
  160. type(type),
  161. request_backoff_(&kBackoffPolicy) {}
  162. PerUserTopicSubscriptionManager::SubscriptionEntry::~SubscriptionEntry() =
  163. default;
  164. void PerUserTopicSubscriptionManager::SubscriptionEntry::SubscriptionFinished(
  165. const Status& code,
  166. const std::string& topic_name) {
  167. completion_callback.Run(topic, code, topic_name, type);
  168. }
  169. PerUserTopicSubscriptionManager::PerUserTopicSubscriptionManager(
  170. IdentityProvider* identity_provider,
  171. PrefService* pref_service,
  172. network::mojom::URLLoaderFactory* url_loader_factory,
  173. const std::string& project_id,
  174. bool migrate_prefs)
  175. : pref_service_(pref_service),
  176. identity_provider_(identity_provider),
  177. url_loader_factory_(url_loader_factory),
  178. project_id_(project_id),
  179. migrate_prefs_(migrate_prefs),
  180. request_access_token_backoff_(&kBackoffPolicy) {}
  181. PerUserTopicSubscriptionManager::~PerUserTopicSubscriptionManager() = default;
  182. // static
  183. std::unique_ptr<PerUserTopicSubscriptionManager>
  184. PerUserTopicSubscriptionManager::Create(
  185. IdentityProvider* identity_provider,
  186. PrefService* pref_service,
  187. network::mojom::URLLoaderFactory* url_loader_factory,
  188. const std::string& project_id,
  189. bool migrate_prefs) {
  190. return std::make_unique<PerUserTopicSubscriptionManager>(
  191. identity_provider, pref_service, url_loader_factory, project_id,
  192. migrate_prefs);
  193. }
  194. void PerUserTopicSubscriptionManager::Init() {
  195. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  196. if (migrate_prefs_) {
  197. MigratePrefs(pref_service_, project_id_);
  198. }
  199. PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
  200. if (update->DictEmpty()) {
  201. return;
  202. }
  203. std::vector<std::string> keys_to_remove;
  204. // Load subscribed topics from prefs.
  205. for (auto it : update->DictItems()) {
  206. Topic topic = it.first;
  207. const std::string* private_topic_name = it.second.GetIfString();
  208. if (private_topic_name && !private_topic_name->empty()) {
  209. topic_to_private_topic_[topic] = *private_topic_name;
  210. private_topic_to_topic_[*private_topic_name] = topic;
  211. } else {
  212. // Couldn't decode the pref value; remove it.
  213. keys_to_remove.push_back(topic);
  214. }
  215. }
  216. // Delete prefs, which weren't decoded successfully.
  217. for (const std::string& key : keys_to_remove) {
  218. update->RemoveKey(key);
  219. }
  220. }
  221. void PerUserTopicSubscriptionManager::UpdateSubscribedTopics(
  222. const Topics& topics,
  223. const std::string& instance_id_token) {
  224. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  225. instance_id_token_ = instance_id_token;
  226. DropAllSavedSubscriptionsOnTokenChange();
  227. for (const auto& topic : topics) {
  228. auto it = pending_subscriptions_.find(topic.first);
  229. if (it != pending_subscriptions_.end() &&
  230. it->second->type == PerUserTopicSubscriptionRequest::SUBSCRIBE) {
  231. // Do not update SubscriptionEntry if there is no changes, to not loose
  232. // backoff timer.
  233. continue;
  234. }
  235. // If the topic isn't subscribed yet, schedule the subscription.
  236. if (topic_to_private_topic_.find(topic.first) ==
  237. topic_to_private_topic_.end()) {
  238. // If there was already a pending unsubscription request for this topic,
  239. // it'll get destroyed and replaced by the new one.
  240. pending_subscriptions_[topic.first] = std::make_unique<SubscriptionEntry>(
  241. topic.first,
  242. base::BindRepeating(
  243. &PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic,
  244. base::Unretained(this)),
  245. PerUserTopicSubscriptionRequest::SUBSCRIBE, topic.second.is_public);
  246. }
  247. }
  248. // There may be subscribed topics which need to be unsubscribed.
  249. // Schedule unsubscription and immediately remove from
  250. // |topic_to_private_topic_| and |private_topic_to_topic_|.
  251. for (auto it = topic_to_private_topic_.begin();
  252. it != topic_to_private_topic_.end();) {
  253. Topic topic = it->first;
  254. if (topics.find(topic) == topics.end()) {
  255. // Unsubscription request may only replace pending subscription request,
  256. // because topic immediately deleted from |topic_to_private_topic_| when
  257. // unsubsciption request scheduled.
  258. DCHECK(pending_subscriptions_.count(topic) == 0 ||
  259. pending_subscriptions_[topic]->type ==
  260. PerUserTopicSubscriptionRequest::SUBSCRIBE);
  261. // If there was already a pending request for this topic, it'll get
  262. // destroyed and replaced by the new one.
  263. pending_subscriptions_[topic] = std::make_unique<SubscriptionEntry>(
  264. topic,
  265. base::BindRepeating(
  266. &PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic,
  267. base::Unretained(this)),
  268. PerUserTopicSubscriptionRequest::UNSUBSCRIBE);
  269. private_topic_to_topic_.erase(it->second);
  270. it = topic_to_private_topic_.erase(it);
  271. // The decision to unsubscribe from invalidations for |topic| was
  272. // made, the preferences should be cleaned up immediately.
  273. PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
  274. update->RemoveKey(topic);
  275. } else {
  276. // Topic is still wanted, nothing to do.
  277. ++it;
  278. }
  279. }
  280. // There might be pending subscriptions for topics which are no longer
  281. // needed, but they could be in half-completed state (i.e. request already
  282. // sent to the server). To reduce subscription leaks they are allowed to
  283. // proceed and unsubscription requests will be scheduled by the next
  284. // UpdateSubscribedTopics() call after they successfully completed.
  285. if (!pending_subscriptions_.empty()) {
  286. // Kick off the process of actually processing the (un)subscriptions we just
  287. // scheduled.
  288. RequestAccessToken();
  289. } else {
  290. // No work to be done, emit ENABLED.
  291. NotifySubscriptionChannelStateChange(SubscriptionChannelState::ENABLED);
  292. }
  293. }
  294. void PerUserTopicSubscriptionManager::ClearInstanceIDToken() {
  295. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  296. instance_id_token_.clear();
  297. DropAllSavedSubscriptionsOnTokenChange();
  298. }
  299. void PerUserTopicSubscriptionManager::StartPendingSubscriptions() {
  300. for (const auto& pending_subscription : pending_subscriptions_) {
  301. StartPendingSubscriptionRequest(pending_subscription.first);
  302. }
  303. }
  304. void PerUserTopicSubscriptionManager::StartPendingSubscriptionRequest(
  305. const Topic& topic) {
  306. auto it = pending_subscriptions_.find(topic);
  307. if (it == pending_subscriptions_.end()) {
  308. NOTREACHED() << "StartPendingSubscriptionRequest called on " << topic
  309. << " which is not in the registration map";
  310. return;
  311. }
  312. if (it->second->request_retry_timer_.IsRunning()) {
  313. // A retry is already scheduled for this request; nothing to do.
  314. return;
  315. }
  316. if (it->second->request &&
  317. it->second->last_request_access_token == access_token_) {
  318. // The request with the same access token was already sent; nothing to do.
  319. return;
  320. }
  321. PerUserTopicSubscriptionRequest::Builder builder;
  322. it->second->last_request_access_token = access_token_;
  323. it->second->request = builder.SetInstanceIdToken(instance_id_token_)
  324. .SetScope(kInvalidationRegistrationScope)
  325. .SetPublicTopicName(topic)
  326. .SetAuthenticationHeader(base::StringPrintf(
  327. "Bearer %s", access_token_.c_str()))
  328. .SetProjectId(project_id_)
  329. .SetType(it->second->type)
  330. .SetTopicIsPublic(it->second->topic_is_public)
  331. .Build();
  332. it->second->request->Start(
  333. base::BindOnce(&PerUserTopicSubscriptionManager::SubscriptionEntry::
  334. SubscriptionFinished,
  335. base::Unretained(it->second.get())),
  336. url_loader_factory_);
  337. }
  338. void PerUserTopicSubscriptionManager::ActOnSuccessfulSubscription(
  339. const Topic& topic,
  340. const std::string& private_topic_name,
  341. PerUserTopicSubscriptionRequest::RequestType type) {
  342. auto it = pending_subscriptions_.find(topic);
  343. it->second->request_backoff_.InformOfRequest(true);
  344. pending_subscriptions_.erase(it);
  345. if (type == PerUserTopicSubscriptionRequest::SUBSCRIBE) {
  346. // If this was a subscription, update the prefs now (if it was an
  347. // unsubscription, we've already updated the prefs when scheduling the
  348. // request).
  349. {
  350. PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
  351. update->SetStringKey(topic, private_topic_name);
  352. topic_to_private_topic_[topic] = private_topic_name;
  353. private_topic_to_topic_[private_topic_name] = topic;
  354. }
  355. pref_service_->CommitPendingWrite();
  356. }
  357. // Check if there are any other subscription (not unsubscription) requests
  358. // pending.
  359. bool all_subscriptions_completed = true;
  360. for (const auto& entry : pending_subscriptions_) {
  361. if (entry.second->type == PerUserTopicSubscriptionRequest::SUBSCRIBE) {
  362. all_subscriptions_completed = false;
  363. }
  364. }
  365. // Emit ENABLED once all requests have finished.
  366. if (all_subscriptions_completed) {
  367. NotifySubscriptionChannelStateChange(SubscriptionChannelState::ENABLED);
  368. }
  369. }
  370. void PerUserTopicSubscriptionManager::ScheduleRequestForRepetition(
  371. const Topic& topic) {
  372. pending_subscriptions_[topic]->request_backoff_.InformOfRequest(false);
  373. // Schedule RequestAccessToken() to ensure that request is performed with
  374. // fresh access token. There should be no redundant request: the identity
  375. // code requests new access token from the network only if the old one
  376. // expired; StartPendingSubscriptionRequest() guarantees that no redundant
  377. // (un)subscribe requests performed.
  378. pending_subscriptions_[topic]->request_retry_timer_.Start(
  379. FROM_HERE,
  380. pending_subscriptions_[topic]->request_backoff_.GetTimeUntilRelease(),
  381. base::BindOnce(&PerUserTopicSubscriptionManager::RequestAccessToken,
  382. base::Unretained(this)));
  383. }
  384. void PerUserTopicSubscriptionManager::SubscriptionFinishedForTopic(
  385. Topic topic,
  386. Status code,
  387. std::string private_topic_name,
  388. PerUserTopicSubscriptionRequest::RequestType type) {
  389. if (code.IsSuccess()) {
  390. ActOnSuccessfulSubscription(topic, private_topic_name, type);
  391. return;
  392. }
  393. auto it = pending_subscriptions_.find(topic);
  394. // Reset |request| to make sure it will be rescheduled during the next
  395. // attempt.
  396. it->second->request.reset();
  397. // If this is the first auth error we've encountered, then most likely the
  398. // access token has just expired. Get a new one and retry immediately.
  399. if (code.IsAuthFailure() && !it->second->has_retried_on_auth_error) {
  400. it->second->has_retried_on_auth_error = true;
  401. // Invalidate previous token if it's not already refreshed, otherwise
  402. // the identity provider will return the same token again.
  403. if (!access_token_.empty() &&
  404. it->second->last_request_access_token == access_token_) {
  405. identity_provider_->InvalidateAccessToken({GaiaConstants::kFCMOAuthScope},
  406. access_token_);
  407. access_token_.clear();
  408. }
  409. // Re-request access token and try subscription requests again.
  410. RequestAccessToken();
  411. return;
  412. }
  413. // If one of the subscription requests failed (and we need to either observe
  414. // backoff before retrying, or won't retry at all), emit SUBSCRIPTION_FAILURE.
  415. if (type == PerUserTopicSubscriptionRequest::SUBSCRIBE) {
  416. // TODO(crbug.com/1020117): case !code.ShouldRetry() now leads to
  417. // inconsistent behavior depending on requests completion order: if any
  418. // request was successful after it, we may have no |pending_subscriptions_|
  419. // and emit ENABLED; otherwise, if failed request is the last one, state
  420. // would be SUBSCRIPTION_FAILURE.
  421. NotifySubscriptionChannelStateChange(
  422. SubscriptionChannelState::SUBSCRIPTION_FAILURE);
  423. }
  424. if (!code.ShouldRetry()) {
  425. // Note: This is a pretty bad (and "silent") failure case. The subscription
  426. // will generally not be retried until the next Chrome restart (or user
  427. // sign-out + re-sign-in).
  428. DVLOG(1) << "Got a persistent error while trying to subscribe to topic "
  429. << topic << ", giving up.";
  430. pending_subscriptions_.erase(it);
  431. return;
  432. }
  433. ScheduleRequestForRepetition(topic);
  434. }
  435. TopicSet PerUserTopicSubscriptionManager::GetSubscribedTopicsForTest() const {
  436. TopicSet topics;
  437. for (const auto& t : topic_to_private_topic_)
  438. topics.insert(t.first);
  439. return topics;
  440. }
  441. void PerUserTopicSubscriptionManager::AddObserver(Observer* observer) {
  442. observers_.AddObserver(observer);
  443. }
  444. void PerUserTopicSubscriptionManager::RemoveObserver(Observer* observer) {
  445. observers_.RemoveObserver(observer);
  446. }
  447. void PerUserTopicSubscriptionManager::RequestAccessToken() {
  448. // Only one active request at a time.
  449. if (access_token_fetcher_ != nullptr) {
  450. return;
  451. }
  452. if (request_access_token_retry_timer_.IsRunning()) {
  453. // Previous access token request failed and new request shouldn't be issued
  454. // until backoff timer passed.
  455. return;
  456. }
  457. access_token_.clear();
  458. access_token_fetcher_ = identity_provider_->FetchAccessToken(
  459. "fcm_invalidation", {GaiaConstants::kFCMOAuthScope},
  460. base::BindOnce(
  461. &PerUserTopicSubscriptionManager::OnAccessTokenRequestCompleted,
  462. base::Unretained(this)));
  463. }
  464. void PerUserTopicSubscriptionManager::OnAccessTokenRequestCompleted(
  465. GoogleServiceAuthError error,
  466. std::string access_token) {
  467. access_token_fetcher_.reset();
  468. if (error.state() == GoogleServiceAuthError::NONE)
  469. OnAccessTokenRequestSucceeded(access_token);
  470. else
  471. OnAccessTokenRequestFailed(error);
  472. }
  473. void PerUserTopicSubscriptionManager::OnAccessTokenRequestSucceeded(
  474. const std::string& access_token) {
  475. // Reset backoff time after successful response.
  476. request_access_token_backoff_.InformOfRequest(/*succeeded=*/true);
  477. access_token_ = access_token;
  478. StartPendingSubscriptions();
  479. }
  480. void PerUserTopicSubscriptionManager::OnAccessTokenRequestFailed(
  481. GoogleServiceAuthError error) {
  482. DCHECK_NE(error.state(), GoogleServiceAuthError::NONE);
  483. NotifySubscriptionChannelStateChange(
  484. SubscriptionChannelState::ACCESS_TOKEN_FAILURE);
  485. request_access_token_backoff_.InformOfRequest(false);
  486. request_access_token_retry_timer_.Start(
  487. FROM_HERE, request_access_token_backoff_.GetTimeUntilRelease(),
  488. base::BindOnce(&PerUserTopicSubscriptionManager::RequestAccessToken,
  489. base::Unretained(this)));
  490. }
  491. void PerUserTopicSubscriptionManager::DropAllSavedSubscriptionsOnTokenChange() {
  492. TokenStateOnSubscriptionRequest outcome =
  493. DropAllSavedSubscriptionsOnTokenChangeImpl();
  494. base::UmaHistogramEnumeration(
  495. "FCMInvalidations.TokenStateOnRegistrationRequest2", outcome);
  496. }
  497. PerUserTopicSubscriptionManager::TokenStateOnSubscriptionRequest
  498. PerUserTopicSubscriptionManager::DropAllSavedSubscriptionsOnTokenChangeImpl() {
  499. {
  500. DictionaryPrefUpdate token_update(pref_service_, kActiveRegistrationTokens);
  501. std::string previous_token;
  502. if (const std::string* str_ptr = token_update->FindStringKey(project_id_)) {
  503. previous_token = *str_ptr;
  504. }
  505. if (previous_token == instance_id_token_) {
  506. // Note: This includes the case where the token was and still is empty.
  507. return TokenStateOnSubscriptionRequest::kTokenUnchanged;
  508. }
  509. token_update->SetStringKey(project_id_, instance_id_token_);
  510. if (previous_token.empty()) {
  511. // If we didn't have a registration token before, we shouldn't have had
  512. // any subscriptions either, so no need to drop them.
  513. return TokenStateOnSubscriptionRequest::kTokenWasEmpty;
  514. }
  515. }
  516. // The token has been cleared or changed. In either case, clear all existing
  517. // subscriptions since they won't be valid anymore. (No need to send
  518. // unsubscribe requests - if the token was revoked, the server will drop the
  519. // subscriptions anyway.)
  520. PerProjectDictionaryPrefUpdate update(pref_service_, project_id_);
  521. *update = base::Value(base::Value::Type::DICTIONARY);
  522. topic_to_private_topic_.clear();
  523. private_topic_to_topic_.clear();
  524. pending_subscriptions_.clear();
  525. return instance_id_token_.empty()
  526. ? TokenStateOnSubscriptionRequest::kTokenCleared
  527. : TokenStateOnSubscriptionRequest::kTokenChanged;
  528. }
  529. void PerUserTopicSubscriptionManager::NotifySubscriptionChannelStateChange(
  530. SubscriptionChannelState state) {
  531. // NOT_STARTED is the default state of the subscription
  532. // channel and shouldn't explicitly issued.
  533. DCHECK(state != SubscriptionChannelState::NOT_STARTED);
  534. if (last_issued_state_ == state) {
  535. // Notify only on state change.
  536. return;
  537. }
  538. last_issued_state_ = state;
  539. for (auto& observer : observers_) {
  540. observer.OnSubscriptionChannelStateChanged(state);
  541. }
  542. }
  543. base::Value::Dict PerUserTopicSubscriptionManager::CollectDebugData() const {
  544. base::Value::Dict status;
  545. for (const auto& topic_to_private_topic : topic_to_private_topic_) {
  546. status.Set(topic_to_private_topic.first, topic_to_private_topic.second);
  547. }
  548. status.Set("Instance id token", instance_id_token_);
  549. return status;
  550. }
  551. absl::optional<Topic>
  552. PerUserTopicSubscriptionManager::LookupSubscribedPublicTopicByPrivateTopic(
  553. const std::string& private_topic) const {
  554. auto it = private_topic_to_topic_.find(private_topic);
  555. if (it == private_topic_to_topic_.end()) {
  556. return absl::nullopt;
  557. }
  558. return it->second;
  559. }
  560. } // namespace invalidation