Commit dfc85f6b authored by Anthony Vallee-Dubois's avatar Anthony Vallee-Dubois Committed by Commit Bot

Refactor FCMInvalidationService

FCMInvalidationService currently does 2 things: it observes changes to
its IdentityProvider and maintains invalidations-related state. The seam
between those responsibilities is pretty clear.

In order to support invalidations for Machine-Level cloud policies on
desktop browsers, we need to use the invalidations-related bits of
FCMInvalidationService in a context where there is no IdentityProvider.
This is different than the ChromeOS case where there's no Profile but a
Device Level Robot Account is available (See
AffiliatedInvalidationServiceProvider). Desktop browsers have no such
robot account, therefore no IdentityProvider in the context of Machine-
Level policies.

This CL splits off the profile-agnostic parts of FCMInvalidationService
into their own base class: FCMInvalidationServiceBase. This refactor
should be a no-op, except it will allow us to add a second implementation
of FCMInvalidationServiceBase that doesn't care about the presence of an
account.

The linked bug and the design doc (go/cbcm-machine-policy-invalidations)
contain more context about this change.

Bug: 1019791
Change-Id: Ib659aaa7d52f18797c67eaa9faae6d78d746942c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1910423Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Reviewed-by: default avatarTatiana Gornak <melandory@chromium.org>
Commit-Queue: anthonyvd <anthonyvd@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714938}
parent 326f43ed
......@@ -27,6 +27,8 @@ static_library("impl") {
"fcm_invalidation_listener.h",
"fcm_invalidation_service.cc",
"fcm_invalidation_service.h",
"fcm_invalidation_service_base.cc",
"fcm_invalidation_service_base.h",
"fcm_network_handler.cc",
"fcm_network_handler.h",
"fcm_sync_network_channel.cc",
......
......@@ -4,50 +4,14 @@
#include "components/invalidation/impl/fcm_invalidation_service.h"
#include <memory>
#include "base/bind.h"
#include "base/i18n/time_formatting.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "build/build_config.h"
#include "components/gcm_driver/instance_id/instance_id_driver.h"
#include "components/invalidation/impl/fcm_invalidation_listener.h"
#include "components/invalidation/impl/fcm_network_handler.h"
#include "components/invalidation/impl/invalidation_prefs.h"
#include "components/invalidation/impl/invalidation_service_util.h"
#include "components/invalidation/impl/invalidation_switches.h"
#include "components/invalidation/impl/invalidator.h"
#include "components/invalidation/public/invalidation_util.h"
#include "components/invalidation/public/invalidator_state.h"
#include "components/invalidation/public/object_id_invalidation_map.h"
#include "components/invalidation/public/topic_invalidation_map.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "google_apis/gaia/gaia_constants.h"
namespace invalidation {
namespace {
const char kApplicationName[] = "com.google.chrome.fcm.invalidations";
// Sender ID coming from the Firebase console.
const char kInvalidationGCMSenderId[] = "8181035976";
void ReportInvalidatorState(syncer::InvalidatorState state) {
UMA_HISTOGRAM_ENUMERATION("Invalidations.StatusChanged", state);
}
// Added in M76.
void MigratePrefs(PrefService* prefs, const std::string& sender_id) {
if (!prefs->HasPrefPath(prefs::kFCMInvalidationClientIDCacheDeprecated)) {
return;
}
DictionaryPrefUpdate update(prefs, prefs::kInvalidationClientIDCache);
update->SetString(
sender_id,
prefs->GetString(prefs::kFCMInvalidationClientIDCacheDeprecated));
prefs->ClearPref(prefs::kFCMInvalidationClientIDCacheDeprecated);
}
} // namespace
FCMInvalidationService::FCMInvalidationService(
IdentityProvider* identity_provider,
......@@ -57,25 +21,16 @@ FCMInvalidationService::FCMInvalidationService(
instance_id::InstanceIDDriver* instance_id_driver,
PrefService* pref_service,
const std::string& sender_id)
: sender_id_(sender_id.empty() ? kInvalidationGCMSenderId : sender_id),
invalidator_registrar_(pref_service,
sender_id_,
sender_id_ == kInvalidationGCMSenderId),
fcm_network_handler_callback_(std::move(fcm_network_handler_callback)),
per_user_topic_registration_manager_callback_(
std::move(per_user_topic_registration_manager_callback)),
instance_id_driver_(instance_id_driver),
identity_provider_(identity_provider),
pref_service_(pref_service),
update_was_requested_(false) {}
: FCMInvalidationServiceBase(fcm_network_handler_callback,
per_user_topic_registration_manager_callback,
instance_id_driver,
pref_service,
sender_id),
identity_provider_(identity_provider) {}
FCMInvalidationService::~FCMInvalidationService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
invalidator_registrar_.UpdateInvalidatorState(
syncer::INVALIDATOR_SHUTTING_DOWN);
identity_provider_->RemoveObserver(this);
if (IsStarted())
StopInvalidator();
}
void FCMInvalidationService::Init() {
......@@ -94,90 +49,14 @@ void FCMInvalidationService::Init() {
identity_provider_->AddObserver(this);
}
// static
void FCMInvalidationService::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(
invalidation::prefs::kFCMInvalidationClientIDCacheDeprecated,
/*default_value=*/std::string());
registry->RegisterDictionaryPref(
invalidation::prefs::kInvalidationClientIDCache);
}
void FCMInvalidationService::InitForTest(
std::unique_ptr<syncer::FCMInvalidationListener> invalidation_listener) {
// Here we perform the equivalent of Init() and StartInvalidator(), but with
// some minor changes to account for the fact that we're injecting the
// invalidation_listener.
// StartInvalidator initializes the invalidation_listener and starts it.
invalidation_listener_ = std::move(invalidation_listener);
invalidation_listener_->StartForTest(this);
DoUpdateRegisteredIdsIfNeeded();
}
void FCMInvalidationService::RegisterInvalidationHandler(
syncer::InvalidationHandler* handler) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Registering an invalidation handler";
invalidator_registrar_.RegisterHandler(handler);
// Populate the id for newly registered handlers.
handler->OnInvalidatorClientIdChange(client_id_);
logger_.OnRegistration(handler->GetOwnerName());
}
bool FCMInvalidationService::UpdateRegisteredInvalidationIds(
syncer::InvalidationHandler* handler,
const syncer::ObjectIdSet& ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
update_was_requested_ = true;
DVLOG(2) << "Registering ids: " << ids.size();
syncer::Topics topics = ConvertIdsToTopics(ids, handler);
if (!invalidator_registrar_.UpdateRegisteredTopics(handler, topics))
return false;
DoUpdateRegisteredIdsIfNeeded();
logger_.OnUpdateTopics(invalidator_registrar_.GetHandlerNameToTopicsMap());
return true;
}
void FCMInvalidationService::UnregisterInvalidationHandler(
syncer::InvalidationHandler* handler) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Unregistering";
invalidator_registrar_.UnregisterHandler(handler);
logger_.OnUnregistration(handler->GetOwnerName());
}
syncer::InvalidatorState FCMInvalidationService::GetInvalidatorState() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (invalidation_listener_) {
DVLOG(2) << "GetInvalidatorState returning "
<< invalidator_registrar_.GetInvalidatorState();
return invalidator_registrar_.GetInvalidatorState();
}
DVLOG(2) << "Invalidator currently stopped";
return syncer::STOPPED;
}
std::string FCMInvalidationService::GetInvalidatorClientId() const {
return client_id_;
}
InvalidationLogger* FCMInvalidationService::GetInvalidationLogger() {
return &logger_;
}
void FCMInvalidationService::RequestDetailedStatus(
base::RepeatingCallback<void(const base::DictionaryValue&)> return_callback)
const {
return_callback.Run(diagnostic_info_.CollectDebugData());
invalidator_registrar_.RequestDetailedStatus(return_callback);
FCMInvalidationServiceBase::RequestDetailedStatus(return_callback);
if (identity_provider_) {
identity_provider_->RequestDetailedStatus(return_callback);
}
if (IsStarted()) {
invalidation_listener_->RequestDetailedStatus(return_callback);
}
}
void FCMInvalidationService::OnActiveAccountLogin() {
......@@ -206,25 +85,31 @@ void FCMInvalidationService::OnActiveAccountLogout() {
diagnostic_info_.active_account_logged_out = base::Time::Now();
diagnostic_info_.active_account_id = CoreAccountId();
if (IsStarted()) {
StopInvalidator();
if (!client_id_.empty())
ResetClientID();
StopInvalidatorPermanently();
}
}
void FCMInvalidationService::OnInvalidate(
const syncer::TopicInvalidationMap& invalidation_map) {
invalidator_registrar_.DispatchInvalidationsToHandlers(invalidation_map);
base::DictionaryValue FCMInvalidationService::CollectDebugData() const {
base::DictionaryValue status = FCMInvalidationServiceBase::CollectDebugData();
logger_.OnInvalidation(
ConvertTopicInvalidationMapToObjectIdInvalidationMap(invalidation_map));
}
status.SetString(
"InvalidationService.Active-account-login",
base::TimeFormatShortDateAndTime(diagnostic_info_.active_account_login));
status.SetString("InvalidationService.Active-account-token-updated",
base::TimeFormatShortDateAndTime(
diagnostic_info_.active_account_token_updated));
status.SetString("InvalidationService.Active-account-logged-out",
base::TimeFormatShortDateAndTime(
diagnostic_info_.active_account_logged_out));
status.SetBoolean("InvalidationService.Started-on-active-account-login",
diagnostic_info_.was_already_started_on_login);
status.SetBoolean(
"InvalidationService.Ready-to-start-on-active-account-login",
diagnostic_info_.was_ready_to_start_on_login);
status.SetString("InvalidationService.Active-account-id",
diagnostic_info_.active_account_id.id);
void FCMInvalidationService::OnInvalidatorStateChange(
syncer::InvalidatorState state) {
ReportInvalidatorState(state);
invalidator_registrar_.UpdateInvalidatorState(state);
logger_.OnStateChange(state);
return status;
}
bool FCMInvalidationService::IsReadyToStart() {
......@@ -252,127 +137,4 @@ bool FCMInvalidationService::IsReadyToStart() {
return true;
}
bool FCMInvalidationService::IsStarted() const {
return invalidation_listener_ != nullptr;
}
void FCMInvalidationService::StartInvalidator() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!invalidation_listener_);
DCHECK(IsReadyToStart());
diagnostic_info_.service_was_started = base::Time::Now();
auto network =
fcm_network_handler_callback_.Run(sender_id_, GetApplicationName());
// The order of calls is important. Do not change.
// We should start listening before requesting the id, because
// valid id is only generated, once there is an app handler
// for the app. StartListening registers the app handler.
// We should create InvalidationListener first, because it registers the
// handler for the incoming messages, which is crucial on Android, because on
// the startup cached messages might exists.
invalidation_listener_ =
std::make_unique<syncer::FCMInvalidationListener>(std::move(network));
auto registration_manager = per_user_topic_registration_manager_callback_.Run(
sender_id_, /*migrate_prefs=*/sender_id_ == kInvalidationGCMSenderId);
invalidation_listener_->Start(this, std::move(registration_manager));
PopulateClientID();
DoUpdateRegisteredIdsIfNeeded();
}
void FCMInvalidationService::StopInvalidator() {
DCHECK(invalidation_listener_);
diagnostic_info_.service_was_stopped = base::Time::Now();
// TODO(melandory): reset the network.
invalidation_listener_.reset();
}
void FCMInvalidationService::PopulateClientID() {
diagnostic_info_.instance_id_requested = base::Time::Now();
if (sender_id_ == kInvalidationGCMSenderId) {
MigratePrefs(pref_service_, sender_id_);
}
const std::string* client_id_pref =
pref_service_->GetDictionary(prefs::kInvalidationClientIDCache)
->FindStringKey(sender_id_);
client_id_ = client_id_pref ? *client_id_pref : "";
instance_id::InstanceID* instance_id =
instance_id_driver_->GetInstanceID(GetApplicationName());
instance_id->GetID(base::Bind(&FCMInvalidationService::OnInstanceIdRecieved,
base::Unretained(this)));
}
void FCMInvalidationService::ResetClientID() {
instance_id::InstanceID* instance_id =
instance_id_driver_->GetInstanceID(GetApplicationName());
instance_id->DeleteID(base::Bind(&FCMInvalidationService::OnDeleteIDCompleted,
base::Unretained(this)));
DictionaryPrefUpdate update(pref_service_, prefs::kInvalidationClientIDCache);
update->RemoveKey(sender_id_);
}
void FCMInvalidationService::OnInstanceIdRecieved(const std::string& id) {
diagnostic_info_.instance_id_received = base::Time::Now();
if (client_id_ != id) {
client_id_ = id;
DictionaryPrefUpdate update(pref_service_,
prefs::kInvalidationClientIDCache);
update->SetStringKey(sender_id_, id);
invalidator_registrar_.UpdateInvalidatorInstanceId(id);
}
}
void FCMInvalidationService::OnDeleteIDCompleted(
instance_id::InstanceID::Result) {
// TODO(melandory): report metric in case of unsuccessful deletion.
}
void FCMInvalidationService::DoUpdateRegisteredIdsIfNeeded() {
if (!invalidation_listener_ || !update_was_requested_)
return;
auto subscribed_topics = invalidator_registrar_.GetAllSubscribedTopics();
invalidation_listener_->UpdateRegisteredTopics(subscribed_topics);
update_was_requested_ = false;
}
const std::string FCMInvalidationService::GetApplicationName() {
// If using the default |sender_id_|, use the bare |kApplicationName|, so the
// old app name is maintained.
if (sender_id_ == kInvalidationGCMSenderId) {
return kApplicationName;
}
return base::StrCat({kApplicationName, "-", sender_id_});
}
FCMInvalidationService::Diagnostics::Diagnostics() {}
base::DictionaryValue FCMInvalidationService::Diagnostics::CollectDebugData()
const {
base::DictionaryValue status;
status.SetString("InvalidationService.Active-account-login",
base::TimeFormatShortDateAndTime(active_account_login));
status.SetString(
"InvalidationService.Active-account-token-updated",
base::TimeFormatShortDateAndTime(active_account_token_updated));
status.SetString("InvalidationService.Active-account-logged-out",
base::TimeFormatShortDateAndTime(active_account_logged_out));
status.SetString("InvalidationService.IID-requested",
base::TimeFormatShortDateAndTime(instance_id_requested));
status.SetString("InvalidationService.IID-received",
base::TimeFormatShortDateAndTime(instance_id_received));
status.SetString("InvalidationService.Service-stopped",
base::TimeFormatShortDateAndTime(service_was_stopped));
status.SetString("InvalidationService.Service-started",
base::TimeFormatShortDateAndTime(service_was_started));
status.SetBoolean("InvalidationService.Started-on-active-account-login",
was_already_started_on_login);
status.SetBoolean(
"InvalidationService.Ready-to-start-on-active-account-login",
was_ready_to_start_on_login);
status.SetString("InvalidationService.Active-account-id",
active_account_id.id);
return status;
}
} // namespace invalidation
......@@ -6,48 +6,16 @@
#define COMPONENTS_INVALIDATION_IMPL_FCM_INVALIDATION_SERVICE_H_
#include "base/macros.h"
#include "base/time/time.h"
#include "components/gcm_driver/instance_id/instance_id.h"
#include "components/invalidation/impl/fcm_invalidation_listener.h"
#include "components/invalidation/impl/invalidation_listener.h"
#include "components/invalidation/impl/invalidation_logger.h"
#include "components/invalidation/impl/invalidator_registrar_with_memory.h"
#include "components/invalidation/impl/fcm_invalidation_service_base.h"
#include "components/invalidation/public/identity_provider.h"
#include "components/invalidation/public/invalidation_handler.h"
#include "components/invalidation/public/invalidation_service.h"
#include "components/keyed_service/core/keyed_service.h"
#include "net/base/backoff_entry.h"
class PrefService;
class PrefRegistrySimple;
namespace instance_id {
class InstanceIDDriver;
}
namespace syncer {
class FCMNetworkHandler;
class PerUserTopicRegistrationManager;
} // namespace syncer
namespace invalidation {
using FCMNetworkHandlerCallback =
base::RepeatingCallback<std::unique_ptr<syncer::FCMNetworkHandler>(
const std::string& sender_id,
const std::string& app_id)>;
using PerUserTopicRegistrationManagerCallback =
base::RepeatingCallback<std::unique_ptr<
syncer::PerUserTopicRegistrationManager>(const std::string& project_id,
bool migrate_prefs)>;
// This InvalidationService wraps the C++ Invalidation Client (FCM) library.
// It provides invalidations for desktop platforms (Win, Mac, Linux).
class FCMInvalidationService
: public InvalidationService,
public IdentityProvider::Observer,
public syncer::FCMInvalidationListener::Delegate {
// This concrete implementation of FCMInvalidationServiceBase starts the
// invalidation service machinery once an account is signed in and conversely
// stops it when the account is signed out.
class FCMInvalidationService : public FCMInvalidationServiceBase,
public IdentityProvider::Observer {
public:
FCMInvalidationService(IdentityProvider* identity_provider,
FCMNetworkHandlerCallback fcm_network_handler_callback,
......@@ -58,21 +26,8 @@ class FCMInvalidationService
const std::string& sender_id = {});
~FCMInvalidationService() override;
void Init();
static void RegisterPrefs(PrefRegistrySimple* registry);
// InvalidationService implementation.
// It is an error to have registered handlers when the service is destroyed.
void RegisterInvalidationHandler(
syncer::InvalidationHandler* handler) override;
bool UpdateRegisteredInvalidationIds(syncer::InvalidationHandler* handler,
const syncer::ObjectIdSet& ids) override;
void UnregisterInvalidationHandler(
syncer::InvalidationHandler* handler) override;
syncer::InvalidatorState GetInvalidatorState() const override;
std::string GetInvalidatorClientId() const override;
InvalidationLogger* GetInvalidationLogger() override;
void Init() override;
void RequestDetailedStatus(
base::RepeatingCallback<void(const base::DictionaryValue&)> caller)
const override;
......@@ -82,76 +37,26 @@ class FCMInvalidationService
void OnActiveAccountLogin() override;
void OnActiveAccountLogout() override;
// syncer::FCMInvalidationListener::Delegate implementation.
void OnInvalidate(
const syncer::TopicInvalidationMap& invalidation_map) override;
void OnInvalidatorStateChange(syncer::InvalidatorState state) override;
protected:
friend class FCMInvalidationServiceTestDelegate;
// Initializes with an injected invalidator.
void InitForTest(
std::unique_ptr<syncer::FCMInvalidationListener> invalidation_listener);
base::DictionaryValue CollectDebugData() const override;
private:
struct Diagnostics {
Diagnostics();
// Collect all the internal variables in a single readable dictionary.
base::DictionaryValue CollectDebugData() const;
base::Time active_account_login;
base::Time active_account_token_updated;
base::Time active_account_logged_out;
base::Time instance_id_requested;
base::Time instance_id_received;
base::Time service_was_stopped;
base::Time service_was_started;
bool was_already_started_on_login = false;
bool was_ready_to_start_on_login = false;
CoreAccountId active_account_id;
};
bool IsReadyToStart();
bool IsStarted() const;
void StartInvalidator();
void StopInvalidator();
void PopulateClientID();
void ResetClientID();
void OnInstanceIdRecieved(const std::string& id);
void OnDeleteIDCompleted(instance_id::InstanceID::Result);
void DoUpdateRegisteredIdsIfNeeded();
const std::string GetApplicationName();
const std::string sender_id_;
syncer::InvalidatorRegistrarWithMemory invalidator_registrar_;
// The invalidation logger object we use to record state changes
// and invalidations.
InvalidationLogger logger_;
FCMNetworkHandlerCallback fcm_network_handler_callback_;
PerUserTopicRegistrationManagerCallback
per_user_topic_registration_manager_callback_;
instance_id::InstanceIDDriver* const instance_id_driver_;
std::string client_id_;
IdentityProvider* const identity_provider_;
PrefService* const pref_service_;
bool update_was_requested_ = false;
Diagnostics diagnostic_info_;
// The invalidation listener.
std::unique_ptr<syncer::FCMInvalidationListener> invalidation_listener_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(FCMInvalidationService);
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/invalidation/impl/fcm_invalidation_service_base.h"
#include <memory>
#include "base/bind.h"
#include "base/i18n/time_formatting.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "components/gcm_driver/instance_id/instance_id_driver.h"
#include "components/invalidation/impl/fcm_network_handler.h"
#include "components/invalidation/impl/invalidation_prefs.h"
#include "components/invalidation/public/invalidator_state.h"
#include "components/invalidation/public/object_id_invalidation_map.h"
#include "components/invalidation/public/topic_invalidation_map.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace invalidation {
namespace {
const char kApplicationName[] = "com.google.chrome.fcm.invalidations";
// Sender ID coming from the Firebase console.
const char kInvalidationGCMSenderId[] = "8181035976";
// Added in M76.
void MigratePrefs(PrefService* prefs, const std::string& sender_id) {
if (!prefs->HasPrefPath(prefs::kFCMInvalidationClientIDCacheDeprecated)) {
return;
}
DictionaryPrefUpdate update(prefs, prefs::kInvalidationClientIDCache);
update->SetString(
sender_id,
prefs->GetString(prefs::kFCMInvalidationClientIDCacheDeprecated));
prefs->ClearPref(prefs::kFCMInvalidationClientIDCacheDeprecated);
}
} // namespace
FCMInvalidationServiceBase::FCMInvalidationServiceBase(
FCMNetworkHandlerCallback fcm_network_handler_callback,
PerUserTopicRegistrationManagerCallback
per_user_topic_registration_manager_callback,
instance_id::InstanceIDDriver* instance_id_driver,
PrefService* pref_service,
const std::string& sender_id)
: sender_id_(sender_id.empty() ? kInvalidationGCMSenderId : sender_id),
invalidator_registrar_(pref_service,
sender_id_,
sender_id_ == kInvalidationGCMSenderId),
fcm_network_handler_callback_(std::move(fcm_network_handler_callback)),
per_user_topic_registration_manager_callback_(
std::move(per_user_topic_registration_manager_callback)),
instance_id_driver_(instance_id_driver),
pref_service_(pref_service),
update_was_requested_(false) {}
FCMInvalidationServiceBase::~FCMInvalidationServiceBase() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
invalidator_registrar_.UpdateInvalidatorState(
syncer::INVALIDATOR_SHUTTING_DOWN);
if (IsStarted())
StopInvalidator();
}
// static
void FCMInvalidationServiceBase::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(
invalidation::prefs::kFCMInvalidationClientIDCacheDeprecated,
/*default_value=*/std::string());
registry->RegisterDictionaryPref(
invalidation::prefs::kInvalidationClientIDCache);
}
void FCMInvalidationServiceBase::RegisterInvalidationHandler(
syncer::InvalidationHandler* handler) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Registering an invalidation handler";
invalidator_registrar_.RegisterHandler(handler);
// Populate the id for newly registered handlers.
handler->OnInvalidatorClientIdChange(client_id_);
logger_.OnRegistration(handler->GetOwnerName());
}
bool FCMInvalidationServiceBase::UpdateRegisteredInvalidationIds(
syncer::InvalidationHandler* handler,
const syncer::ObjectIdSet& ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
update_was_requested_ = true;
DVLOG(2) << "Registering ids: " << ids.size();
syncer::Topics topics = ConvertIdsToTopics(ids, handler);
if (!invalidator_registrar_.UpdateRegisteredTopics(handler, topics))
return false;
DoUpdateRegisteredIdsIfNeeded();
logger_.OnUpdateTopics(invalidator_registrar_.GetHandlerNameToTopicsMap());
return true;
}
void FCMInvalidationServiceBase::UnregisterInvalidationHandler(
syncer::InvalidationHandler* handler) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << "Unregistering";
invalidator_registrar_.UnregisterHandler(handler);
logger_.OnUnregistration(handler->GetOwnerName());
}
syncer::InvalidatorState FCMInvalidationServiceBase::GetInvalidatorState()
const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (invalidation_listener_) {
DVLOG(2) << "GetInvalidatorState returning "
<< invalidator_registrar_.GetInvalidatorState();
return invalidator_registrar_.GetInvalidatorState();
}
DVLOG(2) << "Invalidator currently stopped";
return syncer::STOPPED;
}
std::string FCMInvalidationServiceBase::GetInvalidatorClientId() const {
return client_id_;
}
InvalidationLogger* FCMInvalidationServiceBase::GetInvalidationLogger() {
return &logger_;
}
void FCMInvalidationServiceBase::RequestDetailedStatus(
base::RepeatingCallback<void(const base::DictionaryValue&)> return_callback)
const {
return_callback.Run(CollectDebugData());
invalidator_registrar_.RequestDetailedStatus(return_callback);
if (IsStarted()) {
invalidation_listener_->RequestDetailedStatus(return_callback);
}
}
void FCMInvalidationServiceBase::OnInvalidate(
const syncer::TopicInvalidationMap& invalidation_map) {
invalidator_registrar_.DispatchInvalidationsToHandlers(invalidation_map);
logger_.OnInvalidation(
ConvertTopicInvalidationMapToObjectIdInvalidationMap(invalidation_map));
}
void FCMInvalidationServiceBase::OnInvalidatorStateChange(
syncer::InvalidatorState state) {
ReportInvalidatorState(state);
invalidator_registrar_.UpdateInvalidatorState(state);
logger_.OnStateChange(state);
}
void FCMInvalidationServiceBase::InitForTest(
std::unique_ptr<syncer::FCMInvalidationListener> invalidation_listener) {
// Here we perform the equivalent of Init() and StartInvalidator(), but with
// some minor changes to account for the fact that we're injecting the
// invalidation_listener.
// StartInvalidator initializes the invalidation_listener and starts it.
invalidation_listener_ = std::move(invalidation_listener);
invalidation_listener_->StartForTest(this);
DoUpdateRegisteredIdsIfNeeded();
}
base::DictionaryValue FCMInvalidationServiceBase::CollectDebugData() const {
base::DictionaryValue status;
status.SetString(
"InvalidationService.IID-requested",
base::TimeFormatShortDateAndTime(diagnostic_info_.instance_id_requested));
status.SetString(
"InvalidationService.IID-received",
base::TimeFormatShortDateAndTime(diagnostic_info_.instance_id_received));
status.SetString(
"InvalidationService.Service-stopped",
base::TimeFormatShortDateAndTime(diagnostic_info_.service_was_stopped));
status.SetString(
"InvalidationService.Service-started",
base::TimeFormatShortDateAndTime(diagnostic_info_.service_was_started));
return status;
}
void FCMInvalidationServiceBase::ReportInvalidatorState(
syncer::InvalidatorState state) {
UMA_HISTOGRAM_ENUMERATION("Invalidations.StatusChanged", state);
}
bool FCMInvalidationServiceBase::IsStarted() const {
return invalidation_listener_ != nullptr;
}
void FCMInvalidationServiceBase::StartInvalidator() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!invalidation_listener_);
diagnostic_info_.service_was_started = base::Time::Now();
auto network =
fcm_network_handler_callback_.Run(sender_id_, GetApplicationName());
// The order of calls is important. Do not change.
// We should start listening before requesting the id, because
// valid id is only generated, once there is an app handler
// for the app. StartListening registers the app handler.
// We should create InvalidationListener first, because it registers the
// handler for the incoming messages, which is crucial on Android, because on
// the startup cached messages might exists.
invalidation_listener_ =
std::make_unique<syncer::FCMInvalidationListener>(std::move(network));
auto registration_manager = per_user_topic_registration_manager_callback_.Run(
sender_id_, /*migrate_prefs=*/sender_id_ == kInvalidationGCMSenderId);
invalidation_listener_->Start(this, std::move(registration_manager));
PopulateClientID();
DoUpdateRegisteredIdsIfNeeded();
}
void FCMInvalidationServiceBase::StopInvalidator() {
DCHECK(invalidation_listener_);
diagnostic_info_.service_was_stopped = base::Time::Now();
// TODO(melandory): reset the network.
invalidation_listener_.reset();
}
void FCMInvalidationServiceBase::StopInvalidatorPermanently() {
StopInvalidator();
if (!client_id_.empty())
ResetClientID();
}
void FCMInvalidationServiceBase::PopulateClientID() {
diagnostic_info_.instance_id_requested = base::Time::Now();
if (sender_id_ == kInvalidationGCMSenderId) {
MigratePrefs(pref_service_, sender_id_);
}
const std::string* client_id_pref =
pref_service_->GetDictionary(prefs::kInvalidationClientIDCache)
->FindStringKey(sender_id_);
client_id_ = client_id_pref ? *client_id_pref : "";
instance_id::InstanceID* instance_id =
instance_id_driver_->GetInstanceID(GetApplicationName());
instance_id->GetID(
base::Bind(&FCMInvalidationServiceBase::OnInstanceIdReceived,
base::Unretained(this)));
}
void FCMInvalidationServiceBase::ResetClientID() {
instance_id::InstanceID* instance_id =
instance_id_driver_->GetInstanceID(GetApplicationName());
instance_id->DeleteID(
base::Bind(&FCMInvalidationServiceBase::OnDeleteIDCompleted,
base::Unretained(this)));
DictionaryPrefUpdate update(pref_service_, prefs::kInvalidationClientIDCache);
update->RemoveKey(sender_id_);
}
void FCMInvalidationServiceBase::OnInstanceIdReceived(const std::string& id) {
diagnostic_info_.instance_id_received = base::Time::Now();
if (client_id_ != id) {
client_id_ = id;
DictionaryPrefUpdate update(pref_service_,
prefs::kInvalidationClientIDCache);
update->SetStringKey(sender_id_, id);
invalidator_registrar_.UpdateInvalidatorInstanceId(id);
}
}
void FCMInvalidationServiceBase::OnDeleteIDCompleted(
instance_id::InstanceID::Result) {
// TODO(melandory): report metric in case of unsuccessful deletion.
}
void FCMInvalidationServiceBase::DoUpdateRegisteredIdsIfNeeded() {
if (!invalidation_listener_ || !update_was_requested_)
return;
auto subscribed_topics = invalidator_registrar_.GetAllSubscribedTopics();
invalidation_listener_->UpdateRegisteredTopics(subscribed_topics);
update_was_requested_ = false;
}
const std::string FCMInvalidationServiceBase::GetApplicationName() {
// If using the default |sender_id_|, use the bare |kApplicationName|, so the
// old app name is maintained.
if (sender_id_ == kInvalidationGCMSenderId) {
return kApplicationName;
}
return base::StrCat({kApplicationName, "-", sender_id_});
}
} // namespace invalidation
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_INVALIDATION_IMPL_FCM_INVALIDATION_SERVICE_BASE_H_
#define COMPONENTS_INVALIDATION_IMPL_FCM_INVALIDATION_SERVICE_BASE_H_
#include "base/macros.h"
#include "base/time/time.h"
#include "components/gcm_driver/instance_id/instance_id.h"
#include "components/invalidation/impl/fcm_invalidation_listener.h"
#include "components/invalidation/impl/invalidation_listener.h"
#include "components/invalidation/impl/invalidation_logger.h"
#include "components/invalidation/impl/invalidator_registrar_with_memory.h"
#include "components/invalidation/public/invalidation_handler.h"
#include "components/invalidation/public/invalidation_service.h"
class PrefService;
class PrefRegistrySimple;
namespace instance_id {
class InstanceIDDriver;
}
namespace syncer {
class FCMNetworkHandler;
class PerUserTopicRegistrationManager;
} // namespace syncer
namespace invalidation {
using FCMNetworkHandlerCallback =
base::RepeatingCallback<std::unique_ptr<syncer::FCMNetworkHandler>(
const std::string& sender_id,
const std::string& app_id)>;
using PerUserTopicRegistrationManagerCallback =
base::RepeatingCallback<std::unique_ptr<
syncer::PerUserTopicRegistrationManager>(const std::string& project_id,
bool migrate_prefs)>;
// This InvalidationService wraps the C++ Invalidation Client (FCM) library.
// It provides invalidations for desktop platforms (Win, Mac, Linux).
// Subclasses should implement Init to set up their initial state and call
// StartInvalidator/StopInvalidator when they want to start/stop receiving
// invalidations.
class FCMInvalidationServiceBase
: public InvalidationService,
public syncer::FCMInvalidationListener::Delegate {
public:
FCMInvalidationServiceBase(
FCMNetworkHandlerCallback fcm_network_handler_callback,
PerUserTopicRegistrationManagerCallback
per_user_topic_registration_manager_callback,
instance_id::InstanceIDDriver* client_id_driver,
PrefService* pref_service,
const std::string& sender_id = {});
~FCMInvalidationServiceBase() override;
virtual void Init() = 0;
static void RegisterPrefs(PrefRegistrySimple* registry);
// InvalidationService implementation.
// It is an error to have registered handlers when the service is destroyed.
void RegisterInvalidationHandler(
syncer::InvalidationHandler* handler) override;
bool UpdateRegisteredInvalidationIds(syncer::InvalidationHandler* handler,
const syncer::ObjectIdSet& ids) override;
void UnregisterInvalidationHandler(
syncer::InvalidationHandler* handler) override;
syncer::InvalidatorState GetInvalidatorState() const override;
std::string GetInvalidatorClientId() const override;
InvalidationLogger* GetInvalidationLogger() override;
void RequestDetailedStatus(
base::RepeatingCallback<void(const base::DictionaryValue&)> caller)
const override;
// syncer::FCMInvalidationListener::Delegate implementation.
void OnInvalidate(
const syncer::TopicInvalidationMap& invalidation_map) override;
void OnInvalidatorStateChange(syncer::InvalidatorState state) override;
protected:
// Initializes with an injected invalidator.
void InitForTest(
std::unique_ptr<syncer::FCMInvalidationListener> invalidation_listener);
virtual base::DictionaryValue CollectDebugData() const;
void ReportInvalidatorState(syncer::InvalidatorState state);
// Returns true if the service is currently started and able to receive
// invalidations.
bool IsStarted() const;
// Subclasses should be calling StartInvalidator and StopInvalidator when it
// is appropriate for their use case. This class will call StopInvalidator
// when it's destroyed if it's still started at that point.
// Start the invalidation service to start receiving invalidations.
void StartInvalidator();
// Stop the invalidation service to stop receiving invalidations. It's
// appropriate to call this when the service is expected to be started again
// with the same client ID (such as during shutdown).
void StopInvalidator();
// Stops the invalidation service to stop receiving invalidations. This also
// resets the Instance ID driver's client ID and clears the client ID cache.
void StopInvalidatorPermanently();
private:
struct Diagnostics {
base::Time instance_id_requested;
base::Time instance_id_received;
base::Time service_was_stopped;
base::Time service_was_started;
};
void PopulateClientID();
void ResetClientID();
void OnInstanceIdReceived(const std::string& id);
void OnDeleteIDCompleted(instance_id::InstanceID::Result);
void DoUpdateRegisteredIdsIfNeeded();
const std::string GetApplicationName();
const std::string sender_id_;
syncer::InvalidatorRegistrarWithMemory invalidator_registrar_;
// The invalidation logger object we use to record state changes
// and invalidations.
InvalidationLogger logger_;
FCMNetworkHandlerCallback fcm_network_handler_callback_;
PerUserTopicRegistrationManagerCallback
per_user_topic_registration_manager_callback_;
instance_id::InstanceIDDriver* const instance_id_driver_;
std::string client_id_;
PrefService* const pref_service_;
bool update_was_requested_ = false;
Diagnostics diagnostic_info_;
// The invalidation listener.
std::unique_ptr<syncer::FCMInvalidationListener> invalidation_listener_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(FCMInvalidationServiceBase);
};
} // namespace invalidation
#endif // COMPONENTS_INVALIDATION_IMPL_FCM_INVALIDATION_SERVICE_BASE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment