blob: 403e179127f5a90fab05efe18e1361d51b2f4318 [file] [log] [blame]
// Copyright 2015 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 "chrome/browser/chromeos/policy/affiliated_invalidation_service_provider_impl.h"
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/task/post_task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part_chromeos.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/device_identity_provider.h"
#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
#include "chrome/browser/invalidation/deprecated_profile_invalidation_provider_factory.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/invalidation/impl/invalidation_state_tracker.h"
#include "components/invalidation/impl/invalidator_storage.h"
#include "components/invalidation/impl/profile_invalidation_provider.h"
#include "components/invalidation/impl/ticl_invalidation_service.h"
#include "components/invalidation/public/identity_provider.h"
#include "components/invalidation/public/invalidation_handler.h"
#include "components/invalidation/public/invalidation_service.h"
#include "components/invalidation/public/invalidator_state.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/user_manager/user.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/network_context.mojom.h"
namespace policy {
namespace {
// Runs on UI thread.
void RequestProxyResolvingSocketFactoryOnUIThread(
base::WeakPtr<invalidation::TiclInvalidationService> owner,
network::mojom::ProxyResolvingSocketFactoryRequest request) {
if (!owner)
return;
if (g_browser_process->system_network_context_manager()) {
g_browser_process->system_network_context_manager()
->GetContext()
->CreateProxyResolvingSocketFactory(std::move(request));
}
}
// Runs on IO thread.
void RequestProxyResolvingSocketFactory(
base::WeakPtr<invalidation::TiclInvalidationService> owner,
network::mojom::ProxyResolvingSocketFactoryRequest request) {
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&RequestProxyResolvingSocketFactoryOnUIThread, owner,
std::move(request)));
}
} // namespace
class AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver
: public syncer::InvalidationHandler {
public:
explicit InvalidationServiceObserver(
AffiliatedInvalidationServiceProviderImpl* parent,
invalidation::InvalidationService* invalidation_service);
~InvalidationServiceObserver() override;
invalidation::InvalidationService* GetInvalidationService();
bool IsServiceConnected() const;
// public syncer::InvalidationHandler:
void OnInvalidatorStateChange(syncer::InvalidatorState state) override;
void OnIncomingInvalidation(
const syncer::ObjectIdInvalidationMap& invalidation_map) override;
std::string GetOwnerName() const override;
private:
AffiliatedInvalidationServiceProviderImpl* parent_;
invalidation::InvalidationService* invalidation_service_;
bool is_service_connected_;
bool is_observer_ready_;
DISALLOW_COPY_AND_ASSIGN(InvalidationServiceObserver);
};
AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver::
InvalidationServiceObserver(
AffiliatedInvalidationServiceProviderImpl* parent,
invalidation::InvalidationService* invalidation_service)
: parent_(parent),
invalidation_service_(invalidation_service),
is_service_connected_(false),
is_observer_ready_(false) {
invalidation_service_->RegisterInvalidationHandler(this);
is_service_connected_ = invalidation_service->GetInvalidatorState() ==
syncer::INVALIDATIONS_ENABLED;
is_observer_ready_ = true;
}
AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver::
~InvalidationServiceObserver() {
is_observer_ready_ = false;
invalidation_service_->UnregisterInvalidationHandler(this);
}
invalidation::InvalidationService*
AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver::
GetInvalidationService() {
return invalidation_service_;
}
bool AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver::
IsServiceConnected() const {
return is_service_connected_;
}
void AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver::
OnInvalidatorStateChange(syncer::InvalidatorState state) {
if (!is_observer_ready_)
return;
const bool is_service_connected = (state == syncer::INVALIDATIONS_ENABLED);
if (is_service_connected == is_service_connected_)
return;
is_service_connected_ = is_service_connected;
if (is_service_connected_)
parent_->OnInvalidationServiceConnected(invalidation_service_);
else
parent_->OnInvalidationServiceDisconnected(invalidation_service_);
}
void AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver::
OnIncomingInvalidation(
const syncer::ObjectIdInvalidationMap& invalidation_map) {
}
std::string
AffiliatedInvalidationServiceProviderImpl::InvalidationServiceObserver::
GetOwnerName() const {
return "AffiliatedInvalidationService";
}
AffiliatedInvalidationServiceProviderImpl::
AffiliatedInvalidationServiceProviderImpl()
: invalidation_service_(nullptr),
consumer_count_(0),
is_shut_down_(false) {
// The AffiliatedInvalidationServiceProviderImpl should be created before any
// user Profiles.
DCHECK(g_browser_process->profile_manager()->GetLoadedProfiles().empty());
// Subscribe to notification about new user profiles becoming available.
registrar_.Add(this,
chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
content::NotificationService::AllSources());
}
AffiliatedInvalidationServiceProviderImpl::
~AffiliatedInvalidationServiceProviderImpl() {
// Verify that the provider was shut down first.
DCHECK(is_shut_down_);
}
void AffiliatedInvalidationServiceProviderImpl::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED, type);
DCHECK(!is_shut_down_);
Profile* profile = content::Details<Profile>(details).ptr();
invalidation::ProfileInvalidationProvider* invalidation_provider =
invalidation::DeprecatedProfileInvalidationProviderFactory::GetForProfile(
profile);
if (!invalidation_provider) {
// If the Profile does not support invalidation (e.g. guest, incognito),
// ignore it.
return;
}
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
if (!user || !user->IsAffiliated()) {
// If the Profile belongs to a user who is not affiliated on the device,
// ignore it.
return;
}
// Create a state observer for the user's invalidation service.
invalidation::InvalidationService* invalidation_service =
invalidation_provider->GetInvalidationService();
profile_invalidation_service_observers_.push_back(
std::make_unique<InvalidationServiceObserver>(this,
invalidation_service));
if (profile_invalidation_service_observers_.back()->IsServiceConnected()) {
// If the invalidation service is connected, check whether to switch to it.
OnInvalidationServiceConnected(invalidation_service);
}
}
void AffiliatedInvalidationServiceProviderImpl::RegisterConsumer(
Consumer* consumer) {
if (consumers_.HasObserver(consumer) || is_shut_down_)
return;
consumers_.AddObserver(consumer);
++consumer_count_;
if (invalidation_service_)
consumer->OnInvalidationServiceSet(invalidation_service_);
else if (consumer_count_ == 1)
FindConnectedInvalidationService();
}
void AffiliatedInvalidationServiceProviderImpl::UnregisterConsumer(
Consumer* consumer) {
if (!consumers_.HasObserver(consumer))
return;
consumers_.RemoveObserver(consumer);
--consumer_count_;
if (invalidation_service_ && consumer_count_ == 0) {
invalidation_service_ = nullptr;
DestroyDeviceInvalidationService();
}
}
void AffiliatedInvalidationServiceProviderImpl::Shutdown() {
is_shut_down_ = true;
registrar_.RemoveAll();
profile_invalidation_service_observers_.clear();
device_invalidation_service_observer_.reset();
if (invalidation_service_) {
invalidation_service_ = nullptr;
// Explicitly notify consumers that the invalidation service they were using
// is no longer available.
SetInvalidationService(nullptr);
}
DestroyDeviceInvalidationService();
}
invalidation::TiclInvalidationService*
AffiliatedInvalidationServiceProviderImpl::
GetDeviceInvalidationServiceForTest() const {
return device_invalidation_service_.get();
}
void AffiliatedInvalidationServiceProviderImpl::OnInvalidationServiceConnected(
invalidation::InvalidationService* invalidation_service) {
DCHECK(!is_shut_down_);
if (consumer_count_ == 0) {
// If there are no consumers, no invalidation service is required.
return;
}
if (!device_invalidation_service_) {
// The lack of a device-global invalidation service implies that another
// connected invalidation service is being made available to consumers
// already. There is no need to switch from that to the service which just
// connected.
return;
}
// Make the invalidation service that just connected available to consumers.
invalidation_service_ = nullptr;
SetInvalidationService(invalidation_service);
if (invalidation_service_ &&
device_invalidation_service_ &&
invalidation_service_ != device_invalidation_service_.get()) {
// If a different invalidation service is being made available to consumers
// now, destroy the device-global one.
DestroyDeviceInvalidationService();
}
}
void
AffiliatedInvalidationServiceProviderImpl::OnInvalidationServiceDisconnected(
invalidation::InvalidationService* invalidation_service) {
DCHECK(!is_shut_down_);
if (invalidation_service != invalidation_service_) {
// If the invalidation service which disconnected was not being made
// available to consumers, return.
return;
}
// The invalidation service which disconnected was being made available to
// consumers. Stop making it available.
DCHECK(consumer_count_);
invalidation_service_ = nullptr;
// Try to make another invalidation service available to consumers.
FindConnectedInvalidationService();
// If no other connected invalidation service was found, explicitly notify
// consumers that the invalidation service they were using is no longer
// available.
if (!invalidation_service_)
SetInvalidationService(nullptr);
}
void
AffiliatedInvalidationServiceProviderImpl::FindConnectedInvalidationService() {
DCHECK(!invalidation_service_);
DCHECK(consumer_count_);
DCHECK(!is_shut_down_);
for (const auto& observer : profile_invalidation_service_observers_) {
if (observer->IsServiceConnected()) {
// If a connected invalidation service belonging to an affiliated
// logged-in user is found, make it available to consumers.
DestroyDeviceInvalidationService();
SetInvalidationService(observer->GetInvalidationService());
return;
}
}
if (!device_invalidation_service_) {
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
if (g_browser_process->system_network_context_manager()) {
// system_network_context_manager() can be null during unit tests.
url_loader_factory = g_browser_process->system_network_context_manager()
->GetSharedURLLoaderFactory();
}
identity_provider_ = std::make_unique<chromeos::DeviceIdentityProvider>(
chromeos::DeviceOAuth2TokenServiceFactory::Get());
DCHECK(identity_provider_);
// If no other connected invalidation service was found and no device-global
// invalidation service exists, create one.
device_invalidation_service_ =
std::make_unique<invalidation::TiclInvalidationService>(
GetUserAgent(), identity_provider_.get(),
g_browser_process->gcm_driver(),
base::BindRepeating(&RequestProxyResolvingSocketFactory),
base::CreateSingleThreadTaskRunnerWithTraits(
{content::BrowserThread::IO}),
std::move(url_loader_factory),
content::GetNetworkConnectionTracker());
device_invalidation_service_->Init(
std::unique_ptr<syncer::InvalidationStateTracker>(
new invalidation::InvalidatorStorage(
g_browser_process->local_state())));
device_invalidation_service_observer_.reset(
new InvalidationServiceObserver(
this,
device_invalidation_service_.get()));
}
if (device_invalidation_service_observer_->IsServiceConnected()) {
// If the device-global invalidation service is connected already, make it
// available to consumers immediately. Otherwise, the invalidation service
// will be made available to clients when it successfully connects.
OnInvalidationServiceConnected(device_invalidation_service_.get());
}
}
void AffiliatedInvalidationServiceProviderImpl::SetInvalidationService(
invalidation::InvalidationService* invalidation_service) {
DCHECK(!invalidation_service_);
invalidation_service_ = invalidation_service;
for (auto& observer : consumers_)
observer.OnInvalidationServiceSet(invalidation_service_);
}
void
AffiliatedInvalidationServiceProviderImpl::DestroyDeviceInvalidationService() {
device_invalidation_service_observer_.reset();
device_invalidation_service_.reset();
identity_provider_.reset();
}
} // namespace policy